@vm0/cli 9.2.1 → 9.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +750 -154
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command as Command48 } from "commander";
|
|
5
|
-
import
|
|
5
|
+
import chalk50 from "chalk";
|
|
6
6
|
|
|
7
7
|
// src/lib/api/auth.ts
|
|
8
8
|
import chalk from "chalk";
|
|
@@ -1807,19 +1807,11 @@ var MODEL_PROVIDER_TYPES = {
|
|
|
1807
1807
|
label: "Anthropic API Key",
|
|
1808
1808
|
credentialLabel: "API key",
|
|
1809
1809
|
helpText: "Get your API key at: https://console.anthropic.com/settings/keys"
|
|
1810
|
-
},
|
|
1811
|
-
"openai-api-key": {
|
|
1812
|
-
framework: "codex",
|
|
1813
|
-
credentialName: "OPENAI_API_KEY",
|
|
1814
|
-
label: "OpenAI API Key",
|
|
1815
|
-
credentialLabel: "API key",
|
|
1816
|
-
helpText: "Get your API key at: https://platform.openai.com/api-keys"
|
|
1817
1810
|
}
|
|
1818
1811
|
};
|
|
1819
1812
|
var modelProviderTypeSchema = z14.enum([
|
|
1820
1813
|
"claude-code-oauth-token",
|
|
1821
|
-
"anthropic-api-key"
|
|
1822
|
-
"openai-api-key"
|
|
1814
|
+
"anthropic-api-key"
|
|
1823
1815
|
]);
|
|
1824
1816
|
var modelProviderFrameworkSchema = z14.enum(["claude-code", "codex"]);
|
|
1825
1817
|
var modelProviderResponseSchema = z14.object({
|
|
@@ -2296,6 +2288,27 @@ var realtimeTokenContract = c14.router({
|
|
|
2296
2288
|
summary: "Get Ably token for run event subscription"
|
|
2297
2289
|
}
|
|
2298
2290
|
});
|
|
2291
|
+
var runnerRealtimeTokenContract = c14.router({
|
|
2292
|
+
/**
|
|
2293
|
+
* POST /api/runners/realtime/token
|
|
2294
|
+
* Get an Ably token to subscribe to a runner group's job notification channel
|
|
2295
|
+
*/
|
|
2296
|
+
create: {
|
|
2297
|
+
method: "POST",
|
|
2298
|
+
path: "/api/runners/realtime/token",
|
|
2299
|
+
headers: authHeadersSchema,
|
|
2300
|
+
body: z17.object({
|
|
2301
|
+
group: runnerGroupSchema
|
|
2302
|
+
}),
|
|
2303
|
+
responses: {
|
|
2304
|
+
200: ablyTokenRequestSchema,
|
|
2305
|
+
401: apiErrorSchema,
|
|
2306
|
+
403: apiErrorSchema,
|
|
2307
|
+
500: apiErrorSchema
|
|
2308
|
+
},
|
|
2309
|
+
summary: "Get Ably token for runner group job notifications"
|
|
2310
|
+
}
|
|
2311
|
+
});
|
|
2299
2312
|
|
|
2300
2313
|
// ../../packages/core/src/contracts/platform.ts
|
|
2301
2314
|
import { z as z19 } from "zod";
|
|
@@ -3136,10 +3149,10 @@ async function getRawHeaders() {
|
|
|
3136
3149
|
}
|
|
3137
3150
|
return headers;
|
|
3138
3151
|
}
|
|
3139
|
-
async function httpGet(
|
|
3152
|
+
async function httpGet(path16) {
|
|
3140
3153
|
const baseUrl = await getBaseUrl();
|
|
3141
3154
|
const headers = await getRawHeaders();
|
|
3142
|
-
return fetch(`${baseUrl}${
|
|
3155
|
+
return fetch(`${baseUrl}${path16}`, {
|
|
3143
3156
|
method: "GET",
|
|
3144
3157
|
headers
|
|
3145
3158
|
});
|
|
@@ -3619,49 +3632,49 @@ var cliComposeSchema = z24.object({
|
|
|
3619
3632
|
function formatZodError(error) {
|
|
3620
3633
|
const issue = error.issues[0];
|
|
3621
3634
|
if (!issue) return "Validation failed";
|
|
3622
|
-
const
|
|
3635
|
+
const path16 = issue.path.join(".");
|
|
3623
3636
|
const message = issue.message;
|
|
3624
|
-
if (!
|
|
3637
|
+
if (!path16) return message;
|
|
3625
3638
|
if (issue.code === "invalid_type") {
|
|
3626
3639
|
const received = issue.received;
|
|
3627
3640
|
const isMissing = received === "undefined" || message.includes("received undefined") || message === "Required";
|
|
3628
|
-
if (
|
|
3641
|
+
if (path16 === "version" && isMissing) {
|
|
3629
3642
|
return "Missing config.version";
|
|
3630
3643
|
}
|
|
3631
|
-
if (
|
|
3644
|
+
if (path16 === "agents" && isMissing) {
|
|
3632
3645
|
return "Missing agents object in config";
|
|
3633
3646
|
}
|
|
3634
|
-
if (
|
|
3635
|
-
const volumeKey =
|
|
3647
|
+
if (path16.startsWith("volumes.") && path16.endsWith(".name")) {
|
|
3648
|
+
const volumeKey = path16.split(".")[1];
|
|
3636
3649
|
return `Volume "${volumeKey}" must have a 'name' field (string)`;
|
|
3637
3650
|
}
|
|
3638
|
-
if (
|
|
3639
|
-
const volumeKey =
|
|
3651
|
+
if (path16.startsWith("volumes.") && path16.endsWith(".version")) {
|
|
3652
|
+
const volumeKey = path16.split(".")[1];
|
|
3640
3653
|
return `Volume "${volumeKey}" must have a 'version' field (string)`;
|
|
3641
3654
|
}
|
|
3642
3655
|
if (issue.expected === "array") {
|
|
3643
|
-
const fieldName =
|
|
3656
|
+
const fieldName = path16.replace(/^agents\.[^.]+\./, "agent.");
|
|
3644
3657
|
return `${fieldName} must be an array`;
|
|
3645
3658
|
}
|
|
3646
3659
|
if (issue.expected === "string" && received === "number") {
|
|
3647
|
-
const fieldName =
|
|
3660
|
+
const fieldName = path16.replace(/^agents\.[^.]+\./, "agent.");
|
|
3648
3661
|
const match = fieldName.match(/^(agent\.[^.]+)\.\d+$/);
|
|
3649
3662
|
if (match) {
|
|
3650
3663
|
return `Each entry in ${match[1]?.replace("agent.", "")} must be a string`;
|
|
3651
3664
|
}
|
|
3652
3665
|
}
|
|
3653
3666
|
}
|
|
3654
|
-
if (issue.code === "invalid_key" &&
|
|
3667
|
+
if (issue.code === "invalid_key" && path16.startsWith("agents.")) {
|
|
3655
3668
|
return "Invalid agent name format. Must be 3-64 characters, letters, numbers, and hyphens only. Must start and end with letter or number.";
|
|
3656
3669
|
}
|
|
3657
|
-
if (message === "Invalid key in record" &&
|
|
3670
|
+
if (message === "Invalid key in record" && path16.startsWith("agents.")) {
|
|
3658
3671
|
return "Invalid agent name format. Must be 3-64 characters, letters, numbers, and hyphens only. Must start and end with letter or number.";
|
|
3659
3672
|
}
|
|
3660
3673
|
if (issue.code === "custom") {
|
|
3661
3674
|
return message;
|
|
3662
3675
|
}
|
|
3663
|
-
if (
|
|
3664
|
-
const cleanPath =
|
|
3676
|
+
if (path16.startsWith("agents.")) {
|
|
3677
|
+
const cleanPath = path16.replace(/^agents\.[^.]+\./, "agent.");
|
|
3665
3678
|
if (message.startsWith("Invalid input:")) {
|
|
3666
3679
|
const match = message.match(/expected (\w+), received (\w+)/);
|
|
3667
3680
|
if (match && match[1] === "string" && match[2] === "number") {
|
|
@@ -3673,7 +3686,7 @@ function formatZodError(error) {
|
|
|
3673
3686
|
}
|
|
3674
3687
|
return `${cleanPath}: ${message}`;
|
|
3675
3688
|
}
|
|
3676
|
-
return `${
|
|
3689
|
+
return `${path16}: ${message}`;
|
|
3677
3690
|
}
|
|
3678
3691
|
function validateAgentName(name) {
|
|
3679
3692
|
return cliAgentNameSchema.safeParse(name).success;
|
|
@@ -5849,7 +5862,7 @@ var ApiClient = class {
|
|
|
5849
5862
|
/**
|
|
5850
5863
|
* Generic GET request
|
|
5851
5864
|
*/
|
|
5852
|
-
async get(
|
|
5865
|
+
async get(path16) {
|
|
5853
5866
|
const baseUrl = await this.getBaseUrl();
|
|
5854
5867
|
const token = await getToken();
|
|
5855
5868
|
if (!token) {
|
|
@@ -5862,7 +5875,7 @@ var ApiClient = class {
|
|
|
5862
5875
|
if (bypassSecret) {
|
|
5863
5876
|
headers["x-vercel-protection-bypass"] = bypassSecret;
|
|
5864
5877
|
}
|
|
5865
|
-
return fetch(`${baseUrl}${
|
|
5878
|
+
return fetch(`${baseUrl}${path16}`, {
|
|
5866
5879
|
method: "GET",
|
|
5867
5880
|
headers
|
|
5868
5881
|
});
|
|
@@ -5870,7 +5883,7 @@ var ApiClient = class {
|
|
|
5870
5883
|
/**
|
|
5871
5884
|
* Generic POST request
|
|
5872
5885
|
*/
|
|
5873
|
-
async post(
|
|
5886
|
+
async post(path16, options) {
|
|
5874
5887
|
const baseUrl = await this.getBaseUrl();
|
|
5875
5888
|
const token = await getToken();
|
|
5876
5889
|
if (!token) {
|
|
@@ -5886,7 +5899,7 @@ var ApiClient = class {
|
|
|
5886
5899
|
if (bypassSecret) {
|
|
5887
5900
|
headers["x-vercel-protection-bypass"] = bypassSecret;
|
|
5888
5901
|
}
|
|
5889
|
-
return fetch(`${baseUrl}${
|
|
5902
|
+
return fetch(`${baseUrl}${path16}`, {
|
|
5890
5903
|
method: "POST",
|
|
5891
5904
|
headers,
|
|
5892
5905
|
body: options?.body
|
|
@@ -5895,7 +5908,7 @@ var ApiClient = class {
|
|
|
5895
5908
|
/**
|
|
5896
5909
|
* Generic DELETE request
|
|
5897
5910
|
*/
|
|
5898
|
-
async delete(
|
|
5911
|
+
async delete(path16) {
|
|
5899
5912
|
const baseUrl = await this.getBaseUrl();
|
|
5900
5913
|
const token = await getToken();
|
|
5901
5914
|
if (!token) {
|
|
@@ -5908,7 +5921,7 @@ var ApiClient = class {
|
|
|
5908
5921
|
if (bypassSecret) {
|
|
5909
5922
|
headers["x-vercel-protection-bypass"] = bypassSecret;
|
|
5910
5923
|
}
|
|
5911
|
-
return fetch(`${baseUrl}${
|
|
5924
|
+
return fetch(`${baseUrl}${path16}`, {
|
|
5912
5925
|
method: "DELETE",
|
|
5913
5926
|
headers
|
|
5914
5927
|
});
|
|
@@ -7730,7 +7743,7 @@ cookCmd.argument("[prompt]", "Prompt for the agent").option(
|
|
|
7730
7743
|
// eslint-disable-next-line complexity -- TODO: refactor complex function
|
|
7731
7744
|
async (prompt, options) => {
|
|
7732
7745
|
if (!options.noAutoUpdate) {
|
|
7733
|
-
const shouldExit = await checkAndUpgrade("9.
|
|
7746
|
+
const shouldExit = await checkAndUpgrade("9.3.0", prompt);
|
|
7734
7747
|
if (shouldExit) {
|
|
7735
7748
|
process.exit(0);
|
|
7736
7749
|
}
|
|
@@ -10446,18 +10459,708 @@ var setDefaultCommand = new Command44().name("set-default").description("Set a m
|
|
|
10446
10459
|
var modelProviderCommand = new Command45().name("model-provider").description("Manage model providers for agent runs").addCommand(listCommand6).addCommand(setupCommand2).addCommand(deleteCommand3).addCommand(setDefaultCommand);
|
|
10447
10460
|
|
|
10448
10461
|
// src/commands/onboard.ts
|
|
10449
|
-
import { Command as
|
|
10450
|
-
import
|
|
10451
|
-
import prompts3 from "prompts";
|
|
10462
|
+
import { Command as Command46 } from "commander";
|
|
10463
|
+
import chalk48 from "chalk";
|
|
10452
10464
|
import { mkdir as mkdir8 } from "fs/promises";
|
|
10453
10465
|
import { existsSync as existsSync10 } from "fs";
|
|
10454
10466
|
|
|
10455
|
-
// src/
|
|
10456
|
-
import { Command as Command46 } from "commander";
|
|
10467
|
+
// src/lib/ui/welcome-box.ts
|
|
10457
10468
|
import chalk46 from "chalk";
|
|
10469
|
+
function renderWelcomeBox(lines, width) {
|
|
10470
|
+
const maxLineLength = Math.max(...lines.map((line) => line.length));
|
|
10471
|
+
const boxWidth = width ?? maxLineLength + 4;
|
|
10472
|
+
const innerWidth = boxWidth - 2;
|
|
10473
|
+
const horizontalLine = "\u2500".repeat(innerWidth);
|
|
10474
|
+
const topBorder = `\u250C${horizontalLine}\u2510`;
|
|
10475
|
+
const bottomBorder = `\u2514${horizontalLine}\u2518`;
|
|
10476
|
+
console.log(chalk46.cyan(topBorder));
|
|
10477
|
+
for (const line of lines) {
|
|
10478
|
+
const padding = innerWidth - line.length;
|
|
10479
|
+
const leftPad = Math.floor(padding / 2);
|
|
10480
|
+
const rightPad = padding - leftPad;
|
|
10481
|
+
const centeredLine = " ".repeat(leftPad) + line + " ".repeat(rightPad);
|
|
10482
|
+
console.log(chalk46.cyan("\u2502") + centeredLine + chalk46.cyan("\u2502"));
|
|
10483
|
+
}
|
|
10484
|
+
console.log(chalk46.cyan(bottomBorder));
|
|
10485
|
+
}
|
|
10486
|
+
function renderOnboardWelcome() {
|
|
10487
|
+
renderWelcomeBox([
|
|
10488
|
+
"",
|
|
10489
|
+
"Welcome to VM0!",
|
|
10490
|
+
"",
|
|
10491
|
+
"Let's set up your first agent.",
|
|
10492
|
+
""
|
|
10493
|
+
]);
|
|
10494
|
+
}
|
|
10495
|
+
|
|
10496
|
+
// src/lib/ui/progress-line.ts
|
|
10497
|
+
import chalk47 from "chalk";
|
|
10498
|
+
var STATUS_SYMBOLS = {
|
|
10499
|
+
completed: "\u25CF",
|
|
10500
|
+
"in-progress": "\u25D0",
|
|
10501
|
+
pending: "\u25CB",
|
|
10502
|
+
failed: "\u2717"
|
|
10503
|
+
};
|
|
10504
|
+
function getStatusColor(status) {
|
|
10505
|
+
switch (status) {
|
|
10506
|
+
case "completed":
|
|
10507
|
+
return chalk47.green;
|
|
10508
|
+
case "in-progress":
|
|
10509
|
+
return chalk47.yellow;
|
|
10510
|
+
case "failed":
|
|
10511
|
+
return chalk47.red;
|
|
10512
|
+
case "pending":
|
|
10513
|
+
default:
|
|
10514
|
+
return chalk47.dim;
|
|
10515
|
+
}
|
|
10516
|
+
}
|
|
10517
|
+
function renderProgressLine(steps) {
|
|
10518
|
+
for (let i = 0; i < steps.length; i++) {
|
|
10519
|
+
const step = steps[i];
|
|
10520
|
+
if (!step) continue;
|
|
10521
|
+
const symbol = STATUS_SYMBOLS[step.status];
|
|
10522
|
+
const color = getStatusColor(step.status);
|
|
10523
|
+
console.log(color(`${symbol} ${step.label}`));
|
|
10524
|
+
if (i < steps.length - 1) {
|
|
10525
|
+
console.log(chalk47.dim("\u2502"));
|
|
10526
|
+
}
|
|
10527
|
+
}
|
|
10528
|
+
}
|
|
10529
|
+
function createOnboardProgress() {
|
|
10530
|
+
const steps = [
|
|
10531
|
+
{ label: "Authentication", status: "pending" },
|
|
10532
|
+
{ label: "Model Provider Setup", status: "pending" },
|
|
10533
|
+
{ label: "Create Agent", status: "pending" },
|
|
10534
|
+
{ label: "Complete", status: "pending" }
|
|
10535
|
+
];
|
|
10536
|
+
return {
|
|
10537
|
+
steps,
|
|
10538
|
+
render: () => renderProgressLine(steps),
|
|
10539
|
+
update: (index, status) => {
|
|
10540
|
+
const step = steps[index];
|
|
10541
|
+
if (step) {
|
|
10542
|
+
step.status = status;
|
|
10543
|
+
}
|
|
10544
|
+
}
|
|
10545
|
+
};
|
|
10546
|
+
}
|
|
10547
|
+
|
|
10548
|
+
// src/lib/domain/onboard/auth.ts
|
|
10549
|
+
function buildHeaders2() {
|
|
10550
|
+
const headers = {
|
|
10551
|
+
"Content-Type": "application/json"
|
|
10552
|
+
};
|
|
10553
|
+
const bypassSecret = process.env.VERCEL_AUTOMATION_BYPASS_SECRET;
|
|
10554
|
+
if (bypassSecret) {
|
|
10555
|
+
headers["x-vercel-protection-bypass"] = bypassSecret;
|
|
10556
|
+
}
|
|
10557
|
+
return headers;
|
|
10558
|
+
}
|
|
10559
|
+
async function requestDeviceCode2(apiUrl) {
|
|
10560
|
+
const response = await fetch(`${apiUrl}/api/cli/auth/device`, {
|
|
10561
|
+
method: "POST",
|
|
10562
|
+
headers: buildHeaders2(),
|
|
10563
|
+
body: JSON.stringify({})
|
|
10564
|
+
});
|
|
10565
|
+
if (!response.ok) {
|
|
10566
|
+
throw new Error(`Failed to request device code: ${response.statusText}`);
|
|
10567
|
+
}
|
|
10568
|
+
return response.json();
|
|
10569
|
+
}
|
|
10570
|
+
async function exchangeToken2(apiUrl, deviceCode) {
|
|
10571
|
+
const response = await fetch(`${apiUrl}/api/cli/auth/token`, {
|
|
10572
|
+
method: "POST",
|
|
10573
|
+
headers: buildHeaders2(),
|
|
10574
|
+
body: JSON.stringify({ device_code: deviceCode })
|
|
10575
|
+
});
|
|
10576
|
+
return response.json();
|
|
10577
|
+
}
|
|
10578
|
+
function delay2(ms) {
|
|
10579
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
10580
|
+
}
|
|
10581
|
+
async function isAuthenticated() {
|
|
10582
|
+
const token = await getToken();
|
|
10583
|
+
return !!token;
|
|
10584
|
+
}
|
|
10585
|
+
function handleTokenResult(tokenResult) {
|
|
10586
|
+
if (tokenResult.access_token) {
|
|
10587
|
+
return tokenResult.access_token;
|
|
10588
|
+
}
|
|
10589
|
+
if (tokenResult.error === "authorization_pending") {
|
|
10590
|
+
return null;
|
|
10591
|
+
}
|
|
10592
|
+
if (tokenResult.error === "expired_token") {
|
|
10593
|
+
throw new Error("The device code has expired. Please try again.");
|
|
10594
|
+
}
|
|
10595
|
+
if (tokenResult.error) {
|
|
10596
|
+
throw new Error(
|
|
10597
|
+
`Authentication failed: ${tokenResult.error_description ?? tokenResult.error}`
|
|
10598
|
+
);
|
|
10599
|
+
}
|
|
10600
|
+
return null;
|
|
10601
|
+
}
|
|
10602
|
+
async function pollForToken(apiUrl, deviceAuth, callbacks) {
|
|
10603
|
+
const startTime = Date.now();
|
|
10604
|
+
const maxWaitTime = deviceAuth.expires_in * 1e3;
|
|
10605
|
+
const pollInterval = (deviceAuth.interval || 5) * 1e3;
|
|
10606
|
+
let isFirstPoll = true;
|
|
10607
|
+
while (Date.now() - startTime < maxWaitTime) {
|
|
10608
|
+
if (!isFirstPoll) {
|
|
10609
|
+
await delay2(pollInterval);
|
|
10610
|
+
}
|
|
10611
|
+
isFirstPoll = false;
|
|
10612
|
+
const tokenResult = await exchangeToken2(apiUrl, deviceAuth.device_code);
|
|
10613
|
+
const accessToken = handleTokenResult(tokenResult);
|
|
10614
|
+
if (accessToken) {
|
|
10615
|
+
return accessToken;
|
|
10616
|
+
}
|
|
10617
|
+
callbacks?.onPolling?.();
|
|
10618
|
+
}
|
|
10619
|
+
throw new Error("Authentication timed out. Please try again.");
|
|
10620
|
+
}
|
|
10621
|
+
async function runAuthFlow(callbacks, apiUrl) {
|
|
10622
|
+
const targetApiUrl = apiUrl ?? await getApiUrl();
|
|
10623
|
+
callbacks?.onInitiating?.();
|
|
10624
|
+
const deviceAuth = await requestDeviceCode2(targetApiUrl);
|
|
10625
|
+
const verificationUrl = `${targetApiUrl}${deviceAuth.verification_path}`;
|
|
10626
|
+
const expiresInMinutes = Math.floor(deviceAuth.expires_in / 60);
|
|
10627
|
+
callbacks?.onDeviceCodeReady?.(
|
|
10628
|
+
verificationUrl,
|
|
10629
|
+
deviceAuth.user_code,
|
|
10630
|
+
expiresInMinutes
|
|
10631
|
+
);
|
|
10632
|
+
try {
|
|
10633
|
+
const accessToken = await pollForToken(targetApiUrl, deviceAuth, callbacks);
|
|
10634
|
+
await saveConfig({
|
|
10635
|
+
token: accessToken,
|
|
10636
|
+
apiUrl: targetApiUrl
|
|
10637
|
+
});
|
|
10638
|
+
callbacks?.onSuccess?.();
|
|
10639
|
+
} catch (error) {
|
|
10640
|
+
callbacks?.onError?.(error);
|
|
10641
|
+
throw error;
|
|
10642
|
+
}
|
|
10643
|
+
}
|
|
10644
|
+
|
|
10645
|
+
// src/lib/domain/onboard/model-provider.ts
|
|
10646
|
+
async function checkModelProviderStatus() {
|
|
10647
|
+
const response = await listModelProviders();
|
|
10648
|
+
return {
|
|
10649
|
+
hasProvider: response.modelProviders.length > 0,
|
|
10650
|
+
providers: response.modelProviders
|
|
10651
|
+
};
|
|
10652
|
+
}
|
|
10653
|
+
function getProviderChoices() {
|
|
10654
|
+
return Object.keys(MODEL_PROVIDER_TYPES).map(
|
|
10655
|
+
(type) => ({
|
|
10656
|
+
type,
|
|
10657
|
+
label: MODEL_PROVIDER_TYPES[type].label,
|
|
10658
|
+
helpText: MODEL_PROVIDER_TYPES[type].helpText,
|
|
10659
|
+
credentialLabel: MODEL_PROVIDER_TYPES[type].credentialLabel
|
|
10660
|
+
})
|
|
10661
|
+
);
|
|
10662
|
+
}
|
|
10663
|
+
async function setupModelProvider(type, credential, options) {
|
|
10664
|
+
const response = await upsertModelProvider({
|
|
10665
|
+
type,
|
|
10666
|
+
credential,
|
|
10667
|
+
convert: options?.convert
|
|
10668
|
+
});
|
|
10669
|
+
return {
|
|
10670
|
+
provider: response.provider,
|
|
10671
|
+
created: response.created,
|
|
10672
|
+
isDefault: response.provider.isDefault,
|
|
10673
|
+
framework: response.provider.framework
|
|
10674
|
+
};
|
|
10675
|
+
}
|
|
10676
|
+
|
|
10677
|
+
// src/lib/domain/onboard/claude-setup.ts
|
|
10458
10678
|
import { mkdir as mkdir7, writeFile as writeFile7 } from "fs/promises";
|
|
10459
10679
|
import path14 from "path";
|
|
10460
10680
|
var SKILL_DIR = ".claude/skills/vm0-agent-builder";
|
|
10681
|
+
var SKILL_FILE = "SKILL.md";
|
|
10682
|
+
function getSkillContent() {
|
|
10683
|
+
return `---
|
|
10684
|
+
name: vm0-agent-builder
|
|
10685
|
+
description: Build VM0 agents by creating AGENTS.md and vm0.yaml. Use when users describe what agent they want to build.
|
|
10686
|
+
---
|
|
10687
|
+
|
|
10688
|
+
# VM0 Agent Builder
|
|
10689
|
+
|
|
10690
|
+
Build AI agents that run in VM0's secure sandbox environment. This skill helps you create the two essential files: \`AGENTS.md\` (agent instructions) and \`vm0.yaml\` (configuration).
|
|
10691
|
+
|
|
10692
|
+
## Workflow
|
|
10693
|
+
|
|
10694
|
+
### Step 1: Understand the Goal
|
|
10695
|
+
|
|
10696
|
+
First, clarify what the user wants their agent to do:
|
|
10697
|
+
- What task should the agent accomplish?
|
|
10698
|
+
- What inputs does it need? (files, APIs, websites)
|
|
10699
|
+
- What outputs should it produce? (reports, files, notifications)
|
|
10700
|
+
- Should it run once or on a schedule?
|
|
10701
|
+
|
|
10702
|
+
### Step 2: Create AGENTS.md
|
|
10703
|
+
|
|
10704
|
+
Write clear, step-by-step instructions. The agent will follow these exactly.
|
|
10705
|
+
|
|
10706
|
+
**Template:**
|
|
10707
|
+
|
|
10708
|
+
\`\`\`markdown
|
|
10709
|
+
# [Agent Name]
|
|
10710
|
+
|
|
10711
|
+
You are a [role description].
|
|
10712
|
+
|
|
10713
|
+
## Workflow
|
|
10714
|
+
|
|
10715
|
+
1. [First action - be specific]
|
|
10716
|
+
2. [Second action - include details]
|
|
10717
|
+
3. [Continue with clear steps...]
|
|
10718
|
+
|
|
10719
|
+
## Output
|
|
10720
|
+
|
|
10721
|
+
Write results to \`[filename]\` in the current directory.
|
|
10722
|
+
\`\`\`
|
|
10723
|
+
|
|
10724
|
+
**Writing Tips:**
|
|
10725
|
+
- Be specific: "Read the top 10 stories" not "Read some stories"
|
|
10726
|
+
- One action per step: Keep steps focused and atomic
|
|
10727
|
+
- Specify output: Exact filenames and formats
|
|
10728
|
+
- Use active voice: "Create a file" not "A file should be created"
|
|
10729
|
+
|
|
10730
|
+
### Step 3: Create vm0.yaml
|
|
10731
|
+
|
|
10732
|
+
Configure the agent with required skills and environment variables.
|
|
10733
|
+
|
|
10734
|
+
\`\`\`yaml
|
|
10735
|
+
version: "1.0"
|
|
10736
|
+
|
|
10737
|
+
agents:
|
|
10738
|
+
[agent-name]:
|
|
10739
|
+
framework: claude-code
|
|
10740
|
+
instructions: AGENTS.md
|
|
10741
|
+
# Pre-install GitHub CLI (optional)
|
|
10742
|
+
apps:
|
|
10743
|
+
- github
|
|
10744
|
+
# Add skills the agent needs (optional)
|
|
10745
|
+
skills:
|
|
10746
|
+
- https://github.com/vm0-ai/vm0-skills/tree/main/[skill-name]
|
|
10747
|
+
# Mount volumes for input files (optional)
|
|
10748
|
+
volumes:
|
|
10749
|
+
- my-volume:/home/user/input
|
|
10750
|
+
# Environment variables (optional)
|
|
10751
|
+
environment:
|
|
10752
|
+
API_KEY: "\${{ secrets.API_KEY }}"
|
|
10753
|
+
\`\`\`
|
|
10754
|
+
|
|
10755
|
+
### Step 4: Test the Agent
|
|
10756
|
+
|
|
10757
|
+
After creating both files, the user runs:
|
|
10758
|
+
|
|
10759
|
+
\`\`\`bash
|
|
10760
|
+
vm0 cook "start working"
|
|
10761
|
+
\`\`\`
|
|
10762
|
+
|
|
10763
|
+
This command:
|
|
10764
|
+
1. Uploads the configuration to VM0
|
|
10765
|
+
2. Runs the agent in a secure sandbox
|
|
10766
|
+
3. Downloads results to the \`artifact/\` directory
|
|
10767
|
+
|
|
10768
|
+
## Available Skills
|
|
10769
|
+
|
|
10770
|
+
Skills give agents access to external services. Add them to vm0.yaml when needed.
|
|
10771
|
+
|
|
10772
|
+
**Popular Skills:**
|
|
10773
|
+
|
|
10774
|
+
| Skill | Use Case |
|
|
10775
|
+
|-------|----------|
|
|
10776
|
+
| \`github\` | Read/write issues, PRs, files |
|
|
10777
|
+
| \`slack\` | Send messages to channels |
|
|
10778
|
+
| \`notion\` | Access Notion pages/databases |
|
|
10779
|
+
| \`firecrawl\` | Scrape and extract web content |
|
|
10780
|
+
| \`supabase\` | Database operations |
|
|
10781
|
+
| \`google-sheets\` | Read/write spreadsheets |
|
|
10782
|
+
| \`linear\` | Project management |
|
|
10783
|
+
| \`discord\` | Send Discord messages |
|
|
10784
|
+
| \`gmail\` | Send emails |
|
|
10785
|
+
| \`openai\` | Embeddings, additional AI calls |
|
|
10786
|
+
|
|
10787
|
+
**All 79 skills:** https://github.com/vm0-ai/vm0-skills
|
|
10788
|
+
|
|
10789
|
+
**Skill URL format:**
|
|
10790
|
+
\`\`\`
|
|
10791
|
+
https://github.com/vm0-ai/vm0-skills/tree/main/[skill-name]
|
|
10792
|
+
\`\`\`
|
|
10793
|
+
|
|
10794
|
+
## Examples
|
|
10795
|
+
|
|
10796
|
+
### HackerNews Curator
|
|
10797
|
+
|
|
10798
|
+
**AGENTS.md:**
|
|
10799
|
+
\`\`\`markdown
|
|
10800
|
+
# HackerNews AI Curator
|
|
10801
|
+
|
|
10802
|
+
You are a content curator that finds AI-related articles on HackerNews.
|
|
10803
|
+
|
|
10804
|
+
## Workflow
|
|
10805
|
+
|
|
10806
|
+
1. Go to https://news.ycombinator.com
|
|
10807
|
+
2. Read the top 30 stories
|
|
10808
|
+
3. Filter for AI, ML, and LLM related content
|
|
10809
|
+
4. For each relevant article, extract:
|
|
10810
|
+
- Title and URL
|
|
10811
|
+
- 2-3 sentence summary
|
|
10812
|
+
- Why it matters
|
|
10813
|
+
5. Write findings to \`daily-digest.md\`
|
|
10814
|
+
|
|
10815
|
+
## Output
|
|
10816
|
+
|
|
10817
|
+
Create \`daily-digest.md\` with today's date as the header.
|
|
10818
|
+
Format as a bulleted list with links.
|
|
10819
|
+
\`\`\`
|
|
10820
|
+
|
|
10821
|
+
**vm0.yaml:**
|
|
10822
|
+
\`\`\`yaml
|
|
10823
|
+
version: "1.0"
|
|
10824
|
+
|
|
10825
|
+
agents:
|
|
10826
|
+
hn-curator:
|
|
10827
|
+
framework: claude-code
|
|
10828
|
+
instructions: AGENTS.md
|
|
10829
|
+
\`\`\`
|
|
10830
|
+
|
|
10831
|
+
### GitHub Issue Reporter
|
|
10832
|
+
|
|
10833
|
+
**AGENTS.md:**
|
|
10834
|
+
\`\`\`markdown
|
|
10835
|
+
# GitHub Issue Reporter
|
|
10836
|
+
|
|
10837
|
+
You are a GitHub analyst that creates issue summary reports.
|
|
10838
|
+
|
|
10839
|
+
## Workflow
|
|
10840
|
+
|
|
10841
|
+
1. List all open issues in the repository
|
|
10842
|
+
2. Group by labels: bug, feature, documentation, other
|
|
10843
|
+
3. For each group, report:
|
|
10844
|
+
- Total count
|
|
10845
|
+
- Oldest issue (with age in days)
|
|
10846
|
+
- Most commented issue
|
|
10847
|
+
4. Write report to \`issue-report.md\`
|
|
10848
|
+
|
|
10849
|
+
## Output
|
|
10850
|
+
|
|
10851
|
+
Create \`issue-report.md\` with sections for each label group.
|
|
10852
|
+
Include links to referenced issues.
|
|
10853
|
+
\`\`\`
|
|
10854
|
+
|
|
10855
|
+
**vm0.yaml:**
|
|
10856
|
+
\`\`\`yaml
|
|
10857
|
+
version: "1.0"
|
|
10858
|
+
|
|
10859
|
+
agents:
|
|
10860
|
+
issue-reporter:
|
|
10861
|
+
framework: claude-code
|
|
10862
|
+
instructions: AGENTS.md
|
|
10863
|
+
skills:
|
|
10864
|
+
- https://github.com/vm0-ai/vm0-skills/tree/main/github
|
|
10865
|
+
environment:
|
|
10866
|
+
GITHUB_REPO: "\${{ vars.GITHUB_REPO }}"
|
|
10867
|
+
\`\`\`
|
|
10868
|
+
|
|
10869
|
+
### Slack Daily Digest
|
|
10870
|
+
|
|
10871
|
+
**AGENTS.md:**
|
|
10872
|
+
\`\`\`markdown
|
|
10873
|
+
# Slack Daily Digest
|
|
10874
|
+
|
|
10875
|
+
You are an assistant that posts daily summaries to Slack.
|
|
10876
|
+
|
|
10877
|
+
## Workflow
|
|
10878
|
+
|
|
10879
|
+
1. Read the contents of \`updates.md\` from the input volume
|
|
10880
|
+
2. Summarize the key points (max 5 bullets)
|
|
10881
|
+
3. Format as a Slack message with emoji headers
|
|
10882
|
+
4. Post to the #daily-updates channel
|
|
10883
|
+
|
|
10884
|
+
## Output
|
|
10885
|
+
|
|
10886
|
+
Post the summary to Slack. Write a copy to \`sent-message.md\`.
|
|
10887
|
+
\`\`\`
|
|
10888
|
+
|
|
10889
|
+
**vm0.yaml:**
|
|
10890
|
+
\`\`\`yaml
|
|
10891
|
+
version: "1.0"
|
|
10892
|
+
|
|
10893
|
+
agents:
|
|
10894
|
+
slack-digest:
|
|
10895
|
+
framework: claude-code
|
|
10896
|
+
instructions: AGENTS.md
|
|
10897
|
+
skills:
|
|
10898
|
+
- https://github.com/vm0-ai/vm0-skills/tree/main/slack
|
|
10899
|
+
environment:
|
|
10900
|
+
SLACK_CHANNEL: "\${{ vars.SLACK_CHANNEL }}"
|
|
10901
|
+
\`\`\`
|
|
10902
|
+
|
|
10903
|
+
## Environment Variables
|
|
10904
|
+
|
|
10905
|
+
Use environment variables for sensitive data and configuration:
|
|
10906
|
+
|
|
10907
|
+
\`\`\`yaml
|
|
10908
|
+
environment:
|
|
10909
|
+
# Secrets (encrypted, for API keys)
|
|
10910
|
+
API_KEY: "\${{ secrets.API_KEY }}"
|
|
10911
|
+
|
|
10912
|
+
# Variables (plain text, for config)
|
|
10913
|
+
REPO_NAME: "\${{ vars.REPO_NAME }}"
|
|
10914
|
+
|
|
10915
|
+
# Credentials (from vm0 credential storage)
|
|
10916
|
+
MY_TOKEN: "\${{ credentials.MY_TOKEN }}"
|
|
10917
|
+
\`\`\`
|
|
10918
|
+
|
|
10919
|
+
Set credentials with (names must be UPPERCASE):
|
|
10920
|
+
\`\`\`bash
|
|
10921
|
+
vm0 credential set API_KEY "your-api-key"
|
|
10922
|
+
\`\`\`
|
|
10923
|
+
|
|
10924
|
+
## Troubleshooting
|
|
10925
|
+
|
|
10926
|
+
**Agent doesn't follow instructions:**
|
|
10927
|
+
- Make steps more specific and explicit
|
|
10928
|
+
- Add "Do not..." constraints for unwanted behavior
|
|
10929
|
+
- Break complex steps into smaller sub-steps
|
|
10930
|
+
|
|
10931
|
+
**Agent can't access a service:**
|
|
10932
|
+
- Add the required skill to vm0.yaml
|
|
10933
|
+
- Set up credentials with \`vm0 credential set\`
|
|
10934
|
+
|
|
10935
|
+
**Output is in wrong format:**
|
|
10936
|
+
- Provide an exact template in the instructions
|
|
10937
|
+
- Include a small example of expected output
|
|
10938
|
+
|
|
10939
|
+
## Next Steps After Creating Files
|
|
10940
|
+
|
|
10941
|
+
\`\`\`bash
|
|
10942
|
+
# Run your agent
|
|
10943
|
+
vm0 cook "start working"
|
|
10944
|
+
|
|
10945
|
+
# View logs if needed
|
|
10946
|
+
vm0 logs [run-id]
|
|
10947
|
+
|
|
10948
|
+
# Results are in artifact/ directory
|
|
10949
|
+
ls artifact/
|
|
10950
|
+
|
|
10951
|
+
# Continue from where agent left off
|
|
10952
|
+
vm0 cook continue "keep going"
|
|
10953
|
+
|
|
10954
|
+
# Resume from a checkpoint
|
|
10955
|
+
vm0 cook resume "try again"
|
|
10956
|
+
\`\`\`
|
|
10957
|
+
`;
|
|
10958
|
+
}
|
|
10959
|
+
async function installClaudeSkill(targetDir = process.cwd()) {
|
|
10960
|
+
const skillDirPath = path14.join(targetDir, SKILL_DIR);
|
|
10961
|
+
const skillFilePath = path14.join(skillDirPath, SKILL_FILE);
|
|
10962
|
+
await mkdir7(skillDirPath, { recursive: true });
|
|
10963
|
+
await writeFile7(skillFilePath, getSkillContent());
|
|
10964
|
+
return {
|
|
10965
|
+
skillDir: skillDirPath,
|
|
10966
|
+
skillFile: skillFilePath
|
|
10967
|
+
};
|
|
10968
|
+
}
|
|
10969
|
+
|
|
10970
|
+
// src/commands/onboard.ts
|
|
10971
|
+
var DEFAULT_AGENT_NAME = "my-vm0-agent";
|
|
10972
|
+
async function handleAuthentication(ctx) {
|
|
10973
|
+
ctx.updateProgress(0, "in-progress");
|
|
10974
|
+
const authenticated = await isAuthenticated();
|
|
10975
|
+
if (authenticated) {
|
|
10976
|
+
ctx.updateProgress(0, "completed");
|
|
10977
|
+
return;
|
|
10978
|
+
}
|
|
10979
|
+
if (!ctx.interactive) {
|
|
10980
|
+
console.error(chalk48.red("Error: Not authenticated"));
|
|
10981
|
+
console.error("Run 'vm0 auth login' first or set VM0_TOKEN");
|
|
10982
|
+
process.exit(1);
|
|
10983
|
+
}
|
|
10984
|
+
console.log(chalk48.dim("Authentication required..."));
|
|
10985
|
+
console.log();
|
|
10986
|
+
await runAuthFlow({
|
|
10987
|
+
onInitiating: () => {
|
|
10988
|
+
console.log("Initiating authentication...");
|
|
10989
|
+
},
|
|
10990
|
+
onDeviceCodeReady: (url, code, expiresIn) => {
|
|
10991
|
+
console.log(chalk48.green("\nDevice code generated"));
|
|
10992
|
+
console.log(chalk48.cyan(`
|
|
10993
|
+
To authenticate, visit: ${url}`));
|
|
10994
|
+
console.log(`And enter this code: ${chalk48.bold(code)}`);
|
|
10995
|
+
console.log(`
|
|
10996
|
+
The code expires in ${expiresIn} minutes.`);
|
|
10997
|
+
console.log("\nWaiting for authentication...");
|
|
10998
|
+
},
|
|
10999
|
+
onPolling: () => {
|
|
11000
|
+
process.stdout.write(chalk48.dim("."));
|
|
11001
|
+
},
|
|
11002
|
+
onSuccess: () => {
|
|
11003
|
+
console.log(chalk48.green("\nAuthentication successful!"));
|
|
11004
|
+
console.log("Your credentials have been saved.");
|
|
11005
|
+
},
|
|
11006
|
+
onError: (error) => {
|
|
11007
|
+
console.error(chalk48.red(`
|
|
11008
|
+
${error.message}`));
|
|
11009
|
+
process.exit(1);
|
|
11010
|
+
}
|
|
11011
|
+
});
|
|
11012
|
+
ctx.updateProgress(0, "completed");
|
|
11013
|
+
}
|
|
11014
|
+
async function handleModelProvider(ctx) {
|
|
11015
|
+
ctx.updateProgress(1, "in-progress");
|
|
11016
|
+
const providerStatus = await checkModelProviderStatus();
|
|
11017
|
+
if (providerStatus.hasProvider) {
|
|
11018
|
+
ctx.updateProgress(1, "completed");
|
|
11019
|
+
return;
|
|
11020
|
+
}
|
|
11021
|
+
if (!ctx.interactive) {
|
|
11022
|
+
console.error(chalk48.red("Error: No model provider configured"));
|
|
11023
|
+
console.error("Run 'vm0 model-provider setup' first");
|
|
11024
|
+
process.exit(1);
|
|
11025
|
+
}
|
|
11026
|
+
console.log(chalk48.dim("Model provider setup required..."));
|
|
11027
|
+
console.log();
|
|
11028
|
+
const choices = getProviderChoices();
|
|
11029
|
+
const providerType = await promptSelect(
|
|
11030
|
+
"Select provider type:",
|
|
11031
|
+
choices.map((c20) => ({
|
|
11032
|
+
title: c20.label,
|
|
11033
|
+
value: c20.type,
|
|
11034
|
+
description: c20.helpText
|
|
11035
|
+
}))
|
|
11036
|
+
);
|
|
11037
|
+
if (!providerType) {
|
|
11038
|
+
process.exit(0);
|
|
11039
|
+
}
|
|
11040
|
+
const selectedChoice = choices.find((c20) => c20.type === providerType);
|
|
11041
|
+
if (selectedChoice) {
|
|
11042
|
+
console.log();
|
|
11043
|
+
console.log(chalk48.dim(selectedChoice.helpText));
|
|
11044
|
+
console.log();
|
|
11045
|
+
}
|
|
11046
|
+
const credential = await promptPassword(
|
|
11047
|
+
`Enter your ${selectedChoice?.credentialLabel ?? "credential"}:`
|
|
11048
|
+
);
|
|
11049
|
+
if (!credential) {
|
|
11050
|
+
console.log(chalk48.dim("Cancelled"));
|
|
11051
|
+
process.exit(0);
|
|
11052
|
+
}
|
|
11053
|
+
const result = await setupModelProvider(providerType, credential);
|
|
11054
|
+
console.log(
|
|
11055
|
+
chalk48.green(
|
|
11056
|
+
`
|
|
11057
|
+
\u2713 Model provider "${providerType}" ${result.created ? "created" : "updated"}${result.isDefault ? ` (default for ${result.framework})` : ""}`
|
|
11058
|
+
)
|
|
11059
|
+
);
|
|
11060
|
+
ctx.updateProgress(1, "completed");
|
|
11061
|
+
}
|
|
11062
|
+
async function handleAgentCreation(ctx) {
|
|
11063
|
+
ctx.updateProgress(2, "in-progress");
|
|
11064
|
+
let agentName = ctx.options.name ?? DEFAULT_AGENT_NAME;
|
|
11065
|
+
if (!ctx.options.yes && !ctx.options.name && ctx.interactive) {
|
|
11066
|
+
const inputName = await promptText(
|
|
11067
|
+
"Enter agent name:",
|
|
11068
|
+
DEFAULT_AGENT_NAME,
|
|
11069
|
+
(value) => {
|
|
11070
|
+
if (!validateAgentName(value)) {
|
|
11071
|
+
return "Invalid name: 3-64 chars, alphanumeric + hyphens, start/end with letter/number";
|
|
11072
|
+
}
|
|
11073
|
+
return true;
|
|
11074
|
+
}
|
|
11075
|
+
);
|
|
11076
|
+
if (!inputName) {
|
|
11077
|
+
process.exit(0);
|
|
11078
|
+
}
|
|
11079
|
+
agentName = inputName;
|
|
11080
|
+
}
|
|
11081
|
+
if (!validateAgentName(agentName)) {
|
|
11082
|
+
console.error(
|
|
11083
|
+
chalk48.red(
|
|
11084
|
+
"Invalid agent name: must be 3-64 chars, alphanumeric + hyphens"
|
|
11085
|
+
)
|
|
11086
|
+
);
|
|
11087
|
+
process.exit(1);
|
|
11088
|
+
}
|
|
11089
|
+
if (existsSync10(agentName)) {
|
|
11090
|
+
console.error(chalk48.red(`
|
|
11091
|
+
\u2717 ${agentName}/ already exists`));
|
|
11092
|
+
console.log();
|
|
11093
|
+
console.log("Remove it first or choose a different name:");
|
|
11094
|
+
console.log(chalk48.cyan(` rm -rf ${agentName}`));
|
|
11095
|
+
process.exit(1);
|
|
11096
|
+
}
|
|
11097
|
+
if (!ctx.options.yes && ctx.interactive) {
|
|
11098
|
+
const confirmed = await promptConfirm(`Create ${agentName}/?`, true);
|
|
11099
|
+
if (!confirmed) {
|
|
11100
|
+
console.log(chalk48.dim("Cancelled"));
|
|
11101
|
+
process.exit(0);
|
|
11102
|
+
}
|
|
11103
|
+
}
|
|
11104
|
+
await mkdir8(agentName, { recursive: true });
|
|
11105
|
+
console.log(chalk48.green(`\u2713 Created ${agentName}/`));
|
|
11106
|
+
ctx.updateProgress(2, "completed");
|
|
11107
|
+
return agentName;
|
|
11108
|
+
}
|
|
11109
|
+
async function handleSkillInstallation(ctx, agentName) {
|
|
11110
|
+
ctx.updateProgress(3, "in-progress");
|
|
11111
|
+
const skillResult = await installClaudeSkill(agentName);
|
|
11112
|
+
console.log(
|
|
11113
|
+
chalk48.green(
|
|
11114
|
+
`\u2713 Installed vm0-agent-builder skill to ${skillResult.skillDir}`
|
|
11115
|
+
)
|
|
11116
|
+
);
|
|
11117
|
+
ctx.updateProgress(3, "completed");
|
|
11118
|
+
}
|
|
11119
|
+
function printNextSteps(agentName) {
|
|
11120
|
+
console.log();
|
|
11121
|
+
console.log(chalk48.bold("Next step:"));
|
|
11122
|
+
console.log();
|
|
11123
|
+
console.log(
|
|
11124
|
+
` ${chalk48.cyan(`cd ${agentName} && claude "/vm0-agent-builder I want to build an agent that..."`)}`
|
|
11125
|
+
);
|
|
11126
|
+
console.log();
|
|
11127
|
+
}
|
|
11128
|
+
var onboardCommand = new Command46().name("onboard").description("Guided setup for new VM0 users").option("-y, --yes", "Skip confirmation prompts").option("--name <name>", `Agent name (default: ${DEFAULT_AGENT_NAME})`).action(async (options) => {
|
|
11129
|
+
const interactive = isInteractive();
|
|
11130
|
+
if (interactive) {
|
|
11131
|
+
console.log();
|
|
11132
|
+
renderOnboardWelcome();
|
|
11133
|
+
console.log();
|
|
11134
|
+
}
|
|
11135
|
+
const progress = createOnboardProgress();
|
|
11136
|
+
const updateProgress = (index, status) => {
|
|
11137
|
+
progress.update(index, status);
|
|
11138
|
+
if (interactive) {
|
|
11139
|
+
console.clear();
|
|
11140
|
+
renderOnboardWelcome();
|
|
11141
|
+
console.log();
|
|
11142
|
+
progress.render();
|
|
11143
|
+
console.log();
|
|
11144
|
+
}
|
|
11145
|
+
};
|
|
11146
|
+
if (interactive) {
|
|
11147
|
+
progress.render();
|
|
11148
|
+
console.log();
|
|
11149
|
+
}
|
|
11150
|
+
const ctx = { interactive, options, updateProgress };
|
|
11151
|
+
await handleAuthentication(ctx);
|
|
11152
|
+
await handleModelProvider(ctx);
|
|
11153
|
+
const agentName = await handleAgentCreation(ctx);
|
|
11154
|
+
await handleSkillInstallation(ctx, agentName);
|
|
11155
|
+
printNextSteps(agentName);
|
|
11156
|
+
});
|
|
11157
|
+
|
|
11158
|
+
// src/commands/setup-claude.ts
|
|
11159
|
+
import { Command as Command47 } from "commander";
|
|
11160
|
+
import chalk49 from "chalk";
|
|
11161
|
+
import { mkdir as mkdir9, writeFile as writeFile8 } from "fs/promises";
|
|
11162
|
+
import path15 from "path";
|
|
11163
|
+
var SKILL_DIR2 = ".claude/skills/vm0-agent-builder";
|
|
10461
11164
|
var SKILL_CONTENT = `---
|
|
10462
11165
|
name: vm0-agent-builder
|
|
10463
11166
|
description: Guide for building VM0 agents with Claude's help. Use this skill when users want to create or improve their agent's AGENTS.md and vm0.yaml configuration.
|
|
@@ -10665,138 +11368,31 @@ Write results to the artifact directory.
|
|
|
10665
11368
|
- Provide exact templates for output files
|
|
10666
11369
|
- Include example output in the instructions
|
|
10667
11370
|
`;
|
|
10668
|
-
var setupClaudeCommand = new
|
|
11371
|
+
var setupClaudeCommand = new Command47().name("setup-claude").description("Add/update Claude skill for agent building").option(
|
|
10669
11372
|
"--agent-dir <dir>",
|
|
10670
11373
|
"Agent directory (shown in next step instructions)"
|
|
10671
11374
|
).action(async (options) => {
|
|
10672
|
-
console.log(
|
|
10673
|
-
await
|
|
10674
|
-
await
|
|
11375
|
+
console.log(chalk49.dim("Installing vm0-agent-builder skill..."));
|
|
11376
|
+
await mkdir9(SKILL_DIR2, { recursive: true });
|
|
11377
|
+
await writeFile8(path15.join(SKILL_DIR2, "SKILL.md"), SKILL_CONTENT);
|
|
10675
11378
|
console.log(
|
|
10676
|
-
|
|
11379
|
+
chalk49.green(`Done Installed vm0-agent-builder skill to ${SKILL_DIR2}`)
|
|
10677
11380
|
);
|
|
10678
11381
|
console.log();
|
|
10679
11382
|
console.log("Next step:");
|
|
10680
11383
|
const cdPrefix = options.agentDir ? `cd ${options.agentDir} && ` : "";
|
|
10681
11384
|
console.log(
|
|
10682
|
-
|
|
11385
|
+
chalk49.cyan(
|
|
10683
11386
|
` ${cdPrefix}claude "/vm0-agent-builder I want to build an agent that..."`
|
|
10684
11387
|
)
|
|
10685
11388
|
);
|
|
10686
11389
|
});
|
|
10687
11390
|
|
|
10688
|
-
// src/commands/onboard.ts
|
|
10689
|
-
var DEMO_AGENT_DIR = "vm0-demo-agent";
|
|
10690
|
-
var DEMO_AGENT_NAME = "vm0-demo-agent";
|
|
10691
|
-
var onboardCommand = new Command47().name("onboard").description("Guided setup for new VM0 users").option("-y, --yes", "Skip confirmation prompts").option(
|
|
10692
|
-
"--method <method>",
|
|
10693
|
-
"Agent building method: claude or manual",
|
|
10694
|
-
void 0
|
|
10695
|
-
).action(async (options) => {
|
|
10696
|
-
const token = await getToken();
|
|
10697
|
-
if (token) {
|
|
10698
|
-
console.log(chalk47.green("Done Authenticated"));
|
|
10699
|
-
} else {
|
|
10700
|
-
console.log(chalk47.dim("Authentication required..."));
|
|
10701
|
-
console.log();
|
|
10702
|
-
await loginCommand.parseAsync([], { from: "user" });
|
|
10703
|
-
}
|
|
10704
|
-
try {
|
|
10705
|
-
const result = await listModelProviders();
|
|
10706
|
-
if (result.modelProviders.length > 0) {
|
|
10707
|
-
console.log(chalk47.green("Done Model provider configured"));
|
|
10708
|
-
} else {
|
|
10709
|
-
console.log(chalk47.dim("Model provider setup required..."));
|
|
10710
|
-
console.log();
|
|
10711
|
-
await setupCommand2.parseAsync([], { from: "user" });
|
|
10712
|
-
}
|
|
10713
|
-
} catch {
|
|
10714
|
-
console.log(chalk47.dim("Setting up model provider..."));
|
|
10715
|
-
console.log();
|
|
10716
|
-
await setupCommand2.parseAsync([], { from: "user" });
|
|
10717
|
-
}
|
|
10718
|
-
let createAgent = options.yes;
|
|
10719
|
-
if (!createAgent && isInteractive()) {
|
|
10720
|
-
const response = await prompts3(
|
|
10721
|
-
{
|
|
10722
|
-
type: "confirm",
|
|
10723
|
-
name: "create",
|
|
10724
|
-
message: `Create ${DEMO_AGENT_DIR}?`,
|
|
10725
|
-
initial: true
|
|
10726
|
-
},
|
|
10727
|
-
{ onCancel: () => process.exit(0) }
|
|
10728
|
-
);
|
|
10729
|
-
createAgent = response.create;
|
|
10730
|
-
}
|
|
10731
|
-
if (!createAgent) {
|
|
10732
|
-
console.log(chalk47.dim("Skipped agent creation"));
|
|
10733
|
-
return;
|
|
10734
|
-
}
|
|
10735
|
-
if (existsSync10(DEMO_AGENT_DIR)) {
|
|
10736
|
-
console.log(chalk47.red(`x ${DEMO_AGENT_DIR}/ already exists`));
|
|
10737
|
-
console.log();
|
|
10738
|
-
console.log("Remove it first or use a different directory:");
|
|
10739
|
-
console.log(chalk47.cyan(` rm -rf ${DEMO_AGENT_DIR}`));
|
|
10740
|
-
process.exit(1);
|
|
10741
|
-
}
|
|
10742
|
-
await mkdir8(DEMO_AGENT_DIR, { recursive: true });
|
|
10743
|
-
const originalDir = process.cwd();
|
|
10744
|
-
process.chdir(DEMO_AGENT_DIR);
|
|
10745
|
-
try {
|
|
10746
|
-
await initCommand3.parseAsync(["--name", DEMO_AGENT_NAME], {
|
|
10747
|
-
from: "user"
|
|
10748
|
-
});
|
|
10749
|
-
} finally {
|
|
10750
|
-
process.chdir(originalDir);
|
|
10751
|
-
}
|
|
10752
|
-
console.log();
|
|
10753
|
-
let method = options.method;
|
|
10754
|
-
if (!method && isInteractive()) {
|
|
10755
|
-
const response = await prompts3(
|
|
10756
|
-
{
|
|
10757
|
-
type: "select",
|
|
10758
|
-
name: "method",
|
|
10759
|
-
message: "How would you like to build your agent?",
|
|
10760
|
-
choices: [
|
|
10761
|
-
{
|
|
10762
|
-
title: "Use `vm0 setup-claude` to let Claude help (Recommended)",
|
|
10763
|
-
value: "claude"
|
|
10764
|
-
},
|
|
10765
|
-
{
|
|
10766
|
-
title: "I will do it myself (Edit `AGENTS.md` and `vm0.yaml`)",
|
|
10767
|
-
value: "manual"
|
|
10768
|
-
}
|
|
10769
|
-
]
|
|
10770
|
-
},
|
|
10771
|
-
{ onCancel: () => process.exit(0) }
|
|
10772
|
-
);
|
|
10773
|
-
method = response.method;
|
|
10774
|
-
}
|
|
10775
|
-
if (method === "claude") {
|
|
10776
|
-
process.chdir(DEMO_AGENT_DIR);
|
|
10777
|
-
try {
|
|
10778
|
-
await setupClaudeCommand.parseAsync(["--agent-dir", DEMO_AGENT_DIR], {
|
|
10779
|
-
from: "user"
|
|
10780
|
-
});
|
|
10781
|
-
} finally {
|
|
10782
|
-
process.chdir(originalDir);
|
|
10783
|
-
}
|
|
10784
|
-
} else {
|
|
10785
|
-
console.log("Next steps:");
|
|
10786
|
-
console.log(` 1. ${chalk47.cyan(`cd ${DEMO_AGENT_DIR}`)}`);
|
|
10787
|
-
console.log(
|
|
10788
|
-
` 2. Edit ${chalk47.cyan("AGENTS.md")} to define your agent's workflow`
|
|
10789
|
-
);
|
|
10790
|
-
console.log(` 3. Edit ${chalk47.cyan("vm0.yaml")} to configure skills`);
|
|
10791
|
-
console.log(` 4. Run ${chalk47.cyan('vm0 cook "start working"')} to test`);
|
|
10792
|
-
}
|
|
10793
|
-
});
|
|
10794
|
-
|
|
10795
11391
|
// src/index.ts
|
|
10796
11392
|
var program = new Command48();
|
|
10797
|
-
program.name("vm0").description("VM0 CLI - Build and run agents with natural language").version("9.
|
|
11393
|
+
program.name("vm0").description("VM0 CLI - Build and run agents with natural language").version("9.3.0");
|
|
10798
11394
|
program.command("info").description("Display environment information").action(async () => {
|
|
10799
|
-
console.log(
|
|
11395
|
+
console.log(chalk50.bold("System Information:"));
|
|
10800
11396
|
console.log(`Node Version: ${process.version}`);
|
|
10801
11397
|
console.log(`Platform: ${process.platform}`);
|
|
10802
11398
|
console.log(`Architecture: ${process.arch}`);
|