@vm0/cli 9.20.2 → 9.22.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 +587 -341
- package/package.json +2 -1
package/index.js
CHANGED
|
@@ -1,5 +1,76 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// src/instrument.ts
|
|
4
|
+
import * as Sentry from "@sentry/node";
|
|
5
|
+
import * as os from "os";
|
|
6
|
+
var DSN = process.env.SENTRY_DSN ?? "https://268d9b4cd051531805af76a5b3934dca@o4510583739777024.ingest.us.sentry.io/4510832047947776";
|
|
7
|
+
var OPERATIONAL_ERROR_PATTERNS = [
|
|
8
|
+
// Authentication errors (user needs to login)
|
|
9
|
+
/not authenticated/i,
|
|
10
|
+
// Resource not found (user typo or deleted resource)
|
|
11
|
+
/not found/i,
|
|
12
|
+
/agent not found/i,
|
|
13
|
+
/version not found/i,
|
|
14
|
+
/checkpoint not found/i,
|
|
15
|
+
/session not found/i,
|
|
16
|
+
// File errors (user provided wrong path)
|
|
17
|
+
/file not found/i,
|
|
18
|
+
/environment file not found/i,
|
|
19
|
+
// Validation errors (user input issues)
|
|
20
|
+
/invalid format/i,
|
|
21
|
+
/invalid.*config/i,
|
|
22
|
+
// Rate limiting (expected operational condition)
|
|
23
|
+
/rate limit/i,
|
|
24
|
+
/concurrent run limit/i,
|
|
25
|
+
// Network issues (transient, not bugs)
|
|
26
|
+
/network error/i,
|
|
27
|
+
/network issue/i,
|
|
28
|
+
/connection refused/i,
|
|
29
|
+
/timeout/i,
|
|
30
|
+
/ECONNREFUSED/i,
|
|
31
|
+
/ETIMEDOUT/i,
|
|
32
|
+
// Permission/access errors (operational, not bugs)
|
|
33
|
+
/forbidden/i,
|
|
34
|
+
/access denied/i
|
|
35
|
+
];
|
|
36
|
+
function isOperationalError(error) {
|
|
37
|
+
if (!(error instanceof Error)) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
const message = error.message;
|
|
41
|
+
return OPERATIONAL_ERROR_PATTERNS.some((pattern) => pattern.test(message));
|
|
42
|
+
}
|
|
43
|
+
if (DSN) {
|
|
44
|
+
Sentry.init({
|
|
45
|
+
dsn: DSN,
|
|
46
|
+
sendDefaultPii: false,
|
|
47
|
+
tracesSampleRate: 0,
|
|
48
|
+
shutdownTimeout: 500,
|
|
49
|
+
initialScope: {
|
|
50
|
+
tags: {
|
|
51
|
+
app: "cli"
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
// Filter out operational errors - only send programmer errors (bugs)
|
|
55
|
+
beforeSend(event, hint) {
|
|
56
|
+
const error = hint.originalException;
|
|
57
|
+
if (isOperationalError(error)) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
return event;
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
Sentry.setContext("cli", {
|
|
64
|
+
version: "9.22.0",
|
|
65
|
+
command: process.argv.slice(2).join(" ")
|
|
66
|
+
});
|
|
67
|
+
Sentry.setContext("runtime", {
|
|
68
|
+
node_version: process.version,
|
|
69
|
+
os_platform: os.platform(),
|
|
70
|
+
os_release: os.release()
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
3
74
|
// src/index.ts
|
|
4
75
|
import { Command as Command68 } from "commander";
|
|
5
76
|
|
|
@@ -2092,26 +2163,26 @@ var c13 = initContract();
|
|
|
2092
2163
|
var MODEL_PROVIDER_TYPES = {
|
|
2093
2164
|
"claude-code-oauth-token": {
|
|
2094
2165
|
framework: "claude-code",
|
|
2095
|
-
|
|
2166
|
+
secretName: "CLAUDE_CODE_OAUTH_TOKEN",
|
|
2096
2167
|
label: "Claude Code (OAuth Token)",
|
|
2097
|
-
|
|
2168
|
+
secretLabel: "OAuth token",
|
|
2098
2169
|
helpText: "To get your OAuth token, run: claude setup-token\n(Requires Claude Pro or Max subscription)"
|
|
2099
2170
|
},
|
|
2100
2171
|
"anthropic-api-key": {
|
|
2101
2172
|
framework: "claude-code",
|
|
2102
|
-
|
|
2173
|
+
secretName: "ANTHROPIC_API_KEY",
|
|
2103
2174
|
label: "Anthropic API Key",
|
|
2104
|
-
|
|
2175
|
+
secretLabel: "API key",
|
|
2105
2176
|
helpText: "Get your API key at: https://console.anthropic.com/settings/keys"
|
|
2106
2177
|
},
|
|
2107
2178
|
"openrouter-api-key": {
|
|
2108
2179
|
framework: "claude-code",
|
|
2109
|
-
|
|
2180
|
+
secretName: "OPENROUTER_API_KEY",
|
|
2110
2181
|
label: "OpenRouter",
|
|
2111
|
-
|
|
2182
|
+
secretLabel: "API key",
|
|
2112
2183
|
helpText: "Get your API key at: https://openrouter.ai/settings/keys",
|
|
2113
2184
|
environmentMapping: {
|
|
2114
|
-
ANTHROPIC_AUTH_TOKEN: "$
|
|
2185
|
+
ANTHROPIC_AUTH_TOKEN: "$secret",
|
|
2115
2186
|
ANTHROPIC_BASE_URL: "https://openrouter.ai/api",
|
|
2116
2187
|
ANTHROPIC_API_KEY: "",
|
|
2117
2188
|
ANTHROPIC_MODEL: "$model",
|
|
@@ -2129,12 +2200,12 @@ var MODEL_PROVIDER_TYPES = {
|
|
|
2129
2200
|
},
|
|
2130
2201
|
"moonshot-api-key": {
|
|
2131
2202
|
framework: "claude-code",
|
|
2132
|
-
|
|
2203
|
+
secretName: "MOONSHOT_API_KEY",
|
|
2133
2204
|
label: "Moonshot (Kimi)",
|
|
2134
|
-
|
|
2205
|
+
secretLabel: "API key",
|
|
2135
2206
|
helpText: "Get your API key at: https://platform.moonshot.ai/console/api-keys",
|
|
2136
2207
|
environmentMapping: {
|
|
2137
|
-
ANTHROPIC_AUTH_TOKEN: "$
|
|
2208
|
+
ANTHROPIC_AUTH_TOKEN: "$secret",
|
|
2138
2209
|
ANTHROPIC_BASE_URL: "https://api.moonshot.ai/anthropic",
|
|
2139
2210
|
ANTHROPIC_MODEL: "$model",
|
|
2140
2211
|
ANTHROPIC_DEFAULT_OPUS_MODEL: "$model",
|
|
@@ -2151,12 +2222,12 @@ var MODEL_PROVIDER_TYPES = {
|
|
|
2151
2222
|
},
|
|
2152
2223
|
"minimax-api-key": {
|
|
2153
2224
|
framework: "claude-code",
|
|
2154
|
-
|
|
2225
|
+
secretName: "MINIMAX_API_KEY",
|
|
2155
2226
|
label: "MiniMax",
|
|
2156
|
-
|
|
2227
|
+
secretLabel: "API key",
|
|
2157
2228
|
helpText: "Get your API key at: https://platform.minimax.io/user-center/basic-information/interface-key",
|
|
2158
2229
|
environmentMapping: {
|
|
2159
|
-
ANTHROPIC_AUTH_TOKEN: "$
|
|
2230
|
+
ANTHROPIC_AUTH_TOKEN: "$secret",
|
|
2160
2231
|
ANTHROPIC_BASE_URL: "https://api.minimax.io/anthropic",
|
|
2161
2232
|
ANTHROPIC_MODEL: "$model",
|
|
2162
2233
|
ANTHROPIC_DEFAULT_OPUS_MODEL: "$model",
|
|
@@ -2171,12 +2242,12 @@ var MODEL_PROVIDER_TYPES = {
|
|
|
2171
2242
|
},
|
|
2172
2243
|
"deepseek-api-key": {
|
|
2173
2244
|
framework: "claude-code",
|
|
2174
|
-
|
|
2245
|
+
secretName: "DEEPSEEK_API_KEY",
|
|
2175
2246
|
label: "DeepSeek",
|
|
2176
|
-
|
|
2247
|
+
secretLabel: "API key",
|
|
2177
2248
|
helpText: "Get your API key at: https://platform.deepseek.com/api_keys",
|
|
2178
2249
|
environmentMapping: {
|
|
2179
|
-
ANTHROPIC_AUTH_TOKEN: "$
|
|
2250
|
+
ANTHROPIC_AUTH_TOKEN: "$secret",
|
|
2180
2251
|
ANTHROPIC_BASE_URL: "https://api.deepseek.com/anthropic",
|
|
2181
2252
|
ANTHROPIC_MODEL: "$model",
|
|
2182
2253
|
ANTHROPIC_DEFAULT_OPUS_MODEL: "$model",
|
|
@@ -2191,12 +2262,12 @@ var MODEL_PROVIDER_TYPES = {
|
|
|
2191
2262
|
},
|
|
2192
2263
|
"zai-api-key": {
|
|
2193
2264
|
framework: "claude-code",
|
|
2194
|
-
|
|
2265
|
+
secretName: "ZAI_API_KEY",
|
|
2195
2266
|
label: "Z.AI (GLM)",
|
|
2196
|
-
|
|
2267
|
+
secretLabel: "API key",
|
|
2197
2268
|
helpText: "Get your API key at: https://z.ai/model-api",
|
|
2198
2269
|
environmentMapping: {
|
|
2199
|
-
ANTHROPIC_AUTH_TOKEN: "$
|
|
2270
|
+
ANTHROPIC_AUTH_TOKEN: "$secret",
|
|
2200
2271
|
ANTHROPIC_BASE_URL: "https://api.z.ai/api/anthropic",
|
|
2201
2272
|
ANTHROPIC_MODEL: "$model",
|
|
2202
2273
|
ANTHROPIC_DEFAULT_OPUS_MODEL: "$model",
|
|
@@ -2216,7 +2287,7 @@ var MODEL_PROVIDER_TYPES = {
|
|
|
2216
2287
|
"api-key": {
|
|
2217
2288
|
label: "API Key",
|
|
2218
2289
|
helpText: "Use an Azure Foundry API key for authentication",
|
|
2219
|
-
|
|
2290
|
+
secrets: {
|
|
2220
2291
|
ANTHROPIC_FOUNDRY_API_KEY: {
|
|
2221
2292
|
label: "ANTHROPIC_FOUNDRY_API_KEY",
|
|
2222
2293
|
required: true,
|
|
@@ -2234,8 +2305,8 @@ var MODEL_PROVIDER_TYPES = {
|
|
|
2234
2305
|
defaultAuthMethod: "api-key",
|
|
2235
2306
|
environmentMapping: {
|
|
2236
2307
|
CLAUDE_CODE_USE_FOUNDRY: "1",
|
|
2237
|
-
ANTHROPIC_FOUNDRY_API_KEY: "$
|
|
2238
|
-
ANTHROPIC_FOUNDRY_RESOURCE: "$
|
|
2308
|
+
ANTHROPIC_FOUNDRY_API_KEY: "$secrets.ANTHROPIC_FOUNDRY_API_KEY",
|
|
2309
|
+
ANTHROPIC_FOUNDRY_RESOURCE: "$secrets.ANTHROPIC_FOUNDRY_RESOURCE",
|
|
2239
2310
|
ANTHROPIC_MODEL: "$model"
|
|
2240
2311
|
},
|
|
2241
2312
|
models: [],
|
|
@@ -2246,12 +2317,12 @@ var MODEL_PROVIDER_TYPES = {
|
|
|
2246
2317
|
"aws-bedrock": {
|
|
2247
2318
|
framework: "claude-code",
|
|
2248
2319
|
label: "AWS Bedrock",
|
|
2249
|
-
helpText: "Run Claude on AWS Bedrock.\nSetup guide: https://
|
|
2320
|
+
helpText: "Run Claude on AWS Bedrock.\nSetup guide: https://code.claude.com/docs/en/amazon-bedrock",
|
|
2250
2321
|
authMethods: {
|
|
2251
2322
|
"api-key": {
|
|
2252
2323
|
label: "Bedrock API Key",
|
|
2253
2324
|
helpText: "Use a Bedrock API key for authentication",
|
|
2254
|
-
|
|
2325
|
+
secrets: {
|
|
2255
2326
|
AWS_BEARER_TOKEN_BEDROCK: {
|
|
2256
2327
|
label: "AWS_BEARER_TOKEN_BEDROCK",
|
|
2257
2328
|
required: true,
|
|
@@ -2267,8 +2338,8 @@ var MODEL_PROVIDER_TYPES = {
|
|
|
2267
2338
|
},
|
|
2268
2339
|
"access-keys": {
|
|
2269
2340
|
label: "IAM Access Keys",
|
|
2270
|
-
helpText: "Use IAM access key
|
|
2271
|
-
|
|
2341
|
+
helpText: "Use IAM access key secrets",
|
|
2342
|
+
secrets: {
|
|
2272
2343
|
AWS_ACCESS_KEY_ID: {
|
|
2273
2344
|
label: "AWS_ACCESS_KEY_ID",
|
|
2274
2345
|
required: true,
|
|
@@ -2282,7 +2353,7 @@ var MODEL_PROVIDER_TYPES = {
|
|
|
2282
2353
|
AWS_SESSION_TOKEN: {
|
|
2283
2354
|
label: "AWS_SESSION_TOKEN",
|
|
2284
2355
|
required: false,
|
|
2285
|
-
helpText: "Optional, for temporary
|
|
2356
|
+
helpText: "Optional, for temporary secrets"
|
|
2286
2357
|
},
|
|
2287
2358
|
AWS_REGION: {
|
|
2288
2359
|
label: "AWS_REGION",
|
|
@@ -2296,11 +2367,11 @@ var MODEL_PROVIDER_TYPES = {
|
|
|
2296
2367
|
defaultAuthMethod: "api-key",
|
|
2297
2368
|
environmentMapping: {
|
|
2298
2369
|
CLAUDE_CODE_USE_BEDROCK: "1",
|
|
2299
|
-
AWS_REGION: "$
|
|
2300
|
-
AWS_BEARER_TOKEN_BEDROCK: "$
|
|
2301
|
-
AWS_ACCESS_KEY_ID: "$
|
|
2302
|
-
AWS_SECRET_ACCESS_KEY: "$
|
|
2303
|
-
AWS_SESSION_TOKEN: "$
|
|
2370
|
+
AWS_REGION: "$secrets.AWS_REGION",
|
|
2371
|
+
AWS_BEARER_TOKEN_BEDROCK: "$secrets.AWS_BEARER_TOKEN_BEDROCK",
|
|
2372
|
+
AWS_ACCESS_KEY_ID: "$secrets.AWS_ACCESS_KEY_ID",
|
|
2373
|
+
AWS_SECRET_ACCESS_KEY: "$secrets.AWS_SECRET_ACCESS_KEY",
|
|
2374
|
+
AWS_SESSION_TOKEN: "$secrets.AWS_SESSION_TOKEN",
|
|
2304
2375
|
ANTHROPIC_MODEL: "$model"
|
|
2305
2376
|
},
|
|
2306
2377
|
models: [],
|
|
@@ -2333,13 +2404,13 @@ function getDefaultAuthMethod(type) {
|
|
|
2333
2404
|
const config = MODEL_PROVIDER_TYPES[type];
|
|
2334
2405
|
return "defaultAuthMethod" in config ? config.defaultAuthMethod : void 0;
|
|
2335
2406
|
}
|
|
2336
|
-
function
|
|
2407
|
+
function getSecretsForAuthMethod(type, authMethod) {
|
|
2337
2408
|
const authMethods = getAuthMethodsForType(type);
|
|
2338
2409
|
if (!authMethods || !(authMethod in authMethods)) {
|
|
2339
2410
|
return void 0;
|
|
2340
2411
|
}
|
|
2341
2412
|
const method = authMethods[authMethod];
|
|
2342
|
-
return method?.
|
|
2413
|
+
return method?.secrets;
|
|
2343
2414
|
}
|
|
2344
2415
|
function getModels(type) {
|
|
2345
2416
|
const config = MODEL_PROVIDER_TYPES[type];
|
|
@@ -2365,11 +2436,11 @@ var modelProviderResponseSchema = z16.object({
|
|
|
2365
2436
|
id: z16.string().uuid(),
|
|
2366
2437
|
type: modelProviderTypeSchema,
|
|
2367
2438
|
framework: modelProviderFrameworkSchema,
|
|
2368
|
-
|
|
2369
|
-
// Legacy single-
|
|
2439
|
+
secretName: z16.string().nullable(),
|
|
2440
|
+
// Legacy single-secret (deprecated for multi-auth)
|
|
2370
2441
|
authMethod: z16.string().nullable(),
|
|
2371
2442
|
// For multi-auth providers
|
|
2372
|
-
|
|
2443
|
+
secretNames: z16.array(z16.string()).nullable(),
|
|
2373
2444
|
// For multi-auth providers
|
|
2374
2445
|
isDefault: z16.boolean(),
|
|
2375
2446
|
selectedModel: z16.string().nullable(),
|
|
@@ -2381,23 +2452,21 @@ var modelProviderListResponseSchema = z16.object({
|
|
|
2381
2452
|
});
|
|
2382
2453
|
var upsertModelProviderRequestSchema = z16.object({
|
|
2383
2454
|
type: modelProviderTypeSchema,
|
|
2384
|
-
|
|
2385
|
-
// Legacy single
|
|
2455
|
+
secret: z16.string().min(1).optional(),
|
|
2456
|
+
// Legacy single secret
|
|
2386
2457
|
authMethod: z16.string().optional(),
|
|
2387
2458
|
// For multi-auth providers
|
|
2388
|
-
|
|
2459
|
+
secrets: z16.record(z16.string(), z16.string()).optional(),
|
|
2389
2460
|
// For multi-auth providers
|
|
2390
|
-
convert: z16.boolean().optional(),
|
|
2391
2461
|
selectedModel: z16.string().optional()
|
|
2392
2462
|
});
|
|
2393
2463
|
var upsertModelProviderResponseSchema = z16.object({
|
|
2394
2464
|
provider: modelProviderResponseSchema,
|
|
2395
2465
|
created: z16.boolean()
|
|
2396
2466
|
});
|
|
2397
|
-
var
|
|
2467
|
+
var checkSecretResponseSchema = z16.object({
|
|
2398
2468
|
exists: z16.boolean(),
|
|
2399
|
-
|
|
2400
|
-
currentType: z16.enum(["user", "model-provider"]).optional()
|
|
2469
|
+
secretName: z16.string()
|
|
2401
2470
|
});
|
|
2402
2471
|
var modelProvidersMainContract = c13.router({
|
|
2403
2472
|
list: {
|
|
@@ -2436,11 +2505,11 @@ var modelProvidersCheckContract = c13.router({
|
|
|
2436
2505
|
type: modelProviderTypeSchema
|
|
2437
2506
|
}),
|
|
2438
2507
|
responses: {
|
|
2439
|
-
200:
|
|
2508
|
+
200: checkSecretResponseSchema,
|
|
2440
2509
|
401: apiErrorSchema,
|
|
2441
2510
|
500: apiErrorSchema
|
|
2442
2511
|
},
|
|
2443
|
-
summary: "Check if
|
|
2512
|
+
summary: "Check if secret exists for a model provider type"
|
|
2444
2513
|
}
|
|
2445
2514
|
});
|
|
2446
2515
|
var modelProvidersByTypeContract = c13.router({
|
|
@@ -2476,7 +2545,7 @@ var modelProvidersConvertContract = c13.router({
|
|
|
2476
2545
|
404: apiErrorSchema,
|
|
2477
2546
|
500: apiErrorSchema
|
|
2478
2547
|
},
|
|
2479
|
-
summary: "Convert existing user
|
|
2548
|
+
summary: "Convert existing user secret to model provider"
|
|
2480
2549
|
}
|
|
2481
2550
|
});
|
|
2482
2551
|
var modelProvidersSetDefaultContract = c13.router({
|
|
@@ -3627,13 +3696,14 @@ function getSkillStorageName(fullPath) {
|
|
|
3627
3696
|
|
|
3628
3697
|
// ../../packages/core/src/github-url.ts
|
|
3629
3698
|
function parseGitHubTreeUrl(url) {
|
|
3630
|
-
const
|
|
3699
|
+
const normalizedUrl = url.replace(/\/+$/, "");
|
|
3700
|
+
const fullPathMatch = normalizedUrl.match(/^https:\/\/github\.com\/(.+)$/);
|
|
3631
3701
|
if (!fullPathMatch) {
|
|
3632
3702
|
return null;
|
|
3633
3703
|
}
|
|
3634
3704
|
const fullPath = fullPathMatch[1];
|
|
3635
3705
|
const regex = /^https:\/\/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)\/(.+)$/;
|
|
3636
|
-
const match =
|
|
3706
|
+
const match = normalizedUrl.match(regex);
|
|
3637
3707
|
if (!match) {
|
|
3638
3708
|
return null;
|
|
3639
3709
|
}
|
|
@@ -3649,6 +3719,39 @@ function parseGitHubTreeUrl(url) {
|
|
|
3649
3719
|
fullPath
|
|
3650
3720
|
};
|
|
3651
3721
|
}
|
|
3722
|
+
function parseGitHubUrl(url) {
|
|
3723
|
+
const normalizedUrl = url.replace(/\/+$/, "");
|
|
3724
|
+
const fullPathMatch = normalizedUrl.match(/^https:\/\/github\.com\/(.+)$/);
|
|
3725
|
+
if (!fullPathMatch) {
|
|
3726
|
+
return null;
|
|
3727
|
+
}
|
|
3728
|
+
const fullPath = fullPathMatch[1];
|
|
3729
|
+
const plainMatch = normalizedUrl.match(
|
|
3730
|
+
/^https:\/\/github\.com\/([^/]+)\/([^/]+)$/
|
|
3731
|
+
);
|
|
3732
|
+
if (plainMatch) {
|
|
3733
|
+
return {
|
|
3734
|
+
owner: plainMatch[1],
|
|
3735
|
+
repo: plainMatch[2],
|
|
3736
|
+
branch: null,
|
|
3737
|
+
path: null,
|
|
3738
|
+
fullPath
|
|
3739
|
+
};
|
|
3740
|
+
}
|
|
3741
|
+
const treeMatch = normalizedUrl.match(
|
|
3742
|
+
/^https:\/\/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)(?:\/(.+))?$/
|
|
3743
|
+
);
|
|
3744
|
+
if (treeMatch) {
|
|
3745
|
+
return {
|
|
3746
|
+
owner: treeMatch[1],
|
|
3747
|
+
repo: treeMatch[2],
|
|
3748
|
+
branch: treeMatch[3],
|
|
3749
|
+
path: treeMatch[4] ?? null,
|
|
3750
|
+
fullPath
|
|
3751
|
+
};
|
|
3752
|
+
}
|
|
3753
|
+
return null;
|
|
3754
|
+
}
|
|
3652
3755
|
|
|
3653
3756
|
// ../../packages/core/src/frameworks.ts
|
|
3654
3757
|
var SUPPORTED_FRAMEWORKS = ["claude-code", "codex"];
|
|
@@ -3710,9 +3813,10 @@ var FEATURE_SWITCHES = {
|
|
|
3710
3813
|
|
|
3711
3814
|
// src/lib/api/core/client-factory.ts
|
|
3712
3815
|
var ApiRequestError = class extends Error {
|
|
3713
|
-
constructor(message, code) {
|
|
3816
|
+
constructor(message, code, status) {
|
|
3714
3817
|
super(message);
|
|
3715
3818
|
this.code = code;
|
|
3819
|
+
this.status = status;
|
|
3716
3820
|
this.name = "ApiRequestError";
|
|
3717
3821
|
}
|
|
3718
3822
|
};
|
|
@@ -3742,11 +3846,27 @@ async function getClientConfig() {
|
|
|
3742
3846
|
const baseHeaders = await getHeaders();
|
|
3743
3847
|
return { baseUrl, baseHeaders, jsonQuery: true };
|
|
3744
3848
|
}
|
|
3745
|
-
function handleError(result, defaultMessage) {
|
|
3849
|
+
function handleError(result, defaultMessage, options) {
|
|
3850
|
+
if (!options?.useServerMessage) {
|
|
3851
|
+
if (result.status === 401) {
|
|
3852
|
+
throw new ApiRequestError(
|
|
3853
|
+
"Not authenticated. Run: vm0 auth login",
|
|
3854
|
+
"UNAUTHORIZED",
|
|
3855
|
+
401
|
|
3856
|
+
);
|
|
3857
|
+
}
|
|
3858
|
+
if (result.status === 403) {
|
|
3859
|
+
throw new ApiRequestError(
|
|
3860
|
+
"An unexpected network issue occurred",
|
|
3861
|
+
"FORBIDDEN",
|
|
3862
|
+
403
|
|
3863
|
+
);
|
|
3864
|
+
}
|
|
3865
|
+
}
|
|
3746
3866
|
const errorBody = result.body;
|
|
3747
3867
|
const message = errorBody.error?.message || defaultMessage;
|
|
3748
3868
|
const code = errorBody.error?.code || "UNKNOWN";
|
|
3749
|
-
throw new ApiRequestError(message, code);
|
|
3869
|
+
throw new ApiRequestError(message, code, result.status);
|
|
3750
3870
|
}
|
|
3751
3871
|
|
|
3752
3872
|
// src/lib/api/core/http.ts
|
|
@@ -3931,7 +4051,7 @@ async function getScope() {
|
|
|
3931
4051
|
if (result.status === 200) {
|
|
3932
4052
|
return result.body;
|
|
3933
4053
|
}
|
|
3934
|
-
handleError(result, "Failed to get scope");
|
|
4054
|
+
handleError(result, "Failed to get scope", { useServerMessage: true });
|
|
3935
4055
|
}
|
|
3936
4056
|
async function createScope(body) {
|
|
3937
4057
|
const config = await getClientConfig();
|
|
@@ -3940,7 +4060,7 @@ async function createScope(body) {
|
|
|
3940
4060
|
if (result.status === 201) {
|
|
3941
4061
|
return result.body;
|
|
3942
4062
|
}
|
|
3943
|
-
handleError(result, "Failed to create scope");
|
|
4063
|
+
handleError(result, "Failed to create scope", { useServerMessage: true });
|
|
3944
4064
|
}
|
|
3945
4065
|
async function updateScope(body) {
|
|
3946
4066
|
const config = await getClientConfig();
|
|
@@ -3949,7 +4069,7 @@ async function updateScope(body) {
|
|
|
3949
4069
|
if (result.status === 200) {
|
|
3950
4070
|
return result.body;
|
|
3951
4071
|
}
|
|
3952
|
-
handleError(result, "Failed to update scope");
|
|
4072
|
+
handleError(result, "Failed to update scope", { useServerMessage: true });
|
|
3953
4073
|
}
|
|
3954
4074
|
|
|
3955
4075
|
// src/lib/api/domains/storages.ts
|
|
@@ -4187,7 +4307,7 @@ async function upsertModelProvider(body) {
|
|
|
4187
4307
|
}
|
|
4188
4308
|
handleError(result, "Failed to set model provider");
|
|
4189
4309
|
}
|
|
4190
|
-
async function
|
|
4310
|
+
async function checkModelProviderSecret(type) {
|
|
4191
4311
|
const config = await getClientConfig();
|
|
4192
4312
|
const client = initClient9(modelProvidersCheckContract, config);
|
|
4193
4313
|
const result = await client.check({
|
|
@@ -4196,7 +4316,7 @@ async function checkModelProviderCredential(type) {
|
|
|
4196
4316
|
if (result.status === 200) {
|
|
4197
4317
|
return result.body;
|
|
4198
4318
|
}
|
|
4199
|
-
handleError(result, "Failed to check
|
|
4319
|
+
handleError(result, "Failed to check secret");
|
|
4200
4320
|
}
|
|
4201
4321
|
async function deleteModelProvider(type) {
|
|
4202
4322
|
const config = await getClientConfig();
|
|
@@ -4209,17 +4329,6 @@ async function deleteModelProvider(type) {
|
|
|
4209
4329
|
}
|
|
4210
4330
|
handleError(result, `Model provider "${type}" not found`);
|
|
4211
4331
|
}
|
|
4212
|
-
async function convertModelProviderCredential(type) {
|
|
4213
|
-
const config = await getClientConfig();
|
|
4214
|
-
const client = initClient9(modelProvidersConvertContract, config);
|
|
4215
|
-
const result = await client.convert({
|
|
4216
|
-
params: { type }
|
|
4217
|
-
});
|
|
4218
|
-
if (result.status === 200) {
|
|
4219
|
-
return result.body;
|
|
4220
|
-
}
|
|
4221
|
-
handleError(result, "Failed to convert credential");
|
|
4222
|
-
}
|
|
4223
4332
|
async function setModelProviderDefault(type) {
|
|
4224
4333
|
const config = await getClientConfig();
|
|
4225
4334
|
const client = initClient9(modelProvidersSetDefaultContract, config);
|
|
@@ -4450,7 +4559,7 @@ function validateAgentCompose(config) {
|
|
|
4450
4559
|
// src/lib/domain/github-skills.ts
|
|
4451
4560
|
import * as fs from "fs/promises";
|
|
4452
4561
|
import * as path from "path";
|
|
4453
|
-
import * as
|
|
4562
|
+
import * as os2 from "os";
|
|
4454
4563
|
import { exec } from "child_process";
|
|
4455
4564
|
import { promisify } from "util";
|
|
4456
4565
|
import { parse as parseYaml } from "yaml";
|
|
@@ -4470,7 +4579,7 @@ function getSkillStorageName2(parsed) {
|
|
|
4470
4579
|
async function downloadGitHubSkill(parsed, destDir) {
|
|
4471
4580
|
const repoUrl = `https://github.com/${parsed.owner}/${parsed.repo}.git`;
|
|
4472
4581
|
const skillDir = path.join(destDir, parsed.skillName);
|
|
4473
|
-
const tempDir = await fs.mkdtemp(path.join(
|
|
4582
|
+
const tempDir = await fs.mkdtemp(path.join(os2.tmpdir(), "vm0-skill-"));
|
|
4474
4583
|
try {
|
|
4475
4584
|
await execAsync(`git init`, { cwd: tempDir });
|
|
4476
4585
|
await execAsync(`git remote add origin "${repoUrl}"`, { cwd: tempDir });
|
|
@@ -4489,10 +4598,41 @@ async function downloadGitHubSkill(parsed, destDir) {
|
|
|
4489
4598
|
await fs.rm(tempDir, { recursive: true, force: true });
|
|
4490
4599
|
}
|
|
4491
4600
|
}
|
|
4601
|
+
async function getDefaultBranch(owner, repo) {
|
|
4602
|
+
const repoUrl = `https://github.com/${owner}/${repo}.git`;
|
|
4603
|
+
try {
|
|
4604
|
+
const { stdout } = await execAsync(
|
|
4605
|
+
`git ls-remote --symref "${repoUrl}" HEAD`
|
|
4606
|
+
);
|
|
4607
|
+
const match = stdout.match(/ref: refs\/heads\/([^\s]+)/);
|
|
4608
|
+
if (!match) {
|
|
4609
|
+
throw new Error(
|
|
4610
|
+
`Could not determine default branch for ${owner}/${repo}`
|
|
4611
|
+
);
|
|
4612
|
+
}
|
|
4613
|
+
return match[1];
|
|
4614
|
+
} catch (error) {
|
|
4615
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4616
|
+
if (message.includes("not found") || message.includes("Repository not found")) {
|
|
4617
|
+
throw new Error(`Repository not found: ${owner}/${repo}`);
|
|
4618
|
+
}
|
|
4619
|
+
if (message.includes("Authentication failed") || message.includes("could not read Username")) {
|
|
4620
|
+
throw new Error(
|
|
4621
|
+
`Cannot access repository ${owner}/${repo}. Is it private?`
|
|
4622
|
+
);
|
|
4623
|
+
}
|
|
4624
|
+
throw error;
|
|
4625
|
+
}
|
|
4626
|
+
}
|
|
4492
4627
|
async function downloadGitHubDirectory(url) {
|
|
4493
|
-
const parsed =
|
|
4628
|
+
const parsed = parseGitHubUrl(url);
|
|
4629
|
+
if (!parsed) {
|
|
4630
|
+
throw new Error(
|
|
4631
|
+
`Invalid GitHub URL: ${url}. Expected format: https://github.com/{owner}/{repo}[/tree/{branch}[/path]]`
|
|
4632
|
+
);
|
|
4633
|
+
}
|
|
4494
4634
|
const repoUrl = `https://github.com/${parsed.owner}/${parsed.repo}.git`;
|
|
4495
|
-
const tempDir = await fs.mkdtemp(path.join(
|
|
4635
|
+
const tempDir = await fs.mkdtemp(path.join(os2.tmpdir(), "vm0-github-"));
|
|
4496
4636
|
try {
|
|
4497
4637
|
try {
|
|
4498
4638
|
await execAsync("git --version");
|
|
@@ -4501,13 +4641,15 @@ async function downloadGitHubDirectory(url) {
|
|
|
4501
4641
|
"git command not found. Please install git to use GitHub URLs."
|
|
4502
4642
|
);
|
|
4503
4643
|
}
|
|
4644
|
+
const branch = parsed.branch ?? await getDefaultBranch(parsed.owner, parsed.repo);
|
|
4504
4645
|
await execAsync(`git init`, { cwd: tempDir });
|
|
4505
4646
|
await execAsync(`git remote add origin "${repoUrl}"`, { cwd: tempDir });
|
|
4506
4647
|
await execAsync(`git config core.sparseCheckout true`, { cwd: tempDir });
|
|
4648
|
+
const sparsePattern = parsed.path ?? "/*";
|
|
4507
4649
|
const sparseFile = path.join(tempDir, ".git", "info", "sparse-checkout");
|
|
4508
|
-
await fs.writeFile(sparseFile,
|
|
4650
|
+
await fs.writeFile(sparseFile, sparsePattern + "\n");
|
|
4509
4651
|
try {
|
|
4510
|
-
await execAsync(`git fetch --depth 1 origin "${
|
|
4652
|
+
await execAsync(`git fetch --depth 1 origin "${branch}"`, {
|
|
4511
4653
|
cwd: tempDir
|
|
4512
4654
|
});
|
|
4513
4655
|
} catch (error) {
|
|
@@ -4516,15 +4658,14 @@ async function downloadGitHubDirectory(url) {
|
|
|
4516
4658
|
throw new Error(`Cannot access repository. Is it private? URL: ${url}`);
|
|
4517
4659
|
}
|
|
4518
4660
|
if (message.includes("couldn't find remote ref")) {
|
|
4519
|
-
throw new Error(
|
|
4520
|
-
`Branch "${parsed.branch}" not found in repository: ${url}`
|
|
4521
|
-
);
|
|
4661
|
+
throw new Error(`Branch "${branch}" not found in repository: ${url}`);
|
|
4522
4662
|
}
|
|
4523
4663
|
throw error;
|
|
4524
4664
|
}
|
|
4525
|
-
await execAsync(`git checkout "${
|
|
4665
|
+
await execAsync(`git checkout "${branch}"`, { cwd: tempDir });
|
|
4666
|
+
const downloadedDir = parsed.path ? path.join(tempDir, parsed.path) : tempDir;
|
|
4526
4667
|
return {
|
|
4527
|
-
dir:
|
|
4668
|
+
dir: downloadedDir,
|
|
4528
4669
|
tempRoot: tempDir
|
|
4529
4670
|
};
|
|
4530
4671
|
} catch (error) {
|
|
@@ -4577,13 +4718,13 @@ async function readSkillFrontmatter(skillDir) {
|
|
|
4577
4718
|
// src/lib/storage/system-storage.ts
|
|
4578
4719
|
import * as fs4 from "fs/promises";
|
|
4579
4720
|
import * as path4 from "path";
|
|
4580
|
-
import * as
|
|
4721
|
+
import * as os4 from "os";
|
|
4581
4722
|
|
|
4582
4723
|
// src/lib/storage/direct-upload.ts
|
|
4583
4724
|
import { createHash } from "crypto";
|
|
4584
4725
|
import * as fs3 from "fs";
|
|
4585
4726
|
import * as path3 from "path";
|
|
4586
|
-
import * as
|
|
4727
|
+
import * as os3 from "os";
|
|
4587
4728
|
import * as tar2 from "tar";
|
|
4588
4729
|
|
|
4589
4730
|
// src/lib/utils/file-utils.ts
|
|
@@ -4746,7 +4887,7 @@ async function collectFileMetadata(cwd, files, onProgress) {
|
|
|
4746
4887
|
return fileEntries;
|
|
4747
4888
|
}
|
|
4748
4889
|
async function createArchive(cwd, files) {
|
|
4749
|
-
const tmpDir = fs3.mkdtempSync(path3.join(
|
|
4890
|
+
const tmpDir = fs3.mkdtempSync(path3.join(os3.tmpdir(), "vm0-"));
|
|
4750
4891
|
const tarPath = path3.join(tmpDir, "archive.tar.gz");
|
|
4751
4892
|
try {
|
|
4752
4893
|
const relativePaths = files.map((file) => path3.relative(cwd, file));
|
|
@@ -4903,7 +5044,7 @@ async function uploadInstructions(agentName, instructionsFilePath, basePath, fra
|
|
|
4903
5044
|
const storageName = getInstructionsStorageName(agentName.toLowerCase());
|
|
4904
5045
|
const absolutePath = path4.isAbsolute(instructionsFilePath) ? instructionsFilePath : path4.join(basePath, instructionsFilePath);
|
|
4905
5046
|
const content = await fs4.readFile(absolutePath, "utf8");
|
|
4906
|
-
const tmpDir = await fs4.mkdtemp(path4.join(
|
|
5047
|
+
const tmpDir = await fs4.mkdtemp(path4.join(os4.tmpdir(), "vm0-instructions-"));
|
|
4907
5048
|
const instructionsDir = path4.join(tmpDir, "instructions");
|
|
4908
5049
|
await fs4.mkdir(instructionsDir);
|
|
4909
5050
|
const filename = getInstructionsFilename(framework);
|
|
@@ -4922,7 +5063,7 @@ async function uploadInstructions(agentName, instructionsFilePath, basePath, fra
|
|
|
4922
5063
|
async function uploadSkill(skillUrl) {
|
|
4923
5064
|
const parsed = parseGitHubTreeUrl2(skillUrl);
|
|
4924
5065
|
const storageName = getSkillStorageName2(parsed);
|
|
4925
|
-
const tmpDir = await fs4.mkdtemp(path4.join(
|
|
5066
|
+
const tmpDir = await fs4.mkdtemp(path4.join(os4.tmpdir(), "vm0-skill-"));
|
|
4926
5067
|
try {
|
|
4927
5068
|
const skillDir = await downloadGitHubSkill(parsed, tmpDir);
|
|
4928
5069
|
await validateSkillDirectory(skillDir);
|
|
@@ -5207,17 +5348,23 @@ async function silentUpgradeAfterCommand(currentVersion) {
|
|
|
5207
5348
|
|
|
5208
5349
|
// src/commands/compose/index.ts
|
|
5209
5350
|
var DEFAULT_CONFIG_FILE = "vm0.yaml";
|
|
5210
|
-
function
|
|
5211
|
-
return
|
|
5351
|
+
function isGitHubUrl(input) {
|
|
5352
|
+
return /^https:\/\/github\.com\/[^/]+\/[^/]+/.test(input);
|
|
5212
5353
|
}
|
|
5213
5354
|
function getSecretsFromComposeContent(content) {
|
|
5214
5355
|
const refs = extractVariableReferences(content);
|
|
5215
5356
|
const grouped = groupVariablesBySource(refs);
|
|
5216
5357
|
return new Set(grouped.secrets.map((r) => r.name));
|
|
5217
5358
|
}
|
|
5218
|
-
async function loadAndValidateConfig(configFile) {
|
|
5359
|
+
async function loadAndValidateConfig(configFile, porcelainMode) {
|
|
5219
5360
|
if (!existsSync4(configFile)) {
|
|
5220
|
-
|
|
5361
|
+
if (porcelainMode) {
|
|
5362
|
+
console.log(
|
|
5363
|
+
JSON.stringify({ error: `Config file not found: ${configFile}` })
|
|
5364
|
+
);
|
|
5365
|
+
} else {
|
|
5366
|
+
console.error(chalk4.red(`\u2717 Config file not found: ${configFile}`));
|
|
5367
|
+
}
|
|
5221
5368
|
process.exit(1);
|
|
5222
5369
|
}
|
|
5223
5370
|
const content = await readFile4(configFile, "utf8");
|
|
@@ -5225,15 +5372,22 @@ async function loadAndValidateConfig(configFile) {
|
|
|
5225
5372
|
try {
|
|
5226
5373
|
config = parseYaml2(content);
|
|
5227
5374
|
} catch (error) {
|
|
5228
|
-
|
|
5229
|
-
if (
|
|
5230
|
-
console.
|
|
5375
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
5376
|
+
if (porcelainMode) {
|
|
5377
|
+
console.log(JSON.stringify({ error: `Invalid YAML format: ${message}` }));
|
|
5378
|
+
} else {
|
|
5379
|
+
console.error(chalk4.red("\u2717 Invalid YAML format"));
|
|
5380
|
+
console.error(chalk4.dim(` ${message}`));
|
|
5231
5381
|
}
|
|
5232
5382
|
process.exit(1);
|
|
5233
5383
|
}
|
|
5234
5384
|
const validation = validateAgentCompose(config);
|
|
5235
5385
|
if (!validation.valid) {
|
|
5236
|
-
|
|
5386
|
+
if (porcelainMode) {
|
|
5387
|
+
console.log(JSON.stringify({ error: validation.error }));
|
|
5388
|
+
} else {
|
|
5389
|
+
console.error(chalk4.red(`\u2717 ${validation.error}`));
|
|
5390
|
+
}
|
|
5237
5391
|
process.exit(1);
|
|
5238
5392
|
}
|
|
5239
5393
|
const cfg = config;
|
|
@@ -5269,33 +5423,43 @@ function checkLegacyImageFormat(config) {
|
|
|
5269
5423
|
}
|
|
5270
5424
|
}
|
|
5271
5425
|
}
|
|
5272
|
-
async function uploadAssets(agentName, agent, basePath) {
|
|
5426
|
+
async function uploadAssets(agentName, agent, basePath, porcelainMode) {
|
|
5273
5427
|
if (agent.instructions) {
|
|
5274
|
-
|
|
5428
|
+
if (!porcelainMode) {
|
|
5429
|
+
console.log(`Uploading instructions: ${agent.instructions}`);
|
|
5430
|
+
}
|
|
5275
5431
|
const result = await uploadInstructions(
|
|
5276
5432
|
agentName,
|
|
5277
5433
|
agent.instructions,
|
|
5278
5434
|
basePath,
|
|
5279
5435
|
agent.framework
|
|
5280
5436
|
);
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5437
|
+
if (!porcelainMode) {
|
|
5438
|
+
console.log(
|
|
5439
|
+
chalk4.green(
|
|
5440
|
+
`\u2713 Instructions ${result.action === "deduplicated" ? "(unchanged)" : "uploaded"}: ${result.versionId.slice(0, 8)}`
|
|
5441
|
+
)
|
|
5442
|
+
);
|
|
5443
|
+
}
|
|
5286
5444
|
}
|
|
5287
5445
|
const skillResults = [];
|
|
5288
5446
|
if (agent.skills && Array.isArray(agent.skills)) {
|
|
5289
|
-
|
|
5447
|
+
if (!porcelainMode) {
|
|
5448
|
+
console.log(`Uploading ${agent.skills.length} skill(s)...`);
|
|
5449
|
+
}
|
|
5290
5450
|
for (const skillUrl of agent.skills) {
|
|
5291
|
-
|
|
5451
|
+
if (!porcelainMode) {
|
|
5452
|
+
console.log(chalk4.dim(` Downloading: ${skillUrl}`));
|
|
5453
|
+
}
|
|
5292
5454
|
const result = await uploadSkill(skillUrl);
|
|
5293
5455
|
skillResults.push(result);
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5456
|
+
if (!porcelainMode) {
|
|
5457
|
+
console.log(
|
|
5458
|
+
chalk4.green(
|
|
5459
|
+
` \u2713 Skill ${result.action === "deduplicated" ? "(unchanged)" : "uploaded"}: ${result.skillName} (${result.versionId.slice(0, 8)})`
|
|
5460
|
+
)
|
|
5461
|
+
);
|
|
5462
|
+
}
|
|
5299
5463
|
}
|
|
5300
5464
|
}
|
|
5301
5465
|
return skillResults;
|
|
@@ -5341,36 +5505,48 @@ async function displayAndConfirmVariables(variables, options) {
|
|
|
5341
5505
|
if (newSecrets.length === 0 && newVars.length === 0) {
|
|
5342
5506
|
return true;
|
|
5343
5507
|
}
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
const
|
|
5353
|
-
|
|
5354
|
-
|
|
5508
|
+
if (!options.porcelain) {
|
|
5509
|
+
console.log();
|
|
5510
|
+
console.log(
|
|
5511
|
+
chalk4.bold("Skills require the following environment variables:")
|
|
5512
|
+
);
|
|
5513
|
+
console.log();
|
|
5514
|
+
if (newSecrets.length > 0) {
|
|
5515
|
+
console.log(chalk4.cyan(" Secrets:"));
|
|
5516
|
+
for (const [name, skills] of newSecrets) {
|
|
5517
|
+
const isNew = trulyNewSecrets.includes(name);
|
|
5518
|
+
const newMarker = isNew ? chalk4.yellow(" (new)") : "";
|
|
5519
|
+
console.log(
|
|
5520
|
+
` ${name.padEnd(24)}${newMarker} <- ${skills.join(", ")}`
|
|
5521
|
+
);
|
|
5522
|
+
}
|
|
5355
5523
|
}
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5524
|
+
if (newVars.length > 0) {
|
|
5525
|
+
console.log(chalk4.cyan(" Vars:"));
|
|
5526
|
+
for (const [name, skills] of newVars) {
|
|
5527
|
+
console.log(` ${name.padEnd(24)} <- ${skills.join(", ")}`);
|
|
5528
|
+
}
|
|
5361
5529
|
}
|
|
5530
|
+
console.log();
|
|
5362
5531
|
}
|
|
5363
|
-
console.log();
|
|
5364
5532
|
if (trulyNewSecrets.length > 0 && !options.yes) {
|
|
5365
5533
|
if (!isInteractive()) {
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5534
|
+
if (options.porcelain) {
|
|
5535
|
+
console.log(
|
|
5536
|
+
JSON.stringify({
|
|
5537
|
+
error: `New secrets detected: ${trulyNewSecrets.join(", ")}. Use --yes flag to approve.`
|
|
5538
|
+
})
|
|
5539
|
+
);
|
|
5540
|
+
} else {
|
|
5541
|
+
console.error(
|
|
5542
|
+
chalk4.red(`\u2717 New secrets detected: ${trulyNewSecrets.join(", ")}`)
|
|
5543
|
+
);
|
|
5544
|
+
console.error(
|
|
5545
|
+
chalk4.dim(
|
|
5546
|
+
" Use --yes flag to approve new secrets in non-interactive mode."
|
|
5547
|
+
)
|
|
5548
|
+
);
|
|
5549
|
+
}
|
|
5374
5550
|
process.exit(1);
|
|
5375
5551
|
}
|
|
5376
5552
|
const confirmed = await promptConfirm(
|
|
@@ -5378,7 +5554,9 @@ async function displayAndConfirmVariables(variables, options) {
|
|
|
5378
5554
|
true
|
|
5379
5555
|
);
|
|
5380
5556
|
if (!confirmed) {
|
|
5381
|
-
|
|
5557
|
+
if (!options.porcelain) {
|
|
5558
|
+
console.log(chalk4.yellow("Compose cancelled"));
|
|
5559
|
+
}
|
|
5382
5560
|
return false;
|
|
5383
5561
|
}
|
|
5384
5562
|
}
|
|
@@ -5406,57 +5584,94 @@ async function finalizeCompose(config, agent, variables, options) {
|
|
|
5406
5584
|
process.exit(0);
|
|
5407
5585
|
}
|
|
5408
5586
|
mergeSkillVariables(agent, variables);
|
|
5409
|
-
|
|
5587
|
+
if (!options.porcelain) {
|
|
5588
|
+
console.log("Uploading compose...");
|
|
5589
|
+
}
|
|
5410
5590
|
const response = await createOrUpdateCompose({ content: config });
|
|
5411
5591
|
const scopeResponse = await getScope();
|
|
5412
5592
|
const shortVersionId = response.versionId.slice(0, 8);
|
|
5413
5593
|
const displayName = `${scopeResponse.slug}/${response.name}`;
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5594
|
+
const result = {
|
|
5595
|
+
composeId: response.composeId,
|
|
5596
|
+
composeName: response.name,
|
|
5597
|
+
versionId: response.versionId,
|
|
5598
|
+
action: response.action,
|
|
5599
|
+
displayName
|
|
5600
|
+
};
|
|
5601
|
+
if (!options.porcelain) {
|
|
5602
|
+
if (response.action === "created") {
|
|
5603
|
+
console.log(chalk4.green(`\u2713 Compose created: ${displayName}`));
|
|
5604
|
+
} else {
|
|
5605
|
+
console.log(chalk4.green(`\u2713 Compose version exists: ${displayName}`));
|
|
5606
|
+
}
|
|
5607
|
+
console.log(chalk4.dim(` Version: ${shortVersionId}`));
|
|
5608
|
+
console.log();
|
|
5609
|
+
console.log(" Run your agent:");
|
|
5610
|
+
console.log(
|
|
5611
|
+
chalk4.cyan(
|
|
5612
|
+
` vm0 run ${displayName}:${shortVersionId} --artifact-name <artifact> "your prompt"`
|
|
5613
|
+
)
|
|
5614
|
+
);
|
|
5418
5615
|
}
|
|
5419
|
-
console.log(chalk4.dim(` Version: ${shortVersionId}`));
|
|
5420
|
-
console.log();
|
|
5421
|
-
console.log(" Run your agent:");
|
|
5422
|
-
console.log(
|
|
5423
|
-
chalk4.cyan(
|
|
5424
|
-
` vm0 run ${displayName}:${shortVersionId} --artifact-name <artifact> "your prompt"`
|
|
5425
|
-
)
|
|
5426
|
-
);
|
|
5427
5616
|
if (options.autoUpdate !== false) {
|
|
5428
|
-
await silentUpgradeAfterCommand("9.
|
|
5617
|
+
await silentUpgradeAfterCommand("9.22.0");
|
|
5429
5618
|
}
|
|
5619
|
+
return result;
|
|
5430
5620
|
}
|
|
5431
5621
|
async function handleGitHubCompose(url, options) {
|
|
5432
|
-
|
|
5622
|
+
if (!options.porcelain) {
|
|
5623
|
+
console.log(`Downloading from GitHub: ${url}`);
|
|
5624
|
+
}
|
|
5433
5625
|
const { dir: downloadedDir, tempRoot } = await downloadGitHubDirectory(url);
|
|
5434
5626
|
const configFile = join6(downloadedDir, "vm0.yaml");
|
|
5435
5627
|
try {
|
|
5436
5628
|
if (!existsSync4(configFile)) {
|
|
5437
|
-
|
|
5438
|
-
|
|
5629
|
+
if (options.porcelain) {
|
|
5630
|
+
console.log(
|
|
5631
|
+
JSON.stringify({
|
|
5632
|
+
error: "vm0.yaml not found in the GitHub directory"
|
|
5633
|
+
})
|
|
5634
|
+
);
|
|
5635
|
+
} else {
|
|
5636
|
+
console.error(
|
|
5637
|
+
chalk4.red(`\u2717 vm0.yaml not found in the GitHub directory`)
|
|
5638
|
+
);
|
|
5639
|
+
console.error(chalk4.dim(` URL: ${url}`));
|
|
5640
|
+
}
|
|
5439
5641
|
process.exit(1);
|
|
5440
5642
|
}
|
|
5441
|
-
const { config, agentName, agent, basePath } = await loadAndValidateConfig(
|
|
5643
|
+
const { config, agentName, agent, basePath } = await loadAndValidateConfig(
|
|
5644
|
+
configFile,
|
|
5645
|
+
options.porcelain
|
|
5646
|
+
);
|
|
5442
5647
|
const existingCompose = await getComposeByName(agentName);
|
|
5443
5648
|
if (existingCompose) {
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5649
|
+
if (!options.porcelain) {
|
|
5650
|
+
console.log();
|
|
5651
|
+
console.log(
|
|
5652
|
+
chalk4.yellow(`\u26A0 An agent named "${agentName}" already exists.`)
|
|
5653
|
+
);
|
|
5654
|
+
}
|
|
5448
5655
|
if (!isInteractive()) {
|
|
5449
5656
|
if (!options.yes) {
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5657
|
+
if (options.porcelain) {
|
|
5658
|
+
console.log(
|
|
5659
|
+
JSON.stringify({
|
|
5660
|
+
error: "Cannot overwrite existing agent in non-interactive mode"
|
|
5661
|
+
})
|
|
5662
|
+
);
|
|
5663
|
+
} else {
|
|
5664
|
+
console.error(
|
|
5665
|
+
chalk4.red(
|
|
5666
|
+
`\u2717 Cannot overwrite existing agent in non-interactive mode`
|
|
5667
|
+
)
|
|
5668
|
+
);
|
|
5669
|
+
console.error(
|
|
5670
|
+
chalk4.dim(
|
|
5671
|
+
` Use --yes flag to confirm overwriting the existing agent.`
|
|
5672
|
+
)
|
|
5673
|
+
);
|
|
5674
|
+
}
|
|
5460
5675
|
process.exit(1);
|
|
5461
5676
|
}
|
|
5462
5677
|
} else {
|
|
@@ -5465,31 +5680,48 @@ async function handleGitHubCompose(url, options) {
|
|
|
5465
5680
|
false
|
|
5466
5681
|
);
|
|
5467
5682
|
if (!confirmed) {
|
|
5468
|
-
|
|
5683
|
+
if (!options.porcelain) {
|
|
5684
|
+
console.log(chalk4.yellow("Compose cancelled."));
|
|
5685
|
+
}
|
|
5469
5686
|
process.exit(0);
|
|
5470
5687
|
}
|
|
5471
5688
|
}
|
|
5472
5689
|
}
|
|
5473
5690
|
if (hasVolumes(config)) {
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5691
|
+
if (options.porcelain) {
|
|
5692
|
+
console.log(
|
|
5693
|
+
JSON.stringify({
|
|
5694
|
+
error: "Volumes are not supported for GitHub URL compose"
|
|
5695
|
+
})
|
|
5696
|
+
);
|
|
5697
|
+
} else {
|
|
5698
|
+
console.error(
|
|
5699
|
+
chalk4.red(`\u2717 Volumes are not supported for GitHub URL compose`)
|
|
5700
|
+
);
|
|
5701
|
+
console.error(
|
|
5702
|
+
chalk4.dim(
|
|
5703
|
+
` Clone the repository locally and run: vm0 compose ./path/to/vm0.yaml`
|
|
5704
|
+
)
|
|
5705
|
+
);
|
|
5706
|
+
}
|
|
5482
5707
|
process.exit(1);
|
|
5483
5708
|
}
|
|
5484
|
-
|
|
5485
|
-
|
|
5709
|
+
if (!options.porcelain) {
|
|
5710
|
+
checkLegacyImageFormat(config);
|
|
5711
|
+
}
|
|
5712
|
+
const skillResults = await uploadAssets(
|
|
5713
|
+
agentName,
|
|
5714
|
+
agent,
|
|
5715
|
+
basePath,
|
|
5716
|
+
options.porcelain
|
|
5717
|
+
);
|
|
5486
5718
|
const environment = agent.environment || {};
|
|
5487
5719
|
const variables = await collectSkillVariables(
|
|
5488
5720
|
skillResults,
|
|
5489
5721
|
environment,
|
|
5490
5722
|
agentName
|
|
5491
5723
|
);
|
|
5492
|
-
await finalizeCompose(config, agent, variables, options);
|
|
5724
|
+
return await finalizeCompose(config, agent, variables, options);
|
|
5493
5725
|
} finally {
|
|
5494
5726
|
await rm3(tempRoot, { recursive: true, force: true });
|
|
5495
5727
|
}
|
|
@@ -5500,42 +5732,73 @@ var composeCommand = new Command7().name("compose").description("Create or updat
|
|
|
5500
5732
|
).option("-y, --yes", "Skip confirmation prompts for skill requirements").option(
|
|
5501
5733
|
"--experimental-shared-compose",
|
|
5502
5734
|
"Enable GitHub URL compose (experimental)"
|
|
5735
|
+
).option(
|
|
5736
|
+
"--porcelain",
|
|
5737
|
+
"Output stable JSON for scripts (suppresses interactive output)"
|
|
5503
5738
|
).addOption(new Option("--no-auto-update").hideHelp()).action(
|
|
5504
5739
|
async (configFile, options) => {
|
|
5505
5740
|
const resolvedConfigFile = configFile ?? DEFAULT_CONFIG_FILE;
|
|
5741
|
+
if (options.porcelain) {
|
|
5742
|
+
options.yes = true;
|
|
5743
|
+
options.autoUpdate = false;
|
|
5744
|
+
}
|
|
5506
5745
|
try {
|
|
5507
|
-
|
|
5746
|
+
let result;
|
|
5747
|
+
if (isGitHubUrl(resolvedConfigFile)) {
|
|
5508
5748
|
if (!options.experimentalSharedCompose) {
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5749
|
+
if (options.porcelain) {
|
|
5750
|
+
console.log(
|
|
5751
|
+
JSON.stringify({
|
|
5752
|
+
error: "Composing shared agents requires --experimental-shared-compose flag"
|
|
5753
|
+
})
|
|
5754
|
+
);
|
|
5755
|
+
} else {
|
|
5756
|
+
console.error(
|
|
5757
|
+
chalk4.red(
|
|
5758
|
+
"\u2717 Composing shared agents requires --experimental-shared-compose flag"
|
|
5759
|
+
)
|
|
5760
|
+
);
|
|
5761
|
+
console.error();
|
|
5762
|
+
console.error(
|
|
5763
|
+
chalk4.dim(
|
|
5764
|
+
" Composing agents from other users carries security risks."
|
|
5765
|
+
)
|
|
5766
|
+
);
|
|
5767
|
+
console.error(
|
|
5768
|
+
chalk4.dim(" Only compose agents from users you trust.")
|
|
5769
|
+
);
|
|
5770
|
+
}
|
|
5523
5771
|
process.exit(1);
|
|
5524
5772
|
}
|
|
5525
|
-
await handleGitHubCompose(resolvedConfigFile, options);
|
|
5773
|
+
result = await handleGitHubCompose(resolvedConfigFile, options);
|
|
5526
5774
|
} else {
|
|
5527
|
-
const { config, agentName, agent, basePath } = await loadAndValidateConfig(resolvedConfigFile);
|
|
5528
|
-
|
|
5529
|
-
|
|
5775
|
+
const { config, agentName, agent, basePath } = await loadAndValidateConfig(resolvedConfigFile, options.porcelain);
|
|
5776
|
+
if (!options.porcelain) {
|
|
5777
|
+
checkLegacyImageFormat(config);
|
|
5778
|
+
}
|
|
5779
|
+
const skillResults = await uploadAssets(
|
|
5780
|
+
agentName,
|
|
5781
|
+
agent,
|
|
5782
|
+
basePath,
|
|
5783
|
+
options.porcelain
|
|
5784
|
+
);
|
|
5530
5785
|
const environment = agent.environment || {};
|
|
5531
5786
|
const variables = await collectSkillVariables(
|
|
5532
5787
|
skillResults,
|
|
5533
5788
|
environment,
|
|
5534
5789
|
agentName
|
|
5535
5790
|
);
|
|
5536
|
-
await finalizeCompose(config, agent, variables, options);
|
|
5791
|
+
result = await finalizeCompose(config, agent, variables, options);
|
|
5792
|
+
}
|
|
5793
|
+
if (options.porcelain) {
|
|
5794
|
+
console.log(JSON.stringify(result));
|
|
5537
5795
|
}
|
|
5538
5796
|
} catch (error) {
|
|
5797
|
+
if (options.porcelain) {
|
|
5798
|
+
const message = error instanceof Error ? error.message : "An unexpected error occurred";
|
|
5799
|
+
console.log(JSON.stringify({ error: message }));
|
|
5800
|
+
process.exit(1);
|
|
5801
|
+
}
|
|
5539
5802
|
if (error instanceof Error) {
|
|
5540
5803
|
if (error.message.includes("Not authenticated")) {
|
|
5541
5804
|
console.error(
|
|
@@ -7793,7 +8056,7 @@ var mainRunCommand = new Command8().name("run").description("Run an agent").argu
|
|
|
7793
8056
|
}
|
|
7794
8057
|
showNextSteps(result);
|
|
7795
8058
|
if (options.autoUpdate !== false) {
|
|
7796
|
-
await silentUpgradeAfterCommand("9.
|
|
8059
|
+
await silentUpgradeAfterCommand("9.22.0");
|
|
7797
8060
|
}
|
|
7798
8061
|
} catch (error) {
|
|
7799
8062
|
handleRunError(error, identifier);
|
|
@@ -8238,7 +8501,7 @@ import { Command as Command15 } from "commander";
|
|
|
8238
8501
|
import chalk17 from "chalk";
|
|
8239
8502
|
import path7 from "path";
|
|
8240
8503
|
import * as fs6 from "fs";
|
|
8241
|
-
import * as
|
|
8504
|
+
import * as os5 from "os";
|
|
8242
8505
|
import * as tar3 from "tar";
|
|
8243
8506
|
|
|
8244
8507
|
// src/lib/storage/pull-utils.ts
|
|
@@ -8290,7 +8553,7 @@ var pullCommand = new Command15().name("pull").description("Pull cloud files to
|
|
|
8290
8553
|
const arrayBuffer = await s3Response.arrayBuffer();
|
|
8291
8554
|
const tarBuffer = Buffer.from(arrayBuffer);
|
|
8292
8555
|
console.log(chalk17.green(`\u2713 Downloaded ${formatBytes(tarBuffer.length)}`));
|
|
8293
|
-
const tmpDir = fs6.mkdtempSync(path7.join(
|
|
8556
|
+
const tmpDir = fs6.mkdtempSync(path7.join(os5.tmpdir(), "vm0-"));
|
|
8294
8557
|
const tarPath = path7.join(tmpDir, "volume.tar.gz");
|
|
8295
8558
|
await fs6.promises.writeFile(tarPath, tarBuffer);
|
|
8296
8559
|
console.log(chalk17.dim("Syncing local files..."));
|
|
@@ -8439,7 +8702,7 @@ import chalk21 from "chalk";
|
|
|
8439
8702
|
import chalk20 from "chalk";
|
|
8440
8703
|
import path8 from "path";
|
|
8441
8704
|
import * as fs7 from "fs";
|
|
8442
|
-
import * as
|
|
8705
|
+
import * as os6 from "os";
|
|
8443
8706
|
import * as tar4 from "tar";
|
|
8444
8707
|
async function cloneStorage(name, type, destination, options = {}) {
|
|
8445
8708
|
const typeLabel = type === "artifact" ? "artifact" : "volume";
|
|
@@ -8479,7 +8742,7 @@ async function cloneStorage(name, type, destination, options = {}) {
|
|
|
8479
8742
|
const arrayBuffer = await s3Response.arrayBuffer();
|
|
8480
8743
|
const tarBuffer = Buffer.from(arrayBuffer);
|
|
8481
8744
|
console.log(chalk20.green(`\u2713 Downloaded ${formatBytes(tarBuffer.length)}`));
|
|
8482
|
-
const tmpDir = fs7.mkdtempSync(path8.join(
|
|
8745
|
+
const tmpDir = fs7.mkdtempSync(path8.join(os6.tmpdir(), "vm0-clone-"));
|
|
8483
8746
|
const tarPath = path8.join(tmpDir, "archive.tar.gz");
|
|
8484
8747
|
await fs7.promises.writeFile(tarPath, tarBuffer);
|
|
8485
8748
|
const files = await listTarFiles(tarPath);
|
|
@@ -8680,7 +8943,7 @@ import { Command as Command22 } from "commander";
|
|
|
8680
8943
|
import chalk24 from "chalk";
|
|
8681
8944
|
import path10 from "path";
|
|
8682
8945
|
import * as fs8 from "fs";
|
|
8683
|
-
import * as
|
|
8946
|
+
import * as os7 from "os";
|
|
8684
8947
|
import * as tar5 from "tar";
|
|
8685
8948
|
var pullCommand2 = new Command22().name("pull").description("Pull cloud artifact to local directory").argument("[versionId]", "Version ID to pull (default: latest)").action(async (versionId) => {
|
|
8686
8949
|
try {
|
|
@@ -8727,7 +8990,7 @@ var pullCommand2 = new Command22().name("pull").description("Pull cloud artifact
|
|
|
8727
8990
|
const arrayBuffer = await s3Response.arrayBuffer();
|
|
8728
8991
|
const tarBuffer = Buffer.from(arrayBuffer);
|
|
8729
8992
|
console.log(chalk24.green(`\u2713 Downloaded ${formatBytes(tarBuffer.length)}`));
|
|
8730
|
-
const tmpDir = fs8.mkdtempSync(path10.join(
|
|
8993
|
+
const tmpDir = fs8.mkdtempSync(path10.join(os7.tmpdir(), "vm0-"));
|
|
8731
8994
|
const tarPath = path10.join(tmpDir, "artifact.tar.gz");
|
|
8732
8995
|
await fs8.promises.writeFile(tarPath, tarBuffer);
|
|
8733
8996
|
console.log(chalk24.dim("Syncing local files..."));
|
|
@@ -9300,7 +9563,7 @@ var cookAction = new Command27().name("cook").description("Quick start: prepare,
|
|
|
9300
9563
|
).option("-y, --yes", "Skip confirmation prompts").option("-v, --verbose", "Show full tool inputs and outputs").addOption(new Option5("--debug-no-mock-claude").hideHelp()).addOption(new Option5("--no-auto-update").hideHelp()).action(
|
|
9301
9564
|
async (prompt, options) => {
|
|
9302
9565
|
if (options.autoUpdate !== false) {
|
|
9303
|
-
const shouldExit = await checkAndUpgrade("9.
|
|
9566
|
+
const shouldExit = await checkAndUpgrade("9.22.0", prompt);
|
|
9304
9567
|
if (shouldExit) {
|
|
9305
9568
|
process.exit(0);
|
|
9306
9569
|
}
|
|
@@ -10049,7 +10312,7 @@ import chalk38 from "chalk";
|
|
|
10049
10312
|
// src/lib/domain/source-derivation.ts
|
|
10050
10313
|
import * as fs9 from "fs/promises";
|
|
10051
10314
|
import * as path14 from "path";
|
|
10052
|
-
import * as
|
|
10315
|
+
import * as os8 from "os";
|
|
10053
10316
|
async function fetchSkillFrontmatter(skillUrl, tempDir) {
|
|
10054
10317
|
try {
|
|
10055
10318
|
const parsed = parseGitHubTreeUrl2(skillUrl);
|
|
@@ -10089,7 +10352,7 @@ async function deriveAgentVariableSources(agent, options) {
|
|
|
10089
10352
|
};
|
|
10090
10353
|
}
|
|
10091
10354
|
const tempDir = await fs9.mkdtemp(
|
|
10092
|
-
path14.join(
|
|
10355
|
+
path14.join(os8.tmpdir(), "vm0-source-derivation-")
|
|
10093
10356
|
);
|
|
10094
10357
|
try {
|
|
10095
10358
|
const skillResults = await Promise.all(
|
|
@@ -11840,7 +12103,7 @@ var listCommand6 = new Command53().name("list").alias("ls").description("List al
|
|
|
11840
12103
|
console.log(chalk53.dim("No secrets found"));
|
|
11841
12104
|
console.log();
|
|
11842
12105
|
console.log("To add a secret:");
|
|
11843
|
-
console.log(chalk53.cyan(" vm0 secret set MY_API_KEY <value>"));
|
|
12106
|
+
console.log(chalk53.cyan(" vm0 secret set MY_API_KEY --body <value>"));
|
|
11844
12107
|
return;
|
|
11845
12108
|
}
|
|
11846
12109
|
console.log(chalk53.bold("Secrets:"));
|
|
@@ -11874,9 +12137,32 @@ var listCommand6 = new Command53().name("list").alias("ls").description("List al
|
|
|
11874
12137
|
// src/commands/secret/set.ts
|
|
11875
12138
|
import { Command as Command54 } from "commander";
|
|
11876
12139
|
import chalk54 from "chalk";
|
|
11877
|
-
var setCommand2 = new Command54().name("set").description("Create or update a secret").argument("<name>", "Secret name (uppercase, e.g., MY_API_KEY)").
|
|
11878
|
-
|
|
12140
|
+
var setCommand2 = new Command54().name("set").description("Create or update a secret").argument("<name>", "Secret name (uppercase, e.g., MY_API_KEY)").option(
|
|
12141
|
+
"-b, --body <value>",
|
|
12142
|
+
"Secret value (required in non-interactive mode)"
|
|
12143
|
+
).option("-d, --description <description>", "Optional description").action(
|
|
12144
|
+
async (name, options) => {
|
|
11879
12145
|
try {
|
|
12146
|
+
let value;
|
|
12147
|
+
if (options.body !== void 0) {
|
|
12148
|
+
value = options.body;
|
|
12149
|
+
} else if (isInteractive()) {
|
|
12150
|
+
const prompted = await promptPassword("Enter secret value:");
|
|
12151
|
+
if (prompted === void 0) {
|
|
12152
|
+
process.exit(0);
|
|
12153
|
+
}
|
|
12154
|
+
value = prompted;
|
|
12155
|
+
} else {
|
|
12156
|
+
console.error(
|
|
12157
|
+
chalk54.red("\u2717 --body is required in non-interactive mode")
|
|
12158
|
+
);
|
|
12159
|
+
console.log();
|
|
12160
|
+
console.log("Usage:");
|
|
12161
|
+
console.log(
|
|
12162
|
+
chalk54.cyan(` vm0 secret set ${name} --body "your-secret-value"`)
|
|
12163
|
+
);
|
|
12164
|
+
process.exit(1);
|
|
12165
|
+
}
|
|
11880
12166
|
const secret = await setSecret({
|
|
11881
12167
|
name,
|
|
11882
12168
|
value,
|
|
@@ -12203,63 +12489,61 @@ function validateAuthMethod(type, authMethodStr) {
|
|
|
12203
12489
|
}
|
|
12204
12490
|
return authMethodStr;
|
|
12205
12491
|
}
|
|
12206
|
-
function
|
|
12207
|
-
const
|
|
12208
|
-
if (!
|
|
12492
|
+
function parseSecrets(type, authMethod, secretArgs) {
|
|
12493
|
+
const secretsConfig = getSecretsForAuthMethod(type, authMethod);
|
|
12494
|
+
if (!secretsConfig) {
|
|
12209
12495
|
console.error(chalk60.red(`\u2717 Invalid auth method "${authMethod}"`));
|
|
12210
12496
|
process.exit(1);
|
|
12211
12497
|
}
|
|
12212
|
-
const
|
|
12213
|
-
const firstArg =
|
|
12214
|
-
if (
|
|
12215
|
-
if (
|
|
12498
|
+
const secretNames = Object.keys(secretsConfig);
|
|
12499
|
+
const firstArg = secretArgs[0];
|
|
12500
|
+
if (secretArgs.length === 1 && firstArg && !firstArg.includes("=")) {
|
|
12501
|
+
if (secretNames.length !== 1) {
|
|
12216
12502
|
console.error(
|
|
12217
|
-
chalk60.red(
|
|
12218
|
-
"\u2717 Must use KEY=VALUE format for multi-credential auth methods"
|
|
12219
|
-
)
|
|
12503
|
+
chalk60.red("\u2717 Must use KEY=VALUE format for multi-secret auth methods")
|
|
12220
12504
|
);
|
|
12221
12505
|
console.log();
|
|
12222
|
-
console.log("Required
|
|
12223
|
-
for (const [name, fieldConfig] of Object.entries(
|
|
12506
|
+
console.log("Required secrets:");
|
|
12507
|
+
for (const [name, fieldConfig] of Object.entries(secretsConfig)) {
|
|
12224
12508
|
const requiredNote = fieldConfig.required ? " (required)" : "";
|
|
12225
12509
|
console.log(` ${chalk60.cyan(name)}${requiredNote}`);
|
|
12226
12510
|
}
|
|
12227
12511
|
process.exit(1);
|
|
12228
12512
|
}
|
|
12229
|
-
const
|
|
12230
|
-
if (!
|
|
12231
|
-
console.error(chalk60.red("\u2717 No
|
|
12513
|
+
const firstSecretName = secretNames[0];
|
|
12514
|
+
if (!firstSecretName) {
|
|
12515
|
+
console.error(chalk60.red("\u2717 No secrets defined for this auth method"));
|
|
12232
12516
|
process.exit(1);
|
|
12233
12517
|
}
|
|
12234
|
-
return { [
|
|
12518
|
+
return { [firstSecretName]: firstArg };
|
|
12235
12519
|
}
|
|
12236
|
-
const
|
|
12237
|
-
for (const arg of
|
|
12520
|
+
const secrets = {};
|
|
12521
|
+
for (const arg of secretArgs) {
|
|
12238
12522
|
const eqIndex = arg.indexOf("=");
|
|
12239
12523
|
if (eqIndex === -1) {
|
|
12240
|
-
console.error(chalk60.red(`\u2717 Invalid
|
|
12524
|
+
console.error(chalk60.red(`\u2717 Invalid secret format "${arg}"`));
|
|
12241
12525
|
console.log();
|
|
12242
12526
|
console.log("Use KEY=VALUE format (e.g., AWS_REGION=us-east-1)");
|
|
12243
12527
|
process.exit(1);
|
|
12244
12528
|
}
|
|
12245
12529
|
const key = arg.slice(0, eqIndex);
|
|
12246
12530
|
const value = arg.slice(eqIndex + 1);
|
|
12247
|
-
|
|
12531
|
+
secrets[key] = value;
|
|
12248
12532
|
}
|
|
12249
|
-
return
|
|
12533
|
+
return secrets;
|
|
12250
12534
|
}
|
|
12251
|
-
function
|
|
12252
|
-
const
|
|
12253
|
-
if (!
|
|
12535
|
+
function validateSecrets(type, authMethod, secrets) {
|
|
12536
|
+
const secretsConfig = getSecretsForAuthMethod(type, authMethod);
|
|
12537
|
+
if (!secretsConfig) {
|
|
12254
12538
|
console.error(chalk60.red(`\u2717 Invalid auth method "${authMethod}"`));
|
|
12255
12539
|
process.exit(1);
|
|
12256
12540
|
}
|
|
12257
|
-
for (const [name, fieldConfig] of Object.entries(
|
|
12258
|
-
if (fieldConfig.required && !
|
|
12259
|
-
console.error(chalk60.red(`\u2717 Missing required
|
|
12541
|
+
for (const [name, fieldConfig] of Object.entries(secretsConfig)) {
|
|
12542
|
+
if (fieldConfig.required && !secrets[name]) {
|
|
12543
|
+
console.error(chalk60.red(`\u2717 Missing required secret: ${name}`));
|
|
12260
12544
|
console.log();
|
|
12261
|
-
console.log("Required
|
|
12262
|
-
for (const [n, fc] of Object.entries(
|
|
12545
|
+
console.log("Required secrets:");
|
|
12546
|
+
for (const [n, fc] of Object.entries(secretsConfig)) {
|
|
12263
12547
|
if (fc.required) {
|
|
12264
12548
|
console.log(` ${chalk60.cyan(n)} - ${fc.label}`);
|
|
12265
12549
|
}
|
|
@@ -12267,12 +12551,12 @@ function validateCredentials(type, authMethod, credentials) {
|
|
|
12267
12551
|
process.exit(1);
|
|
12268
12552
|
}
|
|
12269
12553
|
}
|
|
12270
|
-
for (const name of Object.keys(
|
|
12271
|
-
if (!(name in
|
|
12272
|
-
console.error(chalk60.red(`\u2717 Unknown
|
|
12554
|
+
for (const name of Object.keys(secrets)) {
|
|
12555
|
+
if (!(name in secretsConfig)) {
|
|
12556
|
+
console.error(chalk60.red(`\u2717 Unknown secret: ${name}`));
|
|
12273
12557
|
console.log();
|
|
12274
|
-
console.log("Valid
|
|
12275
|
-
for (const [n, fc] of Object.entries(
|
|
12558
|
+
console.log("Valid secrets:");
|
|
12559
|
+
for (const [n, fc] of Object.entries(secretsConfig)) {
|
|
12276
12560
|
const requiredNote = fc.required ? " (required)" : " (optional)";
|
|
12277
12561
|
console.log(` ${chalk60.cyan(n)}${requiredNote}`);
|
|
12278
12562
|
}
|
|
@@ -12321,37 +12605,37 @@ function handleNonInteractiveMode(options) {
|
|
|
12321
12605
|
console.log("Example:");
|
|
12322
12606
|
console.log(
|
|
12323
12607
|
chalk60.cyan(
|
|
12324
|
-
` vm0 model-provider setup --type ${type} --auth-method ${authMethodNames[0]} --
|
|
12608
|
+
` vm0 model-provider setup --type ${type} --auth-method ${authMethodNames[0]} --secret KEY=VALUE`
|
|
12325
12609
|
)
|
|
12326
12610
|
);
|
|
12327
12611
|
process.exit(1);
|
|
12328
12612
|
}
|
|
12329
12613
|
}
|
|
12330
|
-
const
|
|
12331
|
-
|
|
12614
|
+
const secrets = parseSecrets(type, authMethod, options.secret);
|
|
12615
|
+
validateSecrets(type, authMethod, secrets);
|
|
12332
12616
|
return {
|
|
12333
12617
|
type,
|
|
12334
12618
|
authMethod,
|
|
12335
|
-
|
|
12619
|
+
secrets,
|
|
12336
12620
|
selectedModel,
|
|
12337
12621
|
isInteractiveMode: false
|
|
12338
12622
|
};
|
|
12339
12623
|
}
|
|
12340
|
-
const
|
|
12341
|
-
const firstArg =
|
|
12624
|
+
const secretArgs = options.secret;
|
|
12625
|
+
const firstArg = secretArgs[0];
|
|
12342
12626
|
if (!firstArg) {
|
|
12343
|
-
console.error(chalk60.red("\u2717
|
|
12627
|
+
console.error(chalk60.red("\u2717 Secret is required"));
|
|
12344
12628
|
process.exit(1);
|
|
12345
12629
|
}
|
|
12346
|
-
let
|
|
12630
|
+
let secret;
|
|
12347
12631
|
if (firstArg.includes("=")) {
|
|
12348
|
-
|
|
12632
|
+
secret = firstArg.slice(firstArg.indexOf("=") + 1);
|
|
12349
12633
|
} else {
|
|
12350
|
-
|
|
12634
|
+
secret = firstArg;
|
|
12351
12635
|
}
|
|
12352
12636
|
return {
|
|
12353
12637
|
type,
|
|
12354
|
-
|
|
12638
|
+
secret,
|
|
12355
12639
|
selectedModel,
|
|
12356
12640
|
isInteractiveMode: false
|
|
12357
12641
|
};
|
|
@@ -12425,29 +12709,29 @@ async function promptForAuthMethod(type) {
|
|
|
12425
12709
|
);
|
|
12426
12710
|
return response.authMethod;
|
|
12427
12711
|
}
|
|
12428
|
-
function
|
|
12712
|
+
function isSensitiveSecret(name) {
|
|
12429
12713
|
const nonSecretPatterns = ["REGION", "ENDPOINT", "URL"];
|
|
12430
12714
|
return !nonSecretPatterns.some(
|
|
12431
12715
|
(pattern) => name.toUpperCase().includes(pattern)
|
|
12432
12716
|
);
|
|
12433
12717
|
}
|
|
12434
|
-
async function
|
|
12435
|
-
const
|
|
12436
|
-
if (!
|
|
12718
|
+
async function promptForSecrets(type, authMethod) {
|
|
12719
|
+
const secretsConfig = getSecretsForAuthMethod(type, authMethod);
|
|
12720
|
+
if (!secretsConfig) {
|
|
12437
12721
|
console.error(chalk60.red(`\u2717 Invalid auth method "${authMethod}"`));
|
|
12438
12722
|
process.exit(1);
|
|
12439
12723
|
}
|
|
12440
|
-
const
|
|
12441
|
-
for (const [name, fieldConfig] of Object.entries(
|
|
12724
|
+
const secrets = {};
|
|
12725
|
+
for (const [name, fieldConfig] of Object.entries(secretsConfig)) {
|
|
12442
12726
|
if (fieldConfig.helpText) {
|
|
12443
12727
|
console.log(chalk60.dim(fieldConfig.helpText));
|
|
12444
12728
|
}
|
|
12445
|
-
const
|
|
12729
|
+
const isSensitive = isSensitiveSecret(name);
|
|
12446
12730
|
const placeholder = "placeholder" in fieldConfig ? fieldConfig.placeholder : "";
|
|
12447
12731
|
if (fieldConfig.required) {
|
|
12448
12732
|
const response = await prompts2(
|
|
12449
12733
|
{
|
|
12450
|
-
type:
|
|
12734
|
+
type: isSensitive ? "password" : "text",
|
|
12451
12735
|
name: "value",
|
|
12452
12736
|
message: `${fieldConfig.label}:`,
|
|
12453
12737
|
initial: placeholder ? "" : void 0,
|
|
@@ -12455,11 +12739,11 @@ async function promptForCredentials(type, authMethod) {
|
|
|
12455
12739
|
},
|
|
12456
12740
|
{ onCancel: () => process.exit(0) }
|
|
12457
12741
|
);
|
|
12458
|
-
|
|
12742
|
+
secrets[name] = response.value;
|
|
12459
12743
|
} else {
|
|
12460
12744
|
const response = await prompts2(
|
|
12461
12745
|
{
|
|
12462
|
-
type:
|
|
12746
|
+
type: isSensitive ? "password" : "text",
|
|
12463
12747
|
name: "value",
|
|
12464
12748
|
message: `${fieldConfig.label} (optional):`
|
|
12465
12749
|
},
|
|
@@ -12467,11 +12751,11 @@ async function promptForCredentials(type, authMethod) {
|
|
|
12467
12751
|
);
|
|
12468
12752
|
const value = response.value;
|
|
12469
12753
|
if (value && value.trim()) {
|
|
12470
|
-
|
|
12754
|
+
secrets[name] = value.trim();
|
|
12471
12755
|
}
|
|
12472
12756
|
}
|
|
12473
12757
|
}
|
|
12474
|
-
return
|
|
12758
|
+
return secrets;
|
|
12475
12759
|
}
|
|
12476
12760
|
async function handleInteractiveMode() {
|
|
12477
12761
|
if (!isInteractive()) {
|
|
@@ -12479,9 +12763,7 @@ async function handleInteractiveMode() {
|
|
|
12479
12763
|
console.log();
|
|
12480
12764
|
console.log("Use non-interactive mode:");
|
|
12481
12765
|
console.log(
|
|
12482
|
-
chalk60.cyan(
|
|
12483
|
-
' vm0 model-provider setup --type <type> --credential "<value>"'
|
|
12484
|
-
)
|
|
12766
|
+
chalk60.cyan(' vm0 model-provider setup --type <type> --secret "<value>"')
|
|
12485
12767
|
);
|
|
12486
12768
|
process.exit(1);
|
|
12487
12769
|
}
|
|
@@ -12514,32 +12796,8 @@ async function handleInteractiveMode() {
|
|
|
12514
12796
|
{ onCancel: () => process.exit(0) }
|
|
12515
12797
|
);
|
|
12516
12798
|
const type = typeResponse.type;
|
|
12517
|
-
const checkResult = await
|
|
12518
|
-
if (checkResult.exists
|
|
12519
|
-
const convertResponse = await prompts2(
|
|
12520
|
-
{
|
|
12521
|
-
type: "confirm",
|
|
12522
|
-
name: "convert",
|
|
12523
|
-
message: `Credential "${checkResult.credentialName}" already exists. Convert to model provider?`,
|
|
12524
|
-
initial: true
|
|
12525
|
-
},
|
|
12526
|
-
{ onCancel: () => process.exit(0) }
|
|
12527
|
-
);
|
|
12528
|
-
if (convertResponse.convert) {
|
|
12529
|
-
const provider = await convertModelProviderCredential(type);
|
|
12530
|
-
const defaultNote = provider.isDefault ? ` (default for ${provider.framework})` : "";
|
|
12531
|
-
console.log(
|
|
12532
|
-
chalk60.green(
|
|
12533
|
-
`\u2713 Converted "${checkResult.credentialName}" to model provider${defaultNote}`
|
|
12534
|
-
)
|
|
12535
|
-
);
|
|
12536
|
-
await promptSetAsDefault(type, provider.framework, provider.isDefault);
|
|
12537
|
-
return null;
|
|
12538
|
-
}
|
|
12539
|
-
console.log(chalk60.dim("Aborted"));
|
|
12540
|
-
process.exit(0);
|
|
12541
|
-
}
|
|
12542
|
-
if (checkResult.exists && checkResult.currentType === "model-provider") {
|
|
12799
|
+
const checkResult = await checkModelProviderSecret(type);
|
|
12800
|
+
if (checkResult.exists) {
|
|
12543
12801
|
console.log();
|
|
12544
12802
|
console.log(`"${type}" is already configured.`);
|
|
12545
12803
|
console.log();
|
|
@@ -12549,8 +12807,8 @@ async function handleInteractiveMode() {
|
|
|
12549
12807
|
name: "action",
|
|
12550
12808
|
message: "",
|
|
12551
12809
|
choices: [
|
|
12552
|
-
{ title: "Keep existing
|
|
12553
|
-
{ title: "Update
|
|
12810
|
+
{ title: "Keep existing secret", value: "keep" },
|
|
12811
|
+
{ title: "Update secret", value: "update" }
|
|
12554
12812
|
]
|
|
12555
12813
|
},
|
|
12556
12814
|
{ onCancel: () => process.exit(0) }
|
|
@@ -12559,7 +12817,7 @@ async function handleInteractiveMode() {
|
|
|
12559
12817
|
const selectedModel2 = await promptForModelSelection(type);
|
|
12560
12818
|
return {
|
|
12561
12819
|
type,
|
|
12562
|
-
|
|
12820
|
+
keepExistingSecret: true,
|
|
12563
12821
|
selectedModel: selectedModel2,
|
|
12564
12822
|
isInteractiveMode: true
|
|
12565
12823
|
};
|
|
@@ -12571,38 +12829,33 @@ async function handleInteractiveMode() {
|
|
|
12571
12829
|
console.log();
|
|
12572
12830
|
if (hasAuthMethods(type)) {
|
|
12573
12831
|
const authMethod = await promptForAuthMethod(type);
|
|
12574
|
-
const
|
|
12832
|
+
const secrets = await promptForSecrets(type, authMethod);
|
|
12575
12833
|
const selectedModel2 = await promptForModelSelection(type);
|
|
12576
12834
|
return {
|
|
12577
12835
|
type,
|
|
12578
12836
|
authMethod,
|
|
12579
|
-
|
|
12837
|
+
secrets,
|
|
12580
12838
|
selectedModel: selectedModel2,
|
|
12581
12839
|
isInteractiveMode: true
|
|
12582
12840
|
};
|
|
12583
12841
|
}
|
|
12584
|
-
const
|
|
12585
|
-
const
|
|
12842
|
+
const secretLabel = "secretLabel" in config ? config.secretLabel : "secret";
|
|
12843
|
+
const secretResponse = await prompts2(
|
|
12586
12844
|
{
|
|
12587
12845
|
type: "password",
|
|
12588
|
-
name: "
|
|
12589
|
-
message: `Enter your ${
|
|
12590
|
-
validate: (value) => value.length > 0 || `${
|
|
12846
|
+
name: "secret",
|
|
12847
|
+
message: `Enter your ${secretLabel}:`,
|
|
12848
|
+
validate: (value) => value.length > 0 || `${secretLabel} is required`
|
|
12591
12849
|
},
|
|
12592
12850
|
{ onCancel: () => process.exit(0) }
|
|
12593
12851
|
);
|
|
12594
|
-
const
|
|
12852
|
+
const secret = secretResponse.secret;
|
|
12595
12853
|
const selectedModel = await promptForModelSelection(type);
|
|
12596
|
-
return { type,
|
|
12854
|
+
return { type, secret, selectedModel, isInteractiveMode: true };
|
|
12597
12855
|
}
|
|
12598
12856
|
function handleSetupError2(error) {
|
|
12599
12857
|
if (error instanceof Error) {
|
|
12600
|
-
if (error.message.includes("
|
|
12601
|
-
console.error(chalk60.red(`\u2717 ${error.message}`));
|
|
12602
|
-
console.log();
|
|
12603
|
-
console.log("To convert the existing credential, run:");
|
|
12604
|
-
console.log(chalk60.cyan(" vm0 model-provider setup --convert"));
|
|
12605
|
-
} else if (error.message.includes("Not authenticated")) {
|
|
12858
|
+
if (error.message.includes("Not authenticated")) {
|
|
12606
12859
|
console.error(chalk60.red("\u2717 Not authenticated. Run: vm0 auth login"));
|
|
12607
12860
|
} else {
|
|
12608
12861
|
console.error(chalk60.red(`\u2717 ${error.message}`));
|
|
@@ -12628,34 +12881,31 @@ async function promptSetAsDefault(type, framework, isDefault) {
|
|
|
12628
12881
|
console.log(chalk60.green(`\u2713 Default for ${framework} set to "${type}"`));
|
|
12629
12882
|
}
|
|
12630
12883
|
}
|
|
12631
|
-
function
|
|
12884
|
+
function collectSecrets(value, previous) {
|
|
12632
12885
|
return previous.concat([value]);
|
|
12633
12886
|
}
|
|
12634
12887
|
var setupCommand2 = new Command62().name("setup").description("Configure a model provider").option("-t, --type <type>", "Provider type (for non-interactive mode)").option(
|
|
12635
|
-
"-
|
|
12636
|
-
"
|
|
12637
|
-
|
|
12888
|
+
"-s, --secret <value>",
|
|
12889
|
+
"Secret value (can be used multiple times, supports VALUE or KEY=VALUE format)",
|
|
12890
|
+
collectSecrets,
|
|
12638
12891
|
[]
|
|
12639
12892
|
).option(
|
|
12640
12893
|
"-a, --auth-method <method>",
|
|
12641
12894
|
"Auth method (required for multi-auth providers like aws-bedrock)"
|
|
12642
|
-
).option("-m, --model <model>", "Model selection (for non-interactive mode)").
|
|
12895
|
+
).option("-m, --model <model>", "Model selection (for non-interactive mode)").action(
|
|
12643
12896
|
async (options) => {
|
|
12644
12897
|
try {
|
|
12645
12898
|
let input;
|
|
12646
|
-
const
|
|
12647
|
-
|
|
12648
|
-
if (options.type && credentialArgs.length > 0) {
|
|
12899
|
+
const secretArgs = options.secret ?? [];
|
|
12900
|
+
if (options.type && secretArgs.length > 0) {
|
|
12649
12901
|
input = handleNonInteractiveMode({
|
|
12650
12902
|
type: options.type,
|
|
12651
|
-
|
|
12903
|
+
secret: secretArgs,
|
|
12652
12904
|
authMethod: options.authMethod,
|
|
12653
12905
|
model: options.model
|
|
12654
12906
|
});
|
|
12655
|
-
} else if (options.type ||
|
|
12656
|
-
console.error(
|
|
12657
|
-
chalk60.red("\u2717 Both --type and --credential are required")
|
|
12658
|
-
);
|
|
12907
|
+
} else if (options.type || secretArgs.length > 0) {
|
|
12908
|
+
console.error(chalk60.red("\u2717 Both --type and --secret are required"));
|
|
12659
12909
|
process.exit(1);
|
|
12660
12910
|
} else {
|
|
12661
12911
|
const result = await handleInteractiveMode();
|
|
@@ -12664,7 +12914,7 @@ var setupCommand2 = new Command62().name("setup").description("Configure a model
|
|
|
12664
12914
|
}
|
|
12665
12915
|
input = result;
|
|
12666
12916
|
}
|
|
12667
|
-
if (input.
|
|
12917
|
+
if (input.keepExistingSecret) {
|
|
12668
12918
|
const provider2 = await updateModelProviderModel(
|
|
12669
12919
|
input.type,
|
|
12670
12920
|
input.selectedModel
|
|
@@ -12693,10 +12943,9 @@ var setupCommand2 = new Command62().name("setup").description("Configure a model
|
|
|
12693
12943
|
}
|
|
12694
12944
|
const { provider, created } = await upsertModelProvider({
|
|
12695
12945
|
type: input.type,
|
|
12696
|
-
|
|
12946
|
+
secret: input.secret,
|
|
12697
12947
|
authMethod: input.authMethod,
|
|
12698
|
-
|
|
12699
|
-
convert: shouldConvert,
|
|
12948
|
+
secrets: input.secrets,
|
|
12700
12949
|
selectedModel: input.selectedModel
|
|
12701
12950
|
});
|
|
12702
12951
|
const action = created ? "created" : "updated";
|
|
@@ -13030,17 +13279,16 @@ function getProviderChoices() {
|
|
|
13030
13279
|
type,
|
|
13031
13280
|
label: config.label,
|
|
13032
13281
|
helpText: config.helpText,
|
|
13033
|
-
|
|
13282
|
+
secretLabel: "secretLabel" in config ? config.secretLabel : "",
|
|
13034
13283
|
models: getModels(type),
|
|
13035
13284
|
defaultModel: getDefaultModel(type)
|
|
13036
13285
|
};
|
|
13037
13286
|
});
|
|
13038
13287
|
}
|
|
13039
|
-
async function setupModelProvider(type,
|
|
13288
|
+
async function setupModelProvider(type, secret, options) {
|
|
13040
13289
|
const response = await upsertModelProvider({
|
|
13041
13290
|
type,
|
|
13042
|
-
|
|
13043
|
-
convert: options?.convert,
|
|
13291
|
+
secret,
|
|
13044
13292
|
selectedModel: options?.selectedModel
|
|
13045
13293
|
});
|
|
13046
13294
|
return {
|
|
@@ -13231,12 +13479,10 @@ async function handleModelProvider(ctx) {
|
|
|
13231
13479
|
step.detail(chalk66.dim(line));
|
|
13232
13480
|
}
|
|
13233
13481
|
}
|
|
13234
|
-
const
|
|
13235
|
-
() => promptPassword(
|
|
13236
|
-
`Enter your ${selectedChoice?.credentialLabel ?? "credential"}:`
|
|
13237
|
-
)
|
|
13482
|
+
const secret = await step.prompt(
|
|
13483
|
+
() => promptPassword(`Enter your ${selectedChoice?.secretLabel ?? "secret"}:`)
|
|
13238
13484
|
);
|
|
13239
|
-
if (!
|
|
13485
|
+
if (!secret) {
|
|
13240
13486
|
console.log(chalk66.dim("Cancelled"));
|
|
13241
13487
|
process.exit(0);
|
|
13242
13488
|
}
|
|
@@ -13261,7 +13507,7 @@ async function handleModelProvider(ctx) {
|
|
|
13261
13507
|
}
|
|
13262
13508
|
selectedModel = modelSelection === "" ? void 0 : modelSelection;
|
|
13263
13509
|
}
|
|
13264
|
-
const result = await setupModelProvider(providerType,
|
|
13510
|
+
const result = await setupModelProvider(providerType, secret, {
|
|
13265
13511
|
selectedModel
|
|
13266
13512
|
});
|
|
13267
13513
|
const modelNote = result.provider.selectedModel ? ` with model: ${result.provider.selectedModel}` : "";
|
|
@@ -13415,7 +13661,7 @@ var setupClaudeCommand = new Command67().name("setup-claude").description("Insta
|
|
|
13415
13661
|
|
|
13416
13662
|
// src/index.ts
|
|
13417
13663
|
var program = new Command68();
|
|
13418
|
-
program.name("vm0").description("VM0 CLI - Build and run agents with natural language").version("9.
|
|
13664
|
+
program.name("vm0").description("VM0 CLI - Build and run agents with natural language").version("9.22.0");
|
|
13419
13665
|
program.addCommand(authCommand);
|
|
13420
13666
|
program.addCommand(infoCommand);
|
|
13421
13667
|
program.addCommand(composeCommand);
|