@vm0/cli 7.1.1 → 8.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +1168 -1644
- 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
|
|
5
|
-
import
|
|
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((
|
|
82
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
83
83
|
}
|
|
84
84
|
async function authenticate(apiUrl) {
|
|
85
85
|
const targetApiUrl = apiUrl ?? await getApiUrl();
|
|
@@ -2950,10 +2950,6 @@ var FEATURE_SWITCHES = {
|
|
|
2950
2950
|
maintainer: "ethan@vm0.ai",
|
|
2951
2951
|
enabled: true
|
|
2952
2952
|
},
|
|
2953
|
-
["platformOnboarding" /* PlatformOnboarding */]: {
|
|
2954
|
-
maintainer: "ethan@vm0.ai",
|
|
2955
|
-
enabled: false
|
|
2956
|
-
},
|
|
2957
2953
|
["platformAgents" /* PlatformAgents */]: {
|
|
2958
2954
|
maintainer: "ethan@vm0.ai",
|
|
2959
2955
|
enabled: false
|
|
@@ -3024,10 +3020,10 @@ async function getRawHeaders() {
|
|
|
3024
3020
|
}
|
|
3025
3021
|
return headers;
|
|
3026
3022
|
}
|
|
3027
|
-
async function httpGet(
|
|
3023
|
+
async function httpGet(path14) {
|
|
3028
3024
|
const baseUrl = await getBaseUrl();
|
|
3029
3025
|
const headers = await getRawHeaders();
|
|
3030
|
-
return fetch(`${baseUrl}${
|
|
3026
|
+
return fetch(`${baseUrl}${path14}`, {
|
|
3031
3027
|
method: "GET",
|
|
3032
3028
|
headers
|
|
3033
3029
|
});
|
|
@@ -3588,49 +3584,49 @@ var cliComposeSchema = z23.object({
|
|
|
3588
3584
|
function formatZodError(error) {
|
|
3589
3585
|
const issue = error.issues[0];
|
|
3590
3586
|
if (!issue) return "Validation failed";
|
|
3591
|
-
const
|
|
3587
|
+
const path14 = issue.path.join(".");
|
|
3592
3588
|
const message = issue.message;
|
|
3593
|
-
if (!
|
|
3589
|
+
if (!path14) return message;
|
|
3594
3590
|
if (issue.code === "invalid_type") {
|
|
3595
3591
|
const received = issue.received;
|
|
3596
3592
|
const isMissing = received === "undefined" || message.includes("received undefined") || message === "Required";
|
|
3597
|
-
if (
|
|
3593
|
+
if (path14 === "version" && isMissing) {
|
|
3598
3594
|
return "Missing config.version";
|
|
3599
3595
|
}
|
|
3600
|
-
if (
|
|
3596
|
+
if (path14 === "agents" && isMissing) {
|
|
3601
3597
|
return "Missing agents object in config";
|
|
3602
3598
|
}
|
|
3603
|
-
if (
|
|
3604
|
-
const volumeKey =
|
|
3599
|
+
if (path14.startsWith("volumes.") && path14.endsWith(".name")) {
|
|
3600
|
+
const volumeKey = path14.split(".")[1];
|
|
3605
3601
|
return `Volume "${volumeKey}" must have a 'name' field (string)`;
|
|
3606
3602
|
}
|
|
3607
|
-
if (
|
|
3608
|
-
const volumeKey =
|
|
3603
|
+
if (path14.startsWith("volumes.") && path14.endsWith(".version")) {
|
|
3604
|
+
const volumeKey = path14.split(".")[1];
|
|
3609
3605
|
return `Volume "${volumeKey}" must have a 'version' field (string)`;
|
|
3610
3606
|
}
|
|
3611
3607
|
if (issue.expected === "array") {
|
|
3612
|
-
const fieldName =
|
|
3608
|
+
const fieldName = path14.replace(/^agents\.[^.]+\./, "agent.");
|
|
3613
3609
|
return `${fieldName} must be an array`;
|
|
3614
3610
|
}
|
|
3615
3611
|
if (issue.expected === "string" && received === "number") {
|
|
3616
|
-
const fieldName =
|
|
3612
|
+
const fieldName = path14.replace(/^agents\.[^.]+\./, "agent.");
|
|
3617
3613
|
const match = fieldName.match(/^(agent\.[^.]+)\.\d+$/);
|
|
3618
3614
|
if (match) {
|
|
3619
3615
|
return `Each entry in ${match[1]?.replace("agent.", "")} must be a string`;
|
|
3620
3616
|
}
|
|
3621
3617
|
}
|
|
3622
3618
|
}
|
|
3623
|
-
if (issue.code === "invalid_key" &&
|
|
3619
|
+
if (issue.code === "invalid_key" && path14.startsWith("agents.")) {
|
|
3624
3620
|
return "Invalid agent name format. Must be 3-64 characters, letters, numbers, and hyphens only. Must start and end with letter or number.";
|
|
3625
3621
|
}
|
|
3626
|
-
if (message === "Invalid key in record" &&
|
|
3622
|
+
if (message === "Invalid key in record" && path14.startsWith("agents.")) {
|
|
3627
3623
|
return "Invalid agent name format. Must be 3-64 characters, letters, numbers, and hyphens only. Must start and end with letter or number.";
|
|
3628
3624
|
}
|
|
3629
3625
|
if (issue.code === "custom") {
|
|
3630
3626
|
return message;
|
|
3631
3627
|
}
|
|
3632
|
-
if (
|
|
3633
|
-
const cleanPath =
|
|
3628
|
+
if (path14.startsWith("agents.")) {
|
|
3629
|
+
const cleanPath = path14.replace(/^agents\.[^.]+\./, "agent.");
|
|
3634
3630
|
if (message.startsWith("Invalid input:")) {
|
|
3635
3631
|
const match = message.match(/expected (\w+), received (\w+)/);
|
|
3636
3632
|
if (match && match[1] === "string" && match[2] === "number") {
|
|
@@ -3642,7 +3638,7 @@ function formatZodError(error) {
|
|
|
3642
3638
|
}
|
|
3643
3639
|
return `${cleanPath}: ${message}`;
|
|
3644
3640
|
}
|
|
3645
|
-
return `${
|
|
3641
|
+
return `${path14}: ${message}`;
|
|
3646
3642
|
}
|
|
3647
3643
|
function validateAgentName(name) {
|
|
3648
3644
|
return cliAgentNameSchema.safeParse(name).success;
|
|
@@ -3815,7 +3811,7 @@ function excludeVm0Filter(filePath) {
|
|
|
3815
3811
|
return !shouldExclude;
|
|
3816
3812
|
}
|
|
3817
3813
|
function listTarFiles(tarPath) {
|
|
3818
|
-
return new Promise((
|
|
3814
|
+
return new Promise((resolve, reject) => {
|
|
3819
3815
|
const files = [];
|
|
3820
3816
|
tar.list({
|
|
3821
3817
|
file: tarPath,
|
|
@@ -3824,7 +3820,7 @@ function listTarFiles(tarPath) {
|
|
|
3824
3820
|
files.push(entry.path);
|
|
3825
3821
|
}
|
|
3826
3822
|
}
|
|
3827
|
-
}).then(() =>
|
|
3823
|
+
}).then(() => resolve(files)).catch(reject);
|
|
3828
3824
|
});
|
|
3829
3825
|
}
|
|
3830
3826
|
async function listLocalFiles(dir, excludeDirs = [".vm0"]) {
|
|
@@ -3886,11 +3882,11 @@ async function removeEmptyDirs(dir, excludeDirs = [".vm0"]) {
|
|
|
3886
3882
|
|
|
3887
3883
|
// src/lib/storage/direct-upload.ts
|
|
3888
3884
|
async function hashFileStream(filePath) {
|
|
3889
|
-
return new Promise((
|
|
3885
|
+
return new Promise((resolve, reject) => {
|
|
3890
3886
|
const hash = createHash("sha256");
|
|
3891
3887
|
const stream = fs3.createReadStream(filePath);
|
|
3892
3888
|
stream.on("data", (chunk) => hash.update(chunk));
|
|
3893
|
-
stream.on("end", () =>
|
|
3889
|
+
stream.on("end", () => resolve(hash.digest("hex")));
|
|
3894
3890
|
stream.on("error", reject);
|
|
3895
3891
|
});
|
|
3896
3892
|
}
|
|
@@ -3975,7 +3971,7 @@ function createManifest(files) {
|
|
|
3975
3971
|
return Buffer.from(JSON.stringify(manifest, null, 2));
|
|
3976
3972
|
}
|
|
3977
3973
|
function sleep(ms) {
|
|
3978
|
-
return new Promise((
|
|
3974
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3979
3975
|
}
|
|
3980
3976
|
async function uploadToPresignedUrl(presignedUrl, data, contentType, maxRetries = 3) {
|
|
3981
3977
|
let lastError = null;
|
|
@@ -4549,7 +4545,6 @@ var EventRenderer = class {
|
|
|
4549
4545
|
// src/commands/run/shared.ts
|
|
4550
4546
|
import chalk5 from "chalk";
|
|
4551
4547
|
import * as fs5 from "fs";
|
|
4552
|
-
import * as path5 from "path";
|
|
4553
4548
|
import { config as dotenvConfig } from "dotenv";
|
|
4554
4549
|
|
|
4555
4550
|
// src/lib/events/claude-event-parser.ts
|
|
@@ -5783,7 +5778,7 @@ var ApiClient = class {
|
|
|
5783
5778
|
/**
|
|
5784
5779
|
* Generic GET request
|
|
5785
5780
|
*/
|
|
5786
|
-
async get(
|
|
5781
|
+
async get(path14) {
|
|
5787
5782
|
const baseUrl = await this.getBaseUrl();
|
|
5788
5783
|
const token = await getToken();
|
|
5789
5784
|
if (!token) {
|
|
@@ -5796,7 +5791,7 @@ var ApiClient = class {
|
|
|
5796
5791
|
if (bypassSecret) {
|
|
5797
5792
|
headers["x-vercel-protection-bypass"] = bypassSecret;
|
|
5798
5793
|
}
|
|
5799
|
-
return fetch(`${baseUrl}${
|
|
5794
|
+
return fetch(`${baseUrl}${path14}`, {
|
|
5800
5795
|
method: "GET",
|
|
5801
5796
|
headers
|
|
5802
5797
|
});
|
|
@@ -5804,7 +5799,7 @@ var ApiClient = class {
|
|
|
5804
5799
|
/**
|
|
5805
5800
|
* Generic POST request
|
|
5806
5801
|
*/
|
|
5807
|
-
async post(
|
|
5802
|
+
async post(path14, options) {
|
|
5808
5803
|
const baseUrl = await this.getBaseUrl();
|
|
5809
5804
|
const token = await getToken();
|
|
5810
5805
|
if (!token) {
|
|
@@ -5820,7 +5815,7 @@ var ApiClient = class {
|
|
|
5820
5815
|
if (bypassSecret) {
|
|
5821
5816
|
headers["x-vercel-protection-bypass"] = bypassSecret;
|
|
5822
5817
|
}
|
|
5823
|
-
return fetch(`${baseUrl}${
|
|
5818
|
+
return fetch(`${baseUrl}${path14}`, {
|
|
5824
5819
|
method: "POST",
|
|
5825
5820
|
headers,
|
|
5826
5821
|
body: options?.body
|
|
@@ -5829,7 +5824,7 @@ var ApiClient = class {
|
|
|
5829
5824
|
/**
|
|
5830
5825
|
* Generic DELETE request
|
|
5831
5826
|
*/
|
|
5832
|
-
async delete(
|
|
5827
|
+
async delete(path14) {
|
|
5833
5828
|
const baseUrl = await this.getBaseUrl();
|
|
5834
5829
|
const token = await getToken();
|
|
5835
5830
|
if (!token) {
|
|
@@ -5842,7 +5837,7 @@ var ApiClient = class {
|
|
|
5842
5837
|
if (bypassSecret) {
|
|
5843
5838
|
headers["x-vercel-protection-bypass"] = bypassSecret;
|
|
5844
5839
|
}
|
|
5845
|
-
return fetch(`${baseUrl}${
|
|
5840
|
+
return fetch(`${baseUrl}${path14}`, {
|
|
5846
5841
|
method: "DELETE",
|
|
5847
5842
|
headers
|
|
5848
5843
|
});
|
|
@@ -5887,7 +5882,7 @@ async function streamEvents(runId, options) {
|
|
|
5887
5882
|
const ablyClient = createRealtimeClient(async () => {
|
|
5888
5883
|
return apiClient.getRealtimeToken(runId);
|
|
5889
5884
|
});
|
|
5890
|
-
return new Promise((
|
|
5885
|
+
return new Promise((resolve, reject) => {
|
|
5891
5886
|
const channelName = getRunChannelName(runId);
|
|
5892
5887
|
const channel = ablyClient.channels.get(channelName, {
|
|
5893
5888
|
params: { rewind: "2m" }
|
|
@@ -5948,7 +5943,7 @@ async function streamEvents(runId, options) {
|
|
|
5948
5943
|
result = { succeeded: false, runId };
|
|
5949
5944
|
}
|
|
5950
5945
|
cleanup();
|
|
5951
|
-
|
|
5946
|
+
resolve(result);
|
|
5952
5947
|
}
|
|
5953
5948
|
}
|
|
5954
5949
|
channel.subscribe(handleMessage).catch((err) => {
|
|
@@ -5992,30 +5987,32 @@ function extractSecretNames(composeContent) {
|
|
|
5992
5987
|
const grouped = groupVariablesBySource(refs);
|
|
5993
5988
|
return grouped.secrets.map((r) => r.name);
|
|
5994
5989
|
}
|
|
5995
|
-
function loadValues(cliValues, configNames) {
|
|
5990
|
+
function loadValues(cliValues, configNames, envFilePath) {
|
|
5996
5991
|
const result = { ...cliValues };
|
|
5997
5992
|
const missingNames = configNames.filter((name) => !(name in result));
|
|
5998
5993
|
if (missingNames.length > 0) {
|
|
5999
|
-
const
|
|
6000
|
-
|
|
6001
|
-
|
|
5994
|
+
const envValues = {};
|
|
5995
|
+
for (const name of missingNames) {
|
|
5996
|
+
const envValue = process.env[name];
|
|
5997
|
+
if (envValue !== void 0) {
|
|
5998
|
+
envValues[name] = envValue;
|
|
5999
|
+
}
|
|
6000
|
+
}
|
|
6001
|
+
let fileValues = {};
|
|
6002
|
+
if (envFilePath) {
|
|
6003
|
+
if (!fs5.existsSync(envFilePath)) {
|
|
6004
|
+
throw new Error(`Environment file not found: ${envFilePath}`);
|
|
6005
|
+
}
|
|
6002
6006
|
const dotenvResult = dotenvConfig({ path: envFilePath, quiet: true });
|
|
6003
6007
|
if (dotenvResult.parsed) {
|
|
6004
|
-
|
|
6008
|
+
fileValues = Object.fromEntries(
|
|
6005
6009
|
Object.entries(dotenvResult.parsed).filter(
|
|
6006
6010
|
([key]) => missingNames.includes(key)
|
|
6007
6011
|
)
|
|
6008
6012
|
);
|
|
6009
6013
|
}
|
|
6010
6014
|
}
|
|
6011
|
-
|
|
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);
|
|
6015
|
+
Object.assign(result, envValues, fileValues);
|
|
6019
6016
|
}
|
|
6020
6017
|
return Object.keys(result).length > 0 ? result : void 0;
|
|
6021
6018
|
}
|
|
@@ -6130,7 +6127,7 @@ async function pollEvents(runId, options) {
|
|
|
6130
6127
|
result = { succeeded: false, runId };
|
|
6131
6128
|
}
|
|
6132
6129
|
if (!complete) {
|
|
6133
|
-
await new Promise((
|
|
6130
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
6134
6131
|
}
|
|
6135
6132
|
}
|
|
6136
6133
|
return result;
|
|
@@ -6173,13 +6170,16 @@ var mainRunCommand = new Command2().name("run").description("Run an agent").argu
|
|
|
6173
6170
|
"<agent-name>",
|
|
6174
6171
|
"Agent reference: [scope/]name[:version] (e.g., 'my-agent', 'lancy/my-agent:abc123', 'my-agent:latest')"
|
|
6175
6172
|
).argument("<prompt>", "Prompt for the agent").option(
|
|
6173
|
+
"--env-file <path>",
|
|
6174
|
+
"Load environment variables from file (priority: CLI flags > file > env vars)"
|
|
6175
|
+
).option(
|
|
6176
6176
|
"--vars <KEY=value>",
|
|
6177
|
-
"Variables for ${{ vars.xxx }} (repeatable, falls back to env
|
|
6177
|
+
"Variables for ${{ vars.xxx }} (repeatable, falls back to --env-file or env vars)",
|
|
6178
6178
|
collectKeyValue,
|
|
6179
6179
|
{}
|
|
6180
6180
|
).option(
|
|
6181
6181
|
"--secrets <KEY=value>",
|
|
6182
|
-
"Secrets for ${{ secrets.xxx }} (repeatable, falls back to env
|
|
6182
|
+
"Secrets for ${{ secrets.xxx }} (repeatable, falls back to --env-file or env vars)",
|
|
6183
6183
|
collectKeyValue,
|
|
6184
6184
|
{}
|
|
6185
6185
|
).option("--artifact-name <name>", "Artifact storage name (required for run)").option(
|
|
@@ -6256,9 +6256,13 @@ var mainRunCommand = new Command2().name("run").description("Run an agent").argu
|
|
|
6256
6256
|
}
|
|
6257
6257
|
}
|
|
6258
6258
|
const varNames = extractVarNames(composeContent);
|
|
6259
|
-
const vars = loadValues(options.vars, varNames);
|
|
6259
|
+
const vars = loadValues(options.vars, varNames, options.envFile);
|
|
6260
6260
|
const secretNames = extractSecretNames(composeContent);
|
|
6261
|
-
const secrets = loadValues(
|
|
6261
|
+
const secrets = loadValues(
|
|
6262
|
+
options.secrets,
|
|
6263
|
+
secretNames,
|
|
6264
|
+
options.envFile
|
|
6265
|
+
);
|
|
6262
6266
|
if (verbose && varNames.length > 0) {
|
|
6263
6267
|
console.log(chalk6.dim(` Required vars: ${varNames.join(", ")}`));
|
|
6264
6268
|
if (vars) {
|
|
@@ -6350,6 +6354,8 @@ var mainRunCommand = new Command2().name("run").description("Run an agent").argu
|
|
|
6350
6354
|
console.error(
|
|
6351
6355
|
chalk6.dim(" Make sure the version hash is correct.")
|
|
6352
6356
|
);
|
|
6357
|
+
} else if (error.message.startsWith("Environment file not found:")) {
|
|
6358
|
+
console.error(chalk6.red(`\u2717 ${error.message}`));
|
|
6353
6359
|
} else if (error.message.includes("not found")) {
|
|
6354
6360
|
console.error(chalk6.red(`\u2717 Agent not found: ${identifier}`));
|
|
6355
6361
|
console.error(
|
|
@@ -6373,13 +6379,16 @@ var mainRunCommand = new Command2().name("run").description("Run an agent").argu
|
|
|
6373
6379
|
import { Command as Command3, Option as Option2 } from "commander";
|
|
6374
6380
|
import chalk7 from "chalk";
|
|
6375
6381
|
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(
|
|
6382
|
+
"--env-file <path>",
|
|
6383
|
+
"Load environment variables from file (priority: CLI flags > file > env vars)"
|
|
6384
|
+
).option(
|
|
6376
6385
|
"--vars <KEY=value>",
|
|
6377
|
-
"Variables for ${{ vars.xxx }} (repeatable, falls back to env
|
|
6386
|
+
"Variables for ${{ vars.xxx }} (repeatable, falls back to --env-file or env vars)",
|
|
6378
6387
|
collectKeyValue,
|
|
6379
6388
|
{}
|
|
6380
6389
|
).option(
|
|
6381
6390
|
"--secrets <KEY=value>",
|
|
6382
|
-
"Secrets for ${{ secrets.xxx }} (repeatable,
|
|
6391
|
+
"Secrets for ${{ secrets.xxx }} (repeatable, falls back to --env-file or env vars)",
|
|
6383
6392
|
collectKeyValue,
|
|
6384
6393
|
{}
|
|
6385
6394
|
).option(
|
|
@@ -6411,7 +6420,8 @@ var resumeCommand = new Command3().name("resume").description("Resume an agent r
|
|
|
6411
6420
|
}
|
|
6412
6421
|
const checkpointInfo = await getCheckpoint(checkpointId);
|
|
6413
6422
|
const requiredSecretNames = checkpointInfo.agentComposeSnapshot.secretNames || [];
|
|
6414
|
-
const
|
|
6423
|
+
const envFile = options.envFile || allOpts.envFile;
|
|
6424
|
+
const loadedSecrets = loadValues(secrets, requiredSecretNames, envFile);
|
|
6415
6425
|
if (verbose) {
|
|
6416
6426
|
logVerbosePreFlight("Resuming agent run from checkpoint", [
|
|
6417
6427
|
{ label: "Checkpoint ID", value: checkpointId },
|
|
@@ -6474,6 +6484,8 @@ var resumeCommand = new Command3().name("resume").description("Resume an agent r
|
|
|
6474
6484
|
console.error(
|
|
6475
6485
|
chalk7.dim(" Try running without --experimental-realtime")
|
|
6476
6486
|
);
|
|
6487
|
+
} else if (error.message.startsWith("Environment file not found:")) {
|
|
6488
|
+
console.error(chalk7.red(`\u2717 ${error.message}`));
|
|
6477
6489
|
} else if (error.message.includes("not found")) {
|
|
6478
6490
|
console.error(chalk7.red(`\u2717 Checkpoint not found: ${checkpointId}`));
|
|
6479
6491
|
} else {
|
|
@@ -6494,13 +6506,16 @@ import chalk8 from "chalk";
|
|
|
6494
6506
|
var continueCommand = new Command4().name("continue").description(
|
|
6495
6507
|
"Continue an agent run from a session (uses latest artifact version)"
|
|
6496
6508
|
).argument("<agentSessionId>", "Agent session ID to continue from").argument("<prompt>", "Prompt for the continued agent").option(
|
|
6509
|
+
"--env-file <path>",
|
|
6510
|
+
"Load environment variables from file (priority: CLI flags > file > env vars)"
|
|
6511
|
+
).option(
|
|
6497
6512
|
"--vars <KEY=value>",
|
|
6498
|
-
"Variables for ${{ vars.xxx }} (repeatable, falls back to env
|
|
6513
|
+
"Variables for ${{ vars.xxx }} (repeatable, falls back to --env-file or env vars)",
|
|
6499
6514
|
collectKeyValue,
|
|
6500
6515
|
{}
|
|
6501
6516
|
).option(
|
|
6502
6517
|
"--secrets <KEY=value>",
|
|
6503
|
-
"Secrets for ${{ secrets.xxx }} (repeatable,
|
|
6518
|
+
"Secrets for ${{ secrets.xxx }} (repeatable, falls back to --env-file or env vars)",
|
|
6504
6519
|
collectKeyValue,
|
|
6505
6520
|
{}
|
|
6506
6521
|
).option(
|
|
@@ -6532,7 +6547,8 @@ var continueCommand = new Command4().name("continue").description(
|
|
|
6532
6547
|
}
|
|
6533
6548
|
const sessionInfo = await getSession(agentSessionId);
|
|
6534
6549
|
const requiredSecretNames = sessionInfo.secretNames || [];
|
|
6535
|
-
const
|
|
6550
|
+
const envFile = options.envFile || allOpts.envFile;
|
|
6551
|
+
const loadedSecrets = loadValues(secrets, requiredSecretNames, envFile);
|
|
6536
6552
|
if (verbose) {
|
|
6537
6553
|
logVerbosePreFlight("Continuing agent run from session", [
|
|
6538
6554
|
{ label: "Session ID", value: agentSessionId },
|
|
@@ -6596,6 +6612,8 @@ var continueCommand = new Command4().name("continue").description(
|
|
|
6596
6612
|
console.error(
|
|
6597
6613
|
chalk8.dim(" Try running without --experimental-realtime")
|
|
6598
6614
|
);
|
|
6615
|
+
} else if (error.message.startsWith("Environment file not found:")) {
|
|
6616
|
+
console.error(chalk8.red(`\u2717 ${error.message}`));
|
|
6599
6617
|
} else if (error.message.includes("not found")) {
|
|
6600
6618
|
console.error(
|
|
6601
6619
|
chalk8.red(`\u2717 Agent session not found: ${agentSessionId}`)
|
|
@@ -6623,13 +6641,13 @@ import { Command as Command11 } from "commander";
|
|
|
6623
6641
|
// src/commands/volume/init.ts
|
|
6624
6642
|
import { Command as Command5 } from "commander";
|
|
6625
6643
|
import chalk9 from "chalk";
|
|
6626
|
-
import
|
|
6644
|
+
import path6 from "path";
|
|
6627
6645
|
|
|
6628
6646
|
// src/lib/storage/storage-utils.ts
|
|
6629
6647
|
import { readFile as readFile5, writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
|
|
6630
6648
|
import { existsSync as existsSync5 } from "fs";
|
|
6631
6649
|
import { parse as parseYaml3, stringify as stringifyYaml } from "yaml";
|
|
6632
|
-
import
|
|
6650
|
+
import path5 from "path";
|
|
6633
6651
|
var CONFIG_DIR2 = ".vm0";
|
|
6634
6652
|
var CONFIG_FILE2 = "storage.yaml";
|
|
6635
6653
|
function isValidStorageName(name) {
|
|
@@ -6640,8 +6658,8 @@ function isValidStorageName(name) {
|
|
|
6640
6658
|
return pattern.test(name) && !name.includes("--");
|
|
6641
6659
|
}
|
|
6642
6660
|
async function readStorageConfig(basePath = process.cwd()) {
|
|
6643
|
-
const configPath =
|
|
6644
|
-
const legacyConfigPath =
|
|
6661
|
+
const configPath = path5.join(basePath, CONFIG_DIR2, CONFIG_FILE2);
|
|
6662
|
+
const legacyConfigPath = path5.join(basePath, CONFIG_DIR2, "volume.yaml");
|
|
6645
6663
|
let actualPath = null;
|
|
6646
6664
|
if (existsSync5(configPath)) {
|
|
6647
6665
|
actualPath = configPath;
|
|
@@ -6659,8 +6677,8 @@ async function readStorageConfig(basePath = process.cwd()) {
|
|
|
6659
6677
|
return config;
|
|
6660
6678
|
}
|
|
6661
6679
|
async function writeStorageConfig(storageName, basePath = process.cwd(), type = "volume") {
|
|
6662
|
-
const configDir =
|
|
6663
|
-
const configPath =
|
|
6680
|
+
const configDir = path5.join(basePath, CONFIG_DIR2);
|
|
6681
|
+
const configPath = path5.join(configDir, CONFIG_FILE2);
|
|
6664
6682
|
if (!existsSync5(configDir)) {
|
|
6665
6683
|
await mkdir4(configDir, { recursive: true });
|
|
6666
6684
|
}
|
|
@@ -6736,19 +6754,37 @@ async function promptSelect(message, choices, initial) {
|
|
|
6736
6754
|
);
|
|
6737
6755
|
return response.value;
|
|
6738
6756
|
}
|
|
6757
|
+
async function promptPassword(message) {
|
|
6758
|
+
if (!isInteractive()) {
|
|
6759
|
+
return void 0;
|
|
6760
|
+
}
|
|
6761
|
+
const response = await prompts2(
|
|
6762
|
+
{
|
|
6763
|
+
type: "password",
|
|
6764
|
+
name: "value",
|
|
6765
|
+
message
|
|
6766
|
+
},
|
|
6767
|
+
{
|
|
6768
|
+
onCancel: () => {
|
|
6769
|
+
return false;
|
|
6770
|
+
}
|
|
6771
|
+
}
|
|
6772
|
+
);
|
|
6773
|
+
return response.value;
|
|
6774
|
+
}
|
|
6739
6775
|
|
|
6740
6776
|
// src/commands/volume/init.ts
|
|
6741
6777
|
var initCommand = new Command5().name("init").description("Initialize a volume in the current directory").option("-n, --name <name>", "Volume name (required in non-interactive mode)").action(async (options) => {
|
|
6742
6778
|
try {
|
|
6743
6779
|
const cwd = process.cwd();
|
|
6744
|
-
const dirName =
|
|
6780
|
+
const dirName = path6.basename(cwd);
|
|
6745
6781
|
const existingConfig = await readStorageConfig(cwd);
|
|
6746
6782
|
if (existingConfig) {
|
|
6747
6783
|
console.log(
|
|
6748
6784
|
chalk9.yellow(`Volume already initialized: ${existingConfig.name}`)
|
|
6749
6785
|
);
|
|
6750
6786
|
console.log(
|
|
6751
|
-
chalk9.dim(`Config file: ${
|
|
6787
|
+
chalk9.dim(`Config file: ${path6.join(cwd, ".vm0", "storage.yaml")}`)
|
|
6752
6788
|
);
|
|
6753
6789
|
return;
|
|
6754
6790
|
}
|
|
@@ -6797,7 +6833,7 @@ var initCommand = new Command5().name("init").description("Initialize a volume i
|
|
|
6797
6833
|
console.log(chalk9.green(`\u2713 Initialized volume: ${volumeName}`));
|
|
6798
6834
|
console.log(
|
|
6799
6835
|
chalk9.dim(
|
|
6800
|
-
`\u2713 Config saved to ${
|
|
6836
|
+
`\u2713 Config saved to ${path6.join(cwd, ".vm0", "storage.yaml")}`
|
|
6801
6837
|
)
|
|
6802
6838
|
);
|
|
6803
6839
|
} catch (error) {
|
|
@@ -6865,7 +6901,7 @@ var pushCommand = new Command6().name("push").description("Push local files to c
|
|
|
6865
6901
|
// src/commands/volume/pull.ts
|
|
6866
6902
|
import { Command as Command7 } from "commander";
|
|
6867
6903
|
import chalk12 from "chalk";
|
|
6868
|
-
import
|
|
6904
|
+
import path7 from "path";
|
|
6869
6905
|
import * as fs6 from "fs";
|
|
6870
6906
|
import * as os4 from "os";
|
|
6871
6907
|
import * as tar3 from "tar";
|
|
@@ -6926,8 +6962,8 @@ var pullCommand = new Command7().name("pull").description("Pull cloud files to l
|
|
|
6926
6962
|
const arrayBuffer = await s3Response.arrayBuffer();
|
|
6927
6963
|
const tarBuffer = Buffer.from(arrayBuffer);
|
|
6928
6964
|
console.log(chalk12.green(`\u2713 Downloaded ${formatBytes3(tarBuffer.length)}`));
|
|
6929
|
-
const tmpDir = fs6.mkdtempSync(
|
|
6930
|
-
const tarPath =
|
|
6965
|
+
const tmpDir = fs6.mkdtempSync(path7.join(os4.tmpdir(), "vm0-"));
|
|
6966
|
+
const tarPath = path7.join(tmpDir, "volume.tar.gz");
|
|
6931
6967
|
await fs6.promises.writeFile(tarPath, tarBuffer);
|
|
6932
6968
|
console.log(chalk12.dim("Syncing local files..."));
|
|
6933
6969
|
const remoteFiles = await listTarFiles(tarPath);
|
|
@@ -7080,7 +7116,7 @@ import chalk16 from "chalk";
|
|
|
7080
7116
|
|
|
7081
7117
|
// src/lib/storage/clone-utils.ts
|
|
7082
7118
|
import chalk15 from "chalk";
|
|
7083
|
-
import
|
|
7119
|
+
import path8 from "path";
|
|
7084
7120
|
import * as fs7 from "fs";
|
|
7085
7121
|
import * as os5 from "os";
|
|
7086
7122
|
import * as tar4 from "tar";
|
|
@@ -7121,8 +7157,8 @@ async function cloneStorage(name, type, destination, options = {}) {
|
|
|
7121
7157
|
const arrayBuffer = await s3Response.arrayBuffer();
|
|
7122
7158
|
const tarBuffer = Buffer.from(arrayBuffer);
|
|
7123
7159
|
console.log(chalk15.green(`\u2713 Downloaded ${formatBytes(tarBuffer.length)}`));
|
|
7124
|
-
const tmpDir = fs7.mkdtempSync(
|
|
7125
|
-
const tarPath =
|
|
7160
|
+
const tmpDir = fs7.mkdtempSync(path8.join(os5.tmpdir(), "vm0-clone-"));
|
|
7161
|
+
const tarPath = path8.join(tmpDir, "archive.tar.gz");
|
|
7126
7162
|
await fs7.promises.writeFile(tarPath, tarBuffer);
|
|
7127
7163
|
const files = await listTarFiles(tarPath);
|
|
7128
7164
|
console.log(chalk15.dim("Extracting files..."));
|
|
@@ -7176,14 +7212,14 @@ import { Command as Command18 } from "commander";
|
|
|
7176
7212
|
// src/commands/artifact/init.ts
|
|
7177
7213
|
import { Command as Command12 } from "commander";
|
|
7178
7214
|
import chalk17 from "chalk";
|
|
7179
|
-
import
|
|
7215
|
+
import path9 from "path";
|
|
7180
7216
|
var initCommand2 = new Command12().name("init").description("Initialize an artifact in the current directory").option(
|
|
7181
7217
|
"-n, --name <name>",
|
|
7182
7218
|
"Artifact name (required in non-interactive mode)"
|
|
7183
7219
|
).action(async (options) => {
|
|
7184
7220
|
try {
|
|
7185
7221
|
const cwd = process.cwd();
|
|
7186
|
-
const dirName =
|
|
7222
|
+
const dirName = path9.basename(cwd);
|
|
7187
7223
|
const existingConfig = await readStorageConfig(cwd);
|
|
7188
7224
|
if (existingConfig) {
|
|
7189
7225
|
if (existingConfig.type === "artifact") {
|
|
@@ -7205,7 +7241,7 @@ var initCommand2 = new Command12().name("init").description("Initialize an artif
|
|
|
7205
7241
|
);
|
|
7206
7242
|
}
|
|
7207
7243
|
console.log(
|
|
7208
|
-
chalk17.dim(`Config file: ${
|
|
7244
|
+
chalk17.dim(`Config file: ${path9.join(cwd, ".vm0", "storage.yaml")}`)
|
|
7209
7245
|
);
|
|
7210
7246
|
return;
|
|
7211
7247
|
}
|
|
@@ -7254,7 +7290,7 @@ var initCommand2 = new Command12().name("init").description("Initialize an artif
|
|
|
7254
7290
|
console.log(chalk17.green(`\u2713 Initialized artifact: ${artifactName}`));
|
|
7255
7291
|
console.log(
|
|
7256
7292
|
chalk17.dim(
|
|
7257
|
-
`\u2713 Config saved to ${
|
|
7293
|
+
`\u2713 Config saved to ${path9.join(cwd, ".vm0", "storage.yaml")}`
|
|
7258
7294
|
)
|
|
7259
7295
|
);
|
|
7260
7296
|
} catch (error) {
|
|
@@ -7327,7 +7363,7 @@ var pushCommand2 = new Command13().name("push").description("Push local files to
|
|
|
7327
7363
|
// src/commands/artifact/pull.ts
|
|
7328
7364
|
import { Command as Command14 } from "commander";
|
|
7329
7365
|
import chalk19 from "chalk";
|
|
7330
|
-
import
|
|
7366
|
+
import path10 from "path";
|
|
7331
7367
|
import * as fs8 from "fs";
|
|
7332
7368
|
import * as os6 from "os";
|
|
7333
7369
|
import * as tar5 from "tar";
|
|
@@ -7383,8 +7419,8 @@ var pullCommand2 = new Command14().name("pull").description("Pull cloud artifact
|
|
|
7383
7419
|
const arrayBuffer = await s3Response.arrayBuffer();
|
|
7384
7420
|
const tarBuffer = Buffer.from(arrayBuffer);
|
|
7385
7421
|
console.log(chalk19.green(`\u2713 Downloaded ${formatBytes6(tarBuffer.length)}`));
|
|
7386
|
-
const tmpDir = fs8.mkdtempSync(
|
|
7387
|
-
const tarPath =
|
|
7422
|
+
const tmpDir = fs8.mkdtempSync(path10.join(os6.tmpdir(), "vm0-"));
|
|
7423
|
+
const tarPath = path10.join(tmpDir, "artifact.tar.gz");
|
|
7388
7424
|
await fs8.promises.writeFile(tarPath, tarBuffer);
|
|
7389
7425
|
console.log(chalk19.dim("Syncing local files..."));
|
|
7390
7426
|
const remoteFiles = await listTarFiles(tarPath);
|
|
@@ -7556,9 +7592,9 @@ var artifactCommand = new Command18().name("artifact").description("Manage artif
|
|
|
7556
7592
|
// src/commands/cook.ts
|
|
7557
7593
|
import { Command as Command19, Option as Option4 } from "commander";
|
|
7558
7594
|
import chalk24 from "chalk";
|
|
7559
|
-
import { readFile as readFile7, mkdir as mkdir6
|
|
7560
|
-
import { existsSync as existsSync8
|
|
7561
|
-
import
|
|
7595
|
+
import { readFile as readFile7, mkdir as mkdir6 } from "fs/promises";
|
|
7596
|
+
import { existsSync as existsSync8 } from "fs";
|
|
7597
|
+
import path11 from "path";
|
|
7562
7598
|
import { spawn as spawn2 } from "child_process";
|
|
7563
7599
|
import { parse as parseYaml4 } from "yaml";
|
|
7564
7600
|
import { config as dotenvConfig2 } from "dotenv";
|
|
@@ -7630,7 +7666,7 @@ async function getLatestVersion() {
|
|
|
7630
7666
|
}
|
|
7631
7667
|
}
|
|
7632
7668
|
function performUpgrade(packageManager) {
|
|
7633
|
-
return new Promise((
|
|
7669
|
+
return new Promise((resolve) => {
|
|
7634
7670
|
const isWindows = process.platform === "win32";
|
|
7635
7671
|
const command = isWindows ? `${packageManager}.cmd` : packageManager;
|
|
7636
7672
|
const args = packageManager === "pnpm" ? ["add", "-g", `${PACKAGE_NAME}@latest`] : ["install", "-g", `${PACKAGE_NAME}@latest`];
|
|
@@ -7639,10 +7675,10 @@ function performUpgrade(packageManager) {
|
|
|
7639
7675
|
shell: isWindows
|
|
7640
7676
|
});
|
|
7641
7677
|
child.on("close", (code) => {
|
|
7642
|
-
|
|
7678
|
+
resolve(code === 0);
|
|
7643
7679
|
});
|
|
7644
7680
|
child.on("error", () => {
|
|
7645
|
-
|
|
7681
|
+
resolve(false);
|
|
7646
7682
|
});
|
|
7647
7683
|
});
|
|
7648
7684
|
}
|
|
@@ -7774,7 +7810,7 @@ function printCommand(cmd) {
|
|
|
7774
7810
|
console.log(chalk24.dim(`> ${cmd}`));
|
|
7775
7811
|
}
|
|
7776
7812
|
function execVm0Command(args, options = {}) {
|
|
7777
|
-
return new Promise((
|
|
7813
|
+
return new Promise((resolve, reject) => {
|
|
7778
7814
|
const stdio = options.silent ? "pipe" : "inherit";
|
|
7779
7815
|
const proc = spawn2("vm0", args, {
|
|
7780
7816
|
cwd: options.cwd,
|
|
@@ -7793,7 +7829,7 @@ function execVm0Command(args, options = {}) {
|
|
|
7793
7829
|
}
|
|
7794
7830
|
proc.on("close", (code) => {
|
|
7795
7831
|
if (code === 0) {
|
|
7796
|
-
|
|
7832
|
+
resolve(stdout);
|
|
7797
7833
|
} else {
|
|
7798
7834
|
reject(new Error(stderr || `Command failed with exit code ${code}`));
|
|
7799
7835
|
}
|
|
@@ -7804,7 +7840,7 @@ function execVm0Command(args, options = {}) {
|
|
|
7804
7840
|
});
|
|
7805
7841
|
}
|
|
7806
7842
|
function execVm0RunWithCapture(args, options = {}) {
|
|
7807
|
-
return new Promise((
|
|
7843
|
+
return new Promise((resolve, reject) => {
|
|
7808
7844
|
const proc = spawn2("vm0", args, {
|
|
7809
7845
|
cwd: options.cwd,
|
|
7810
7846
|
stdio: ["inherit", "pipe", "pipe"],
|
|
@@ -7824,7 +7860,7 @@ function execVm0RunWithCapture(args, options = {}) {
|
|
|
7824
7860
|
});
|
|
7825
7861
|
proc.on("close", (code) => {
|
|
7826
7862
|
if (code === 0) {
|
|
7827
|
-
|
|
7863
|
+
resolve(stdout);
|
|
7828
7864
|
} else {
|
|
7829
7865
|
reject(new Error(stderr || `Command failed with exit code ${code}`));
|
|
7830
7866
|
}
|
|
@@ -7871,36 +7907,26 @@ function extractRequiredVarNames(config) {
|
|
|
7871
7907
|
return [.../* @__PURE__ */ new Set([...varNames, ...secretNames])];
|
|
7872
7908
|
}
|
|
7873
7909
|
function checkMissingVariables(varNames, envFilePath) {
|
|
7874
|
-
let
|
|
7875
|
-
if (
|
|
7910
|
+
let fileValues = {};
|
|
7911
|
+
if (envFilePath) {
|
|
7912
|
+
if (!existsSync8(envFilePath)) {
|
|
7913
|
+
throw new Error(`Environment file not found: ${envFilePath}`);
|
|
7914
|
+
}
|
|
7876
7915
|
const result = dotenvConfig2({ path: envFilePath, quiet: true });
|
|
7877
7916
|
if (result.parsed) {
|
|
7878
|
-
|
|
7917
|
+
fileValues = result.parsed;
|
|
7879
7918
|
}
|
|
7880
7919
|
}
|
|
7881
7920
|
const missing = [];
|
|
7882
7921
|
for (const name of varNames) {
|
|
7883
7922
|
const inEnv = process.env[name] !== void 0;
|
|
7884
|
-
const
|
|
7885
|
-
if (!inEnv && !
|
|
7923
|
+
const inFile = fileValues[name] !== void 0;
|
|
7924
|
+
if (!inEnv && !inFile) {
|
|
7886
7925
|
missing.push(name);
|
|
7887
7926
|
}
|
|
7888
7927
|
}
|
|
7889
7928
|
return missing;
|
|
7890
7929
|
}
|
|
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
7930
|
async function autoPullArtifact(runOutput, artifactDir) {
|
|
7905
7931
|
const serverVersion = parseArtifactVersionFromCompletion(
|
|
7906
7932
|
runOutput,
|
|
@@ -7926,11 +7952,14 @@ async function autoPullArtifact(runOutput, artifactDir) {
|
|
|
7926
7952
|
}
|
|
7927
7953
|
}
|
|
7928
7954
|
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(
|
|
7955
|
+
cookCmd.argument("[prompt]", "Prompt for the agent").option(
|
|
7956
|
+
"--env-file <path>",
|
|
7957
|
+
"Load environment variables from file (priority: CLI flags > file > env vars)"
|
|
7958
|
+
).option("-y, --yes", "Skip confirmation prompts").addOption(new Option4("--debug-no-mock-claude").hideHelp()).addOption(new Option4("--no-auto-update").hideHelp()).action(
|
|
7930
7959
|
// eslint-disable-next-line complexity -- TODO: refactor complex function
|
|
7931
7960
|
async (prompt, options) => {
|
|
7932
7961
|
if (!options.noAutoUpdate) {
|
|
7933
|
-
const shouldExit = await checkAndUpgrade("
|
|
7962
|
+
const shouldExit = await checkAndUpgrade("8.0.1", prompt);
|
|
7934
7963
|
if (shouldExit) {
|
|
7935
7964
|
process.exit(0);
|
|
7936
7965
|
}
|
|
@@ -7965,21 +7994,27 @@ cookCmd.argument("[prompt]", "Prompt for the agent").option("-y, --yes", "Skip c
|
|
|
7965
7994
|
);
|
|
7966
7995
|
const requiredVarNames = extractRequiredVarNames(config);
|
|
7967
7996
|
if (requiredVarNames.length > 0) {
|
|
7968
|
-
|
|
7969
|
-
|
|
7970
|
-
|
|
7971
|
-
|
|
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
|
-
)
|
|
7997
|
+
try {
|
|
7998
|
+
const missingVars = checkMissingVariables(
|
|
7999
|
+
requiredVarNames,
|
|
8000
|
+
options.envFile
|
|
7980
8001
|
);
|
|
7981
|
-
|
|
7982
|
-
console.log(
|
|
8002
|
+
if (missingVars.length > 0) {
|
|
8003
|
+
console.log();
|
|
8004
|
+
console.error(chalk24.red("\u2717 Missing required variables:"));
|
|
8005
|
+
for (const varName of missingVars) {
|
|
8006
|
+
console.error(chalk24.red(` ${varName}`));
|
|
8007
|
+
}
|
|
8008
|
+
console.error(
|
|
8009
|
+
chalk24.dim(
|
|
8010
|
+
"\n Provide via --env-file, or set as environment variables"
|
|
8011
|
+
)
|
|
8012
|
+
);
|
|
8013
|
+
process.exit(1);
|
|
8014
|
+
}
|
|
8015
|
+
} catch (error) {
|
|
8016
|
+
if (error instanceof Error) {
|
|
8017
|
+
console.error(chalk24.red(`\u2717 ${error.message}`));
|
|
7983
8018
|
}
|
|
7984
8019
|
process.exit(1);
|
|
7985
8020
|
}
|
|
@@ -7988,7 +8023,7 @@ cookCmd.argument("[prompt]", "Prompt for the agent").option("-y, --yes", "Skip c
|
|
|
7988
8023
|
console.log();
|
|
7989
8024
|
console.log(chalk24.bold("Processing volumes:"));
|
|
7990
8025
|
for (const volumeConfig of Object.values(config.volumes)) {
|
|
7991
|
-
const volumeDir =
|
|
8026
|
+
const volumeDir = path11.join(cwd, volumeConfig.name);
|
|
7992
8027
|
if (!existsSync8(volumeDir)) {
|
|
7993
8028
|
console.error(
|
|
7994
8029
|
chalk24.red(
|
|
@@ -8027,7 +8062,7 @@ cookCmd.argument("[prompt]", "Prompt for the agent").option("-y, --yes", "Skip c
|
|
|
8027
8062
|
}
|
|
8028
8063
|
console.log();
|
|
8029
8064
|
console.log(chalk24.bold("Processing artifact:"));
|
|
8030
|
-
const artifactDir =
|
|
8065
|
+
const artifactDir = path11.join(cwd, ARTIFACT_DIR);
|
|
8031
8066
|
try {
|
|
8032
8067
|
if (!existsSync8(artifactDir)) {
|
|
8033
8068
|
printCommand(`mkdir ${ARTIFACT_DIR}`);
|
|
@@ -8156,80 +8191,98 @@ cookCmd.command("logs").description("View logs from the last cook run").option("
|
|
|
8156
8191
|
);
|
|
8157
8192
|
cookCmd.command("continue").description(
|
|
8158
8193
|
"Continue from the last session (latest conversation and artifact)"
|
|
8159
|
-
).argument("<prompt>", "Prompt for the continued agent").
|
|
8160
|
-
|
|
8161
|
-
|
|
8162
|
-
|
|
8163
|
-
|
|
8164
|
-
|
|
8165
|
-
|
|
8166
|
-
|
|
8167
|
-
|
|
8168
|
-
|
|
8169
|
-
|
|
8170
|
-
|
|
8171
|
-
|
|
8172
|
-
|
|
8173
|
-
|
|
8174
|
-
|
|
8175
|
-
"continue",
|
|
8176
|
-
state.lastSessionId,
|
|
8177
|
-
...options.debugNoMockClaude ? ["--debug-no-mock-claude"] : [],
|
|
8178
|
-
prompt
|
|
8179
|
-
],
|
|
8180
|
-
{ cwd }
|
|
8194
|
+
).argument("<prompt>", "Prompt for the continued agent").option(
|
|
8195
|
+
"--env-file <path>",
|
|
8196
|
+
"Load environment variables from file (priority: CLI flags > file > env vars)"
|
|
8197
|
+
).addOption(new Option4("--debug-no-mock-claude").hideHelp()).action(
|
|
8198
|
+
async (prompt, options) => {
|
|
8199
|
+
const state = await loadCookState();
|
|
8200
|
+
if (!state.lastSessionId) {
|
|
8201
|
+
console.error(chalk24.red("\u2717 No previous session found"));
|
|
8202
|
+
console.error(chalk24.dim(" Run 'vm0 cook <prompt>' first"));
|
|
8203
|
+
process.exit(1);
|
|
8204
|
+
}
|
|
8205
|
+
const cwd = process.cwd();
|
|
8206
|
+
const artifactDir = path11.join(cwd, ARTIFACT_DIR);
|
|
8207
|
+
const envFileArg = options.envFile ? ` --env-file ${options.envFile}` : "";
|
|
8208
|
+
printCommand(
|
|
8209
|
+
`vm0 run continue${envFileArg} ${state.lastSessionId} "${prompt}"`
|
|
8181
8210
|
);
|
|
8182
|
-
|
|
8183
|
-
|
|
8184
|
-
|
|
8185
|
-
|
|
8186
|
-
|
|
8187
|
-
|
|
8188
|
-
|
|
8189
|
-
|
|
8190
|
-
|
|
8191
|
-
|
|
8211
|
+
console.log();
|
|
8212
|
+
let runOutput;
|
|
8213
|
+
try {
|
|
8214
|
+
runOutput = await execVm0RunWithCapture(
|
|
8215
|
+
[
|
|
8216
|
+
"run",
|
|
8217
|
+
"continue",
|
|
8218
|
+
...options.envFile ? ["--env-file", options.envFile] : [],
|
|
8219
|
+
state.lastSessionId,
|
|
8220
|
+
...options.debugNoMockClaude ? ["--debug-no-mock-claude"] : [],
|
|
8221
|
+
prompt
|
|
8222
|
+
],
|
|
8223
|
+
{ cwd }
|
|
8224
|
+
);
|
|
8225
|
+
} catch {
|
|
8226
|
+
process.exit(1);
|
|
8227
|
+
}
|
|
8228
|
+
const newIds = parseRunIdsFromOutput(runOutput);
|
|
8229
|
+
if (newIds.runId || newIds.sessionId || newIds.checkpointId) {
|
|
8230
|
+
await saveCookState({
|
|
8231
|
+
lastRunId: newIds.runId,
|
|
8232
|
+
lastSessionId: newIds.sessionId,
|
|
8233
|
+
lastCheckpointId: newIds.checkpointId
|
|
8234
|
+
});
|
|
8235
|
+
}
|
|
8236
|
+
await autoPullArtifact(runOutput, artifactDir);
|
|
8192
8237
|
}
|
|
8193
|
-
|
|
8194
|
-
});
|
|
8238
|
+
);
|
|
8195
8239
|
cookCmd.command("resume").description(
|
|
8196
8240
|
"Resume from the last checkpoint (snapshotted conversation and artifact)"
|
|
8197
|
-
).argument("<prompt>", "Prompt for the resumed agent").
|
|
8198
|
-
|
|
8199
|
-
|
|
8200
|
-
|
|
8201
|
-
|
|
8202
|
-
|
|
8203
|
-
|
|
8204
|
-
|
|
8205
|
-
|
|
8206
|
-
|
|
8207
|
-
|
|
8208
|
-
|
|
8209
|
-
|
|
8210
|
-
|
|
8211
|
-
|
|
8212
|
-
|
|
8213
|
-
"resume",
|
|
8214
|
-
state.lastCheckpointId,
|
|
8215
|
-
...options.debugNoMockClaude ? ["--debug-no-mock-claude"] : [],
|
|
8216
|
-
prompt
|
|
8217
|
-
],
|
|
8218
|
-
{ cwd }
|
|
8241
|
+
).argument("<prompt>", "Prompt for the resumed agent").option(
|
|
8242
|
+
"--env-file <path>",
|
|
8243
|
+
"Load environment variables from file (priority: CLI flags > file > env vars)"
|
|
8244
|
+
).addOption(new Option4("--debug-no-mock-claude").hideHelp()).action(
|
|
8245
|
+
async (prompt, options) => {
|
|
8246
|
+
const state = await loadCookState();
|
|
8247
|
+
if (!state.lastCheckpointId) {
|
|
8248
|
+
console.error(chalk24.red("\u2717 No previous checkpoint found"));
|
|
8249
|
+
console.error(chalk24.dim(" Run 'vm0 cook <prompt>' first"));
|
|
8250
|
+
process.exit(1);
|
|
8251
|
+
}
|
|
8252
|
+
const cwd = process.cwd();
|
|
8253
|
+
const artifactDir = path11.join(cwd, ARTIFACT_DIR);
|
|
8254
|
+
const envFileArg = options.envFile ? ` --env-file ${options.envFile}` : "";
|
|
8255
|
+
printCommand(
|
|
8256
|
+
`vm0 run resume${envFileArg} ${state.lastCheckpointId} "${prompt}"`
|
|
8219
8257
|
);
|
|
8220
|
-
|
|
8221
|
-
|
|
8222
|
-
|
|
8223
|
-
|
|
8224
|
-
|
|
8225
|
-
|
|
8226
|
-
|
|
8227
|
-
|
|
8228
|
-
|
|
8229
|
-
|
|
8258
|
+
console.log();
|
|
8259
|
+
let runOutput;
|
|
8260
|
+
try {
|
|
8261
|
+
runOutput = await execVm0RunWithCapture(
|
|
8262
|
+
[
|
|
8263
|
+
"run",
|
|
8264
|
+
"resume",
|
|
8265
|
+
...options.envFile ? ["--env-file", options.envFile] : [],
|
|
8266
|
+
state.lastCheckpointId,
|
|
8267
|
+
...options.debugNoMockClaude ? ["--debug-no-mock-claude"] : [],
|
|
8268
|
+
prompt
|
|
8269
|
+
],
|
|
8270
|
+
{ cwd }
|
|
8271
|
+
);
|
|
8272
|
+
} catch {
|
|
8273
|
+
process.exit(1);
|
|
8274
|
+
}
|
|
8275
|
+
const newIds = parseRunIdsFromOutput(runOutput);
|
|
8276
|
+
if (newIds.runId || newIds.sessionId || newIds.checkpointId) {
|
|
8277
|
+
await saveCookState({
|
|
8278
|
+
lastRunId: newIds.runId,
|
|
8279
|
+
lastSessionId: newIds.sessionId,
|
|
8280
|
+
lastCheckpointId: newIds.checkpointId
|
|
8281
|
+
});
|
|
8282
|
+
}
|
|
8283
|
+
await autoPullArtifact(runOutput, artifactDir);
|
|
8230
8284
|
}
|
|
8231
|
-
|
|
8232
|
-
});
|
|
8285
|
+
);
|
|
8233
8286
|
var cookCommand = cookCmd;
|
|
8234
8287
|
|
|
8235
8288
|
// src/commands/logs/index.ts
|
|
@@ -8616,10 +8669,9 @@ import { Command as Command26 } from "commander";
|
|
|
8616
8669
|
// src/commands/agent/list.ts
|
|
8617
8670
|
import { Command as Command24 } from "commander";
|
|
8618
8671
|
import chalk28 from "chalk";
|
|
8619
|
-
var listCommand3 = new Command24().name("list").alias("ls").description("List all agent composes").
|
|
8672
|
+
var listCommand3 = new Command24().name("list").alias("ls").description("List all agent composes").action(async () => {
|
|
8620
8673
|
try {
|
|
8621
|
-
const
|
|
8622
|
-
const response = await httpGet(url);
|
|
8674
|
+
const response = await httpGet("/api/agent/composes/list");
|
|
8623
8675
|
if (!response.ok) {
|
|
8624
8676
|
const error = await response.json();
|
|
8625
8677
|
throw new Error(error.error?.message || "List failed");
|
|
@@ -8659,35 +8711,14 @@ var listCommand3 = new Command24().name("list").alias("ls").description("List al
|
|
|
8659
8711
|
}
|
|
8660
8712
|
});
|
|
8661
8713
|
|
|
8662
|
-
// src/commands/agent/
|
|
8714
|
+
// src/commands/agent/status.ts
|
|
8663
8715
|
import { Command as Command25 } from "commander";
|
|
8664
8716
|
import chalk29 from "chalk";
|
|
8665
8717
|
|
|
8666
8718
|
// src/lib/domain/source-derivation.ts
|
|
8667
8719
|
import * as fs9 from "fs/promises";
|
|
8668
|
-
import * as
|
|
8720
|
+
import * as path12 from "path";
|
|
8669
8721
|
import * as os7 from "os";
|
|
8670
|
-
function extractVariableReferences2(environment) {
|
|
8671
|
-
const secrets = [];
|
|
8672
|
-
const vars = [];
|
|
8673
|
-
for (const value of Object.values(environment)) {
|
|
8674
|
-
const matches = value.matchAll(/\$\{?([A-Z_][A-Z0-9_]*)\}?/g);
|
|
8675
|
-
for (const match of matches) {
|
|
8676
|
-
const varName = match[1];
|
|
8677
|
-
if (!varName) continue;
|
|
8678
|
-
if (varName.endsWith("_KEY") || varName.endsWith("_SECRET") || varName.endsWith("_TOKEN") || varName.endsWith("_PASSWORD") || varName.includes("API_KEY") || varName.includes("SECRET")) {
|
|
8679
|
-
if (!secrets.includes(varName)) {
|
|
8680
|
-
secrets.push(varName);
|
|
8681
|
-
}
|
|
8682
|
-
} else {
|
|
8683
|
-
if (!vars.includes(varName)) {
|
|
8684
|
-
vars.push(varName);
|
|
8685
|
-
}
|
|
8686
|
-
}
|
|
8687
|
-
}
|
|
8688
|
-
}
|
|
8689
|
-
return { secrets: secrets.sort(), vars: vars.sort() };
|
|
8690
|
-
}
|
|
8691
8722
|
async function fetchSkillFrontmatter(skillUrl, tempDir) {
|
|
8692
8723
|
try {
|
|
8693
8724
|
const parsed = parseGitHubTreeUrl2(skillUrl);
|
|
@@ -8699,23 +8730,35 @@ async function fetchSkillFrontmatter(skillUrl, tempDir) {
|
|
|
8699
8730
|
}
|
|
8700
8731
|
}
|
|
8701
8732
|
async function deriveAgentVariableSources(agent, options) {
|
|
8702
|
-
const
|
|
8733
|
+
const refs = agent.environment ? extractVariableReferences(agent.environment) : [];
|
|
8734
|
+
const grouped = groupVariablesBySource(refs);
|
|
8703
8735
|
const secretSources = /* @__PURE__ */ new Map();
|
|
8704
8736
|
const varSources = /* @__PURE__ */ new Map();
|
|
8705
|
-
|
|
8706
|
-
|
|
8737
|
+
const credentialSources = /* @__PURE__ */ new Map();
|
|
8738
|
+
for (const ref of grouped.secrets) {
|
|
8739
|
+
secretSources.set(ref.name, {
|
|
8740
|
+
name: ref.name,
|
|
8741
|
+
source: "agent environment"
|
|
8742
|
+
});
|
|
8707
8743
|
}
|
|
8708
|
-
for (const
|
|
8709
|
-
varSources.set(name, { name, source: "agent environment" });
|
|
8744
|
+
for (const ref of grouped.vars) {
|
|
8745
|
+
varSources.set(ref.name, { name: ref.name, source: "agent environment" });
|
|
8746
|
+
}
|
|
8747
|
+
for (const ref of grouped.credentials) {
|
|
8748
|
+
credentialSources.set(ref.name, {
|
|
8749
|
+
name: ref.name,
|
|
8750
|
+
source: "agent environment"
|
|
8751
|
+
});
|
|
8710
8752
|
}
|
|
8711
8753
|
if (options?.skipNetwork || !agent.skills || agent.skills.length === 0) {
|
|
8712
8754
|
return {
|
|
8713
8755
|
secrets: Array.from(secretSources.values()),
|
|
8714
|
-
vars: Array.from(varSources.values())
|
|
8756
|
+
vars: Array.from(varSources.values()),
|
|
8757
|
+
credentials: Array.from(credentialSources.values())
|
|
8715
8758
|
};
|
|
8716
8759
|
}
|
|
8717
8760
|
const tempDir = await fs9.mkdtemp(
|
|
8718
|
-
|
|
8761
|
+
path12.join(os7.tmpdir(), "vm0-source-derivation-")
|
|
8719
8762
|
);
|
|
8720
8763
|
try {
|
|
8721
8764
|
const skillResults = await Promise.all(
|
|
@@ -8752,7 +8795,8 @@ async function deriveAgentVariableSources(agent, options) {
|
|
|
8752
8795
|
}
|
|
8753
8796
|
return {
|
|
8754
8797
|
secrets: Array.from(secretSources.values()),
|
|
8755
|
-
vars: Array.from(varSources.values())
|
|
8798
|
+
vars: Array.from(varSources.values()),
|
|
8799
|
+
credentials: Array.from(credentialSources.values())
|
|
8756
8800
|
};
|
|
8757
8801
|
}
|
|
8758
8802
|
async function deriveComposeVariableSources(content, options) {
|
|
@@ -8769,156 +8813,166 @@ async function deriveComposeVariableSources(content, options) {
|
|
|
8769
8813
|
return results;
|
|
8770
8814
|
}
|
|
8771
8815
|
|
|
8772
|
-
// src/commands/agent/
|
|
8816
|
+
// src/commands/agent/status.ts
|
|
8817
|
+
function formatListSection(label, items) {
|
|
8818
|
+
if (items.length === 0) return;
|
|
8819
|
+
console.log(` ${label}:`);
|
|
8820
|
+
for (const item of items) {
|
|
8821
|
+
console.log(` - ${item}`);
|
|
8822
|
+
}
|
|
8823
|
+
}
|
|
8824
|
+
function formatVolumes(volumes, volumeConfigs) {
|
|
8825
|
+
if (volumes.length === 0) return;
|
|
8826
|
+
console.log(` Volumes:`);
|
|
8827
|
+
for (const vol of volumes) {
|
|
8828
|
+
const volumeDef = volumeConfigs?.[vol];
|
|
8829
|
+
if (volumeDef) {
|
|
8830
|
+
console.log(` - ${vol}:${volumeDef.version.slice(0, 8)}`);
|
|
8831
|
+
} else {
|
|
8832
|
+
console.log(` - ${vol}`);
|
|
8833
|
+
}
|
|
8834
|
+
}
|
|
8835
|
+
}
|
|
8836
|
+
function formatVariableSources(sources) {
|
|
8837
|
+
if (sources.secrets.length > 0) {
|
|
8838
|
+
console.log(` Secrets:`);
|
|
8839
|
+
for (const secret of sources.secrets) {
|
|
8840
|
+
const sourceInfo = chalk29.dim(`(${secret.source})`);
|
|
8841
|
+
console.log(` - ${secret.name.padEnd(20)} ${sourceInfo}`);
|
|
8842
|
+
}
|
|
8843
|
+
}
|
|
8844
|
+
if (sources.vars.length > 0) {
|
|
8845
|
+
console.log(` Vars:`);
|
|
8846
|
+
for (const v of sources.vars) {
|
|
8847
|
+
const sourceInfo = chalk29.dim(`(${v.source})`);
|
|
8848
|
+
console.log(` - ${v.name.padEnd(20)} ${sourceInfo}`);
|
|
8849
|
+
}
|
|
8850
|
+
}
|
|
8851
|
+
if (sources.credentials.length > 0) {
|
|
8852
|
+
console.log(` Credentials:`);
|
|
8853
|
+
for (const cred of sources.credentials) {
|
|
8854
|
+
const sourceInfo = chalk29.dim(`(${cred.source})`);
|
|
8855
|
+
console.log(` - ${cred.name.padEnd(20)} ${sourceInfo}`);
|
|
8856
|
+
}
|
|
8857
|
+
}
|
|
8858
|
+
}
|
|
8859
|
+
function formatAgentDetails(agentName, agent, agentSources, volumeConfigs) {
|
|
8860
|
+
console.log(` ${chalk29.cyan(agentName)}:`);
|
|
8861
|
+
console.log(` Framework: ${agent.framework}`);
|
|
8862
|
+
if (agent.image) {
|
|
8863
|
+
console.log(` Image: ${agent.image}`);
|
|
8864
|
+
}
|
|
8865
|
+
formatListSection("Apps", agent.apps ?? []);
|
|
8866
|
+
if (agent.working_dir) {
|
|
8867
|
+
console.log(` Working Dir: ${agent.working_dir}`);
|
|
8868
|
+
}
|
|
8869
|
+
formatVolumes(agent.volumes ?? [], volumeConfigs);
|
|
8870
|
+
formatListSection("Skills", agent.skills ?? []);
|
|
8871
|
+
if (agentSources) {
|
|
8872
|
+
formatVariableSources(agentSources);
|
|
8873
|
+
}
|
|
8874
|
+
if (agent.experimental_runner) {
|
|
8875
|
+
console.log(` Runner: ${agent.experimental_runner.group}`);
|
|
8876
|
+
}
|
|
8877
|
+
}
|
|
8773
8878
|
function formatComposeOutput(name, versionId, content, variableSources) {
|
|
8774
8879
|
console.log(chalk29.bold("Name:") + ` ${name}`);
|
|
8775
8880
|
console.log(chalk29.bold("Version:") + ` ${versionId}`);
|
|
8776
8881
|
console.log();
|
|
8777
8882
|
console.log(chalk29.bold("Agents:"));
|
|
8778
8883
|
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
8884
|
const agentSources = variableSources?.get(agentName);
|
|
8811
|
-
|
|
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
|
-
}
|
|
8885
|
+
formatAgentDetails(agentName, agent, agentSources, content.volumes);
|
|
8830
8886
|
}
|
|
8831
8887
|
}
|
|
8832
|
-
var
|
|
8888
|
+
var statusCommand4 = new Command25().name("status").description("Show status of agent compose").argument(
|
|
8833
8889
|
"<name[:version]>",
|
|
8834
8890
|
"Agent name with optional version (e.g., my-agent:latest or my-agent:a1b2c3d4)"
|
|
8835
|
-
).option("
|
|
8836
|
-
|
|
8837
|
-
|
|
8838
|
-
|
|
8839
|
-
|
|
8840
|
-
|
|
8841
|
-
|
|
8842
|
-
|
|
8843
|
-
|
|
8844
|
-
|
|
8845
|
-
|
|
8846
|
-
|
|
8847
|
-
|
|
8848
|
-
|
|
8849
|
-
|
|
8850
|
-
|
|
8851
|
-
|
|
8852
|
-
|
|
8853
|
-
|
|
8854
|
-
|
|
8855
|
-
if (version
|
|
8856
|
-
if (version.length < 64) {
|
|
8857
|
-
try {
|
|
8858
|
-
const versionInfo = await getComposeVersion(compose.id, version);
|
|
8859
|
-
resolvedVersionId = versionInfo.versionId;
|
|
8860
|
-
} catch (error) {
|
|
8861
|
-
if (error instanceof Error && error.message.includes("not found")) {
|
|
8862
|
-
console.error(chalk29.red(`\u2717 Version not found: ${version}`));
|
|
8863
|
-
console.error(
|
|
8864
|
-
chalk29.dim(
|
|
8865
|
-
` HEAD version: ${compose.headVersionId?.slice(0, 8)}`
|
|
8866
|
-
)
|
|
8867
|
-
);
|
|
8868
|
-
process.exit(1);
|
|
8869
|
-
}
|
|
8870
|
-
throw error;
|
|
8871
|
-
}
|
|
8872
|
-
} else {
|
|
8873
|
-
resolvedVersionId = version;
|
|
8874
|
-
}
|
|
8875
|
-
}
|
|
8876
|
-
if (!resolvedVersionId || !compose.content) {
|
|
8877
|
-
console.error(chalk29.red(`\u2717 No version found for: ${name}`));
|
|
8878
|
-
process.exit(1);
|
|
8879
|
-
}
|
|
8880
|
-
const content = compose.content;
|
|
8881
|
-
let variableSources;
|
|
8882
|
-
if (options.sources !== false) {
|
|
8891
|
+
).option("--no-sources", "Skip fetching skills to determine variable sources").action(async (argument, options) => {
|
|
8892
|
+
try {
|
|
8893
|
+
const colonIndex = argument.lastIndexOf(":");
|
|
8894
|
+
let name;
|
|
8895
|
+
let version;
|
|
8896
|
+
if (colonIndex === -1) {
|
|
8897
|
+
name = argument;
|
|
8898
|
+
version = "latest";
|
|
8899
|
+
} else {
|
|
8900
|
+
name = argument.slice(0, colonIndex);
|
|
8901
|
+
version = argument.slice(colonIndex + 1) || "latest";
|
|
8902
|
+
}
|
|
8903
|
+
const compose = await getComposeByName(name);
|
|
8904
|
+
if (!compose) {
|
|
8905
|
+
console.error(chalk29.red(`\u2717 Agent compose not found: ${name}`));
|
|
8906
|
+
console.error(chalk29.dim(" Run: vm0 agent list"));
|
|
8907
|
+
process.exit(1);
|
|
8908
|
+
}
|
|
8909
|
+
let resolvedVersionId = compose.headVersionId;
|
|
8910
|
+
if (version !== "latest" && compose.headVersionId) {
|
|
8911
|
+
if (version.length < 64) {
|
|
8883
8912
|
try {
|
|
8884
|
-
|
|
8885
|
-
|
|
8886
|
-
|
|
8887
|
-
|
|
8888
|
-
|
|
8889
|
-
|
|
8890
|
-
|
|
8913
|
+
const versionInfo = await getComposeVersion(compose.id, version);
|
|
8914
|
+
resolvedVersionId = versionInfo.versionId;
|
|
8915
|
+
} catch (error) {
|
|
8916
|
+
if (error instanceof Error && error.message.includes("not found")) {
|
|
8917
|
+
console.error(chalk29.red(`\u2717 Version not found: ${version}`));
|
|
8918
|
+
console.error(
|
|
8919
|
+
chalk29.dim(
|
|
8920
|
+
` HEAD version: ${compose.headVersionId?.slice(0, 8)}`
|
|
8921
|
+
)
|
|
8922
|
+
);
|
|
8923
|
+
process.exit(1);
|
|
8924
|
+
}
|
|
8925
|
+
throw error;
|
|
8891
8926
|
}
|
|
8927
|
+
} else {
|
|
8928
|
+
resolvedVersionId = version;
|
|
8892
8929
|
}
|
|
8893
|
-
|
|
8894
|
-
|
|
8895
|
-
|
|
8896
|
-
|
|
8897
|
-
|
|
8930
|
+
}
|
|
8931
|
+
if (!resolvedVersionId || !compose.content) {
|
|
8932
|
+
console.error(chalk29.red(`\u2717 No version found for: ${name}`));
|
|
8933
|
+
process.exit(1);
|
|
8934
|
+
}
|
|
8935
|
+
const content = compose.content;
|
|
8936
|
+
let variableSources;
|
|
8937
|
+
try {
|
|
8938
|
+
variableSources = await deriveComposeVariableSources(content, {
|
|
8939
|
+
skipNetwork: options.sources === false
|
|
8940
|
+
});
|
|
8941
|
+
} catch {
|
|
8942
|
+
console.error(
|
|
8943
|
+
chalk29.yellow(
|
|
8944
|
+
"\u26A0 Warning: Failed to fetch skill sources, showing basic info"
|
|
8945
|
+
)
|
|
8898
8946
|
);
|
|
8899
|
-
}
|
|
8900
|
-
|
|
8901
|
-
|
|
8902
|
-
|
|
8903
|
-
|
|
8904
|
-
|
|
8905
|
-
|
|
8906
|
-
|
|
8947
|
+
}
|
|
8948
|
+
formatComposeOutput(
|
|
8949
|
+
compose.name,
|
|
8950
|
+
resolvedVersionId,
|
|
8951
|
+
content,
|
|
8952
|
+
variableSources
|
|
8953
|
+
);
|
|
8954
|
+
} catch (error) {
|
|
8955
|
+
console.error(chalk29.red("\u2717 Failed to get agent compose status"));
|
|
8956
|
+
if (error instanceof Error) {
|
|
8957
|
+
if (error.message.includes("Not authenticated")) {
|
|
8958
|
+
console.error(chalk29.dim(" Run: vm0 auth login"));
|
|
8959
|
+
} else {
|
|
8960
|
+
console.error(chalk29.dim(` ${error.message}`));
|
|
8907
8961
|
}
|
|
8908
|
-
process.exit(1);
|
|
8909
8962
|
}
|
|
8963
|
+
process.exit(1);
|
|
8910
8964
|
}
|
|
8911
|
-
);
|
|
8965
|
+
});
|
|
8912
8966
|
|
|
8913
8967
|
// src/commands/agent/index.ts
|
|
8914
|
-
var agentCommand = new Command26().name("agent").description("Manage agent composes").addCommand(listCommand3).addCommand(
|
|
8968
|
+
var agentCommand = new Command26().name("agent").description("Manage agent composes").addCommand(listCommand3).addCommand(statusCommand4);
|
|
8915
8969
|
|
|
8916
8970
|
// src/commands/init.ts
|
|
8917
8971
|
import { Command as Command27 } from "commander";
|
|
8918
8972
|
import chalk30 from "chalk";
|
|
8919
|
-
import
|
|
8973
|
+
import path13 from "path";
|
|
8920
8974
|
import { existsSync as existsSync9 } from "fs";
|
|
8921
|
-
import { writeFile as
|
|
8975
|
+
import { writeFile as writeFile6 } from "fs/promises";
|
|
8922
8976
|
var VM0_YAML_FILE = "vm0.yaml";
|
|
8923
8977
|
var AGENTS_MD_FILE = "AGENTS.md";
|
|
8924
8978
|
function generateVm0Yaml(agentName) {
|
|
@@ -8973,7 +9027,7 @@ var initCommand3 = new Command27().name("init").description("Initialize a new VM
|
|
|
8973
9027
|
console.error(chalk30.dim(" Usage: vm0 init --name <agent-name>"));
|
|
8974
9028
|
process.exit(1);
|
|
8975
9029
|
} else {
|
|
8976
|
-
const dirName =
|
|
9030
|
+
const dirName = path13.basename(process.cwd());
|
|
8977
9031
|
const defaultName = validateAgentName(dirName) ? dirName : void 0;
|
|
8978
9032
|
const name = await promptText(
|
|
8979
9033
|
"Enter agent name",
|
|
@@ -8999,10 +9053,10 @@ var initCommand3 = new Command27().name("init").description("Initialize a new VM
|
|
|
8999
9053
|
console.log(chalk30.dim(" Must start and end with letter or number"));
|
|
9000
9054
|
process.exit(1);
|
|
9001
9055
|
}
|
|
9002
|
-
await
|
|
9056
|
+
await writeFile6(VM0_YAML_FILE, generateVm0Yaml(agentName));
|
|
9003
9057
|
const vm0Status = existingFiles.includes(VM0_YAML_FILE) ? " (overwritten)" : "";
|
|
9004
9058
|
console.log(chalk30.green(`\u2713 Created ${VM0_YAML_FILE}${vm0Status}`));
|
|
9005
|
-
await
|
|
9059
|
+
await writeFile6(AGENTS_MD_FILE, generateAgentsMd());
|
|
9006
9060
|
const agentsStatus = existingFiles.includes(AGENTS_MD_FILE) ? " (overwritten)" : "";
|
|
9007
9061
|
console.log(chalk30.green(`\u2713 Created ${AGENTS_MD_FILE}${agentsStatus}`));
|
|
9008
9062
|
console.log();
|
|
@@ -9018,541 +9072,33 @@ var initCommand3 = new Command27().name("init").description("Initialize a new VM
|
|
|
9018
9072
|
);
|
|
9019
9073
|
});
|
|
9020
9074
|
|
|
9021
|
-
// src/commands/
|
|
9075
|
+
// src/commands/schedule/index.ts
|
|
9076
|
+
import { Command as Command34 } from "commander";
|
|
9077
|
+
|
|
9078
|
+
// src/commands/schedule/setup.ts
|
|
9022
9079
|
import { Command as Command28 } from "commander";
|
|
9023
9080
|
import chalk31 from "chalk";
|
|
9024
|
-
|
|
9025
|
-
|
|
9026
|
-
import { execSync, spawnSync } from "child_process";
|
|
9027
|
-
import path15 from "path";
|
|
9081
|
+
|
|
9082
|
+
// src/lib/domain/schedule-utils.ts
|
|
9028
9083
|
import { parse as parseYaml5 } from "yaml";
|
|
9029
|
-
function
|
|
9030
|
-
|
|
9031
|
-
|
|
9032
|
-
|
|
9033
|
-
|
|
9034
|
-
|
|
9035
|
-
|
|
9036
|
-
|
|
9037
|
-
|
|
9038
|
-
|
|
9039
|
-
|
|
9040
|
-
return
|
|
9041
|
-
}
|
|
9042
|
-
return
|
|
9043
|
-
}
|
|
9044
|
-
}
|
|
9045
|
-
|
|
9046
|
-
|
|
9047
|
-
return execSync("git rev-parse --show-toplevel", {
|
|
9048
|
-
encoding: "utf8"
|
|
9049
|
-
}).trim();
|
|
9050
|
-
} catch {
|
|
9051
|
-
return null;
|
|
9052
|
-
}
|
|
9053
|
-
}
|
|
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, "/");
|
|
9061
|
-
}
|
|
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);
|
|
9073
|
-
}
|
|
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);
|
|
9089
|
-
}
|
|
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);
|
|
9100
|
-
}
|
|
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);
|
|
9112
|
-
}
|
|
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);
|
|
9125
|
-
}
|
|
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);
|
|
9210
|
-
}
|
|
9211
|
-
for (const ref of grouped.vars) {
|
|
9212
|
-
vars.add(ref.name);
|
|
9213
|
-
}
|
|
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";
|
|
9084
|
+
function formatRelativeTime2(dateStr) {
|
|
9085
|
+
if (!dateStr) return "-";
|
|
9086
|
+
const date = new Date(dateStr);
|
|
9087
|
+
const now = /* @__PURE__ */ new Date();
|
|
9088
|
+
const diffMs = date.getTime() - now.getTime();
|
|
9089
|
+
const diffAbs = Math.abs(diffMs);
|
|
9090
|
+
const minutes = Math.floor(diffAbs / (1e3 * 60));
|
|
9091
|
+
const hours = Math.floor(diffAbs / (1e3 * 60 * 60));
|
|
9092
|
+
const days = Math.floor(diffAbs / (1e3 * 60 * 60 * 24));
|
|
9093
|
+
const isPast = diffMs < 0;
|
|
9094
|
+
if (days > 0) {
|
|
9095
|
+
return isPast ? `${days}d ago` : `in ${days}d`;
|
|
9096
|
+
} else if (hours > 0) {
|
|
9097
|
+
return isPast ? `${hours}h ago` : `in ${hours}h`;
|
|
9098
|
+
} else if (minutes > 0) {
|
|
9099
|
+
return isPast ? `${minutes}m ago` : `in ${minutes}m`;
|
|
9100
|
+
} else {
|
|
9101
|
+
return isPast ? "just now" : "soon";
|
|
9556
9102
|
}
|
|
9557
9103
|
}
|
|
9558
9104
|
function formatDateTime(dateStr) {
|
|
@@ -9583,42 +9129,6 @@ function generateCronExpression(frequency, time, day) {
|
|
|
9583
9129
|
function detectTimezone() {
|
|
9584
9130
|
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
9585
9131
|
}
|
|
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
9132
|
function validateTimeFormat(time) {
|
|
9623
9133
|
const match = time.match(/^(\d{1,2}):(\d{2})$/);
|
|
9624
9134
|
if (!match) {
|
|
@@ -9679,11 +9189,27 @@ function toISODateTime(dateTimeStr) {
|
|
|
9679
9189
|
const date = new Date(isoStr);
|
|
9680
9190
|
return date.toISOString();
|
|
9681
9191
|
}
|
|
9682
|
-
|
|
9192
|
+
function extractRequiredConfiguration(composeContent) {
|
|
9193
|
+
const result = {
|
|
9194
|
+
secrets: [],
|
|
9195
|
+
vars: [],
|
|
9196
|
+
credentials: []
|
|
9197
|
+
};
|
|
9198
|
+
if (!composeContent) {
|
|
9199
|
+
return result;
|
|
9200
|
+
}
|
|
9201
|
+
const refs = extractVariableReferences(composeContent);
|
|
9202
|
+
const grouped = groupVariablesBySource(refs);
|
|
9203
|
+
result.secrets = grouped.secrets.map((r) => r.name);
|
|
9204
|
+
result.vars = grouped.vars.map((r) => r.name);
|
|
9205
|
+
result.credentials = grouped.credentials.map((r) => r.name);
|
|
9206
|
+
return result;
|
|
9207
|
+
}
|
|
9208
|
+
async function resolveScheduleByAgent(agentName) {
|
|
9683
9209
|
const { schedules } = await listSchedules();
|
|
9684
|
-
const schedule = schedules.find((s) => s.
|
|
9210
|
+
const schedule = schedules.find((s) => s.composeName === agentName);
|
|
9685
9211
|
if (!schedule) {
|
|
9686
|
-
throw new Error(`
|
|
9212
|
+
throw new Error(`No schedule found for agent "${agentName}"`);
|
|
9687
9213
|
}
|
|
9688
9214
|
return {
|
|
9689
9215
|
name: schedule.name,
|
|
@@ -9692,8 +9218,7 @@ async function resolveScheduleByName(name) {
|
|
|
9692
9218
|
};
|
|
9693
9219
|
}
|
|
9694
9220
|
|
|
9695
|
-
// src/commands/schedule/
|
|
9696
|
-
var SCHEDULE_FILE2 = "schedule.yaml";
|
|
9221
|
+
// src/commands/schedule/setup.ts
|
|
9697
9222
|
var FREQUENCY_CHOICES = [
|
|
9698
9223
|
{ title: "Daily", value: "daily", description: "Run every day" },
|
|
9699
9224
|
{
|
|
@@ -9741,281 +9266,12 @@ function parseDayOption(day, frequency) {
|
|
|
9741
9266
|
}
|
|
9742
9267
|
return void 0;
|
|
9743
9268
|
}
|
|
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
9269
|
function expandEnvVars(value) {
|
|
10014
9270
|
return value.replace(/\$\{([^}]+)\}/g, (match, varName) => {
|
|
10015
9271
|
const envValue = process.env[varName];
|
|
10016
9272
|
if (envValue === void 0) {
|
|
10017
9273
|
console.warn(
|
|
10018
|
-
|
|
9274
|
+
chalk31.yellow(` Warning: Environment variable ${varName} not set`)
|
|
10019
9275
|
);
|
|
10020
9276
|
return match;
|
|
10021
9277
|
}
|
|
@@ -10044,134 +9300,465 @@ function formatInTimezone(isoDate, timezone) {
|
|
|
10044
9300
|
const get = (type) => parts.find((p) => p.type === type)?.value ?? "";
|
|
10045
9301
|
return `${get("year")}-${get("month")}-${get("day")} ${get("hour")}:${get("minute")}`;
|
|
10046
9302
|
}
|
|
10047
|
-
|
|
9303
|
+
function parseFrequencyFromCron(cron) {
|
|
9304
|
+
const parts = cron.split(" ");
|
|
9305
|
+
if (parts.length !== 5) return null;
|
|
9306
|
+
const [minute, hour, dayOfMonth, , dayOfWeek] = parts;
|
|
9307
|
+
const time = `${hour.padStart(2, "0")}:${minute.padStart(2, "0")}`;
|
|
9308
|
+
if (dayOfMonth === "*" && dayOfWeek === "*") {
|
|
9309
|
+
return { frequency: "daily", time };
|
|
9310
|
+
} else if (dayOfMonth === "*" && dayOfWeek !== "*") {
|
|
9311
|
+
return { frequency: "weekly", day: parseInt(dayOfWeek, 10), time };
|
|
9312
|
+
} else if (dayOfMonth !== "*" && dayOfWeek === "*") {
|
|
9313
|
+
return { frequency: "monthly", day: parseInt(dayOfMonth, 10), time };
|
|
9314
|
+
}
|
|
9315
|
+
return null;
|
|
9316
|
+
}
|
|
9317
|
+
function collect(value, previous) {
|
|
9318
|
+
return previous.concat([value]);
|
|
9319
|
+
}
|
|
9320
|
+
function parseKeyValuePairs(pairs) {
|
|
9321
|
+
const result = {};
|
|
9322
|
+
for (const pair of pairs) {
|
|
9323
|
+
const eqIndex = pair.indexOf("=");
|
|
9324
|
+
if (eqIndex > 0) {
|
|
9325
|
+
const key = pair.slice(0, eqIndex);
|
|
9326
|
+
const value = pair.slice(eqIndex + 1);
|
|
9327
|
+
result[key] = value;
|
|
9328
|
+
}
|
|
9329
|
+
}
|
|
9330
|
+
return result;
|
|
9331
|
+
}
|
|
9332
|
+
function getExistingDefaults(existingSchedule) {
|
|
9333
|
+
const defaults = {};
|
|
9334
|
+
if (existingSchedule?.cronExpression) {
|
|
9335
|
+
const parsed = parseFrequencyFromCron(existingSchedule.cronExpression);
|
|
9336
|
+
if (parsed) {
|
|
9337
|
+
defaults.frequency = parsed.frequency;
|
|
9338
|
+
defaults.day = parsed.day;
|
|
9339
|
+
defaults.time = parsed.time;
|
|
9340
|
+
}
|
|
9341
|
+
} else if (existingSchedule?.atTime) {
|
|
9342
|
+
defaults.frequency = "once";
|
|
9343
|
+
}
|
|
9344
|
+
return defaults;
|
|
9345
|
+
}
|
|
9346
|
+
async function gatherFrequency(optionFrequency, existingFrequency) {
|
|
9347
|
+
let frequency = optionFrequency;
|
|
9348
|
+
if (frequency && ["daily", "weekly", "monthly", "once"].includes(frequency)) {
|
|
9349
|
+
return frequency;
|
|
9350
|
+
}
|
|
9351
|
+
if (!isInteractive()) {
|
|
9352
|
+
console.error(
|
|
9353
|
+
chalk31.red("\u2717 --frequency is required (daily|weekly|monthly|once)")
|
|
9354
|
+
);
|
|
9355
|
+
process.exit(1);
|
|
9356
|
+
}
|
|
9357
|
+
const defaultIndex = existingFrequency ? FREQUENCY_CHOICES.findIndex((c19) => c19.value === existingFrequency) : 0;
|
|
9358
|
+
frequency = await promptSelect(
|
|
9359
|
+
"Schedule frequency",
|
|
9360
|
+
FREQUENCY_CHOICES,
|
|
9361
|
+
defaultIndex >= 0 ? defaultIndex : 0
|
|
9362
|
+
);
|
|
9363
|
+
return frequency || null;
|
|
9364
|
+
}
|
|
9365
|
+
async function gatherDay(frequency, optionDay, existingDay) {
|
|
9366
|
+
if (frequency !== "weekly" && frequency !== "monthly") {
|
|
9367
|
+
return null;
|
|
9368
|
+
}
|
|
9369
|
+
if (optionDay) {
|
|
9370
|
+
const day2 = parseDayOption(optionDay, frequency);
|
|
9371
|
+
if (day2 === void 0) {
|
|
9372
|
+
console.error(
|
|
9373
|
+
chalk31.red(
|
|
9374
|
+
`\u2717 Invalid day: ${optionDay}. Use mon-sun for weekly or 1-31 for monthly.`
|
|
9375
|
+
)
|
|
9376
|
+
);
|
|
9377
|
+
process.exit(1);
|
|
9378
|
+
}
|
|
9379
|
+
return day2;
|
|
9380
|
+
}
|
|
9381
|
+
if (!isInteractive()) {
|
|
9382
|
+
console.error(chalk31.red("\u2717 --day is required for weekly/monthly"));
|
|
9383
|
+
process.exit(1);
|
|
9384
|
+
}
|
|
9385
|
+
if (frequency === "weekly") {
|
|
9386
|
+
const defaultDayIndex = existingDay !== void 0 ? DAY_OF_WEEK_CHOICES.findIndex((c19) => c19.value === existingDay) : 0;
|
|
9387
|
+
const day2 = await promptSelect(
|
|
9388
|
+
"Day of week",
|
|
9389
|
+
DAY_OF_WEEK_CHOICES,
|
|
9390
|
+
defaultDayIndex >= 0 ? defaultDayIndex : 0
|
|
9391
|
+
);
|
|
9392
|
+
return day2 ?? null;
|
|
9393
|
+
}
|
|
9394
|
+
const dayStr = await promptText(
|
|
9395
|
+
"Day of month (1-31)",
|
|
9396
|
+
existingDay?.toString() || "1"
|
|
9397
|
+
);
|
|
9398
|
+
if (!dayStr) return null;
|
|
9399
|
+
const day = parseInt(dayStr, 10);
|
|
9400
|
+
if (isNaN(day) || day < 1 || day > 31) {
|
|
9401
|
+
console.error(chalk31.red("\u2717 Day must be between 1 and 31"));
|
|
9402
|
+
process.exit(1);
|
|
9403
|
+
}
|
|
9404
|
+
return day;
|
|
9405
|
+
}
|
|
9406
|
+
async function gatherRecurringTime(optionTime, existingTime) {
|
|
9407
|
+
if (optionTime) {
|
|
9408
|
+
const validation = validateTimeFormat(optionTime);
|
|
9409
|
+
if (validation !== true) {
|
|
9410
|
+
console.error(chalk31.red(`\u2717 Invalid time: ${validation}`));
|
|
9411
|
+
process.exit(1);
|
|
9412
|
+
}
|
|
9413
|
+
return optionTime;
|
|
9414
|
+
}
|
|
9415
|
+
if (!isInteractive()) {
|
|
9416
|
+
console.error(chalk31.red("\u2717 --time is required (HH:MM format)"));
|
|
9417
|
+
process.exit(1);
|
|
9418
|
+
}
|
|
9419
|
+
return await promptText(
|
|
9420
|
+
"Time (HH:MM)",
|
|
9421
|
+
existingTime || "09:00",
|
|
9422
|
+
validateTimeFormat
|
|
9423
|
+
);
|
|
9424
|
+
}
|
|
9425
|
+
async function gatherOneTimeSchedule(optionDay, optionTime, existingTime) {
|
|
9426
|
+
if (optionDay && optionTime) {
|
|
9427
|
+
if (!validateDateFormat(optionDay)) {
|
|
9428
|
+
console.error(
|
|
9429
|
+
chalk31.red(
|
|
9430
|
+
`\u2717 Invalid date format: ${optionDay}. Use YYYY-MM-DD format.`
|
|
9431
|
+
)
|
|
9432
|
+
);
|
|
9433
|
+
process.exit(1);
|
|
9434
|
+
}
|
|
9435
|
+
if (!validateTimeFormat(optionTime)) {
|
|
9436
|
+
console.error(
|
|
9437
|
+
chalk31.red(`\u2717 Invalid time format: ${optionTime}. Use HH:MM format.`)
|
|
9438
|
+
);
|
|
9439
|
+
process.exit(1);
|
|
9440
|
+
}
|
|
9441
|
+
return `${optionDay} ${optionTime}`;
|
|
9442
|
+
}
|
|
9443
|
+
if (!isInteractive()) {
|
|
9444
|
+
console.error(chalk31.red("\u2717 One-time schedules require interactive mode"));
|
|
9445
|
+
console.error(
|
|
9446
|
+
chalk31.dim(" Or provide --day (YYYY-MM-DD) and --time (HH:MM) flags")
|
|
9447
|
+
);
|
|
9448
|
+
process.exit(1);
|
|
9449
|
+
}
|
|
9450
|
+
const tomorrowDate = getTomorrowDateLocal();
|
|
9451
|
+
const date = await promptText(
|
|
9452
|
+
"Date (YYYY-MM-DD, default tomorrow)",
|
|
9453
|
+
tomorrowDate,
|
|
9454
|
+
validateDateFormat
|
|
9455
|
+
);
|
|
9456
|
+
if (!date) return null;
|
|
9457
|
+
const currentTime = getCurrentTimeLocal();
|
|
9458
|
+
const time = await promptText(
|
|
9459
|
+
"Time (HH:MM)",
|
|
9460
|
+
existingTime || currentTime,
|
|
9461
|
+
validateTimeFormat
|
|
9462
|
+
);
|
|
9463
|
+
if (!time) return null;
|
|
9464
|
+
return `${date} ${time}`;
|
|
9465
|
+
}
|
|
9466
|
+
async function gatherTimezone(optionTimezone, existingTimezone) {
|
|
9467
|
+
if (optionTimezone) return optionTimezone;
|
|
9468
|
+
const detectedTimezone = detectTimezone();
|
|
9469
|
+
if (!isInteractive()) {
|
|
9470
|
+
return detectedTimezone;
|
|
9471
|
+
}
|
|
9472
|
+
return await promptText("Timezone", existingTimezone || detectedTimezone);
|
|
9473
|
+
}
|
|
9474
|
+
async function gatherPromptText(optionPrompt, existingPrompt) {
|
|
9475
|
+
if (optionPrompt) return optionPrompt;
|
|
9476
|
+
if (!isInteractive()) {
|
|
9477
|
+
console.error(chalk31.red("\u2717 --prompt is required"));
|
|
9478
|
+
process.exit(1);
|
|
9479
|
+
}
|
|
9480
|
+
return await promptText(
|
|
9481
|
+
"Prompt to run",
|
|
9482
|
+
existingPrompt || "let's start working."
|
|
9483
|
+
);
|
|
9484
|
+
}
|
|
9485
|
+
async function gatherVars(optionVars, existingVars) {
|
|
9486
|
+
if (optionVars.length > 0) {
|
|
9487
|
+
return parseKeyValuePairs(optionVars);
|
|
9488
|
+
}
|
|
9489
|
+
if (isInteractive() && existingVars) {
|
|
9490
|
+
const keepVars = await promptConfirm(
|
|
9491
|
+
`Keep existing variables? (${Object.keys(existingVars).join(", ")})`,
|
|
9492
|
+
true
|
|
9493
|
+
);
|
|
9494
|
+
if (keepVars) {
|
|
9495
|
+
return existingVars;
|
|
9496
|
+
}
|
|
9497
|
+
}
|
|
9498
|
+
return void 0;
|
|
9499
|
+
}
|
|
9500
|
+
async function gatherSecrets(optionSecrets, existingSecretNames) {
|
|
9501
|
+
if (optionSecrets.length > 0) {
|
|
9502
|
+
return parseKeyValuePairs(optionSecrets);
|
|
9503
|
+
}
|
|
9504
|
+
if (isInteractive() && existingSecretNames && existingSecretNames.length > 0) {
|
|
9505
|
+
const keepSecrets = await promptConfirm(
|
|
9506
|
+
`Keep existing secrets? (${existingSecretNames.join(", ")})`,
|
|
9507
|
+
true
|
|
9508
|
+
);
|
|
9509
|
+
if (!keepSecrets) {
|
|
9510
|
+
console.log(
|
|
9511
|
+
chalk31.dim(" Note: You'll need to provide new secret values")
|
|
9512
|
+
);
|
|
9513
|
+
}
|
|
9514
|
+
}
|
|
9515
|
+
return void 0;
|
|
9516
|
+
}
|
|
9517
|
+
async function gatherMissingConfiguration(required, providedSecrets, providedVars, existingSecretNames) {
|
|
9518
|
+
const secrets = { ...providedSecrets };
|
|
9519
|
+
const vars = { ...providedVars };
|
|
9520
|
+
const providedSecretNames = Object.keys(providedSecrets);
|
|
9521
|
+
const existingNames = existingSecretNames ?? [];
|
|
9522
|
+
const missingSecrets = required.secrets.filter(
|
|
9523
|
+
(name) => !providedSecretNames.includes(name) && !existingNames.includes(name)
|
|
9524
|
+
);
|
|
9525
|
+
const providedVarNames = Object.keys(providedVars);
|
|
9526
|
+
const missingVars = required.vars.filter(
|
|
9527
|
+
(name) => !providedVarNames.includes(name)
|
|
9528
|
+
);
|
|
9529
|
+
if (missingSecrets.length === 0 && missingVars.length === 0) {
|
|
9530
|
+
return { secrets, vars };
|
|
9531
|
+
}
|
|
9532
|
+
if (!isInteractive()) {
|
|
9533
|
+
return { secrets, vars };
|
|
9534
|
+
}
|
|
9535
|
+
if (missingSecrets.length > 0 || missingVars.length > 0) {
|
|
9536
|
+
console.log(chalk31.yellow("\nAgent requires the following configuration:"));
|
|
9537
|
+
if (missingSecrets.length > 0) {
|
|
9538
|
+
console.log(chalk31.dim(" Secrets:"));
|
|
9539
|
+
for (const name of missingSecrets) {
|
|
9540
|
+
console.log(chalk31.dim(` ${name}`));
|
|
9541
|
+
}
|
|
9542
|
+
}
|
|
9543
|
+
if (missingVars.length > 0) {
|
|
9544
|
+
console.log(chalk31.dim(" Vars:"));
|
|
9545
|
+
for (const name of missingVars) {
|
|
9546
|
+
console.log(chalk31.dim(` ${name}`));
|
|
9547
|
+
}
|
|
9548
|
+
}
|
|
9549
|
+
console.log("");
|
|
9550
|
+
}
|
|
9551
|
+
for (const name of missingSecrets) {
|
|
9552
|
+
const value = await promptPassword(
|
|
9553
|
+
`Enter value for secret ${chalk31.cyan(name)}`
|
|
9554
|
+
);
|
|
9555
|
+
if (value) {
|
|
9556
|
+
secrets[name] = value;
|
|
9557
|
+
}
|
|
9558
|
+
}
|
|
9559
|
+
for (const name of missingVars) {
|
|
9560
|
+
const value = await promptText(
|
|
9561
|
+
`Enter value for var ${chalk31.cyan(name)}`,
|
|
9562
|
+
""
|
|
9563
|
+
);
|
|
9564
|
+
if (value) {
|
|
9565
|
+
vars[name] = value;
|
|
9566
|
+
}
|
|
9567
|
+
}
|
|
9568
|
+
return { secrets, vars };
|
|
9569
|
+
}
|
|
9570
|
+
async function resolveAgent(agentName) {
|
|
9571
|
+
const compose = await getComposeByName(agentName);
|
|
9572
|
+
if (!compose) {
|
|
9573
|
+
console.error(chalk31.red(`\u2717 Agent not found: ${agentName}`));
|
|
9574
|
+
console.error(chalk31.dim(" Make sure the agent is composed first"));
|
|
9575
|
+
process.exit(1);
|
|
9576
|
+
}
|
|
9577
|
+
return {
|
|
9578
|
+
composeId: compose.id,
|
|
9579
|
+
scheduleName: `${agentName}-schedule`,
|
|
9580
|
+
composeContent: compose.content
|
|
9581
|
+
};
|
|
9582
|
+
}
|
|
9583
|
+
async function gatherTiming(frequency, options, defaults) {
|
|
9584
|
+
if (frequency === "once") {
|
|
9585
|
+
const result = await gatherOneTimeSchedule(
|
|
9586
|
+
options.day,
|
|
9587
|
+
options.time,
|
|
9588
|
+
defaults.time
|
|
9589
|
+
);
|
|
9590
|
+
if (!result) return null;
|
|
9591
|
+
return { day: void 0, time: void 0, atTime: result };
|
|
9592
|
+
}
|
|
9593
|
+
const day = await gatherDay(frequency, options.day, defaults.day) ?? void 0;
|
|
9594
|
+
if (day === null && (frequency === "weekly" || frequency === "monthly")) {
|
|
9595
|
+
return null;
|
|
9596
|
+
}
|
|
9597
|
+
const time = await gatherRecurringTime(options.time, defaults.time);
|
|
9598
|
+
if (!time) return null;
|
|
9599
|
+
return { day, time, atTime: void 0 };
|
|
9600
|
+
}
|
|
9601
|
+
async function findExistingSchedule(agentName) {
|
|
9602
|
+
const { schedules } = await listSchedules();
|
|
9603
|
+
return schedules.find((s) => s.composeName === agentName);
|
|
9604
|
+
}
|
|
9605
|
+
async function buildAndDeploy(params) {
|
|
9606
|
+
let cronExpression;
|
|
9607
|
+
let atTimeISO;
|
|
9608
|
+
if (params.atTime) {
|
|
9609
|
+
atTimeISO = toISODateTime(params.atTime);
|
|
9610
|
+
} else if (params.time && params.frequency !== "once") {
|
|
9611
|
+
cronExpression = generateCronExpression(
|
|
9612
|
+
params.frequency,
|
|
9613
|
+
params.time,
|
|
9614
|
+
params.day
|
|
9615
|
+
);
|
|
9616
|
+
}
|
|
9617
|
+
const expandedVars = expandEnvVarsInObject(params.vars);
|
|
9618
|
+
const expandedSecrets = expandEnvVarsInObject(params.secrets);
|
|
9619
|
+
console.log(
|
|
9620
|
+
`
|
|
9621
|
+
Deploying schedule for agent ${chalk31.cyan(params.agentName)}...`
|
|
9622
|
+
);
|
|
9623
|
+
const deployResult = await deploySchedule({
|
|
9624
|
+
name: params.scheduleName,
|
|
9625
|
+
composeId: params.composeId,
|
|
9626
|
+
cronExpression,
|
|
9627
|
+
atTime: atTimeISO,
|
|
9628
|
+
timezone: params.timezone,
|
|
9629
|
+
prompt: params.prompt,
|
|
9630
|
+
vars: expandedVars,
|
|
9631
|
+
secrets: expandedSecrets,
|
|
9632
|
+
artifactName: params.artifactName
|
|
9633
|
+
});
|
|
9634
|
+
displayDeployResult(params.agentName, deployResult);
|
|
9635
|
+
}
|
|
9636
|
+
function handleSetupError(error) {
|
|
9637
|
+
console.error(chalk31.red("\u2717 Failed to setup schedule"));
|
|
9638
|
+
if (error instanceof Error) {
|
|
9639
|
+
if (error.message.includes("Not authenticated")) {
|
|
9640
|
+
console.error(chalk31.dim(" Run: vm0 auth login"));
|
|
9641
|
+
} else {
|
|
9642
|
+
console.error(chalk31.dim(` ${error.message}`));
|
|
9643
|
+
}
|
|
9644
|
+
}
|
|
9645
|
+
process.exit(1);
|
|
9646
|
+
}
|
|
9647
|
+
function displayDeployResult(agentName, deployResult) {
|
|
9648
|
+
if (deployResult.created) {
|
|
9649
|
+
console.log(
|
|
9650
|
+
chalk31.green(`\u2713 Created schedule for agent ${chalk31.cyan(agentName)}`)
|
|
9651
|
+
);
|
|
9652
|
+
} else {
|
|
9653
|
+
console.log(
|
|
9654
|
+
chalk31.green(`\u2713 Updated schedule for agent ${chalk31.cyan(agentName)}`)
|
|
9655
|
+
);
|
|
9656
|
+
}
|
|
9657
|
+
console.log(chalk31.dim(` Timezone: ${deployResult.schedule.timezone}`));
|
|
9658
|
+
if (deployResult.schedule.cronExpression) {
|
|
9659
|
+
console.log(chalk31.dim(` Cron: ${deployResult.schedule.cronExpression}`));
|
|
9660
|
+
if (deployResult.schedule.nextRunAt) {
|
|
9661
|
+
const nextRun = formatInTimezone(
|
|
9662
|
+
deployResult.schedule.nextRunAt,
|
|
9663
|
+
deployResult.schedule.timezone
|
|
9664
|
+
);
|
|
9665
|
+
console.log(chalk31.dim(` Next run: ${nextRun}`));
|
|
9666
|
+
}
|
|
9667
|
+
} else if (deployResult.schedule.atTime) {
|
|
9668
|
+
const atTimeFormatted = formatInTimezone(
|
|
9669
|
+
deployResult.schedule.atTime,
|
|
9670
|
+
deployResult.schedule.timezone
|
|
9671
|
+
);
|
|
9672
|
+
console.log(chalk31.dim(` At: ${atTimeFormatted}`));
|
|
9673
|
+
}
|
|
9674
|
+
}
|
|
9675
|
+
var setupCommand = new Command28().name("setup").description("Create or edit a schedule for an agent").argument("<agent-name>", "Agent name to configure schedule for").option("-f, --frequency <type>", "Frequency: daily|weekly|monthly|once").option("-t, --time <HH:MM>", "Time to run (24-hour format)").option("-d, --day <day>", "Day of week (mon-sun) or day of month (1-31)").option("-z, --timezone <tz>", "IANA timezone").option("-p, --prompt <text>", "Prompt to run").option("--var <name=value>", "Variable (can be repeated)", collect, []).option("--secret <name=value>", "Secret (can be repeated)", collect, []).option("--artifact-name <name>", "Artifact name", "artifact").action(async (agentName, options) => {
|
|
10048
9676
|
try {
|
|
10049
|
-
|
|
10050
|
-
|
|
10051
|
-
|
|
10052
|
-
|
|
10053
|
-
|
|
10054
|
-
|
|
10055
|
-
|
|
10056
|
-
|
|
10057
|
-
|
|
10058
|
-
|
|
10059
|
-
|
|
10060
|
-
|
|
10061
|
-
|
|
10062
|
-
|
|
10063
|
-
|
|
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
|
-
);
|
|
10072
|
-
}
|
|
10073
|
-
process.exit(1);
|
|
9677
|
+
const { composeId, scheduleName, composeContent } = await resolveAgent(agentName);
|
|
9678
|
+
const requiredConfig = extractRequiredConfiguration(composeContent);
|
|
9679
|
+
const existingSchedule = await findExistingSchedule(agentName);
|
|
9680
|
+
console.log(
|
|
9681
|
+
chalk31.dim(
|
|
9682
|
+
existingSchedule ? `Editing existing schedule for agent ${agentName}` : `Creating new schedule for agent ${agentName}`
|
|
9683
|
+
)
|
|
9684
|
+
);
|
|
9685
|
+
const defaults = getExistingDefaults(existingSchedule);
|
|
9686
|
+
const frequency = await gatherFrequency(
|
|
9687
|
+
options.frequency,
|
|
9688
|
+
defaults.frequency
|
|
9689
|
+
);
|
|
9690
|
+
if (!frequency) {
|
|
9691
|
+
console.log(chalk31.dim("Cancelled"));
|
|
9692
|
+
return;
|
|
10074
9693
|
}
|
|
10075
|
-
const
|
|
10076
|
-
|
|
10077
|
-
|
|
10078
|
-
|
|
10079
|
-
process.exit(1);
|
|
9694
|
+
const timing = await gatherTiming(frequency, options, defaults);
|
|
9695
|
+
if (!timing) {
|
|
9696
|
+
console.log(chalk31.dim("Cancelled"));
|
|
9697
|
+
return;
|
|
10080
9698
|
}
|
|
10081
|
-
|
|
10082
|
-
|
|
10083
|
-
|
|
10084
|
-
|
|
10085
|
-
|
|
10086
|
-
|
|
9699
|
+
const { day, time, atTime } = timing;
|
|
9700
|
+
const timezone = await gatherTimezone(
|
|
9701
|
+
options.timezone,
|
|
9702
|
+
existingSchedule?.timezone
|
|
9703
|
+
);
|
|
9704
|
+
if (!timezone) {
|
|
9705
|
+
console.log(chalk31.dim("Cancelled"));
|
|
9706
|
+
return;
|
|
10087
9707
|
}
|
|
10088
|
-
const
|
|
10089
|
-
|
|
10090
|
-
|
|
10091
|
-
|
|
10092
|
-
|
|
10093
|
-
|
|
10094
|
-
|
|
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);
|
|
9708
|
+
const promptText_ = await gatherPromptText(
|
|
9709
|
+
options.prompt,
|
|
9710
|
+
existingSchedule?.prompt
|
|
9711
|
+
);
|
|
9712
|
+
if (!promptText_) {
|
|
9713
|
+
console.log(chalk31.dim("Cancelled"));
|
|
9714
|
+
return;
|
|
10098
9715
|
}
|
|
10099
|
-
const
|
|
10100
|
-
|
|
10101
|
-
|
|
10102
|
-
|
|
10103
|
-
const
|
|
10104
|
-
|
|
9716
|
+
const initialVars = await gatherVars(
|
|
9717
|
+
options.var || [],
|
|
9718
|
+
existingSchedule?.vars
|
|
9719
|
+
);
|
|
9720
|
+
const initialSecrets = await gatherSecrets(
|
|
9721
|
+
options.secret || [],
|
|
9722
|
+
existingSchedule?.secretNames
|
|
9723
|
+
);
|
|
9724
|
+
const { secrets, vars } = await gatherMissingConfiguration(
|
|
9725
|
+
requiredConfig,
|
|
9726
|
+
initialSecrets ?? {},
|
|
9727
|
+
initialVars ?? {},
|
|
9728
|
+
existingSchedule?.secretNames
|
|
9729
|
+
);
|
|
9730
|
+
await buildAndDeploy({
|
|
9731
|
+
scheduleName,
|
|
10105
9732
|
composeId,
|
|
10106
|
-
|
|
9733
|
+
agentName,
|
|
9734
|
+
frequency,
|
|
9735
|
+
time,
|
|
9736
|
+
day,
|
|
10107
9737
|
atTime,
|
|
10108
|
-
timezone
|
|
10109
|
-
prompt:
|
|
10110
|
-
vars:
|
|
10111
|
-
secrets:
|
|
10112
|
-
artifactName:
|
|
10113
|
-
|
|
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
|
-
}
|
|
9738
|
+
timezone,
|
|
9739
|
+
prompt: promptText_,
|
|
9740
|
+
vars: Object.keys(vars).length > 0 ? vars : void 0,
|
|
9741
|
+
secrets: Object.keys(secrets).length > 0 ? secrets : void 0,
|
|
9742
|
+
artifactName: options.artifactName
|
|
9743
|
+
});
|
|
10145
9744
|
} catch (error) {
|
|
10146
|
-
|
|
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);
|
|
9745
|
+
handleSetupError(error);
|
|
10155
9746
|
}
|
|
10156
9747
|
});
|
|
10157
9748
|
|
|
10158
9749
|
// src/commands/schedule/list.ts
|
|
10159
|
-
import { Command as
|
|
10160
|
-
import
|
|
10161
|
-
var listCommand4 = new
|
|
9750
|
+
import { Command as Command29 } from "commander";
|
|
9751
|
+
import chalk32 from "chalk";
|
|
9752
|
+
var listCommand4 = new Command29().name("list").alias("ls").description("List all schedules").action(async () => {
|
|
10162
9753
|
try {
|
|
10163
9754
|
const result = await listSchedules();
|
|
10164
9755
|
if (result.schedules.length === 0) {
|
|
10165
|
-
console.log(
|
|
9756
|
+
console.log(chalk32.dim("No schedules found"));
|
|
10166
9757
|
console.log(
|
|
10167
|
-
|
|
9758
|
+
chalk32.dim(" Create one with: vm0 schedule setup <agent-name>")
|
|
10168
9759
|
);
|
|
10169
9760
|
return;
|
|
10170
9761
|
}
|
|
10171
|
-
const nameWidth = Math.max(
|
|
10172
|
-
4,
|
|
10173
|
-
...result.schedules.map((s) => s.name.length)
|
|
10174
|
-
);
|
|
10175
9762
|
const agentWidth = Math.max(
|
|
10176
9763
|
5,
|
|
10177
9764
|
...result.schedules.map((s) => s.composeName.length)
|
|
@@ -10183,19 +9770,17 @@ var listCommand4 = new Command31().name("list").alias("ls").description("List al
|
|
|
10183
9770
|
)
|
|
10184
9771
|
);
|
|
10185
9772
|
const header = [
|
|
10186
|
-
"NAME".padEnd(nameWidth),
|
|
10187
9773
|
"AGENT".padEnd(agentWidth),
|
|
10188
9774
|
"TRIGGER".padEnd(triggerWidth),
|
|
10189
9775
|
"STATUS".padEnd(8),
|
|
10190
9776
|
"NEXT RUN"
|
|
10191
9777
|
].join(" ");
|
|
10192
|
-
console.log(
|
|
9778
|
+
console.log(chalk32.dim(header));
|
|
10193
9779
|
for (const schedule of result.schedules) {
|
|
10194
9780
|
const trigger = schedule.cronExpression ? `${schedule.cronExpression} (${schedule.timezone})` : schedule.atTime || "-";
|
|
10195
|
-
const status = schedule.enabled ?
|
|
9781
|
+
const status = schedule.enabled ? chalk32.green("enabled") : chalk32.yellow("disabled");
|
|
10196
9782
|
const nextRun = schedule.enabled ? formatRelativeTime2(schedule.nextRunAt) : "-";
|
|
10197
9783
|
const row = [
|
|
10198
|
-
schedule.name.padEnd(nameWidth),
|
|
10199
9784
|
schedule.composeName.padEnd(agentWidth),
|
|
10200
9785
|
trigger.padEnd(triggerWidth),
|
|
10201
9786
|
status.padEnd(8 + (schedule.enabled ? 0 : 2)),
|
|
@@ -10205,12 +9790,12 @@ var listCommand4 = new Command31().name("list").alias("ls").description("List al
|
|
|
10205
9790
|
console.log(row);
|
|
10206
9791
|
}
|
|
10207
9792
|
} catch (error) {
|
|
10208
|
-
console.error(
|
|
9793
|
+
console.error(chalk32.red("\u2717 Failed to list schedules"));
|
|
10209
9794
|
if (error instanceof Error) {
|
|
10210
9795
|
if (error.message.includes("Not authenticated")) {
|
|
10211
|
-
console.error(
|
|
9796
|
+
console.error(chalk32.dim(" Run: vm0 auth login"));
|
|
10212
9797
|
} else {
|
|
10213
|
-
console.error(
|
|
9798
|
+
console.error(chalk32.dim(` ${error.message}`));
|
|
10214
9799
|
}
|
|
10215
9800
|
}
|
|
10216
9801
|
process.exit(1);
|
|
@@ -10218,213 +9803,181 @@ var listCommand4 = new Command31().name("list").alias("ls").description("List al
|
|
|
10218
9803
|
});
|
|
10219
9804
|
|
|
10220
9805
|
// src/commands/schedule/status.ts
|
|
10221
|
-
import { Command as
|
|
10222
|
-
import
|
|
9806
|
+
import { Command as Command30 } from "commander";
|
|
9807
|
+
import chalk33 from "chalk";
|
|
10223
9808
|
function formatDateTimeStyled(dateStr) {
|
|
10224
|
-
if (!dateStr) return
|
|
9809
|
+
if (!dateStr) return chalk33.dim("-");
|
|
10225
9810
|
const formatted = formatDateTime(dateStr);
|
|
10226
|
-
return formatted.replace(/\(([^)]+)\)$/,
|
|
9811
|
+
return formatted.replace(/\(([^)]+)\)$/, chalk33.dim("($1)"));
|
|
10227
9812
|
}
|
|
10228
9813
|
function formatTrigger(schedule) {
|
|
10229
9814
|
if (schedule.cronExpression) {
|
|
10230
9815
|
return schedule.cronExpression;
|
|
10231
9816
|
}
|
|
10232
9817
|
if (schedule.atTime) {
|
|
10233
|
-
return `${schedule.atTime} ${
|
|
9818
|
+
return `${schedule.atTime} ${chalk33.dim("(one-time)")}`;
|
|
10234
9819
|
}
|
|
10235
|
-
return
|
|
9820
|
+
return chalk33.dim("-");
|
|
10236
9821
|
}
|
|
10237
9822
|
function formatRunStatus(status) {
|
|
10238
9823
|
switch (status) {
|
|
10239
9824
|
case "completed":
|
|
10240
|
-
return
|
|
9825
|
+
return chalk33.green(status);
|
|
10241
9826
|
case "failed":
|
|
10242
9827
|
case "timeout":
|
|
10243
|
-
return
|
|
9828
|
+
return chalk33.red(status);
|
|
10244
9829
|
case "running":
|
|
10245
|
-
return
|
|
9830
|
+
return chalk33.blue(status);
|
|
10246
9831
|
case "pending":
|
|
10247
|
-
return
|
|
9832
|
+
return chalk33.yellow(status);
|
|
10248
9833
|
default:
|
|
10249
9834
|
return status;
|
|
10250
9835
|
}
|
|
10251
9836
|
}
|
|
10252
|
-
|
|
10253
|
-
"
|
|
10254
|
-
"
|
|
10255
|
-
|
|
10256
|
-
|
|
10257
|
-
|
|
10258
|
-
"
|
|
10259
|
-
|
|
10260
|
-
|
|
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}`);
|
|
9837
|
+
function printRunConfiguration(schedule) {
|
|
9838
|
+
const statusText = schedule.enabled ? chalk33.green("enabled") : chalk33.yellow("disabled");
|
|
9839
|
+
console.log(`${"Status:".padEnd(16)}${statusText}`);
|
|
9840
|
+
console.log(
|
|
9841
|
+
`${"Agent:".padEnd(16)}${schedule.composeName} ${chalk33.dim(`(${schedule.scopeSlug})`)}`
|
|
9842
|
+
);
|
|
9843
|
+
const promptPreview = schedule.prompt.length > 60 ? schedule.prompt.slice(0, 57) + "..." : schedule.prompt;
|
|
9844
|
+
console.log(`${"Prompt:".padEnd(16)}${chalk33.dim(promptPreview)}`);
|
|
9845
|
+
if (schedule.vars && Object.keys(schedule.vars).length > 0) {
|
|
10287
9846
|
console.log(
|
|
10288
|
-
`${"
|
|
9847
|
+
`${"Variables:".padEnd(16)}${Object.keys(schedule.vars).join(", ")}`
|
|
10289
9848
|
);
|
|
10290
|
-
|
|
10291
|
-
|
|
10292
|
-
|
|
10293
|
-
|
|
10294
|
-
|
|
10295
|
-
|
|
10296
|
-
}
|
|
10297
|
-
|
|
10298
|
-
|
|
10299
|
-
|
|
10300
|
-
)
|
|
10301
|
-
|
|
10302
|
-
|
|
10303
|
-
|
|
10304
|
-
|
|
10305
|
-
|
|
10306
|
-
|
|
9849
|
+
}
|
|
9850
|
+
if (schedule.secretNames && schedule.secretNames.length > 0) {
|
|
9851
|
+
console.log(`${"Secrets:".padEnd(16)}${schedule.secretNames.join(", ")}`);
|
|
9852
|
+
}
|
|
9853
|
+
if (schedule.artifactName) {
|
|
9854
|
+
const artifactInfo = schedule.artifactVersion ? `${schedule.artifactName}:${schedule.artifactVersion}` : schedule.artifactName;
|
|
9855
|
+
console.log(`${"Artifact:".padEnd(16)}${artifactInfo}`);
|
|
9856
|
+
}
|
|
9857
|
+
if (schedule.volumeVersions && Object.keys(schedule.volumeVersions).length > 0) {
|
|
9858
|
+
console.log(
|
|
9859
|
+
`${"Volumes:".padEnd(16)}${Object.keys(schedule.volumeVersions).join(", ")}`
|
|
9860
|
+
);
|
|
9861
|
+
}
|
|
9862
|
+
}
|
|
9863
|
+
function printTimeSchedule(schedule) {
|
|
9864
|
+
console.log();
|
|
9865
|
+
console.log(`${"Trigger:".padEnd(16)}${formatTrigger(schedule)}`);
|
|
9866
|
+
console.log(`${"Timezone:".padEnd(16)}${detectTimezone()}`);
|
|
9867
|
+
if (schedule.enabled) {
|
|
9868
|
+
console.log(
|
|
9869
|
+
`${"Next Run:".padEnd(16)}${formatDateTimeStyled(schedule.nextRunAt)}`
|
|
9870
|
+
);
|
|
9871
|
+
}
|
|
9872
|
+
}
|
|
9873
|
+
async function printRecentRuns(name, composeId, limit) {
|
|
9874
|
+
if (limit <= 0) return;
|
|
9875
|
+
try {
|
|
9876
|
+
const { runs } = await listScheduleRuns({ name, composeId, limit });
|
|
9877
|
+
if (runs.length > 0) {
|
|
9878
|
+
console.log();
|
|
9879
|
+
console.log("Recent Runs:");
|
|
10307
9880
|
console.log(
|
|
10308
|
-
|
|
9881
|
+
chalk33.dim("RUN ID STATUS CREATED")
|
|
10309
9882
|
);
|
|
9883
|
+
for (const run of runs) {
|
|
9884
|
+
const id = run.id;
|
|
9885
|
+
const status = formatRunStatus(run.status).padEnd(10);
|
|
9886
|
+
const created = formatDateTimeStyled(run.createdAt);
|
|
9887
|
+
console.log(`${id} ${status} ${created}`);
|
|
9888
|
+
}
|
|
10310
9889
|
}
|
|
9890
|
+
} catch {
|
|
10311
9891
|
console.log();
|
|
10312
|
-
console.log(
|
|
10313
|
-
|
|
10314
|
-
|
|
10315
|
-
|
|
10316
|
-
|
|
10317
|
-
|
|
9892
|
+
console.log(chalk33.dim("Recent Runs: (unable to fetch)"));
|
|
9893
|
+
}
|
|
9894
|
+
}
|
|
9895
|
+
function handleStatusError(error, agentName) {
|
|
9896
|
+
console.error(chalk33.red("\u2717 Failed to get schedule status"));
|
|
9897
|
+
if (error instanceof Error) {
|
|
9898
|
+
if (error.message.includes("Not authenticated")) {
|
|
9899
|
+
console.error(chalk33.dim(" Run: vm0 auth login"));
|
|
9900
|
+
} else if (error.message.includes("not found") || error.message.includes("Not found") || error.message.includes("No schedule found")) {
|
|
9901
|
+
console.error(chalk33.dim(` No schedule found for agent "${agentName}"`));
|
|
9902
|
+
console.error(chalk33.dim(" Run: vm0 schedule list"));
|
|
9903
|
+
} else {
|
|
9904
|
+
console.error(chalk33.dim(` ${error.message}`));
|
|
10318
9905
|
}
|
|
9906
|
+
}
|
|
9907
|
+
process.exit(1);
|
|
9908
|
+
}
|
|
9909
|
+
var statusCommand5 = new Command30().name("status").description("Show detailed status of a schedule").argument("<agent-name>", "Agent name").option(
|
|
9910
|
+
"-l, --limit <number>",
|
|
9911
|
+
"Number of recent runs to show (0 to hide)",
|
|
9912
|
+
"5"
|
|
9913
|
+
).action(async (agentName, options) => {
|
|
9914
|
+
try {
|
|
9915
|
+
const resolved = await resolveScheduleByAgent(agentName);
|
|
9916
|
+
const { name, composeId } = resolved;
|
|
9917
|
+
const schedule = await getScheduleByName({ name, composeId });
|
|
9918
|
+
console.log();
|
|
9919
|
+
console.log(`Schedule for agent: ${chalk33.cyan(agentName)}`);
|
|
9920
|
+
console.log(chalk33.dim("\u2501".repeat(50)));
|
|
9921
|
+
printRunConfiguration(schedule);
|
|
9922
|
+
printTimeSchedule(schedule);
|
|
10319
9923
|
const limit = Math.min(
|
|
10320
9924
|
Math.max(0, parseInt(options.limit, 10) || 5),
|
|
10321
9925
|
100
|
|
10322
9926
|
);
|
|
10323
|
-
|
|
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
|
-
}
|
|
9927
|
+
await printRecentRuns(name, composeId, limit);
|
|
10350
9928
|
console.log();
|
|
10351
9929
|
} catch (error) {
|
|
10352
|
-
|
|
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);
|
|
9930
|
+
handleStatusError(error, agentName);
|
|
10366
9931
|
}
|
|
10367
9932
|
});
|
|
10368
9933
|
|
|
10369
9934
|
// src/commands/schedule/delete.ts
|
|
10370
|
-
import { Command as
|
|
10371
|
-
import
|
|
9935
|
+
import { Command as Command31 } from "commander";
|
|
9936
|
+
import chalk34 from "chalk";
|
|
10372
9937
|
import * as readline from "readline";
|
|
10373
9938
|
async function confirm(message) {
|
|
10374
9939
|
const rl = readline.createInterface({
|
|
10375
9940
|
input: process.stdin,
|
|
10376
9941
|
output: process.stdout
|
|
10377
9942
|
});
|
|
10378
|
-
return new Promise((
|
|
9943
|
+
return new Promise((resolve) => {
|
|
10379
9944
|
rl.question(`${message} (y/N) `, (answer) => {
|
|
10380
9945
|
rl.close();
|
|
10381
|
-
|
|
9946
|
+
resolve(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
|
|
10382
9947
|
});
|
|
10383
9948
|
});
|
|
10384
9949
|
}
|
|
10385
|
-
var deleteCommand = new
|
|
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;
|
|
9950
|
+
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
9951
|
try {
|
|
10391
|
-
|
|
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);
|
|
9952
|
+
const resolved = await resolveScheduleByAgent(agentName);
|
|
10409
9953
|
if (!options.force) {
|
|
10410
|
-
const confirmed = await confirm(
|
|
9954
|
+
const confirmed = await confirm(
|
|
9955
|
+
`Delete schedule for agent ${chalk34.cyan(agentName)}?`
|
|
9956
|
+
);
|
|
10411
9957
|
if (!confirmed) {
|
|
10412
|
-
console.log(
|
|
9958
|
+
console.log(chalk34.dim("Cancelled"));
|
|
10413
9959
|
return;
|
|
10414
9960
|
}
|
|
10415
9961
|
}
|
|
10416
|
-
await deleteSchedule({
|
|
10417
|
-
|
|
9962
|
+
await deleteSchedule({
|
|
9963
|
+
name: resolved.name,
|
|
9964
|
+
composeId: resolved.composeId
|
|
9965
|
+
});
|
|
9966
|
+
console.log(
|
|
9967
|
+
chalk34.green(`\u2713 Deleted schedule for agent ${chalk34.cyan(agentName)}`)
|
|
9968
|
+
);
|
|
10418
9969
|
} catch (error) {
|
|
10419
|
-
console.error(
|
|
9970
|
+
console.error(chalk34.red("\u2717 Failed to delete schedule"));
|
|
10420
9971
|
if (error instanceof Error) {
|
|
10421
9972
|
if (error.message.includes("Not authenticated")) {
|
|
10422
|
-
console.error(
|
|
10423
|
-
} else if (error.message.toLowerCase().includes("not found")) {
|
|
10424
|
-
console.error(
|
|
10425
|
-
|
|
9973
|
+
console.error(chalk34.dim(" Run: vm0 auth login"));
|
|
9974
|
+
} else if (error.message.toLowerCase().includes("not found") || error.message.includes("No schedule found")) {
|
|
9975
|
+
console.error(
|
|
9976
|
+
chalk34.dim(` No schedule found for agent "${agentName}"`)
|
|
9977
|
+
);
|
|
9978
|
+
console.error(chalk34.dim(" Run: vm0 schedule list"));
|
|
10426
9979
|
} else {
|
|
10427
|
-
console.error(
|
|
9980
|
+
console.error(chalk34.dim(` ${error.message}`));
|
|
10428
9981
|
}
|
|
10429
9982
|
}
|
|
10430
9983
|
process.exit(1);
|
|
@@ -10432,44 +9985,30 @@ var deleteCommand = new Command33().name("delete").alias("rm").description("Dele
|
|
|
10432
9985
|
});
|
|
10433
9986
|
|
|
10434
9987
|
// src/commands/schedule/enable.ts
|
|
10435
|
-
import { Command as
|
|
10436
|
-
import
|
|
10437
|
-
var enableCommand = new
|
|
10438
|
-
"[name]",
|
|
10439
|
-
"Schedule name (auto-detected from schedule.yaml if omitted)"
|
|
10440
|
-
).action(async (nameArg) => {
|
|
9988
|
+
import { Command as Command32 } from "commander";
|
|
9989
|
+
import chalk35 from "chalk";
|
|
9990
|
+
var enableCommand = new Command32().name("enable").description("Enable a schedule").argument("<agent-name>", "Agent name").action(async (agentName) => {
|
|
10441
9991
|
try {
|
|
10442
|
-
|
|
10443
|
-
|
|
10444
|
-
|
|
10445
|
-
|
|
10446
|
-
|
|
10447
|
-
|
|
10448
|
-
}
|
|
10449
|
-
|
|
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)}`));
|
|
9992
|
+
const resolved = await resolveScheduleByAgent(agentName);
|
|
9993
|
+
await enableSchedule({
|
|
9994
|
+
name: resolved.name,
|
|
9995
|
+
composeId: resolved.composeId
|
|
9996
|
+
});
|
|
9997
|
+
console.log(
|
|
9998
|
+
chalk35.green(`\u2713 Enabled schedule for agent ${chalk35.cyan(agentName)}`)
|
|
9999
|
+
);
|
|
10463
10000
|
} catch (error) {
|
|
10464
|
-
console.error(
|
|
10001
|
+
console.error(chalk35.red("\u2717 Failed to enable schedule"));
|
|
10465
10002
|
if (error instanceof Error) {
|
|
10466
10003
|
if (error.message.includes("Not authenticated")) {
|
|
10467
|
-
console.error(
|
|
10468
|
-
} else if (error.message.toLowerCase().includes("not found")) {
|
|
10469
|
-
console.error(
|
|
10470
|
-
|
|
10004
|
+
console.error(chalk35.dim(" Run: vm0 auth login"));
|
|
10005
|
+
} else if (error.message.toLowerCase().includes("not found") || error.message.includes("No schedule found")) {
|
|
10006
|
+
console.error(
|
|
10007
|
+
chalk35.dim(` No schedule found for agent "${agentName}"`)
|
|
10008
|
+
);
|
|
10009
|
+
console.error(chalk35.dim(" Run: vm0 schedule list"));
|
|
10471
10010
|
} else {
|
|
10472
|
-
console.error(
|
|
10011
|
+
console.error(chalk35.dim(` ${error.message}`));
|
|
10473
10012
|
}
|
|
10474
10013
|
}
|
|
10475
10014
|
process.exit(1);
|
|
@@ -10477,44 +10016,30 @@ var enableCommand = new Command34().name("enable").description("Enable a schedul
|
|
|
10477
10016
|
});
|
|
10478
10017
|
|
|
10479
10018
|
// src/commands/schedule/disable.ts
|
|
10480
|
-
import { Command as
|
|
10481
|
-
import
|
|
10482
|
-
var disableCommand = new
|
|
10483
|
-
"[name]",
|
|
10484
|
-
"Schedule name (auto-detected from schedule.yaml if omitted)"
|
|
10485
|
-
).action(async (nameArg) => {
|
|
10019
|
+
import { Command as Command33 } from "commander";
|
|
10020
|
+
import chalk36 from "chalk";
|
|
10021
|
+
var disableCommand = new Command33().name("disable").description("Disable a schedule").argument("<agent-name>", "Agent name").action(async (agentName) => {
|
|
10486
10022
|
try {
|
|
10487
|
-
|
|
10488
|
-
|
|
10489
|
-
|
|
10490
|
-
|
|
10491
|
-
|
|
10492
|
-
|
|
10493
|
-
}
|
|
10494
|
-
|
|
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)}`));
|
|
10023
|
+
const resolved = await resolveScheduleByAgent(agentName);
|
|
10024
|
+
await disableSchedule({
|
|
10025
|
+
name: resolved.name,
|
|
10026
|
+
composeId: resolved.composeId
|
|
10027
|
+
});
|
|
10028
|
+
console.log(
|
|
10029
|
+
chalk36.green(`\u2713 Disabled schedule for agent ${chalk36.cyan(agentName)}`)
|
|
10030
|
+
);
|
|
10508
10031
|
} catch (error) {
|
|
10509
|
-
console.error(
|
|
10032
|
+
console.error(chalk36.red("\u2717 Failed to disable schedule"));
|
|
10510
10033
|
if (error instanceof Error) {
|
|
10511
10034
|
if (error.message.includes("Not authenticated")) {
|
|
10512
|
-
console.error(
|
|
10513
|
-
} else if (error.message.toLowerCase().includes("not found")) {
|
|
10514
|
-
console.error(
|
|
10515
|
-
|
|
10035
|
+
console.error(chalk36.dim(" Run: vm0 auth login"));
|
|
10036
|
+
} else if (error.message.toLowerCase().includes("not found") || error.message.includes("No schedule found")) {
|
|
10037
|
+
console.error(
|
|
10038
|
+
chalk36.dim(` No schedule found for agent "${agentName}"`)
|
|
10039
|
+
);
|
|
10040
|
+
console.error(chalk36.dim(" Run: vm0 schedule list"));
|
|
10516
10041
|
} else {
|
|
10517
|
-
console.error(
|
|
10042
|
+
console.error(chalk36.dim(` ${error.message}`));
|
|
10518
10043
|
}
|
|
10519
10044
|
}
|
|
10520
10045
|
process.exit(1);
|
|
@@ -10522,11 +10047,11 @@ var disableCommand = new Command35().name("disable").description("Disable a sche
|
|
|
10522
10047
|
});
|
|
10523
10048
|
|
|
10524
10049
|
// src/commands/schedule/index.ts
|
|
10525
|
-
var scheduleCommand = new
|
|
10050
|
+
var scheduleCommand = new Command34().name("schedule").description("Manage agent schedules").addCommand(setupCommand).addCommand(listCommand4).addCommand(statusCommand5).addCommand(deleteCommand).addCommand(enableCommand).addCommand(disableCommand);
|
|
10526
10051
|
|
|
10527
10052
|
// src/commands/usage.ts
|
|
10528
|
-
import { Command as
|
|
10529
|
-
import
|
|
10053
|
+
import { Command as Command35 } from "commander";
|
|
10054
|
+
import chalk37 from "chalk";
|
|
10530
10055
|
|
|
10531
10056
|
// src/lib/utils/duration-formatter.ts
|
|
10532
10057
|
function formatDuration(ms) {
|
|
@@ -10599,7 +10124,7 @@ function fillMissingDates(daily, startDate, endDate) {
|
|
|
10599
10124
|
result.sort((a, b) => b.date.localeCompare(a.date));
|
|
10600
10125
|
return result;
|
|
10601
10126
|
}
|
|
10602
|
-
var usageCommand = new
|
|
10127
|
+
var usageCommand = new Command35().name("usage").description("View usage statistics").option("--since <date>", "Start date (ISO format or relative: 7d, 30d)").option(
|
|
10603
10128
|
"--until <date>",
|
|
10604
10129
|
"End date (ISO format or relative, defaults to now)"
|
|
10605
10130
|
).action(async (options) => {
|
|
@@ -10613,7 +10138,7 @@ var usageCommand = new Command37().name("usage").description("View usage statist
|
|
|
10613
10138
|
endDate = new Date(untilMs);
|
|
10614
10139
|
} catch {
|
|
10615
10140
|
console.error(
|
|
10616
|
-
|
|
10141
|
+
chalk37.red(
|
|
10617
10142
|
"Error: Invalid --until format. Use ISO (2026-01-01) or relative (7d, 30d)"
|
|
10618
10143
|
)
|
|
10619
10144
|
);
|
|
@@ -10628,7 +10153,7 @@ var usageCommand = new Command37().name("usage").description("View usage statist
|
|
|
10628
10153
|
startDate = new Date(sinceMs);
|
|
10629
10154
|
} catch {
|
|
10630
10155
|
console.error(
|
|
10631
|
-
|
|
10156
|
+
chalk37.red(
|
|
10632
10157
|
"Error: Invalid --since format. Use ISO (2026-01-01) or relative (7d, 30d)"
|
|
10633
10158
|
)
|
|
10634
10159
|
);
|
|
@@ -10638,13 +10163,13 @@ var usageCommand = new Command37().name("usage").description("View usage statist
|
|
|
10638
10163
|
startDate = new Date(endDate.getTime() - DEFAULT_RANGE_MS);
|
|
10639
10164
|
}
|
|
10640
10165
|
if (startDate >= endDate) {
|
|
10641
|
-
console.error(
|
|
10166
|
+
console.error(chalk37.red("Error: --since must be before --until"));
|
|
10642
10167
|
process.exit(1);
|
|
10643
10168
|
}
|
|
10644
10169
|
const rangeMs = endDate.getTime() - startDate.getTime();
|
|
10645
10170
|
if (rangeMs > MAX_RANGE_MS) {
|
|
10646
10171
|
console.error(
|
|
10647
|
-
|
|
10172
|
+
chalk37.red(
|
|
10648
10173
|
"Error: Time range exceeds maximum of 30 days. Use --until to specify an end date."
|
|
10649
10174
|
)
|
|
10650
10175
|
);
|
|
@@ -10661,19 +10186,19 @@ var usageCommand = new Command37().name("usage").description("View usage statist
|
|
|
10661
10186
|
);
|
|
10662
10187
|
console.log();
|
|
10663
10188
|
console.log(
|
|
10664
|
-
|
|
10189
|
+
chalk37.bold(
|
|
10665
10190
|
`Usage Summary (${formatDateRange(usage.period.start, usage.period.end)})`
|
|
10666
10191
|
)
|
|
10667
10192
|
);
|
|
10668
10193
|
console.log();
|
|
10669
|
-
console.log(
|
|
10194
|
+
console.log(chalk37.dim("DATE RUNS RUN TIME"));
|
|
10670
10195
|
for (const day of filledDaily) {
|
|
10671
10196
|
const dateDisplay = formatDateDisplay(day.date).padEnd(10);
|
|
10672
10197
|
const runsDisplay = String(day.run_count).padStart(6);
|
|
10673
10198
|
const timeDisplay = formatDuration(day.run_time_ms);
|
|
10674
10199
|
console.log(`${dateDisplay}${runsDisplay} ${timeDisplay}`);
|
|
10675
10200
|
}
|
|
10676
|
-
console.log(
|
|
10201
|
+
console.log(chalk37.dim("\u2500".repeat(29)));
|
|
10677
10202
|
const totalRunsDisplay = String(usage.summary.total_runs).padStart(6);
|
|
10678
10203
|
const totalTimeDisplay = formatDuration(usage.summary.total_run_time_ms);
|
|
10679
10204
|
console.log(
|
|
@@ -10684,68 +10209,68 @@ var usageCommand = new Command37().name("usage").description("View usage statist
|
|
|
10684
10209
|
if (error instanceof Error) {
|
|
10685
10210
|
if (error.message.includes("Not authenticated")) {
|
|
10686
10211
|
console.error(
|
|
10687
|
-
|
|
10212
|
+
chalk37.red("Error: Not authenticated. Run: vm0 auth login")
|
|
10688
10213
|
);
|
|
10689
10214
|
} else {
|
|
10690
|
-
console.error(
|
|
10215
|
+
console.error(chalk37.red(`Error: ${error.message}`));
|
|
10691
10216
|
}
|
|
10692
10217
|
} else {
|
|
10693
|
-
console.error(
|
|
10218
|
+
console.error(chalk37.red("Error: An unexpected error occurred"));
|
|
10694
10219
|
}
|
|
10695
10220
|
process.exit(1);
|
|
10696
10221
|
}
|
|
10697
10222
|
});
|
|
10698
10223
|
|
|
10699
10224
|
// src/commands/credential/index.ts
|
|
10700
|
-
import { Command as
|
|
10225
|
+
import { Command as Command39 } from "commander";
|
|
10701
10226
|
|
|
10702
10227
|
// src/commands/credential/list.ts
|
|
10703
|
-
import { Command as
|
|
10704
|
-
import
|
|
10705
|
-
var listCommand5 = new
|
|
10228
|
+
import { Command as Command36 } from "commander";
|
|
10229
|
+
import chalk38 from "chalk";
|
|
10230
|
+
var listCommand5 = new Command36().name("list").alias("ls").description("List all credentials").action(async () => {
|
|
10706
10231
|
try {
|
|
10707
10232
|
const result = await listCredentials();
|
|
10708
10233
|
if (result.credentials.length === 0) {
|
|
10709
|
-
console.log(
|
|
10234
|
+
console.log(chalk38.dim("No credentials found."));
|
|
10710
10235
|
console.log();
|
|
10711
10236
|
console.log("To add a credential:");
|
|
10712
|
-
console.log(
|
|
10237
|
+
console.log(chalk38.cyan(" vm0 credential set MY_API_KEY <value>"));
|
|
10713
10238
|
return;
|
|
10714
10239
|
}
|
|
10715
|
-
console.log(
|
|
10240
|
+
console.log(chalk38.bold("Credentials:"));
|
|
10716
10241
|
console.log();
|
|
10717
10242
|
for (const credential of result.credentials) {
|
|
10718
|
-
const typeIndicator = credential.type === "model-provider" ?
|
|
10719
|
-
console.log(` ${
|
|
10243
|
+
const typeIndicator = credential.type === "model-provider" ? chalk38.dim(" [model-provider]") : "";
|
|
10244
|
+
console.log(` ${chalk38.cyan(credential.name)}${typeIndicator}`);
|
|
10720
10245
|
if (credential.description) {
|
|
10721
|
-
console.log(` ${
|
|
10246
|
+
console.log(` ${chalk38.dim(credential.description)}`);
|
|
10722
10247
|
}
|
|
10723
10248
|
console.log(
|
|
10724
|
-
` ${
|
|
10249
|
+
` ${chalk38.dim(`Updated: ${new Date(credential.updatedAt).toLocaleString()}`)}`
|
|
10725
10250
|
);
|
|
10726
10251
|
console.log();
|
|
10727
10252
|
}
|
|
10728
10253
|
console.log(
|
|
10729
|
-
|
|
10254
|
+
chalk38.dim(`Total: ${result.credentials.length} credential(s)`)
|
|
10730
10255
|
);
|
|
10731
10256
|
} catch (error) {
|
|
10732
10257
|
if (error instanceof Error) {
|
|
10733
10258
|
if (error.message.includes("Not authenticated")) {
|
|
10734
|
-
console.error(
|
|
10259
|
+
console.error(chalk38.red("\u2717 Not authenticated. Run: vm0 auth login"));
|
|
10735
10260
|
} else {
|
|
10736
|
-
console.error(
|
|
10261
|
+
console.error(chalk38.red(`\u2717 ${error.message}`));
|
|
10737
10262
|
}
|
|
10738
10263
|
} else {
|
|
10739
|
-
console.error(
|
|
10264
|
+
console.error(chalk38.red("\u2717 An unexpected error occurred"));
|
|
10740
10265
|
}
|
|
10741
10266
|
process.exit(1);
|
|
10742
10267
|
}
|
|
10743
10268
|
});
|
|
10744
10269
|
|
|
10745
10270
|
// src/commands/credential/set.ts
|
|
10746
|
-
import { Command as
|
|
10747
|
-
import
|
|
10748
|
-
var setCommand2 = new
|
|
10271
|
+
import { Command as Command37 } from "commander";
|
|
10272
|
+
import chalk39 from "chalk";
|
|
10273
|
+
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
10274
|
async (name, value, options) => {
|
|
10750
10275
|
try {
|
|
10751
10276
|
const credential = await setCredential({
|
|
@@ -10753,29 +10278,29 @@ var setCommand2 = new Command39().name("set").description("Create or update a cr
|
|
|
10753
10278
|
value,
|
|
10754
10279
|
description: options.description
|
|
10755
10280
|
});
|
|
10756
|
-
console.log(
|
|
10281
|
+
console.log(chalk39.green(`\u2713 Credential "${credential.name}" saved`));
|
|
10757
10282
|
console.log();
|
|
10758
10283
|
console.log("Use in vm0.yaml:");
|
|
10759
|
-
console.log(
|
|
10760
|
-
console.log(
|
|
10284
|
+
console.log(chalk39.cyan(` environment:`));
|
|
10285
|
+
console.log(chalk39.cyan(` ${name}: \${{ credentials.${name} }}`));
|
|
10761
10286
|
} catch (error) {
|
|
10762
10287
|
if (error instanceof Error) {
|
|
10763
10288
|
if (error.message.includes("Not authenticated")) {
|
|
10764
10289
|
console.error(
|
|
10765
|
-
|
|
10290
|
+
chalk39.red("\u2717 Not authenticated. Run: vm0 auth login")
|
|
10766
10291
|
);
|
|
10767
10292
|
} else if (error.message.includes("must contain only uppercase")) {
|
|
10768
|
-
console.error(
|
|
10293
|
+
console.error(chalk39.red(`\u2717 ${error.message}`));
|
|
10769
10294
|
console.log();
|
|
10770
10295
|
console.log("Examples of valid credential names:");
|
|
10771
|
-
console.log(
|
|
10772
|
-
console.log(
|
|
10773
|
-
console.log(
|
|
10296
|
+
console.log(chalk39.dim(" MY_API_KEY"));
|
|
10297
|
+
console.log(chalk39.dim(" GITHUB_TOKEN"));
|
|
10298
|
+
console.log(chalk39.dim(" AWS_ACCESS_KEY_ID"));
|
|
10774
10299
|
} else {
|
|
10775
|
-
console.error(
|
|
10300
|
+
console.error(chalk39.red(`\u2717 ${error.message}`));
|
|
10776
10301
|
}
|
|
10777
10302
|
} else {
|
|
10778
|
-
console.error(
|
|
10303
|
+
console.error(chalk39.red("\u2717 An unexpected error occurred"));
|
|
10779
10304
|
}
|
|
10780
10305
|
process.exit(1);
|
|
10781
10306
|
}
|
|
@@ -10783,14 +10308,14 @@ var setCommand2 = new Command39().name("set").description("Create or update a cr
|
|
|
10783
10308
|
);
|
|
10784
10309
|
|
|
10785
10310
|
// src/commands/credential/delete.ts
|
|
10786
|
-
import { Command as
|
|
10787
|
-
import
|
|
10788
|
-
var deleteCommand2 = new
|
|
10311
|
+
import { Command as Command38 } from "commander";
|
|
10312
|
+
import chalk40 from "chalk";
|
|
10313
|
+
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
10314
|
try {
|
|
10790
10315
|
try {
|
|
10791
10316
|
await getCredential(name);
|
|
10792
10317
|
} catch {
|
|
10793
|
-
console.error(
|
|
10318
|
+
console.error(chalk40.red(`\u2717 Credential "${name}" not found`));
|
|
10794
10319
|
process.exit(1);
|
|
10795
10320
|
}
|
|
10796
10321
|
if (!options.yes) {
|
|
@@ -10799,55 +10324,55 @@ var deleteCommand2 = new Command40().name("delete").description("Delete a creden
|
|
|
10799
10324
|
input: process.stdin,
|
|
10800
10325
|
output: process.stdout
|
|
10801
10326
|
});
|
|
10802
|
-
const confirmed = await new Promise((
|
|
10327
|
+
const confirmed = await new Promise((resolve) => {
|
|
10803
10328
|
rl.question(
|
|
10804
|
-
|
|
10329
|
+
chalk40.yellow(
|
|
10805
10330
|
`Are you sure you want to delete credential "${name}"? (y/N) `
|
|
10806
10331
|
),
|
|
10807
10332
|
(answer) => {
|
|
10808
10333
|
rl.close();
|
|
10809
|
-
|
|
10334
|
+
resolve(answer.toLowerCase() === "y");
|
|
10810
10335
|
}
|
|
10811
10336
|
);
|
|
10812
10337
|
});
|
|
10813
10338
|
if (!confirmed) {
|
|
10814
|
-
console.log(
|
|
10339
|
+
console.log(chalk40.dim("Cancelled."));
|
|
10815
10340
|
return;
|
|
10816
10341
|
}
|
|
10817
10342
|
}
|
|
10818
10343
|
await deleteCredential(name);
|
|
10819
|
-
console.log(
|
|
10344
|
+
console.log(chalk40.green(`\u2713 Credential "${name}" deleted`));
|
|
10820
10345
|
} catch (error) {
|
|
10821
10346
|
if (error instanceof Error) {
|
|
10822
10347
|
if (error.message.includes("Not authenticated")) {
|
|
10823
|
-
console.error(
|
|
10348
|
+
console.error(chalk40.red("\u2717 Not authenticated. Run: vm0 auth login"));
|
|
10824
10349
|
} else {
|
|
10825
|
-
console.error(
|
|
10350
|
+
console.error(chalk40.red(`\u2717 ${error.message}`));
|
|
10826
10351
|
}
|
|
10827
10352
|
} else {
|
|
10828
|
-
console.error(
|
|
10353
|
+
console.error(chalk40.red("\u2717 An unexpected error occurred"));
|
|
10829
10354
|
}
|
|
10830
10355
|
process.exit(1);
|
|
10831
10356
|
}
|
|
10832
10357
|
});
|
|
10833
10358
|
|
|
10834
10359
|
// src/commands/credential/index.ts
|
|
10835
|
-
var credentialCommand = new
|
|
10360
|
+
var credentialCommand = new Command39().name("credential").description("Manage stored credentials for agent runs").addCommand(listCommand5).addCommand(setCommand2).addCommand(deleteCommand2);
|
|
10836
10361
|
|
|
10837
10362
|
// src/commands/model-provider/index.ts
|
|
10838
|
-
import { Command as
|
|
10363
|
+
import { Command as Command44 } from "commander";
|
|
10839
10364
|
|
|
10840
10365
|
// src/commands/model-provider/list.ts
|
|
10841
|
-
import { Command as
|
|
10842
|
-
import
|
|
10843
|
-
var listCommand6 = new
|
|
10366
|
+
import { Command as Command40 } from "commander";
|
|
10367
|
+
import chalk41 from "chalk";
|
|
10368
|
+
var listCommand6 = new Command40().name("list").alias("ls").description("List all model providers").action(async () => {
|
|
10844
10369
|
try {
|
|
10845
10370
|
const result = await listModelProviders();
|
|
10846
10371
|
if (result.modelProviders.length === 0) {
|
|
10847
|
-
console.log(
|
|
10372
|
+
console.log(chalk41.dim("No model providers configured."));
|
|
10848
10373
|
console.log();
|
|
10849
10374
|
console.log("To add a model provider:");
|
|
10850
|
-
console.log(
|
|
10375
|
+
console.log(chalk41.cyan(" vm0 model-provider setup"));
|
|
10851
10376
|
return;
|
|
10852
10377
|
}
|
|
10853
10378
|
const byFramework = result.modelProviders.reduce(
|
|
@@ -10861,15 +10386,15 @@ var listCommand6 = new Command42().name("list").alias("ls").description("List al
|
|
|
10861
10386
|
},
|
|
10862
10387
|
{}
|
|
10863
10388
|
);
|
|
10864
|
-
console.log(
|
|
10389
|
+
console.log(chalk41.bold("Model Providers:"));
|
|
10865
10390
|
console.log();
|
|
10866
10391
|
for (const [framework, providers] of Object.entries(byFramework)) {
|
|
10867
|
-
console.log(` ${
|
|
10392
|
+
console.log(` ${chalk41.cyan(framework)}:`);
|
|
10868
10393
|
for (const provider of providers) {
|
|
10869
|
-
const defaultTag = provider.isDefault ?
|
|
10394
|
+
const defaultTag = provider.isDefault ? chalk41.green(" (default)") : "";
|
|
10870
10395
|
console.log(` ${provider.type}${defaultTag}`);
|
|
10871
10396
|
console.log(
|
|
10872
|
-
|
|
10397
|
+
chalk41.dim(
|
|
10873
10398
|
` Updated: ${new Date(provider.updatedAt).toLocaleString()}`
|
|
10874
10399
|
)
|
|
10875
10400
|
);
|
|
@@ -10877,25 +10402,25 @@ var listCommand6 = new Command42().name("list").alias("ls").description("List al
|
|
|
10877
10402
|
console.log();
|
|
10878
10403
|
}
|
|
10879
10404
|
console.log(
|
|
10880
|
-
|
|
10405
|
+
chalk41.dim(`Total: ${result.modelProviders.length} provider(s)`)
|
|
10881
10406
|
);
|
|
10882
10407
|
} catch (error) {
|
|
10883
10408
|
if (error instanceof Error) {
|
|
10884
10409
|
if (error.message.includes("Not authenticated")) {
|
|
10885
|
-
console.error(
|
|
10410
|
+
console.error(chalk41.red("x Not authenticated. Run: vm0 auth login"));
|
|
10886
10411
|
} else {
|
|
10887
|
-
console.error(
|
|
10412
|
+
console.error(chalk41.red(`x ${error.message}`));
|
|
10888
10413
|
}
|
|
10889
10414
|
} else {
|
|
10890
|
-
console.error(
|
|
10415
|
+
console.error(chalk41.red("x An unexpected error occurred"));
|
|
10891
10416
|
}
|
|
10892
10417
|
process.exit(1);
|
|
10893
10418
|
}
|
|
10894
10419
|
});
|
|
10895
10420
|
|
|
10896
10421
|
// src/commands/model-provider/setup.ts
|
|
10897
|
-
import { Command as
|
|
10898
|
-
import
|
|
10422
|
+
import { Command as Command41 } from "commander";
|
|
10423
|
+
import chalk42 from "chalk";
|
|
10899
10424
|
import prompts3 from "prompts";
|
|
10900
10425
|
var providerChoices = Object.entries(MODEL_PROVIDER_TYPES).map(
|
|
10901
10426
|
([type, config]) => ({
|
|
@@ -10903,7 +10428,7 @@ var providerChoices = Object.entries(MODEL_PROVIDER_TYPES).map(
|
|
|
10903
10428
|
value: type
|
|
10904
10429
|
})
|
|
10905
10430
|
);
|
|
10906
|
-
var
|
|
10431
|
+
var setupCommand2 = new Command41().name("setup").description("Configure a model provider").option("-t, --type <type>", "Provider type (for non-interactive mode)").option(
|
|
10907
10432
|
"-c, --credential <credential>",
|
|
10908
10433
|
"Credential value (for non-interactive mode)"
|
|
10909
10434
|
).option("--convert", "Convert existing user credential to model provider").action(
|
|
@@ -10914,11 +10439,11 @@ var setupCommand = new Command43().name("setup").description("Configure a model
|
|
|
10914
10439
|
const shouldConvert = options.convert ?? false;
|
|
10915
10440
|
if (options.type && options.credential) {
|
|
10916
10441
|
if (!Object.keys(MODEL_PROVIDER_TYPES).includes(options.type)) {
|
|
10917
|
-
console.error(
|
|
10442
|
+
console.error(chalk42.red(`x Invalid type "${options.type}"`));
|
|
10918
10443
|
console.log();
|
|
10919
10444
|
console.log("Valid types:");
|
|
10920
10445
|
for (const [t, config] of Object.entries(MODEL_PROVIDER_TYPES)) {
|
|
10921
|
-
console.log(` ${
|
|
10446
|
+
console.log(` ${chalk42.cyan(t)} - ${config.label}`);
|
|
10922
10447
|
}
|
|
10923
10448
|
process.exit(1);
|
|
10924
10449
|
}
|
|
@@ -10926,16 +10451,16 @@ var setupCommand = new Command43().name("setup").description("Configure a model
|
|
|
10926
10451
|
credential = options.credential;
|
|
10927
10452
|
} else if (options.type || options.credential) {
|
|
10928
10453
|
console.error(
|
|
10929
|
-
|
|
10454
|
+
chalk42.red("x Both --type and --credential are required")
|
|
10930
10455
|
);
|
|
10931
10456
|
process.exit(1);
|
|
10932
10457
|
} else {
|
|
10933
10458
|
if (!isInteractive()) {
|
|
10934
|
-
console.error(
|
|
10459
|
+
console.error(chalk42.red("x Interactive mode requires a TTY"));
|
|
10935
10460
|
console.log();
|
|
10936
10461
|
console.log("Use non-interactive mode:");
|
|
10937
10462
|
console.log(
|
|
10938
|
-
|
|
10463
|
+
chalk42.cyan(
|
|
10939
10464
|
' vm0 model-provider setup --type <type> --credential "<value>"'
|
|
10940
10465
|
)
|
|
10941
10466
|
);
|
|
@@ -10966,19 +10491,19 @@ var setupCommand = new Command43().name("setup").description("Configure a model
|
|
|
10966
10491
|
const provider2 = await convertModelProviderCredential(type);
|
|
10967
10492
|
const defaultNote2 = provider2.isDefault ? ` (default for ${provider2.framework})` : "";
|
|
10968
10493
|
console.log(
|
|
10969
|
-
|
|
10494
|
+
chalk42.green(
|
|
10970
10495
|
`Done Converted "${checkResult.credentialName}" to model provider${defaultNote2}`
|
|
10971
10496
|
)
|
|
10972
10497
|
);
|
|
10973
10498
|
return;
|
|
10974
10499
|
} else {
|
|
10975
|
-
console.log(
|
|
10500
|
+
console.log(chalk42.dim("Aborted."));
|
|
10976
10501
|
process.exit(0);
|
|
10977
10502
|
}
|
|
10978
10503
|
}
|
|
10979
10504
|
const config = MODEL_PROVIDER_TYPES[type];
|
|
10980
10505
|
console.log();
|
|
10981
|
-
console.log(
|
|
10506
|
+
console.log(chalk42.dim(config.helpText));
|
|
10982
10507
|
console.log();
|
|
10983
10508
|
const credentialResponse = await prompts3(
|
|
10984
10509
|
{
|
|
@@ -10999,24 +10524,24 @@ var setupCommand = new Command43().name("setup").description("Configure a model
|
|
|
10999
10524
|
const action = created ? "created" : "updated";
|
|
11000
10525
|
const defaultNote = provider.isDefault ? ` (default for ${provider.framework})` : "";
|
|
11001
10526
|
console.log(
|
|
11002
|
-
|
|
10527
|
+
chalk42.green(`Done Model provider "${type}" ${action}${defaultNote}`)
|
|
11003
10528
|
);
|
|
11004
10529
|
} catch (error) {
|
|
11005
10530
|
if (error instanceof Error) {
|
|
11006
10531
|
if (error.message.includes("already exists")) {
|
|
11007
|
-
console.error(
|
|
10532
|
+
console.error(chalk42.red(`x ${error.message}`));
|
|
11008
10533
|
console.log();
|
|
11009
10534
|
console.log("To convert the existing credential, run:");
|
|
11010
|
-
console.log(
|
|
10535
|
+
console.log(chalk42.cyan(" vm0 model-provider setup --convert"));
|
|
11011
10536
|
} else if (error.message.includes("Not authenticated")) {
|
|
11012
10537
|
console.error(
|
|
11013
|
-
|
|
10538
|
+
chalk42.red("x Not authenticated. Run: vm0 auth login")
|
|
11014
10539
|
);
|
|
11015
10540
|
} else {
|
|
11016
|
-
console.error(
|
|
10541
|
+
console.error(chalk42.red(`x ${error.message}`));
|
|
11017
10542
|
}
|
|
11018
10543
|
} else {
|
|
11019
|
-
console.error(
|
|
10544
|
+
console.error(chalk42.red("x An unexpected error occurred"));
|
|
11020
10545
|
}
|
|
11021
10546
|
process.exit(1);
|
|
11022
10547
|
}
|
|
@@ -11024,81 +10549,81 @@ var setupCommand = new Command43().name("setup").description("Configure a model
|
|
|
11024
10549
|
);
|
|
11025
10550
|
|
|
11026
10551
|
// src/commands/model-provider/delete.ts
|
|
11027
|
-
import { Command as
|
|
11028
|
-
import
|
|
11029
|
-
var deleteCommand3 = new
|
|
10552
|
+
import { Command as Command42 } from "commander";
|
|
10553
|
+
import chalk43 from "chalk";
|
|
10554
|
+
var deleteCommand3 = new Command42().name("delete").description("Delete a model provider").argument("<type>", "Model provider type to delete").action(async (type) => {
|
|
11030
10555
|
try {
|
|
11031
10556
|
if (!Object.keys(MODEL_PROVIDER_TYPES).includes(type)) {
|
|
11032
|
-
console.error(
|
|
10557
|
+
console.error(chalk43.red(`x Invalid type "${type}"`));
|
|
11033
10558
|
console.log();
|
|
11034
10559
|
console.log("Valid types:");
|
|
11035
10560
|
for (const [t, config] of Object.entries(MODEL_PROVIDER_TYPES)) {
|
|
11036
|
-
console.log(` ${
|
|
10561
|
+
console.log(` ${chalk43.cyan(t)} - ${config.label}`);
|
|
11037
10562
|
}
|
|
11038
10563
|
process.exit(1);
|
|
11039
10564
|
}
|
|
11040
10565
|
await deleteModelProvider(type);
|
|
11041
|
-
console.log(
|
|
10566
|
+
console.log(chalk43.green(`Done Model provider "${type}" deleted`));
|
|
11042
10567
|
} catch (error) {
|
|
11043
10568
|
if (error instanceof Error) {
|
|
11044
10569
|
if (error.message.includes("not found")) {
|
|
11045
|
-
console.error(
|
|
10570
|
+
console.error(chalk43.red(`x Model provider "${type}" not found`));
|
|
11046
10571
|
} else if (error.message.includes("Not authenticated")) {
|
|
11047
|
-
console.error(
|
|
10572
|
+
console.error(chalk43.red("x Not authenticated. Run: vm0 auth login"));
|
|
11048
10573
|
} else {
|
|
11049
|
-
console.error(
|
|
10574
|
+
console.error(chalk43.red(`x ${error.message}`));
|
|
11050
10575
|
}
|
|
11051
10576
|
} else {
|
|
11052
|
-
console.error(
|
|
10577
|
+
console.error(chalk43.red("x An unexpected error occurred"));
|
|
11053
10578
|
}
|
|
11054
10579
|
process.exit(1);
|
|
11055
10580
|
}
|
|
11056
10581
|
});
|
|
11057
10582
|
|
|
11058
10583
|
// src/commands/model-provider/set-default.ts
|
|
11059
|
-
import { Command as
|
|
11060
|
-
import
|
|
11061
|
-
var setDefaultCommand = new
|
|
10584
|
+
import { Command as Command43 } from "commander";
|
|
10585
|
+
import chalk44 from "chalk";
|
|
10586
|
+
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
10587
|
try {
|
|
11063
10588
|
if (!Object.keys(MODEL_PROVIDER_TYPES).includes(type)) {
|
|
11064
|
-
console.error(
|
|
10589
|
+
console.error(chalk44.red(`x Invalid type "${type}"`));
|
|
11065
10590
|
console.log();
|
|
11066
10591
|
console.log("Valid types:");
|
|
11067
10592
|
for (const [t, config] of Object.entries(MODEL_PROVIDER_TYPES)) {
|
|
11068
|
-
console.log(` ${
|
|
10593
|
+
console.log(` ${chalk44.cyan(t)} - ${config.label}`);
|
|
11069
10594
|
}
|
|
11070
10595
|
process.exit(1);
|
|
11071
10596
|
}
|
|
11072
10597
|
const provider = await setModelProviderDefault(type);
|
|
11073
10598
|
console.log(
|
|
11074
|
-
|
|
10599
|
+
chalk44.green(
|
|
11075
10600
|
`Done Default for ${provider.framework} set to "${provider.type}"`
|
|
11076
10601
|
)
|
|
11077
10602
|
);
|
|
11078
10603
|
} catch (error) {
|
|
11079
10604
|
if (error instanceof Error) {
|
|
11080
10605
|
if (error.message.includes("not found")) {
|
|
11081
|
-
console.error(
|
|
10606
|
+
console.error(chalk44.red(`x Model provider "${type}" not found`));
|
|
11082
10607
|
} else if (error.message.includes("Not authenticated")) {
|
|
11083
|
-
console.error(
|
|
10608
|
+
console.error(chalk44.red("x Not authenticated. Run: vm0 auth login"));
|
|
11084
10609
|
} else {
|
|
11085
|
-
console.error(
|
|
10610
|
+
console.error(chalk44.red(`x ${error.message}`));
|
|
11086
10611
|
}
|
|
11087
10612
|
} else {
|
|
11088
|
-
console.error(
|
|
10613
|
+
console.error(chalk44.red("x An unexpected error occurred"));
|
|
11089
10614
|
}
|
|
11090
10615
|
process.exit(1);
|
|
11091
10616
|
}
|
|
11092
10617
|
});
|
|
11093
10618
|
|
|
11094
10619
|
// src/commands/model-provider/index.ts
|
|
11095
|
-
var modelProviderCommand = new
|
|
10620
|
+
var modelProviderCommand = new Command44().name("model-provider").description("Manage model providers for agent runs").addCommand(listCommand6).addCommand(setupCommand2).addCommand(deleteCommand3).addCommand(setDefaultCommand);
|
|
11096
10621
|
|
|
11097
10622
|
// src/index.ts
|
|
11098
|
-
var program = new
|
|
11099
|
-
program.name("vm0").description("VM0 CLI - Build and run agents with natural language").version("
|
|
10623
|
+
var program = new Command45();
|
|
10624
|
+
program.name("vm0").description("VM0 CLI - Build and run agents with natural language").version("8.0.1");
|
|
11100
10625
|
program.command("info").description("Display environment information").action(async () => {
|
|
11101
|
-
console.log(
|
|
10626
|
+
console.log(chalk45.bold("System Information:"));
|
|
11102
10627
|
console.log(`Node Version: ${process.version}`);
|
|
11103
10628
|
console.log(`Platform: ${process.platform}`);
|
|
11104
10629
|
console.log(`Architecture: ${process.arch}`);
|
|
@@ -11127,7 +10652,6 @@ program.addCommand(logsCommand);
|
|
|
11127
10652
|
program.addCommand(scopeCommand);
|
|
11128
10653
|
program.addCommand(agentCommand);
|
|
11129
10654
|
program.addCommand(initCommand3);
|
|
11130
|
-
program.addCommand(setupGithubCommand);
|
|
11131
10655
|
program.addCommand(scheduleCommand);
|
|
11132
10656
|
program.addCommand(usageCommand);
|
|
11133
10657
|
program.addCommand(credentialCommand);
|