@vm0/cli 9.21.0 → 9.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/index.js +673 -300
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -3,13 +3,7 @@
3
3
  // src/instrument.ts
4
4
  import * as Sentry from "@sentry/node";
5
5
  import * as os from "os";
6
- var TELEMETRY_DISABLED = process.env.VM0_TELEMETRY === "false";
7
- var IS_CI = Boolean(process.env.CI || process.env.GITHUB_ACTIONS);
8
- var IS_DEV = process.env.NODE_ENV === "development";
9
- var PRODUCTION_API_URL = "https://www.vm0.ai";
10
- var API_URL = process.env.VM0_API_URL ?? "";
11
- var IS_PRODUCTION_API = API_URL === "" || API_URL === PRODUCTION_API_URL;
12
- var DSN = "https://268d9b4cd051531805af76a5b3934dca@o4510583739777024.ingest.us.sentry.io/4510832047947776";
6
+ var DSN = process.env.SENTRY_DSN ?? "https://268d9b4cd051531805af76a5b3934dca@o4510583739777024.ingest.us.sentry.io/4510832047947776";
13
7
  var OPERATIONAL_ERROR_PATTERNS = [
14
8
  // Authentication errors (user needs to login)
15
9
  /not authenticated/i,
@@ -46,7 +40,7 @@ function isOperationalError(error) {
46
40
  const message = error.message;
47
41
  return OPERATIONAL_ERROR_PATTERNS.some((pattern) => pattern.test(message));
48
42
  }
49
- if (!TELEMETRY_DISABLED && !IS_CI && !IS_DEV && IS_PRODUCTION_API) {
43
+ if (DSN) {
50
44
  Sentry.init({
51
45
  dsn: DSN,
52
46
  sendDefaultPii: false,
@@ -67,7 +61,7 @@ if (!TELEMETRY_DISABLED && !IS_CI && !IS_DEV && IS_PRODUCTION_API) {
67
61
  }
68
62
  });
69
63
  Sentry.setContext("cli", {
70
- version: "9.21.0",
64
+ version: "9.23.0",
71
65
  command: process.argv.slice(2).join(" ")
72
66
  });
73
67
  Sentry.setContext("runtime", {
@@ -78,7 +72,7 @@ if (!TELEMETRY_DISABLED && !IS_CI && !IS_DEV && IS_PRODUCTION_API) {
78
72
  }
79
73
 
80
74
  // src/index.ts
81
- import { Command as Command68 } from "commander";
75
+ import { Command as Command70 } from "commander";
82
76
 
83
77
  // src/commands/auth/index.ts
84
78
  import { Command as Command5 } from "commander";
@@ -2323,7 +2317,7 @@ var MODEL_PROVIDER_TYPES = {
2323
2317
  "aws-bedrock": {
2324
2318
  framework: "claude-code",
2325
2319
  label: "AWS Bedrock",
2326
- helpText: "Run Claude on AWS Bedrock.\nSetup guide: https://docs.anthropic.com/en/docs/claude-code/bedrock",
2320
+ helpText: "Run Claude on AWS Bedrock.\nSetup guide: https://code.claude.com/docs/en/amazon-bedrock",
2327
2321
  authMethods: {
2328
2322
  "api-key": {
2329
2323
  label: "Bedrock API Key",
@@ -3144,29 +3138,126 @@ var llmChatContract = c18.router({
3144
3138
  }
3145
3139
  });
3146
3140
 
3147
- // ../../packages/core/src/contracts/public/agents.ts
3141
+ // ../../packages/core/src/contracts/compose-jobs.ts
3148
3142
  import { z as z23 } from "zod";
3149
3143
  var c19 = initContract();
3150
- var publicAgentSchema = z23.object({
3151
- id: z23.string(),
3152
- name: z23.string(),
3153
- currentVersionId: z23.string().nullable(),
3144
+ var composeJobStatusSchema = z23.enum([
3145
+ "pending",
3146
+ "running",
3147
+ "completed",
3148
+ "failed"
3149
+ ]);
3150
+ var composeJobResultSchema = z23.object({
3151
+ composeId: z23.string(),
3152
+ composeName: z23.string(),
3153
+ versionId: z23.string(),
3154
+ warnings: z23.array(z23.string())
3155
+ });
3156
+ var createComposeJobRequestSchema = z23.object({
3157
+ githubUrl: z23.string().url().startsWith("https://github.com/"),
3158
+ overwrite: z23.boolean().optional().default(false)
3159
+ });
3160
+ var composeJobResponseSchema = z23.object({
3161
+ jobId: z23.string(),
3162
+ status: composeJobStatusSchema,
3163
+ githubUrl: z23.string(),
3164
+ result: composeJobResultSchema.optional(),
3165
+ error: z23.string().optional(),
3166
+ createdAt: z23.string(),
3167
+ startedAt: z23.string().optional(),
3168
+ completedAt: z23.string().optional()
3169
+ });
3170
+ var composeJobsMainContract = c19.router({
3171
+ /**
3172
+ * POST /api/compose/from-github
3173
+ * Create a new compose job from GitHub URL
3174
+ */
3175
+ create: {
3176
+ method: "POST",
3177
+ path: "/api/compose/from-github",
3178
+ headers: authHeadersSchema,
3179
+ body: createComposeJobRequestSchema,
3180
+ responses: {
3181
+ 201: composeJobResponseSchema,
3182
+ 200: composeJobResponseSchema,
3183
+ // Returned when existing job found (idempotency)
3184
+ 400: apiErrorSchema,
3185
+ 401: apiErrorSchema
3186
+ },
3187
+ summary: "Create compose job from GitHub URL"
3188
+ }
3189
+ });
3190
+ var composeJobsByIdContract = c19.router({
3191
+ /**
3192
+ * GET /api/compose/from-github/:jobId
3193
+ * Get compose job status and result
3194
+ */
3195
+ getById: {
3196
+ method: "GET",
3197
+ path: "/api/compose/from-github/:jobId",
3198
+ headers: authHeadersSchema,
3199
+ pathParams: z23.object({
3200
+ jobId: z23.string().uuid()
3201
+ }),
3202
+ responses: {
3203
+ 200: composeJobResponseSchema,
3204
+ 401: apiErrorSchema,
3205
+ 404: apiErrorSchema
3206
+ },
3207
+ summary: "Get compose job status"
3208
+ }
3209
+ });
3210
+ var webhookComposeCompleteContract = c19.router({
3211
+ /**
3212
+ * POST /api/webhooks/compose/complete
3213
+ * Handle compose job completion from sandbox
3214
+ */
3215
+ complete: {
3216
+ method: "POST",
3217
+ path: "/api/webhooks/compose/complete",
3218
+ headers: authHeadersSchema,
3219
+ body: z23.object({
3220
+ jobId: z23.string().uuid(),
3221
+ success: z23.boolean(),
3222
+ // Result from CLI compose command
3223
+ result: composeJobResultSchema.optional(),
3224
+ error: z23.string().optional()
3225
+ }),
3226
+ responses: {
3227
+ 200: z23.object({
3228
+ success: z23.boolean()
3229
+ }),
3230
+ 400: apiErrorSchema,
3231
+ 401: apiErrorSchema,
3232
+ 404: apiErrorSchema
3233
+ },
3234
+ summary: "Handle compose job completion"
3235
+ }
3236
+ });
3237
+
3238
+ // ../../packages/core/src/contracts/public/agents.ts
3239
+ import { z as z24 } from "zod";
3240
+ var c20 = initContract();
3241
+ var publicAgentSchema = z24.object({
3242
+ id: z24.string(),
3243
+ name: z24.string(),
3244
+ currentVersionId: z24.string().nullable(),
3154
3245
  createdAt: timestampSchema,
3155
3246
  updatedAt: timestampSchema
3156
3247
  });
3157
- var agentVersionSchema = z23.object({
3158
- id: z23.string(),
3159
- agentId: z23.string(),
3160
- versionNumber: z23.number(),
3248
+ var agentVersionSchema = z24.object({
3249
+ id: z24.string(),
3250
+ agentId: z24.string(),
3251
+ versionNumber: z24.number(),
3161
3252
  createdAt: timestampSchema
3162
3253
  });
3163
3254
  var publicAgentDetailSchema = publicAgentSchema;
3164
3255
  var paginatedAgentsSchema = createPaginatedResponseSchema(publicAgentSchema);
3165
3256
  var paginatedAgentVersionsSchema = createPaginatedResponseSchema(agentVersionSchema);
3166
3257
  var agentListQuerySchema = listQuerySchema.extend({
3167
- name: z23.string().optional()
3258
+ name: z24.string().optional()
3168
3259
  });
3169
- var publicAgentsListContract = c19.router({
3260
+ var publicAgentsListContract = c20.router({
3170
3261
  list: {
3171
3262
  method: "GET",
3172
3263
  path: "/v1/agents",
@@ -3181,13 +3272,13 @@ var publicAgentsListContract = c19.router({
3181
3272
  description: "List all agents in the current scope with pagination. Use the `name` query parameter to filter by agent name."
3182
3273
  }
3183
3274
  });
3184
- var publicAgentByIdContract = c19.router({
3275
+ var publicAgentByIdContract = c20.router({
3185
3276
  get: {
3186
3277
  method: "GET",
3187
3278
  path: "/v1/agents/:id",
3188
3279
  headers: authHeadersSchema,
3189
- pathParams: z23.object({
3190
- id: z23.string().min(1, "Agent ID is required")
3280
+ pathParams: z24.object({
3281
+ id: z24.string().min(1, "Agent ID is required")
3191
3282
  }),
3192
3283
  responses: {
3193
3284
  200: publicAgentDetailSchema,
@@ -3199,13 +3290,13 @@ var publicAgentByIdContract = c19.router({
3199
3290
  description: "Get agent details by ID"
3200
3291
  }
3201
3292
  });
3202
- var publicAgentVersionsContract = c19.router({
3293
+ var publicAgentVersionsContract = c20.router({
3203
3294
  list: {
3204
3295
  method: "GET",
3205
3296
  path: "/v1/agents/:id/versions",
3206
3297
  headers: authHeadersSchema,
3207
- pathParams: z23.object({
3208
- id: z23.string().min(1, "Agent ID is required")
3298
+ pathParams: z24.object({
3299
+ id: z24.string().min(1, "Agent ID is required")
3209
3300
  }),
3210
3301
  query: listQuerySchema,
3211
3302
  responses: {
@@ -3220,9 +3311,9 @@ var publicAgentVersionsContract = c19.router({
3220
3311
  });
3221
3312
 
3222
3313
  // ../../packages/core/src/contracts/public/runs.ts
3223
- import { z as z24 } from "zod";
3224
- var c20 = initContract();
3225
- var publicRunStatusSchema = z24.enum([
3314
+ import { z as z25 } from "zod";
3315
+ var c21 = initContract();
3316
+ var publicRunStatusSchema = z25.enum([
3226
3317
  "pending",
3227
3318
  "running",
3228
3319
  "completed",
@@ -3230,54 +3321,54 @@ var publicRunStatusSchema = z24.enum([
3230
3321
  "timeout",
3231
3322
  "cancelled"
3232
3323
  ]);
3233
- var publicRunSchema = z24.object({
3234
- id: z24.string(),
3235
- agentId: z24.string(),
3236
- agentName: z24.string(),
3324
+ var publicRunSchema = z25.object({
3325
+ id: z25.string(),
3326
+ agentId: z25.string(),
3327
+ agentName: z25.string(),
3237
3328
  status: publicRunStatusSchema,
3238
- prompt: z24.string(),
3329
+ prompt: z25.string(),
3239
3330
  createdAt: timestampSchema,
3240
3331
  startedAt: timestampSchema.nullable(),
3241
3332
  completedAt: timestampSchema.nullable()
3242
3333
  });
3243
3334
  var publicRunDetailSchema = publicRunSchema.extend({
3244
- error: z24.string().nullable(),
3245
- executionTimeMs: z24.number().nullable(),
3246
- checkpointId: z24.string().nullable(),
3247
- sessionId: z24.string().nullable(),
3248
- artifactName: z24.string().nullable(),
3249
- artifactVersion: z24.string().nullable(),
3250
- volumes: z24.record(z24.string(), z24.string()).optional()
3335
+ error: z25.string().nullable(),
3336
+ executionTimeMs: z25.number().nullable(),
3337
+ checkpointId: z25.string().nullable(),
3338
+ sessionId: z25.string().nullable(),
3339
+ artifactName: z25.string().nullable(),
3340
+ artifactVersion: z25.string().nullable(),
3341
+ volumes: z25.record(z25.string(), z25.string()).optional()
3251
3342
  });
3252
3343
  var paginatedRunsSchema = createPaginatedResponseSchema(publicRunSchema);
3253
- var createRunRequestSchema = z24.object({
3344
+ var createRunRequestSchema = z25.object({
3254
3345
  // Agent identification (one of: agent, agentId, sessionId, checkpointId)
3255
- agent: z24.string().optional(),
3346
+ agent: z25.string().optional(),
3256
3347
  // Agent name
3257
- agentId: z24.string().optional(),
3348
+ agentId: z25.string().optional(),
3258
3349
  // Agent ID
3259
- agentVersion: z24.string().optional(),
3350
+ agentVersion: z25.string().optional(),
3260
3351
  // Version specifier (e.g., "latest", "v1", specific ID)
3261
3352
  // Continue session
3262
- sessionId: z24.string().optional(),
3353
+ sessionId: z25.string().optional(),
3263
3354
  // Resume from checkpoint
3264
- checkpointId: z24.string().optional(),
3355
+ checkpointId: z25.string().optional(),
3265
3356
  // Required
3266
- prompt: z24.string().min(1, "Prompt is required"),
3357
+ prompt: z25.string().min(1, "Prompt is required"),
3267
3358
  // Optional configuration
3268
- variables: z24.record(z24.string(), z24.string()).optional(),
3269
- secrets: z24.record(z24.string(), z24.string()).optional(),
3270
- artifactName: z24.string().optional(),
3359
+ variables: z25.record(z25.string(), z25.string()).optional(),
3360
+ secrets: z25.record(z25.string(), z25.string()).optional(),
3361
+ artifactName: z25.string().optional(),
3271
3362
  // Artifact name to mount
3272
- artifactVersion: z24.string().optional(),
3363
+ artifactVersion: z25.string().optional(),
3273
3364
  // Artifact version (defaults to latest)
3274
- volumes: z24.record(z24.string(), z24.string()).optional()
3365
+ volumes: z25.record(z25.string(), z25.string()).optional()
3275
3366
  // volume_name -> version
3276
3367
  });
3277
3368
  var runListQuerySchema = listQuerySchema.extend({
3278
3369
  status: publicRunStatusSchema.optional()
3279
3370
  });
3280
- var publicRunsListContract = c20.router({
3371
+ var publicRunsListContract = c21.router({
3281
3372
  list: {
3282
3373
  method: "GET",
3283
3374
  path: "/v1/runs",
@@ -3309,13 +3400,13 @@ var publicRunsListContract = c20.router({
3309
3400
  description: "Create and execute a new agent run. Returns 202 Accepted as runs execute asynchronously."
3310
3401
  }
3311
3402
  });
3312
- var publicRunByIdContract = c20.router({
3403
+ var publicRunByIdContract = c21.router({
3313
3404
  get: {
3314
3405
  method: "GET",
3315
3406
  path: "/v1/runs/:id",
3316
3407
  headers: authHeadersSchema,
3317
- pathParams: z24.object({
3318
- id: z24.string().min(1, "Run ID is required")
3408
+ pathParams: z25.object({
3409
+ id: z25.string().min(1, "Run ID is required")
3319
3410
  }),
3320
3411
  responses: {
3321
3412
  200: publicRunDetailSchema,
@@ -3327,15 +3418,15 @@ var publicRunByIdContract = c20.router({
3327
3418
  description: "Get run details by ID"
3328
3419
  }
3329
3420
  });
3330
- var publicRunCancelContract = c20.router({
3421
+ var publicRunCancelContract = c21.router({
3331
3422
  cancel: {
3332
3423
  method: "POST",
3333
3424
  path: "/v1/runs/:id/cancel",
3334
3425
  headers: authHeadersSchema,
3335
- pathParams: z24.object({
3336
- id: z24.string().min(1, "Run ID is required")
3426
+ pathParams: z25.object({
3427
+ id: z25.string().min(1, "Run ID is required")
3337
3428
  }),
3338
- body: z24.undefined(),
3429
+ body: z25.undefined(),
3339
3430
  responses: {
3340
3431
  200: publicRunDetailSchema,
3341
3432
  400: publicApiErrorSchema,
@@ -3348,27 +3439,27 @@ var publicRunCancelContract = c20.router({
3348
3439
  description: "Cancel a pending or running execution"
3349
3440
  }
3350
3441
  });
3351
- var logEntrySchema = z24.object({
3442
+ var logEntrySchema = z25.object({
3352
3443
  timestamp: timestampSchema,
3353
- type: z24.enum(["agent", "system", "network"]),
3354
- level: z24.enum(["debug", "info", "warn", "error"]),
3355
- message: z24.string(),
3356
- metadata: z24.record(z24.string(), z24.unknown()).optional()
3444
+ type: z25.enum(["agent", "system", "network"]),
3445
+ level: z25.enum(["debug", "info", "warn", "error"]),
3446
+ message: z25.string(),
3447
+ metadata: z25.record(z25.string(), z25.unknown()).optional()
3357
3448
  });
3358
3449
  var paginatedLogsSchema = createPaginatedResponseSchema(logEntrySchema);
3359
3450
  var logsQuerySchema = listQuerySchema.extend({
3360
- type: z24.enum(["agent", "system", "network", "all"]).default("all"),
3451
+ type: z25.enum(["agent", "system", "network", "all"]).default("all"),
3361
3452
  since: timestampSchema.optional(),
3362
3453
  until: timestampSchema.optional(),
3363
- order: z24.enum(["asc", "desc"]).default("asc")
3454
+ order: z25.enum(["asc", "desc"]).default("asc")
3364
3455
  });
3365
- var publicRunLogsContract = c20.router({
3456
+ var publicRunLogsContract = c21.router({
3366
3457
  getLogs: {
3367
3458
  method: "GET",
3368
3459
  path: "/v1/runs/:id/logs",
3369
3460
  headers: authHeadersSchema,
3370
- pathParams: z24.object({
3371
- id: z24.string().min(1, "Run ID is required")
3461
+ pathParams: z25.object({
3462
+ id: z25.string().min(1, "Run ID is required")
3372
3463
  }),
3373
3464
  query: logsQuerySchema,
3374
3465
  responses: {
@@ -3381,30 +3472,30 @@ var publicRunLogsContract = c20.router({
3381
3472
  description: "Get unified logs for a run. Combines agent, system, and network logs."
3382
3473
  }
3383
3474
  });
3384
- var metricPointSchema = z24.object({
3475
+ var metricPointSchema = z25.object({
3385
3476
  timestamp: timestampSchema,
3386
- cpuPercent: z24.number(),
3387
- memoryUsedMb: z24.number(),
3388
- memoryTotalMb: z24.number(),
3389
- diskUsedMb: z24.number(),
3390
- diskTotalMb: z24.number()
3391
- });
3392
- var metricsSummarySchema = z24.object({
3393
- avgCpuPercent: z24.number(),
3394
- maxMemoryUsedMb: z24.number(),
3395
- totalDurationMs: z24.number().nullable()
3396
- });
3397
- var metricsResponseSchema2 = z24.object({
3398
- data: z24.array(metricPointSchema),
3477
+ cpuPercent: z25.number(),
3478
+ memoryUsedMb: z25.number(),
3479
+ memoryTotalMb: z25.number(),
3480
+ diskUsedMb: z25.number(),
3481
+ diskTotalMb: z25.number()
3482
+ });
3483
+ var metricsSummarySchema = z25.object({
3484
+ avgCpuPercent: z25.number(),
3485
+ maxMemoryUsedMb: z25.number(),
3486
+ totalDurationMs: z25.number().nullable()
3487
+ });
3488
+ var metricsResponseSchema2 = z25.object({
3489
+ data: z25.array(metricPointSchema),
3399
3490
  summary: metricsSummarySchema
3400
3491
  });
3401
- var publicRunMetricsContract = c20.router({
3492
+ var publicRunMetricsContract = c21.router({
3402
3493
  getMetrics: {
3403
3494
  method: "GET",
3404
3495
  path: "/v1/runs/:id/metrics",
3405
3496
  headers: authHeadersSchema,
3406
- pathParams: z24.object({
3407
- id: z24.string().min(1, "Run ID is required")
3497
+ pathParams: z25.object({
3498
+ id: z25.string().min(1, "Run ID is required")
3408
3499
  }),
3409
3500
  responses: {
3410
3501
  200: metricsResponseSchema2,
@@ -3416,7 +3507,7 @@ var publicRunMetricsContract = c20.router({
3416
3507
  description: "Get CPU, memory, and disk metrics for a run"
3417
3508
  }
3418
3509
  });
3419
- var sseEventTypeSchema = z24.enum([
3510
+ var sseEventTypeSchema = z25.enum([
3420
3511
  "status",
3421
3512
  // Run status change
3422
3513
  "output",
@@ -3428,26 +3519,26 @@ var sseEventTypeSchema = z24.enum([
3428
3519
  "heartbeat"
3429
3520
  // Keep-alive
3430
3521
  ]);
3431
- var sseEventSchema = z24.object({
3522
+ var sseEventSchema = z25.object({
3432
3523
  event: sseEventTypeSchema,
3433
- data: z24.unknown(),
3434
- id: z24.string().optional()
3524
+ data: z25.unknown(),
3525
+ id: z25.string().optional()
3435
3526
  // For Last-Event-ID reconnection
3436
3527
  });
3437
- var publicRunEventsContract = c20.router({
3528
+ var publicRunEventsContract = c21.router({
3438
3529
  streamEvents: {
3439
3530
  method: "GET",
3440
3531
  path: "/v1/runs/:id/events",
3441
3532
  headers: authHeadersSchema,
3442
- pathParams: z24.object({
3443
- id: z24.string().min(1, "Run ID is required")
3533
+ pathParams: z25.object({
3534
+ id: z25.string().min(1, "Run ID is required")
3444
3535
  }),
3445
- query: z24.object({
3446
- lastEventId: z24.string().optional()
3536
+ query: z25.object({
3537
+ lastEventId: z25.string().optional()
3447
3538
  // For reconnection
3448
3539
  }),
3449
3540
  responses: {
3450
- 200: z24.any(),
3541
+ 200: z25.any(),
3451
3542
  // SSE stream - actual content is text/event-stream
3452
3543
  401: publicApiErrorSchema,
3453
3544
  404: publicApiErrorSchema,
@@ -3459,28 +3550,28 @@ var publicRunEventsContract = c20.router({
3459
3550
  });
3460
3551
 
3461
3552
  // ../../packages/core/src/contracts/public/artifacts.ts
3462
- import { z as z25 } from "zod";
3463
- var c21 = initContract();
3464
- var publicArtifactSchema = z25.object({
3465
- id: z25.string(),
3466
- name: z25.string(),
3467
- currentVersionId: z25.string().nullable(),
3468
- size: z25.number(),
3553
+ import { z as z26 } from "zod";
3554
+ var c22 = initContract();
3555
+ var publicArtifactSchema = z26.object({
3556
+ id: z26.string(),
3557
+ name: z26.string(),
3558
+ currentVersionId: z26.string().nullable(),
3559
+ size: z26.number(),
3469
3560
  // Total size in bytes
3470
- fileCount: z25.number(),
3561
+ fileCount: z26.number(),
3471
3562
  createdAt: timestampSchema,
3472
3563
  updatedAt: timestampSchema
3473
3564
  });
3474
- var artifactVersionSchema = z25.object({
3475
- id: z25.string(),
3565
+ var artifactVersionSchema = z26.object({
3566
+ id: z26.string(),
3476
3567
  // SHA-256 content hash
3477
- artifactId: z25.string(),
3478
- size: z25.number(),
3568
+ artifactId: z26.string(),
3569
+ size: z26.number(),
3479
3570
  // Size in bytes
3480
- fileCount: z25.number(),
3481
- message: z25.string().nullable(),
3571
+ fileCount: z26.number(),
3572
+ message: z26.string().nullable(),
3482
3573
  // Optional commit message
3483
- createdBy: z25.string(),
3574
+ createdBy: z26.string(),
3484
3575
  createdAt: timestampSchema
3485
3576
  });
3486
3577
  var publicArtifactDetailSchema = publicArtifactSchema.extend({
@@ -3490,7 +3581,7 @@ var paginatedArtifactsSchema = createPaginatedResponseSchema(publicArtifactSchem
3490
3581
  var paginatedArtifactVersionsSchema = createPaginatedResponseSchema(
3491
3582
  artifactVersionSchema
3492
3583
  );
3493
- var publicArtifactsListContract = c21.router({
3584
+ var publicArtifactsListContract = c22.router({
3494
3585
  list: {
3495
3586
  method: "GET",
3496
3587
  path: "/v1/artifacts",
@@ -3505,13 +3596,13 @@ var publicArtifactsListContract = c21.router({
3505
3596
  description: "List all artifacts in the current scope with pagination"
3506
3597
  }
3507
3598
  });
3508
- var publicArtifactByIdContract = c21.router({
3599
+ var publicArtifactByIdContract = c22.router({
3509
3600
  get: {
3510
3601
  method: "GET",
3511
3602
  path: "/v1/artifacts/:id",
3512
3603
  headers: authHeadersSchema,
3513
- pathParams: z25.object({
3514
- id: z25.string().min(1, "Artifact ID is required")
3604
+ pathParams: z26.object({
3605
+ id: z26.string().min(1, "Artifact ID is required")
3515
3606
  }),
3516
3607
  responses: {
3517
3608
  200: publicArtifactDetailSchema,
@@ -3523,13 +3614,13 @@ var publicArtifactByIdContract = c21.router({
3523
3614
  description: "Get artifact details by ID"
3524
3615
  }
3525
3616
  });
3526
- var publicArtifactVersionsContract = c21.router({
3617
+ var publicArtifactVersionsContract = c22.router({
3527
3618
  list: {
3528
3619
  method: "GET",
3529
3620
  path: "/v1/artifacts/:id/versions",
3530
3621
  headers: authHeadersSchema,
3531
- pathParams: z25.object({
3532
- id: z25.string().min(1, "Artifact ID is required")
3622
+ pathParams: z26.object({
3623
+ id: z26.string().min(1, "Artifact ID is required")
3533
3624
  }),
3534
3625
  query: listQuerySchema,
3535
3626
  responses: {
@@ -3542,20 +3633,20 @@ var publicArtifactVersionsContract = c21.router({
3542
3633
  description: "List all versions of an artifact with pagination"
3543
3634
  }
3544
3635
  });
3545
- var publicArtifactDownloadContract = c21.router({
3636
+ var publicArtifactDownloadContract = c22.router({
3546
3637
  download: {
3547
3638
  method: "GET",
3548
3639
  path: "/v1/artifacts/:id/download",
3549
3640
  headers: authHeadersSchema,
3550
- pathParams: z25.object({
3551
- id: z25.string().min(1, "Artifact ID is required")
3641
+ pathParams: z26.object({
3642
+ id: z26.string().min(1, "Artifact ID is required")
3552
3643
  }),
3553
- query: z25.object({
3554
- versionId: z25.string().optional()
3644
+ query: z26.object({
3645
+ versionId: z26.string().optional()
3555
3646
  // Defaults to current version
3556
3647
  }),
3557
3648
  responses: {
3558
- 302: z25.undefined(),
3649
+ 302: z26.undefined(),
3559
3650
  // Redirect to presigned URL
3560
3651
  401: publicApiErrorSchema,
3561
3652
  404: publicApiErrorSchema,
@@ -3567,28 +3658,28 @@ var publicArtifactDownloadContract = c21.router({
3567
3658
  });
3568
3659
 
3569
3660
  // ../../packages/core/src/contracts/public/volumes.ts
3570
- import { z as z26 } from "zod";
3571
- var c22 = initContract();
3572
- var publicVolumeSchema = z26.object({
3573
- id: z26.string(),
3574
- name: z26.string(),
3575
- currentVersionId: z26.string().nullable(),
3576
- size: z26.number(),
3661
+ import { z as z27 } from "zod";
3662
+ var c23 = initContract();
3663
+ var publicVolumeSchema = z27.object({
3664
+ id: z27.string(),
3665
+ name: z27.string(),
3666
+ currentVersionId: z27.string().nullable(),
3667
+ size: z27.number(),
3577
3668
  // Total size in bytes
3578
- fileCount: z26.number(),
3669
+ fileCount: z27.number(),
3579
3670
  createdAt: timestampSchema,
3580
3671
  updatedAt: timestampSchema
3581
3672
  });
3582
- var volumeVersionSchema = z26.object({
3583
- id: z26.string(),
3673
+ var volumeVersionSchema = z27.object({
3674
+ id: z27.string(),
3584
3675
  // SHA-256 content hash
3585
- volumeId: z26.string(),
3586
- size: z26.number(),
3676
+ volumeId: z27.string(),
3677
+ size: z27.number(),
3587
3678
  // Size in bytes
3588
- fileCount: z26.number(),
3589
- message: z26.string().nullable(),
3679
+ fileCount: z27.number(),
3680
+ message: z27.string().nullable(),
3590
3681
  // Optional commit message
3591
- createdBy: z26.string(),
3682
+ createdBy: z27.string(),
3592
3683
  createdAt: timestampSchema
3593
3684
  });
3594
3685
  var publicVolumeDetailSchema = publicVolumeSchema.extend({
@@ -3596,7 +3687,7 @@ var publicVolumeDetailSchema = publicVolumeSchema.extend({
3596
3687
  });
3597
3688
  var paginatedVolumesSchema = createPaginatedResponseSchema(publicVolumeSchema);
3598
3689
  var paginatedVolumeVersionsSchema = createPaginatedResponseSchema(volumeVersionSchema);
3599
- var publicVolumesListContract = c22.router({
3690
+ var publicVolumesListContract = c23.router({
3600
3691
  list: {
3601
3692
  method: "GET",
3602
3693
  path: "/v1/volumes",
@@ -3611,13 +3702,13 @@ var publicVolumesListContract = c22.router({
3611
3702
  description: "List all volumes in the current scope with pagination"
3612
3703
  }
3613
3704
  });
3614
- var publicVolumeByIdContract = c22.router({
3705
+ var publicVolumeByIdContract = c23.router({
3615
3706
  get: {
3616
3707
  method: "GET",
3617
3708
  path: "/v1/volumes/:id",
3618
3709
  headers: authHeadersSchema,
3619
- pathParams: z26.object({
3620
- id: z26.string().min(1, "Volume ID is required")
3710
+ pathParams: z27.object({
3711
+ id: z27.string().min(1, "Volume ID is required")
3621
3712
  }),
3622
3713
  responses: {
3623
3714
  200: publicVolumeDetailSchema,
@@ -3629,13 +3720,13 @@ var publicVolumeByIdContract = c22.router({
3629
3720
  description: "Get volume details by ID"
3630
3721
  }
3631
3722
  });
3632
- var publicVolumeVersionsContract = c22.router({
3723
+ var publicVolumeVersionsContract = c23.router({
3633
3724
  list: {
3634
3725
  method: "GET",
3635
3726
  path: "/v1/volumes/:id/versions",
3636
3727
  headers: authHeadersSchema,
3637
- pathParams: z26.object({
3638
- id: z26.string().min(1, "Volume ID is required")
3728
+ pathParams: z27.object({
3729
+ id: z27.string().min(1, "Volume ID is required")
3639
3730
  }),
3640
3731
  query: listQuerySchema,
3641
3732
  responses: {
@@ -3648,20 +3739,20 @@ var publicVolumeVersionsContract = c22.router({
3648
3739
  description: "List all versions of a volume with pagination"
3649
3740
  }
3650
3741
  });
3651
- var publicVolumeDownloadContract = c22.router({
3742
+ var publicVolumeDownloadContract = c23.router({
3652
3743
  download: {
3653
3744
  method: "GET",
3654
3745
  path: "/v1/volumes/:id/download",
3655
3746
  headers: authHeadersSchema,
3656
- pathParams: z26.object({
3657
- id: z26.string().min(1, "Volume ID is required")
3747
+ pathParams: z27.object({
3748
+ id: z27.string().min(1, "Volume ID is required")
3658
3749
  }),
3659
- query: z26.object({
3660
- versionId: z26.string().optional()
3750
+ query: z27.object({
3751
+ versionId: z27.string().optional()
3661
3752
  // Defaults to current version
3662
3753
  }),
3663
3754
  responses: {
3664
- 302: z26.undefined(),
3755
+ 302: z27.undefined(),
3665
3756
  // Redirect to presigned URL
3666
3757
  401: publicApiErrorSchema,
3667
3758
  404: publicApiErrorSchema,
@@ -4379,8 +4470,8 @@ async function getUsage(options) {
4379
4470
  }
4380
4471
 
4381
4472
  // src/lib/domain/yaml-validator.ts
4382
- import { z as z27 } from "zod";
4383
- var cliAgentNameSchema = z27.string().min(3, "Agent name must be at least 3 characters").max(64, "Agent name must be 64 characters or less").regex(
4473
+ import { z as z28 } from "zod";
4474
+ var cliAgentNameSchema = z28.string().min(3, "Agent name must be at least 3 characters").max(64, "Agent name must be 64 characters or less").regex(
4384
4475
  /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,62}[a-zA-Z0-9])?$/,
4385
4476
  "Agent name must start and end with letter or number, and contain only letters, numbers, and hyphens"
4386
4477
  );
@@ -4395,7 +4486,7 @@ var cliAgentDefinitionSchema = agentDefinitionSchema.superRefine(
4395
4486
  const skillUrl = agent.skills[i];
4396
4487
  if (skillUrl && !validateGitHubTreeUrl(skillUrl)) {
4397
4488
  ctx.addIssue({
4398
- code: z27.ZodIssueCode.custom,
4489
+ code: z28.ZodIssueCode.custom,
4399
4490
  message: `Invalid skill URL: ${skillUrl}. Expected format: https://github.com/{owner}/{repo}/tree/{branch}/{path}`,
4400
4491
  path: ["skills", i]
4401
4492
  });
@@ -4404,15 +4495,15 @@ var cliAgentDefinitionSchema = agentDefinitionSchema.superRefine(
4404
4495
  }
4405
4496
  }
4406
4497
  );
4407
- var cliComposeSchema = z27.object({
4408
- version: z27.string().min(1, "Missing config.version"),
4409
- agents: z27.record(cliAgentNameSchema, cliAgentDefinitionSchema),
4410
- volumes: z27.record(z27.string(), volumeConfigSchema).optional()
4498
+ var cliComposeSchema = z28.object({
4499
+ version: z28.string().min(1, "Missing config.version"),
4500
+ agents: z28.record(cliAgentNameSchema, cliAgentDefinitionSchema),
4501
+ volumes: z28.record(z28.string(), volumeConfigSchema).optional()
4411
4502
  }).superRefine((config, ctx) => {
4412
4503
  const agentKeys = Object.keys(config.agents);
4413
4504
  if (agentKeys.length === 0) {
4414
4505
  ctx.addIssue({
4415
- code: z27.ZodIssueCode.custom,
4506
+ code: z28.ZodIssueCode.custom,
4416
4507
  message: "agents must have at least one agent defined",
4417
4508
  path: ["agents"]
4418
4509
  });
@@ -4420,7 +4511,7 @@ var cliComposeSchema = z27.object({
4420
4511
  }
4421
4512
  if (agentKeys.length > 1) {
4422
4513
  ctx.addIssue({
4423
- code: z27.ZodIssueCode.custom,
4514
+ code: z28.ZodIssueCode.custom,
4424
4515
  message: "Multiple agents not supported yet. Only one agent allowed.",
4425
4516
  path: ["agents"]
4426
4517
  });
@@ -4432,7 +4523,7 @@ var cliComposeSchema = z27.object({
4432
4523
  if (agentVolumes && agentVolumes.length > 0) {
4433
4524
  if (!config.volumes) {
4434
4525
  ctx.addIssue({
4435
- code: z27.ZodIssueCode.custom,
4526
+ code: z28.ZodIssueCode.custom,
4436
4527
  message: "Agent references volumes but no volumes section defined. Each volume must have explicit name and version.",
4437
4528
  path: ["volumes"]
4438
4529
  });
@@ -4442,7 +4533,7 @@ var cliComposeSchema = z27.object({
4442
4533
  const parts = volDeclaration.split(":");
4443
4534
  if (parts.length !== 2) {
4444
4535
  ctx.addIssue({
4445
- code: z27.ZodIssueCode.custom,
4536
+ code: z28.ZodIssueCode.custom,
4446
4537
  message: `Invalid volume declaration: ${volDeclaration}. Expected format: volume-key:/mount/path`,
4447
4538
  path: ["agents", agentName, "volumes"]
4448
4539
  });
@@ -4451,7 +4542,7 @@ var cliComposeSchema = z27.object({
4451
4542
  const volumeKey = parts[0].trim();
4452
4543
  if (!config.volumes[volumeKey]) {
4453
4544
  ctx.addIssue({
4454
- code: z27.ZodIssueCode.custom,
4545
+ code: z28.ZodIssueCode.custom,
4455
4546
  message: `Volume "${volumeKey}" is not defined in volumes section. Each volume must have explicit name and version.`,
4456
4547
  path: ["volumes", volumeKey]
4457
4548
  });
@@ -5362,9 +5453,15 @@ function getSecretsFromComposeContent(content) {
5362
5453
  const grouped = groupVariablesBySource(refs);
5363
5454
  return new Set(grouped.secrets.map((r) => r.name));
5364
5455
  }
5365
- async function loadAndValidateConfig(configFile) {
5456
+ async function loadAndValidateConfig(configFile, porcelainMode) {
5366
5457
  if (!existsSync4(configFile)) {
5367
- console.error(chalk4.red(`\u2717 Config file not found: ${configFile}`));
5458
+ if (porcelainMode) {
5459
+ console.log(
5460
+ JSON.stringify({ error: `Config file not found: ${configFile}` })
5461
+ );
5462
+ } else {
5463
+ console.error(chalk4.red(`\u2717 Config file not found: ${configFile}`));
5464
+ }
5368
5465
  process.exit(1);
5369
5466
  }
5370
5467
  const content = await readFile4(configFile, "utf8");
@@ -5372,15 +5469,22 @@ async function loadAndValidateConfig(configFile) {
5372
5469
  try {
5373
5470
  config = parseYaml2(content);
5374
5471
  } catch (error) {
5375
- console.error(chalk4.red("\u2717 Invalid YAML format"));
5376
- if (error instanceof Error) {
5377
- console.error(chalk4.dim(` ${error.message}`));
5472
+ const message = error instanceof Error ? error.message : "Unknown error";
5473
+ if (porcelainMode) {
5474
+ console.log(JSON.stringify({ error: `Invalid YAML format: ${message}` }));
5475
+ } else {
5476
+ console.error(chalk4.red("\u2717 Invalid YAML format"));
5477
+ console.error(chalk4.dim(` ${message}`));
5378
5478
  }
5379
5479
  process.exit(1);
5380
5480
  }
5381
5481
  const validation = validateAgentCompose(config);
5382
5482
  if (!validation.valid) {
5383
- console.error(chalk4.red(`\u2717 ${validation.error}`));
5483
+ if (porcelainMode) {
5484
+ console.log(JSON.stringify({ error: validation.error }));
5485
+ } else {
5486
+ console.error(chalk4.red(`\u2717 ${validation.error}`));
5487
+ }
5384
5488
  process.exit(1);
5385
5489
  }
5386
5490
  const cfg = config;
@@ -5416,33 +5520,43 @@ function checkLegacyImageFormat(config) {
5416
5520
  }
5417
5521
  }
5418
5522
  }
5419
- async function uploadAssets(agentName, agent, basePath) {
5523
+ async function uploadAssets(agentName, agent, basePath, porcelainMode) {
5420
5524
  if (agent.instructions) {
5421
- console.log(`Uploading instructions: ${agent.instructions}`);
5525
+ if (!porcelainMode) {
5526
+ console.log(`Uploading instructions: ${agent.instructions}`);
5527
+ }
5422
5528
  const result = await uploadInstructions(
5423
5529
  agentName,
5424
5530
  agent.instructions,
5425
5531
  basePath,
5426
5532
  agent.framework
5427
5533
  );
5428
- console.log(
5429
- chalk4.green(
5430
- `\u2713 Instructions ${result.action === "deduplicated" ? "(unchanged)" : "uploaded"}: ${result.versionId.slice(0, 8)}`
5431
- )
5432
- );
5534
+ if (!porcelainMode) {
5535
+ console.log(
5536
+ chalk4.green(
5537
+ `\u2713 Instructions ${result.action === "deduplicated" ? "(unchanged)" : "uploaded"}: ${result.versionId.slice(0, 8)}`
5538
+ )
5539
+ );
5540
+ }
5433
5541
  }
5434
5542
  const skillResults = [];
5435
5543
  if (agent.skills && Array.isArray(agent.skills)) {
5436
- console.log(`Uploading ${agent.skills.length} skill(s)...`);
5544
+ if (!porcelainMode) {
5545
+ console.log(`Uploading ${agent.skills.length} skill(s)...`);
5546
+ }
5437
5547
  for (const skillUrl of agent.skills) {
5438
- console.log(chalk4.dim(` Downloading: ${skillUrl}`));
5548
+ if (!porcelainMode) {
5549
+ console.log(chalk4.dim(` Downloading: ${skillUrl}`));
5550
+ }
5439
5551
  const result = await uploadSkill(skillUrl);
5440
5552
  skillResults.push(result);
5441
- console.log(
5442
- chalk4.green(
5443
- ` \u2713 Skill ${result.action === "deduplicated" ? "(unchanged)" : "uploaded"}: ${result.skillName} (${result.versionId.slice(0, 8)})`
5444
- )
5445
- );
5553
+ if (!porcelainMode) {
5554
+ console.log(
5555
+ chalk4.green(
5556
+ ` \u2713 Skill ${result.action === "deduplicated" ? "(unchanged)" : "uploaded"}: ${result.skillName} (${result.versionId.slice(0, 8)})`
5557
+ )
5558
+ );
5559
+ }
5446
5560
  }
5447
5561
  }
5448
5562
  return skillResults;
@@ -5488,36 +5602,48 @@ async function displayAndConfirmVariables(variables, options) {
5488
5602
  if (newSecrets.length === 0 && newVars.length === 0) {
5489
5603
  return true;
5490
5604
  }
5491
- console.log();
5492
- console.log(
5493
- chalk4.bold("Skills require the following environment variables:")
5494
- );
5495
- console.log();
5496
- if (newSecrets.length > 0) {
5497
- console.log(chalk4.cyan(" Secrets:"));
5498
- for (const [name, skills] of newSecrets) {
5499
- const isNew = trulyNewSecrets.includes(name);
5500
- const newMarker = isNew ? chalk4.yellow(" (new)") : "";
5501
- console.log(` ${name.padEnd(24)}${newMarker} <- ${skills.join(", ")}`);
5605
+ if (!options.porcelain) {
5606
+ console.log();
5607
+ console.log(
5608
+ chalk4.bold("Skills require the following environment variables:")
5609
+ );
5610
+ console.log();
5611
+ if (newSecrets.length > 0) {
5612
+ console.log(chalk4.cyan(" Secrets:"));
5613
+ for (const [name, skills] of newSecrets) {
5614
+ const isNew = trulyNewSecrets.includes(name);
5615
+ const newMarker = isNew ? chalk4.yellow(" (new)") : "";
5616
+ console.log(
5617
+ ` ${name.padEnd(24)}${newMarker} <- ${skills.join(", ")}`
5618
+ );
5619
+ }
5502
5620
  }
5503
- }
5504
- if (newVars.length > 0) {
5505
- console.log(chalk4.cyan(" Vars:"));
5506
- for (const [name, skills] of newVars) {
5507
- console.log(` ${name.padEnd(24)} <- ${skills.join(", ")}`);
5621
+ if (newVars.length > 0) {
5622
+ console.log(chalk4.cyan(" Vars:"));
5623
+ for (const [name, skills] of newVars) {
5624
+ console.log(` ${name.padEnd(24)} <- ${skills.join(", ")}`);
5625
+ }
5508
5626
  }
5627
+ console.log();
5509
5628
  }
5510
- console.log();
5511
5629
  if (trulyNewSecrets.length > 0 && !options.yes) {
5512
5630
  if (!isInteractive()) {
5513
- console.error(
5514
- chalk4.red(`\u2717 New secrets detected: ${trulyNewSecrets.join(", ")}`)
5515
- );
5516
- console.error(
5517
- chalk4.dim(
5518
- " Use --yes flag to approve new secrets in non-interactive mode."
5519
- )
5520
- );
5631
+ if (options.porcelain) {
5632
+ console.log(
5633
+ JSON.stringify({
5634
+ error: `New secrets detected: ${trulyNewSecrets.join(", ")}. Use --yes flag to approve.`
5635
+ })
5636
+ );
5637
+ } else {
5638
+ console.error(
5639
+ chalk4.red(`\u2717 New secrets detected: ${trulyNewSecrets.join(", ")}`)
5640
+ );
5641
+ console.error(
5642
+ chalk4.dim(
5643
+ " Use --yes flag to approve new secrets in non-interactive mode."
5644
+ )
5645
+ );
5646
+ }
5521
5647
  process.exit(1);
5522
5648
  }
5523
5649
  const confirmed = await promptConfirm(
@@ -5525,7 +5651,9 @@ async function displayAndConfirmVariables(variables, options) {
5525
5651
  true
5526
5652
  );
5527
5653
  if (!confirmed) {
5528
- console.log(chalk4.yellow("Compose cancelled"));
5654
+ if (!options.porcelain) {
5655
+ console.log(chalk4.yellow("Compose cancelled"));
5656
+ }
5529
5657
  return false;
5530
5658
  }
5531
5659
  }
@@ -5553,57 +5681,94 @@ async function finalizeCompose(config, agent, variables, options) {
5553
5681
  process.exit(0);
5554
5682
  }
5555
5683
  mergeSkillVariables(agent, variables);
5556
- console.log("Uploading compose...");
5684
+ if (!options.porcelain) {
5685
+ console.log("Uploading compose...");
5686
+ }
5557
5687
  const response = await createOrUpdateCompose({ content: config });
5558
5688
  const scopeResponse = await getScope();
5559
5689
  const shortVersionId = response.versionId.slice(0, 8);
5560
5690
  const displayName = `${scopeResponse.slug}/${response.name}`;
5561
- if (response.action === "created") {
5562
- console.log(chalk4.green(`\u2713 Compose created: ${displayName}`));
5563
- } else {
5564
- console.log(chalk4.green(`\u2713 Compose version exists: ${displayName}`));
5691
+ const result = {
5692
+ composeId: response.composeId,
5693
+ composeName: response.name,
5694
+ versionId: response.versionId,
5695
+ action: response.action,
5696
+ displayName
5697
+ };
5698
+ if (!options.porcelain) {
5699
+ if (response.action === "created") {
5700
+ console.log(chalk4.green(`\u2713 Compose created: ${displayName}`));
5701
+ } else {
5702
+ console.log(chalk4.green(`\u2713 Compose version exists: ${displayName}`));
5703
+ }
5704
+ console.log(chalk4.dim(` Version: ${shortVersionId}`));
5705
+ console.log();
5706
+ console.log(" Run your agent:");
5707
+ console.log(
5708
+ chalk4.cyan(
5709
+ ` vm0 run ${displayName}:${shortVersionId} --artifact-name <artifact> "your prompt"`
5710
+ )
5711
+ );
5565
5712
  }
5566
- console.log(chalk4.dim(` Version: ${shortVersionId}`));
5567
- console.log();
5568
- console.log(" Run your agent:");
5569
- console.log(
5570
- chalk4.cyan(
5571
- ` vm0 run ${displayName}:${shortVersionId} --artifact-name <artifact> "your prompt"`
5572
- )
5573
- );
5574
5713
  if (options.autoUpdate !== false) {
5575
- await silentUpgradeAfterCommand("9.21.0");
5714
+ await silentUpgradeAfterCommand("9.23.0");
5576
5715
  }
5716
+ return result;
5577
5717
  }
5578
5718
  async function handleGitHubCompose(url, options) {
5579
- console.log(`Downloading from GitHub: ${url}`);
5719
+ if (!options.porcelain) {
5720
+ console.log(`Downloading from GitHub: ${url}`);
5721
+ }
5580
5722
  const { dir: downloadedDir, tempRoot } = await downloadGitHubDirectory(url);
5581
5723
  const configFile = join6(downloadedDir, "vm0.yaml");
5582
5724
  try {
5583
5725
  if (!existsSync4(configFile)) {
5584
- console.error(chalk4.red(`\u2717 vm0.yaml not found in the GitHub directory`));
5585
- console.error(chalk4.dim(` URL: ${url}`));
5726
+ if (options.porcelain) {
5727
+ console.log(
5728
+ JSON.stringify({
5729
+ error: "vm0.yaml not found in the GitHub directory"
5730
+ })
5731
+ );
5732
+ } else {
5733
+ console.error(
5734
+ chalk4.red(`\u2717 vm0.yaml not found in the GitHub directory`)
5735
+ );
5736
+ console.error(chalk4.dim(` URL: ${url}`));
5737
+ }
5586
5738
  process.exit(1);
5587
5739
  }
5588
- const { config, agentName, agent, basePath } = await loadAndValidateConfig(configFile);
5740
+ const { config, agentName, agent, basePath } = await loadAndValidateConfig(
5741
+ configFile,
5742
+ options.porcelain
5743
+ );
5589
5744
  const existingCompose = await getComposeByName(agentName);
5590
5745
  if (existingCompose) {
5591
- console.log();
5592
- console.log(
5593
- chalk4.yellow(`\u26A0 An agent named "${agentName}" already exists.`)
5594
- );
5746
+ if (!options.porcelain) {
5747
+ console.log();
5748
+ console.log(
5749
+ chalk4.yellow(`\u26A0 An agent named "${agentName}" already exists.`)
5750
+ );
5751
+ }
5595
5752
  if (!isInteractive()) {
5596
5753
  if (!options.yes) {
5597
- console.error(
5598
- chalk4.red(
5599
- `\u2717 Cannot overwrite existing agent in non-interactive mode`
5600
- )
5601
- );
5602
- console.error(
5603
- chalk4.dim(
5604
- ` Use --yes flag to confirm overwriting the existing agent.`
5605
- )
5606
- );
5754
+ if (options.porcelain) {
5755
+ console.log(
5756
+ JSON.stringify({
5757
+ error: "Cannot overwrite existing agent in non-interactive mode"
5758
+ })
5759
+ );
5760
+ } else {
5761
+ console.error(
5762
+ chalk4.red(
5763
+ `\u2717 Cannot overwrite existing agent in non-interactive mode`
5764
+ )
5765
+ );
5766
+ console.error(
5767
+ chalk4.dim(
5768
+ ` Use --yes flag to confirm overwriting the existing agent.`
5769
+ )
5770
+ );
5771
+ }
5607
5772
  process.exit(1);
5608
5773
  }
5609
5774
  } else {
@@ -5612,31 +5777,48 @@ async function handleGitHubCompose(url, options) {
5612
5777
  false
5613
5778
  );
5614
5779
  if (!confirmed) {
5615
- console.log(chalk4.yellow("Compose cancelled."));
5780
+ if (!options.porcelain) {
5781
+ console.log(chalk4.yellow("Compose cancelled."));
5782
+ }
5616
5783
  process.exit(0);
5617
5784
  }
5618
5785
  }
5619
5786
  }
5620
5787
  if (hasVolumes(config)) {
5621
- console.error(
5622
- chalk4.red(`\u2717 Volumes are not supported for GitHub URL compose`)
5623
- );
5624
- console.error(
5625
- chalk4.dim(
5626
- ` Clone the repository locally and run: vm0 compose ./path/to/vm0.yaml`
5627
- )
5628
- );
5788
+ if (options.porcelain) {
5789
+ console.log(
5790
+ JSON.stringify({
5791
+ error: "Volumes are not supported for GitHub URL compose"
5792
+ })
5793
+ );
5794
+ } else {
5795
+ console.error(
5796
+ chalk4.red(`\u2717 Volumes are not supported for GitHub URL compose`)
5797
+ );
5798
+ console.error(
5799
+ chalk4.dim(
5800
+ ` Clone the repository locally and run: vm0 compose ./path/to/vm0.yaml`
5801
+ )
5802
+ );
5803
+ }
5629
5804
  process.exit(1);
5630
5805
  }
5631
- checkLegacyImageFormat(config);
5632
- const skillResults = await uploadAssets(agentName, agent, basePath);
5806
+ if (!options.porcelain) {
5807
+ checkLegacyImageFormat(config);
5808
+ }
5809
+ const skillResults = await uploadAssets(
5810
+ agentName,
5811
+ agent,
5812
+ basePath,
5813
+ options.porcelain
5814
+ );
5633
5815
  const environment = agent.environment || {};
5634
5816
  const variables = await collectSkillVariables(
5635
5817
  skillResults,
5636
5818
  environment,
5637
5819
  agentName
5638
5820
  );
5639
- await finalizeCompose(config, agent, variables, options);
5821
+ return await finalizeCompose(config, agent, variables, options);
5640
5822
  } finally {
5641
5823
  await rm3(tempRoot, { recursive: true, force: true });
5642
5824
  }
@@ -5647,42 +5829,73 @@ var composeCommand = new Command7().name("compose").description("Create or updat
5647
5829
  ).option("-y, --yes", "Skip confirmation prompts for skill requirements").option(
5648
5830
  "--experimental-shared-compose",
5649
5831
  "Enable GitHub URL compose (experimental)"
5832
+ ).option(
5833
+ "--porcelain",
5834
+ "Output stable JSON for scripts (suppresses interactive output)"
5650
5835
  ).addOption(new Option("--no-auto-update").hideHelp()).action(
5651
5836
  async (configFile, options) => {
5652
5837
  const resolvedConfigFile = configFile ?? DEFAULT_CONFIG_FILE;
5838
+ if (options.porcelain) {
5839
+ options.yes = true;
5840
+ options.autoUpdate = false;
5841
+ }
5653
5842
  try {
5843
+ let result;
5654
5844
  if (isGitHubUrl(resolvedConfigFile)) {
5655
5845
  if (!options.experimentalSharedCompose) {
5656
- console.error(
5657
- chalk4.red(
5658
- "\u2717 Composing shared agents requires --experimental-shared-compose flag"
5659
- )
5660
- );
5661
- console.error();
5662
- console.error(
5663
- chalk4.dim(
5664
- " Composing agents from other users carries security risks."
5665
- )
5666
- );
5667
- console.error(
5668
- chalk4.dim(" Only compose agents from users you trust.")
5669
- );
5846
+ if (options.porcelain) {
5847
+ console.log(
5848
+ JSON.stringify({
5849
+ error: "Composing shared agents requires --experimental-shared-compose flag"
5850
+ })
5851
+ );
5852
+ } else {
5853
+ console.error(
5854
+ chalk4.red(
5855
+ "\u2717 Composing shared agents requires --experimental-shared-compose flag"
5856
+ )
5857
+ );
5858
+ console.error();
5859
+ console.error(
5860
+ chalk4.dim(
5861
+ " Composing agents from other users carries security risks."
5862
+ )
5863
+ );
5864
+ console.error(
5865
+ chalk4.dim(" Only compose agents from users you trust.")
5866
+ );
5867
+ }
5670
5868
  process.exit(1);
5671
5869
  }
5672
- await handleGitHubCompose(resolvedConfigFile, options);
5870
+ result = await handleGitHubCompose(resolvedConfigFile, options);
5673
5871
  } else {
5674
- const { config, agentName, agent, basePath } = await loadAndValidateConfig(resolvedConfigFile);
5675
- checkLegacyImageFormat(config);
5676
- const skillResults = await uploadAssets(agentName, agent, basePath);
5872
+ const { config, agentName, agent, basePath } = await loadAndValidateConfig(resolvedConfigFile, options.porcelain);
5873
+ if (!options.porcelain) {
5874
+ checkLegacyImageFormat(config);
5875
+ }
5876
+ const skillResults = await uploadAssets(
5877
+ agentName,
5878
+ agent,
5879
+ basePath,
5880
+ options.porcelain
5881
+ );
5677
5882
  const environment = agent.environment || {};
5678
5883
  const variables = await collectSkillVariables(
5679
5884
  skillResults,
5680
5885
  environment,
5681
5886
  agentName
5682
5887
  );
5683
- await finalizeCompose(config, agent, variables, options);
5888
+ result = await finalizeCompose(config, agent, variables, options);
5889
+ }
5890
+ if (options.porcelain) {
5891
+ console.log(JSON.stringify(result));
5684
5892
  }
5685
5893
  } catch (error) {
5894
+ if (options.porcelain) {
5895
+ const message = error instanceof Error ? error.message : "An unexpected error occurred";
5896
+ console.log(JSON.stringify({ error: message }));
5897
+ process.exit(1);
5898
+ }
5686
5899
  if (error instanceof Error) {
5687
5900
  if (error.message.includes("Not authenticated")) {
5688
5901
  console.error(
@@ -5961,8 +6174,8 @@ var EventRenderer = class _EventRenderer {
5961
6174
  /**
5962
6175
  * Format timestamp for display (without milliseconds, matching metrics format)
5963
6176
  */
5964
- static formatTimestamp(timestamp) {
5965
- return timestamp.toISOString().replace(/\.\d{3}Z$/, "Z");
6177
+ static formatTimestamp(timestamp2) {
6178
+ return timestamp2.toISOString().replace(/\.\d{3}Z$/, "Z");
5966
6179
  }
5967
6180
  /**
5968
6181
  * Render a parsed event to console
@@ -6435,9 +6648,9 @@ var CodexEventParser = class {
6435
6648
  }
6436
6649
  }
6437
6650
  if (itemType === "file_change" && item.changes && item.changes.length > 0) {
6438
- const changes = item.changes.map((c23) => {
6439
- const action = c23.kind === "add" ? "Created" : c23.kind === "modify" ? "Modified" : "Deleted";
6440
- return `${action}: ${c23.path}`;
6651
+ const changes = item.changes.map((c24) => {
6652
+ const action = c24.kind === "add" ? "Created" : c24.kind === "modify" ? "Modified" : "Deleted";
6653
+ return `${action}: ${c24.path}`;
6441
6654
  }).join("\n");
6442
6655
  return {
6443
6656
  type: "text",
@@ -6591,9 +6804,9 @@ var CodexEventRenderer = class {
6591
6804
  return;
6592
6805
  }
6593
6806
  if (itemType === "file_change" && item.changes && item.changes.length > 0) {
6594
- const summary = item.changes.map((c23) => {
6595
- const icon = c23.kind === "add" ? "+" : c23.kind === "delete" ? "-" : "~";
6596
- return `${icon}${c23.path}`;
6807
+ const summary = item.changes.map((c24) => {
6808
+ const icon = c24.kind === "add" ? "+" : c24.kind === "delete" ? "-" : "~";
6809
+ return `${icon}${c24.path}`;
6597
6810
  }).join(", ");
6598
6811
  console.log(chalk7.green("[files]") + ` ${summary}`);
6599
6812
  return;
@@ -7940,7 +8153,7 @@ var mainRunCommand = new Command8().name("run").description("Run an agent").argu
7940
8153
  }
7941
8154
  showNextSteps(result);
7942
8155
  if (options.autoUpdate !== false) {
7943
- await silentUpgradeAfterCommand("9.21.0");
8156
+ await silentUpgradeAfterCommand("9.23.0");
7944
8157
  }
7945
8158
  } catch (error) {
7946
8159
  handleRunError(error, identifier);
@@ -9447,7 +9660,7 @@ var cookAction = new Command27().name("cook").description("Quick start: prepare,
9447
9660
  ).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(
9448
9661
  async (prompt, options) => {
9449
9662
  if (options.autoUpdate !== false) {
9450
- const shouldExit = await checkAndUpgrade("9.21.0", prompt);
9663
+ const shouldExit = await checkAndUpgrade("9.23.0", prompt);
9451
9664
  if (shouldExit) {
9452
9665
  process.exit(0);
9453
9666
  }
@@ -9647,11 +9860,11 @@ function parseTime(timeStr) {
9647
9860
  return parseRelativeTime(value, unit);
9648
9861
  }
9649
9862
  if (/^\d+$/.test(timeStr)) {
9650
- const timestamp = parseInt(timeStr, 10);
9651
- if (timestamp < 1e10) {
9652
- return timestamp * 1e3;
9863
+ const timestamp2 = parseInt(timeStr, 10);
9864
+ if (timestamp2 < 1e10) {
9865
+ return timestamp2 * 1e3;
9653
9866
  }
9654
- return timestamp;
9867
+ return timestamp2;
9655
9868
  }
9656
9869
  const date = new Date(timeStr);
9657
9870
  if (!isNaN(date.getTime())) {
@@ -10162,7 +10375,7 @@ var listCommand4 = new Command36().name("list").alias("ls").description("List al
10162
10375
  );
10163
10376
  return;
10164
10377
  }
10165
- const nameWidth = Math.max(4, ...data.composes.map((c23) => c23.name.length));
10378
+ const nameWidth = Math.max(4, ...data.composes.map((c24) => c24.name.length));
10166
10379
  const header = ["NAME".padEnd(nameWidth), "VERSION", "UPDATED"].join(
10167
10380
  " "
10168
10381
  );
@@ -11136,7 +11349,7 @@ async function gatherFrequency(optionFrequency, existingFrequency) {
11136
11349
  );
11137
11350
  process.exit(1);
11138
11351
  }
11139
- const defaultIndex = existingFrequency ? FREQUENCY_CHOICES.findIndex((c23) => c23.value === existingFrequency) : 0;
11352
+ const defaultIndex = existingFrequency ? FREQUENCY_CHOICES.findIndex((c24) => c24.value === existingFrequency) : 0;
11140
11353
  frequency = await promptSelect(
11141
11354
  "Schedule frequency",
11142
11355
  FREQUENCY_CHOICES,
@@ -11165,7 +11378,7 @@ async function gatherDay(frequency, optionDay, existingDay) {
11165
11378
  process.exit(1);
11166
11379
  }
11167
11380
  if (frequency === "weekly") {
11168
- const defaultDayIndex = existingDay !== void 0 ? DAY_OF_WEEK_CHOICES.findIndex((c23) => c23.value === existingDay) : 0;
11381
+ const defaultDayIndex = existingDay !== void 0 ? DAY_OF_WEEK_CHOICES.findIndex((c24) => c24.value === existingDay) : 0;
11169
11382
  const day2 = await promptSelect(
11170
11383
  "Day of week",
11171
11384
  DAY_OF_WEEK_CHOICES,
@@ -13348,16 +13561,16 @@ async function handleModelProvider(ctx) {
13348
13561
  const providerType = await step.prompt(
13349
13562
  () => promptSelect(
13350
13563
  "Select provider type:",
13351
- choices.map((c23) => ({
13352
- title: c23.label,
13353
- value: c23.type
13564
+ choices.map((c24) => ({
13565
+ title: c24.label,
13566
+ value: c24.type
13354
13567
  }))
13355
13568
  )
13356
13569
  );
13357
13570
  if (!providerType) {
13358
13571
  process.exit(0);
13359
13572
  }
13360
- const selectedChoice = choices.find((c23) => c23.type === providerType);
13573
+ const selectedChoice = choices.find((c24) => c24.type === providerType);
13361
13574
  if (selectedChoice?.helpText) {
13362
13575
  for (const line of selectedChoice.helpText.split("\n")) {
13363
13576
  step.detail(chalk66.dim(line));
@@ -13543,9 +13756,168 @@ var setupClaudeCommand = new Command67().name("setup-claude").description("Insta
13543
13756
  );
13544
13757
  });
13545
13758
 
13759
+ // src/commands/dev-tool/index.ts
13760
+ import { Command as Command69 } from "commander";
13761
+
13762
+ // src/commands/dev-tool/compose.ts
13763
+ import { Command as Command68 } from "commander";
13764
+ import chalk68 from "chalk";
13765
+ import { initClient as initClient11 } from "@ts-rest/core";
13766
+ function sleep2(ms) {
13767
+ return new Promise((resolve) => setTimeout(resolve, ms));
13768
+ }
13769
+ function timestamp() {
13770
+ return (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
13771
+ }
13772
+ async function createComposeJob(githubUrl, overwrite) {
13773
+ const config = await getClientConfig();
13774
+ const client = initClient11(composeJobsMainContract, config);
13775
+ const result = await client.create({
13776
+ body: { githubUrl, overwrite }
13777
+ });
13778
+ if (result.status === 200 || result.status === 201) {
13779
+ return {
13780
+ jobId: result.body.jobId,
13781
+ status: result.body.status
13782
+ };
13783
+ }
13784
+ if (result.status === 400 || result.status === 401) {
13785
+ throw new Error(result.body.error.message);
13786
+ }
13787
+ throw new Error(`Unexpected response: ${result.status}`);
13788
+ }
13789
+ async function getComposeJobStatus(jobId) {
13790
+ const config = await getClientConfig();
13791
+ const client = initClient11(composeJobsByIdContract, config);
13792
+ const result = await client.getById({
13793
+ params: { jobId }
13794
+ });
13795
+ if (result.status === 200) {
13796
+ return {
13797
+ status: result.body.status,
13798
+ result: result.body.result,
13799
+ error: result.body.error
13800
+ };
13801
+ }
13802
+ if (result.status === 404) {
13803
+ throw new Error(`Job not found: ${jobId}`);
13804
+ }
13805
+ if (result.status === 401) {
13806
+ throw new Error(result.body.error.message);
13807
+ }
13808
+ throw new Error(`Unexpected response: ${result.status}`);
13809
+ }
13810
+ async function pollUntilComplete(jobId, intervalMs, timeoutMs, jsonMode) {
13811
+ const startTime = Date.now();
13812
+ while (Date.now() - startTime < timeoutMs) {
13813
+ const job = await getComposeJobStatus(jobId);
13814
+ if (!jsonMode) {
13815
+ console.log(
13816
+ chalk68.dim(`[${timestamp()}] Polling... status=${job.status}`)
13817
+ );
13818
+ }
13819
+ if (job.status === "completed" || job.status === "failed") {
13820
+ return job;
13821
+ }
13822
+ await sleep2(intervalMs);
13823
+ }
13824
+ throw new Error(`Timeout after ${timeoutMs / 1e3} seconds`);
13825
+ }
13826
+ var composeCommand2 = new Command68().name("compose").description("Test server-side GitHub compose API").argument("<github-url>", "GitHub URL to compose from").option("--overwrite", "Overwrite existing compose", false).option(
13827
+ "--interval <seconds>",
13828
+ "Polling interval in seconds",
13829
+ (v) => parseInt(v, 10),
13830
+ 5
13831
+ ).option(
13832
+ "--timeout <seconds>",
13833
+ "Maximum wait time in seconds",
13834
+ (v) => parseInt(v, 10),
13835
+ 300
13836
+ ).option("--json", "Output result as JSON").action(
13837
+ async (githubUrl, options) => {
13838
+ const intervalMs = options.interval * 1e3;
13839
+ const timeoutMs = options.timeout * 1e3;
13840
+ try {
13841
+ if (!options.json) {
13842
+ console.log("Creating compose job...");
13843
+ }
13844
+ const { jobId, status: initialStatus } = await createComposeJob(
13845
+ githubUrl,
13846
+ options.overwrite
13847
+ );
13848
+ if (!options.json) {
13849
+ console.log(`Job ID: ${chalk68.cyan(jobId)}`);
13850
+ console.log();
13851
+ }
13852
+ if (initialStatus === "completed" || initialStatus === "failed") {
13853
+ const finalJob2 = await getComposeJobStatus(jobId);
13854
+ if (options.json) {
13855
+ console.log(JSON.stringify(finalJob2, null, 2));
13856
+ } else {
13857
+ displayResult(finalJob2);
13858
+ }
13859
+ process.exit(finalJob2.status === "completed" ? 0 : 1);
13860
+ }
13861
+ const finalJob = await pollUntilComplete(
13862
+ jobId,
13863
+ intervalMs,
13864
+ timeoutMs,
13865
+ !!options.json
13866
+ );
13867
+ if (options.json) {
13868
+ console.log(JSON.stringify(finalJob, null, 2));
13869
+ } else {
13870
+ console.log();
13871
+ displayResult(finalJob);
13872
+ }
13873
+ process.exit(finalJob.status === "completed" ? 0 : 1);
13874
+ } catch (error) {
13875
+ if (options.json) {
13876
+ console.log(
13877
+ JSON.stringify({
13878
+ error: error instanceof Error ? error.message : String(error)
13879
+ })
13880
+ );
13881
+ } else {
13882
+ console.error(
13883
+ chalk68.red(
13884
+ `\u2717 ${error instanceof Error ? error.message : String(error)}`
13885
+ )
13886
+ );
13887
+ }
13888
+ process.exit(1);
13889
+ }
13890
+ }
13891
+ );
13892
+ function displayResult(job) {
13893
+ if (job.status === "completed" && job.result) {
13894
+ console.log(chalk68.green("\u2713 Compose completed!"));
13895
+ console.log(` Compose ID: ${chalk68.cyan(job.result.composeId)}`);
13896
+ console.log(` Name: ${chalk68.cyan(job.result.composeName)}`);
13897
+ console.log(` Version: ${chalk68.cyan(job.result.versionId.slice(0, 8))}`);
13898
+ if (job.result.warnings.length > 0) {
13899
+ console.log();
13900
+ console.log(chalk68.yellow(" Warnings:"));
13901
+ for (const warning of job.result.warnings) {
13902
+ console.log(chalk68.yellow(` - ${warning}`));
13903
+ }
13904
+ }
13905
+ } else if (job.status === "failed") {
13906
+ console.log(chalk68.red("\u2717 Compose failed!"));
13907
+ if (job.error) {
13908
+ console.log(` Error: ${chalk68.red(job.error)}`);
13909
+ }
13910
+ } else {
13911
+ console.log(`Status: ${job.status}`);
13912
+ }
13913
+ }
13914
+
13915
+ // src/commands/dev-tool/index.ts
13916
+ var devToolCommand = new Command69().name("dev-tool").description("Developer tools for testing and debugging").addCommand(composeCommand2);
13917
+
13546
13918
  // src/index.ts
13547
- var program = new Command68();
13548
- program.name("vm0").description("VM0 CLI - Build and run agents with natural language").version("9.21.0");
13919
+ var program = new Command70();
13920
+ program.name("vm0").description("VM0 CLI - Build and run agents with natural language").version("9.23.0");
13549
13921
  program.addCommand(authCommand);
13550
13922
  program.addCommand(infoCommand);
13551
13923
  program.addCommand(composeCommand);
@@ -13564,6 +13936,7 @@ program.addCommand(variableCommand);
13564
13936
  program.addCommand(modelProviderCommand);
13565
13937
  program.addCommand(onboardCommand);
13566
13938
  program.addCommand(setupClaudeCommand);
13939
+ program.addCommand(devToolCommand, { hidden: true });
13567
13940
  if (process.argv[1]?.endsWith("index.js") || process.argv[1]?.endsWith("index.ts") || process.argv[1]?.endsWith("vm0")) {
13568
13941
  program.parse();
13569
13942
  }