@vm0/cli 4.0.0 → 4.1.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 +232 -5
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Command as Command16 } from "commander";
5
- import chalk15 from "chalk";
4
+ import { Command as Command20 } from "commander";
5
+ import chalk18 from "chalk";
6
6
 
7
7
  // src/lib/auth.ts
8
8
  import chalk from "chalk";
@@ -304,6 +304,20 @@ var ApiClient = class {
304
304
  }
305
305
  return await response.json();
306
306
  }
307
+ async createImage(body) {
308
+ const baseUrl = await this.getBaseUrl();
309
+ const headers = await this.getHeaders();
310
+ const response = await fetch(`${baseUrl}/api/images`, {
311
+ method: "POST",
312
+ headers,
313
+ body: JSON.stringify(body)
314
+ });
315
+ if (!response.ok) {
316
+ const error = await response.json();
317
+ throw new Error(error.error?.message || "Failed to create image");
318
+ }
319
+ return await response.json();
320
+ }
307
321
  /**
308
322
  * Generic GET request
309
323
  */
@@ -2398,11 +2412,223 @@ var cookCommand = new Command15().name("cook").description("One-click agent prep
2398
2412
  }
2399
2413
  });
2400
2414
 
2415
+ // src/commands/image/index.ts
2416
+ import { Command as Command19 } from "commander";
2417
+
2418
+ // src/commands/image/build.ts
2419
+ import { Command as Command16 } from "commander";
2420
+ import chalk15 from "chalk";
2421
+ import { readFile as readFile5 } from "fs/promises";
2422
+ import { existsSync as existsSync5 } from "fs";
2423
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
2424
+ var buildCommand = new Command16().name("build").description("Build a custom image from a Dockerfile").requiredOption("-f, --file <path>", "Path to Dockerfile").requiredOption("-n, --name <name>", "Name for the image").action(async (options) => {
2425
+ const { file, name } = options;
2426
+ if (!existsSync5(file)) {
2427
+ console.error(chalk15.red(`\u2717 Dockerfile not found: ${file}`));
2428
+ process.exit(1);
2429
+ }
2430
+ const nameRegex = /^[a-zA-Z0-9][a-zA-Z0-9-]{1,62}[a-zA-Z0-9]$/;
2431
+ if (!nameRegex.test(name)) {
2432
+ console.error(
2433
+ chalk15.red(
2434
+ "\u2717 Invalid name format. Must be 3-64 characters, letters, numbers, and hyphens only."
2435
+ )
2436
+ );
2437
+ process.exit(1);
2438
+ }
2439
+ if (name.startsWith("vm0-")) {
2440
+ console.error(
2441
+ chalk15.red(
2442
+ '\u2717 Invalid name. Cannot start with "vm0-" (reserved prefix).'
2443
+ )
2444
+ );
2445
+ process.exit(1);
2446
+ }
2447
+ try {
2448
+ const dockerfile = await readFile5(file, "utf8");
2449
+ console.log(chalk15.blue(`Building image: ${name}`));
2450
+ console.log(chalk15.gray(` Dockerfile: ${file}`));
2451
+ console.log();
2452
+ const buildInfo = await apiClient.createImage({
2453
+ dockerfile,
2454
+ alias: name
2455
+ });
2456
+ const { imageId, buildId } = buildInfo;
2457
+ console.log(chalk15.gray(` Build ID: ${buildId}`));
2458
+ console.log();
2459
+ let logsOffset = 0;
2460
+ let status = "building";
2461
+ while (status === "building") {
2462
+ const statusResponse = await apiClient.get(
2463
+ `/api/images/${imageId}/builds/${buildId}?logsOffset=${logsOffset}`
2464
+ );
2465
+ if (!statusResponse.ok) {
2466
+ const error = await statusResponse.json();
2467
+ throw new Error(
2468
+ error.error?.message || "Failed to get build status"
2469
+ );
2470
+ }
2471
+ const statusData = await statusResponse.json();
2472
+ for (const log of statusData.logs) {
2473
+ console.log(chalk15.gray(` ${log}`));
2474
+ }
2475
+ logsOffset = statusData.logsOffset;
2476
+ status = statusData.status;
2477
+ if (status === "building") {
2478
+ await sleep(2e3);
2479
+ }
2480
+ }
2481
+ console.log();
2482
+ if (status === "ready") {
2483
+ console.log(chalk15.green(`\u2713 Image built: ${name}`));
2484
+ console.log();
2485
+ console.log("Use in vm0.yaml:");
2486
+ console.log(chalk15.cyan(` agents:`));
2487
+ console.log(chalk15.cyan(` your-agent:`));
2488
+ console.log(chalk15.cyan(` image: "${name}"`));
2489
+ } else {
2490
+ console.error(chalk15.red(`\u2717 Build failed`));
2491
+ process.exit(1);
2492
+ }
2493
+ } catch (error) {
2494
+ if (error instanceof Error) {
2495
+ if (error.message.includes("Not authenticated")) {
2496
+ console.error(chalk15.red("\u2717 Not authenticated. Run: vm0 auth login"));
2497
+ } else {
2498
+ console.error(chalk15.red(`\u2717 ${error.message}`));
2499
+ }
2500
+ } else {
2501
+ console.error(chalk15.red("\u2717 An unexpected error occurred"));
2502
+ }
2503
+ process.exit(1);
2504
+ }
2505
+ });
2506
+
2507
+ // src/commands/image/list.ts
2508
+ import { Command as Command17 } from "commander";
2509
+ import chalk16 from "chalk";
2510
+ var listCommand2 = new Command17().name("list").alias("ls").description("List your custom images").action(async () => {
2511
+ try {
2512
+ const response = await apiClient.get("/api/images");
2513
+ if (!response.ok) {
2514
+ const error = await response.json();
2515
+ throw new Error(
2516
+ error.error?.message || "Failed to list images"
2517
+ );
2518
+ }
2519
+ const data = await response.json();
2520
+ const { images } = data;
2521
+ if (images.length === 0) {
2522
+ console.log(chalk16.gray("No images found."));
2523
+ console.log();
2524
+ console.log("Build your first image:");
2525
+ console.log(
2526
+ chalk16.cyan(" vm0 image build --file Dockerfile --name my-image")
2527
+ );
2528
+ return;
2529
+ }
2530
+ console.log(chalk16.bold("Your images:"));
2531
+ console.log();
2532
+ console.log(
2533
+ chalk16.gray(
2534
+ `${"NAME".padEnd(30)} ${"STATUS".padEnd(12)} ${"CREATED".padEnd(20)}`
2535
+ )
2536
+ );
2537
+ console.log(chalk16.gray("-".repeat(62)));
2538
+ for (const image of images) {
2539
+ const statusColor = image.status === "ready" ? chalk16.green : image.status === "building" ? chalk16.yellow : chalk16.red;
2540
+ const createdAt = new Date(image.createdAt).toLocaleString();
2541
+ console.log(
2542
+ `${image.alias.padEnd(30)} ${statusColor(image.status.padEnd(12))} ${createdAt.padEnd(20)}`
2543
+ );
2544
+ if (image.status === "error" && image.errorMessage) {
2545
+ console.log(chalk16.red(` Error: ${image.errorMessage}`));
2546
+ }
2547
+ }
2548
+ console.log();
2549
+ console.log(chalk16.gray(`Total: ${images.length} image(s)`));
2550
+ } catch (error) {
2551
+ if (error instanceof Error) {
2552
+ if (error.message.includes("Not authenticated")) {
2553
+ console.error(chalk16.red("Not authenticated. Run: vm0 auth login"));
2554
+ } else {
2555
+ console.error(chalk16.red(`Error: ${error.message}`));
2556
+ }
2557
+ } else {
2558
+ console.error(chalk16.red("An unexpected error occurred"));
2559
+ }
2560
+ process.exit(1);
2561
+ }
2562
+ });
2563
+
2564
+ // src/commands/image/delete.ts
2565
+ import { Command as Command18 } from "commander";
2566
+ import chalk17 from "chalk";
2567
+ var deleteCommand2 = new Command18().name("delete").alias("rm").description("Delete a custom image").argument("<name>", "Name of the image to delete").option("-f, --force", "Skip confirmation prompt").action(async (name, options) => {
2568
+ try {
2569
+ const listResponse = await apiClient.get("/api/images");
2570
+ if (!listResponse.ok) {
2571
+ const error = await listResponse.json();
2572
+ throw new Error(
2573
+ error.error?.message || "Failed to list images"
2574
+ );
2575
+ }
2576
+ const data = await listResponse.json();
2577
+ const image = data.images.find((img) => img.alias === name);
2578
+ if (!image) {
2579
+ console.error(chalk17.red(`Image not found: ${name}`));
2580
+ process.exit(1);
2581
+ }
2582
+ if (!options.force) {
2583
+ const readline = await import("readline");
2584
+ const rl = readline.createInterface({
2585
+ input: process.stdin,
2586
+ output: process.stdout
2587
+ });
2588
+ const answer = await new Promise((resolve) => {
2589
+ rl.question(
2590
+ chalk17.yellow(`Delete image "${name}"? [y/N] `),
2591
+ (answer2) => {
2592
+ rl.close();
2593
+ resolve(answer2);
2594
+ }
2595
+ );
2596
+ });
2597
+ if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "yes") {
2598
+ console.log(chalk17.gray("Cancelled."));
2599
+ return;
2600
+ }
2601
+ }
2602
+ const deleteResponse = await apiClient.delete(`/api/images/${image.id}`);
2603
+ if (!deleteResponse.ok) {
2604
+ const error = await deleteResponse.json();
2605
+ throw new Error(
2606
+ error.error?.message || "Failed to delete image"
2607
+ );
2608
+ }
2609
+ console.log(chalk17.green(`Deleted image: ${name}`));
2610
+ } catch (error) {
2611
+ if (error instanceof Error) {
2612
+ if (error.message.includes("Not authenticated")) {
2613
+ console.error(chalk17.red("Not authenticated. Run: vm0 auth login"));
2614
+ } else {
2615
+ console.error(chalk17.red(`Error: ${error.message}`));
2616
+ }
2617
+ } else {
2618
+ console.error(chalk17.red("An unexpected error occurred"));
2619
+ }
2620
+ process.exit(1);
2621
+ }
2622
+ });
2623
+
2624
+ // src/commands/image/index.ts
2625
+ var imageCommand = new Command19().name("image").description("Manage custom images").addCommand(buildCommand).addCommand(listCommand2).addCommand(deleteCommand2);
2626
+
2401
2627
  // src/index.ts
2402
- var program = new Command16();
2403
- program.name("vm0").description("VM0 CLI - A modern build tool").version("4.0.0");
2628
+ var program = new Command20();
2629
+ program.name("vm0").description("VM0 CLI - A modern build tool").version("4.1.0");
2404
2630
  program.command("info").description("Display environment information").action(async () => {
2405
- console.log(chalk15.cyan("System Information:"));
2631
+ console.log(chalk18.cyan("System Information:"));
2406
2632
  console.log(`Node Version: ${process.version}`);
2407
2633
  console.log(`Platform: ${process.platform}`);
2408
2634
  console.log(`Architecture: ${process.arch}`);
@@ -2425,6 +2651,7 @@ program.addCommand(volumeCommand);
2425
2651
  program.addCommand(artifactCommand);
2426
2652
  program.addCommand(secretCommand);
2427
2653
  program.addCommand(cookCommand);
2654
+ program.addCommand(imageCommand);
2428
2655
  if (process.argv[1]?.endsWith("index.js") || process.argv[1]?.endsWith("index.ts") || process.argv[1]?.endsWith("vm0")) {
2429
2656
  program.parse();
2430
2657
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vm0/cli",
3
- "version": "4.0.0",
3
+ "version": "4.1.0",
4
4
  "description": "CLI application",
5
5
  "repository": {
6
6
  "type": "git",