@vm0/cli 5.7.0 → 5.8.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 +1016 -562
  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 Command35 } from "commander";
5
- import chalk36 from "chalk";
4
+ import { Command as Command36 } from "commander";
5
+ import chalk37 from "chalk";
6
6
 
7
7
  // src/lib/api/auth.ts
8
8
  import chalk from "chalk";
@@ -333,7 +333,9 @@ var storedExecutionContextSchema = z2.object({
333
333
  encryptedSecrets: z2.string().nullable(),
334
334
  // AES-256-GCM encrypted secrets
335
335
  cliAgentType: z2.string(),
336
- experimentalFirewall: experimentalFirewallSchema.optional()
336
+ experimentalFirewall: experimentalFirewallSchema.optional(),
337
+ // Debug flag to force real Claude in mock environments (internal use only)
338
+ debugNoMockClaude: z2.boolean().optional()
337
339
  });
338
340
  var executionContextSchema = z2.object({
339
341
  runId: z2.string().uuid(),
@@ -351,7 +353,9 @@ var executionContextSchema = z2.object({
351
353
  secretValues: z2.array(z2.string()).nullable(),
352
354
  cliAgentType: z2.string(),
353
355
  // Experimental firewall configuration
354
- experimentalFirewall: experimentalFirewallSchema.optional()
356
+ experimentalFirewall: experimentalFirewallSchema.optional(),
357
+ // Debug flag to force real Claude in mock environments (internal use only)
358
+ debugNoMockClaude: z2.boolean().optional()
355
359
  });
356
360
  var runnersJobClaimContract = c.router({
357
361
  claim: {
@@ -634,6 +638,8 @@ var unifiedRunRequestSchema = z4.object({
634
638
  vars: z4.record(z4.string(), z4.string()).optional(),
635
639
  secrets: z4.record(z4.string(), z4.string()).optional(),
636
640
  volumeVersions: z4.record(z4.string(), z4.string()).optional(),
641
+ // Debug flag to force real Claude in mock environments (internal use only)
642
+ debugNoMockClaude: z4.boolean().optional(),
637
643
  // Required
638
644
  prompt: z4.string().min(1, "Missing prompt")
639
645
  });
@@ -2586,6 +2592,16 @@ function getProviderDisplayName(provider) {
2586
2592
  return PROVIDER_DISPLAY_NAMES[provider];
2587
2593
  }
2588
2594
 
2595
+ // ../../packages/core/src/feature-switch.ts
2596
+ var PricingSwitch = {
2597
+ key: "pricing" /* Pricing */,
2598
+ maintainer: "ethan@vm0.ai",
2599
+ enabled: false
2600
+ };
2601
+ var FEATURE_SWITCHES = {
2602
+ ["pricing" /* Pricing */]: PricingSwitch
2603
+ };
2604
+
2589
2605
  // src/lib/api/api-client.ts
2590
2606
  import { initClient } from "@ts-rest/core";
2591
2607
  var ApiClient = class {
@@ -2595,8 +2611,7 @@ var ApiClient = class {
2595
2611
  throw new Error("Not authenticated. Run: vm0 auth login");
2596
2612
  }
2597
2613
  const headers = {
2598
- Authorization: `Bearer ${token}`,
2599
- "Content-Type": "application/json"
2614
+ Authorization: `Bearer ${token}`
2600
2615
  };
2601
2616
  const bypassSecret = process.env.VERCEL_AUTOMATION_BYPASS_SECRET;
2602
2617
  if (bypassSecret) {
@@ -2614,35 +2629,41 @@ var ApiClient = class {
2614
2629
  async getComposeByName(name, scope) {
2615
2630
  const baseUrl = await this.getBaseUrl();
2616
2631
  const headers = await this.getHeaders();
2617
- const params = new URLSearchParams({ name });
2618
- if (scope) {
2619
- params.append("scope", scope);
2620
- }
2621
- const response = await fetch(
2622
- `${baseUrl}/api/agent/composes?${params.toString()}`,
2623
- {
2624
- method: "GET",
2625
- headers
2632
+ const client = initClient(composesMainContract, {
2633
+ baseUrl,
2634
+ baseHeaders: headers,
2635
+ jsonQuery: true
2636
+ });
2637
+ const result = await client.getByName({
2638
+ query: {
2639
+ name,
2640
+ scope
2626
2641
  }
2627
- );
2628
- if (!response.ok) {
2629
- const error = await response.json();
2630
- throw new Error(error.error?.message || `Compose not found: ${name}`);
2642
+ });
2643
+ if (result.status === 200) {
2644
+ return result.body;
2631
2645
  }
2632
- return await response.json();
2646
+ const errorBody = result.body;
2647
+ const message = errorBody.error?.message || `Compose not found: ${name}`;
2648
+ throw new Error(message);
2633
2649
  }
2634
2650
  async getComposeById(id) {
2635
2651
  const baseUrl = await this.getBaseUrl();
2636
2652
  const headers = await this.getHeaders();
2637
- const response = await fetch(`${baseUrl}/api/agent/composes/${id}`, {
2638
- method: "GET",
2639
- headers
2653
+ const client = initClient(composesByIdContract, {
2654
+ baseUrl,
2655
+ baseHeaders: headers,
2656
+ jsonQuery: true
2640
2657
  });
2641
- if (!response.ok) {
2642
- const error = await response.json();
2643
- throw new Error(error.error?.message || `Compose not found: ${id}`);
2658
+ const result = await client.getById({
2659
+ params: { id }
2660
+ });
2661
+ if (result.status === 200) {
2662
+ return result.body;
2644
2663
  }
2645
- return await response.json();
2664
+ const errorBody = result.body;
2665
+ const message = errorBody.error?.message || `Compose not found: ${id}`;
2666
+ throw new Error(message);
2646
2667
  }
2647
2668
  /**
2648
2669
  * Resolve a version specifier to a full version ID
@@ -2651,33 +2672,41 @@ var ApiClient = class {
2651
2672
  async getComposeVersion(composeId, version) {
2652
2673
  const baseUrl = await this.getBaseUrl();
2653
2674
  const headers = await this.getHeaders();
2654
- const quotedVersion = JSON.stringify(version);
2655
- const response = await fetch(
2656
- `${baseUrl}/api/agent/composes/versions?composeId=${encodeURIComponent(composeId)}&version=${encodeURIComponent(quotedVersion)}`,
2657
- {
2658
- method: "GET",
2659
- headers
2675
+ const client = initClient(composesVersionsContract, {
2676
+ baseUrl,
2677
+ baseHeaders: headers,
2678
+ jsonQuery: true
2679
+ });
2680
+ const result = await client.resolveVersion({
2681
+ query: {
2682
+ composeId,
2683
+ version
2660
2684
  }
2661
- );
2662
- if (!response.ok) {
2663
- const error = await response.json();
2664
- throw new Error(error.error?.message || `Version not found: ${version}`);
2685
+ });
2686
+ if (result.status === 200) {
2687
+ return result.body;
2665
2688
  }
2666
- return await response.json();
2689
+ const errorBody = result.body;
2690
+ const message = errorBody.error?.message || `Version not found: ${version}`;
2691
+ throw new Error(message);
2667
2692
  }
2668
2693
  async createOrUpdateCompose(body) {
2669
2694
  const baseUrl = await this.getBaseUrl();
2670
2695
  const headers = await this.getHeaders();
2671
- const response = await fetch(`${baseUrl}/api/agent/composes`, {
2672
- method: "POST",
2673
- headers,
2674
- body: JSON.stringify(body)
2696
+ const client = initClient(composesMainContract, {
2697
+ baseUrl,
2698
+ baseHeaders: headers,
2699
+ jsonQuery: true
2675
2700
  });
2676
- if (!response.ok) {
2677
- const error = await response.json();
2678
- throw new Error(error.error?.message || "Failed to create compose");
2701
+ const result = await client.create({
2702
+ body
2703
+ });
2704
+ if (result.status === 200 || result.status === 201) {
2705
+ return result.body;
2679
2706
  }
2680
- return await response.json();
2707
+ const errorBody = result.body;
2708
+ const message = errorBody.error?.message || "Failed to create compose";
2709
+ throw new Error(message);
2681
2710
  }
2682
2711
  /**
2683
2712
  * Create a run with unified request format
@@ -2703,120 +2732,116 @@ var ApiClient = class {
2703
2732
  async getEvents(runId, options) {
2704
2733
  const baseUrl = await this.getBaseUrl();
2705
2734
  const headers = await this.getHeaders();
2706
- const since = options?.since ?? 0;
2707
- const limit = options?.limit ?? 100;
2708
- const response = await fetch(
2709
- `${baseUrl}/api/agent/runs/${runId}/events?since=${since}&limit=${limit}`,
2710
- {
2711
- method: "GET",
2712
- headers
2735
+ const client = initClient(runEventsContract, {
2736
+ baseUrl,
2737
+ baseHeaders: headers,
2738
+ jsonQuery: true
2739
+ });
2740
+ const result = await client.getEvents({
2741
+ params: { id: runId },
2742
+ query: {
2743
+ since: options?.since ?? 0,
2744
+ limit: options?.limit ?? 100
2713
2745
  }
2714
- );
2715
- if (!response.ok) {
2716
- const error = await response.json();
2717
- throw new Error(error.error?.message || "Failed to fetch events");
2746
+ });
2747
+ if (result.status === 200) {
2748
+ return result.body;
2718
2749
  }
2719
- return await response.json();
2750
+ const errorBody = result.body;
2751
+ const message = errorBody.error?.message || "Failed to fetch events";
2752
+ throw new Error(message);
2720
2753
  }
2721
2754
  async getSystemLog(runId, options) {
2722
2755
  const baseUrl = await this.getBaseUrl();
2723
2756
  const headers = await this.getHeaders();
2724
- const params = new URLSearchParams();
2725
- if (options?.since !== void 0) {
2726
- params.set("since", String(options.since));
2727
- }
2728
- if (options?.limit !== void 0) {
2729
- params.set("limit", String(options.limit));
2730
- }
2731
- if (options?.order !== void 0) {
2732
- params.set("order", options.order);
2733
- }
2734
- const queryString = params.toString();
2735
- const url = `${baseUrl}/api/agent/runs/${runId}/telemetry/system-log${queryString ? `?${queryString}` : ""}`;
2736
- const response = await fetch(url, {
2737
- method: "GET",
2738
- headers
2757
+ const client = initClient(runSystemLogContract, {
2758
+ baseUrl,
2759
+ baseHeaders: headers,
2760
+ jsonQuery: true
2739
2761
  });
2740
- if (!response.ok) {
2741
- const error = await response.json();
2742
- throw new Error(error.error?.message || "Failed to fetch system log");
2762
+ const result = await client.getSystemLog({
2763
+ params: { id: runId },
2764
+ query: {
2765
+ since: options?.since,
2766
+ limit: options?.limit,
2767
+ order: options?.order
2768
+ }
2769
+ });
2770
+ if (result.status === 200) {
2771
+ return result.body;
2743
2772
  }
2744
- return await response.json();
2773
+ const errorBody = result.body;
2774
+ const message = errorBody.error?.message || "Failed to fetch system log";
2775
+ throw new Error(message);
2745
2776
  }
2746
2777
  async getMetrics(runId, options) {
2747
2778
  const baseUrl = await this.getBaseUrl();
2748
2779
  const headers = await this.getHeaders();
2749
- const params = new URLSearchParams();
2750
- if (options?.since !== void 0) {
2751
- params.set("since", String(options.since));
2752
- }
2753
- if (options?.limit !== void 0) {
2754
- params.set("limit", String(options.limit));
2755
- }
2756
- if (options?.order !== void 0) {
2757
- params.set("order", options.order);
2758
- }
2759
- const queryString = params.toString();
2760
- const url = `${baseUrl}/api/agent/runs/${runId}/telemetry/metrics${queryString ? `?${queryString}` : ""}`;
2761
- const response = await fetch(url, {
2762
- method: "GET",
2763
- headers
2780
+ const client = initClient(runMetricsContract, {
2781
+ baseUrl,
2782
+ baseHeaders: headers,
2783
+ jsonQuery: true
2764
2784
  });
2765
- if (!response.ok) {
2766
- const error = await response.json();
2767
- throw new Error(error.error?.message || "Failed to fetch metrics");
2785
+ const result = await client.getMetrics({
2786
+ params: { id: runId },
2787
+ query: {
2788
+ since: options?.since,
2789
+ limit: options?.limit,
2790
+ order: options?.order
2791
+ }
2792
+ });
2793
+ if (result.status === 200) {
2794
+ return result.body;
2768
2795
  }
2769
- return await response.json();
2796
+ const errorBody = result.body;
2797
+ const message = errorBody.error?.message || "Failed to fetch metrics";
2798
+ throw new Error(message);
2770
2799
  }
2771
2800
  async getAgentEvents(runId, options) {
2772
2801
  const baseUrl = await this.getBaseUrl();
2773
2802
  const headers = await this.getHeaders();
2774
- const params = new URLSearchParams();
2775
- if (options?.since !== void 0) {
2776
- params.set("since", String(options.since));
2777
- }
2778
- if (options?.limit !== void 0) {
2779
- params.set("limit", String(options.limit));
2780
- }
2781
- if (options?.order !== void 0) {
2782
- params.set("order", options.order);
2783
- }
2784
- const queryString = params.toString();
2785
- const url = `${baseUrl}/api/agent/runs/${runId}/telemetry/agent${queryString ? `?${queryString}` : ""}`;
2786
- const response = await fetch(url, {
2787
- method: "GET",
2788
- headers
2803
+ const client = initClient(runAgentEventsContract, {
2804
+ baseUrl,
2805
+ baseHeaders: headers,
2806
+ jsonQuery: true
2789
2807
  });
2790
- if (!response.ok) {
2791
- const error = await response.json();
2792
- throw new Error(error.error?.message || "Failed to fetch agent events");
2808
+ const result = await client.getAgentEvents({
2809
+ params: { id: runId },
2810
+ query: {
2811
+ since: options?.since,
2812
+ limit: options?.limit,
2813
+ order: options?.order
2814
+ }
2815
+ });
2816
+ if (result.status === 200) {
2817
+ return result.body;
2793
2818
  }
2794
- return await response.json();
2819
+ const errorBody = result.body;
2820
+ const message = errorBody.error?.message || "Failed to fetch agent events";
2821
+ throw new Error(message);
2795
2822
  }
2796
2823
  async getNetworkLogs(runId, options) {
2797
2824
  const baseUrl = await this.getBaseUrl();
2798
2825
  const headers = await this.getHeaders();
2799
- const params = new URLSearchParams();
2800
- if (options?.since !== void 0) {
2801
- params.set("since", String(options.since));
2802
- }
2803
- if (options?.limit !== void 0) {
2804
- params.set("limit", String(options.limit));
2805
- }
2806
- if (options?.order !== void 0) {
2807
- params.set("order", options.order);
2808
- }
2809
- const queryString = params.toString();
2810
- const url = `${baseUrl}/api/agent/runs/${runId}/telemetry/network${queryString ? `?${queryString}` : ""}`;
2811
- const response = await fetch(url, {
2812
- method: "GET",
2813
- headers
2826
+ const client = initClient(runNetworkLogsContract, {
2827
+ baseUrl,
2828
+ baseHeaders: headers,
2829
+ jsonQuery: true
2814
2830
  });
2815
- if (!response.ok) {
2816
- const error = await response.json();
2817
- throw new Error(error.error?.message || "Failed to fetch network logs");
2831
+ const result = await client.getNetworkLogs({
2832
+ params: { id: runId },
2833
+ query: {
2834
+ since: options?.since,
2835
+ limit: options?.limit,
2836
+ order: options?.order
2837
+ }
2838
+ });
2839
+ if (result.status === 200) {
2840
+ return result.body;
2818
2841
  }
2819
- return await response.json();
2842
+ const errorBody = result.body;
2843
+ const message = errorBody.error?.message || "Failed to fetch network logs";
2844
+ throw new Error(message);
2820
2845
  }
2821
2846
  /**
2822
2847
  * Get current user's scope
@@ -2824,15 +2849,18 @@ var ApiClient = class {
2824
2849
  async getScope() {
2825
2850
  const baseUrl = await this.getBaseUrl();
2826
2851
  const headers = await this.getHeaders();
2827
- const response = await fetch(`${baseUrl}/api/scope`, {
2828
- method: "GET",
2829
- headers
2852
+ const client = initClient(scopeContract, {
2853
+ baseUrl,
2854
+ baseHeaders: headers,
2855
+ jsonQuery: true
2830
2856
  });
2831
- if (!response.ok) {
2832
- const error = await response.json();
2833
- throw new Error(error.error?.message || "Failed to get scope");
2857
+ const result = await client.get();
2858
+ if (result.status === 200) {
2859
+ return result.body;
2834
2860
  }
2835
- return await response.json();
2861
+ const errorBody = result.body;
2862
+ const message = errorBody.error?.message || "Failed to get scope";
2863
+ throw new Error(message);
2836
2864
  }
2837
2865
  /**
2838
2866
  * Create user's scope
@@ -2840,16 +2868,18 @@ var ApiClient = class {
2840
2868
  async createScope(body) {
2841
2869
  const baseUrl = await this.getBaseUrl();
2842
2870
  const headers = await this.getHeaders();
2843
- const response = await fetch(`${baseUrl}/api/scope`, {
2844
- method: "POST",
2845
- headers,
2846
- body: JSON.stringify(body)
2871
+ const client = initClient(scopeContract, {
2872
+ baseUrl,
2873
+ baseHeaders: headers,
2874
+ jsonQuery: true
2847
2875
  });
2848
- if (!response.ok) {
2849
- const error = await response.json();
2850
- throw new Error(error.error?.message || "Failed to create scope");
2876
+ const result = await client.create({ body });
2877
+ if (result.status === 201) {
2878
+ return result.body;
2851
2879
  }
2852
- return await response.json();
2880
+ const errorBody = result.body;
2881
+ const message = errorBody.error?.message || "Failed to create scope";
2882
+ throw new Error(message);
2853
2883
  }
2854
2884
  /**
2855
2885
  * Update user's scope slug
@@ -2857,16 +2887,18 @@ var ApiClient = class {
2857
2887
  async updateScope(body) {
2858
2888
  const baseUrl = await this.getBaseUrl();
2859
2889
  const headers = await this.getHeaders();
2860
- const response = await fetch(`${baseUrl}/api/scope`, {
2861
- method: "PUT",
2862
- headers,
2863
- body: JSON.stringify(body)
2890
+ const client = initClient(scopeContract, {
2891
+ baseUrl,
2892
+ baseHeaders: headers,
2893
+ jsonQuery: true
2864
2894
  });
2865
- if (!response.ok) {
2866
- const error = await response.json();
2867
- throw new Error(error.error?.message || "Failed to update scope");
2895
+ const result = await client.update({ body });
2896
+ if (result.status === 200) {
2897
+ return result.body;
2868
2898
  }
2869
- return await response.json();
2899
+ const errorBody = result.body;
2900
+ const message = errorBody.error?.message || "Failed to update scope";
2901
+ throw new Error(message);
2870
2902
  }
2871
2903
  /**
2872
2904
  * Get session by ID
@@ -2875,17 +2907,20 @@ var ApiClient = class {
2875
2907
  async getSession(sessionId) {
2876
2908
  const baseUrl = await this.getBaseUrl();
2877
2909
  const headers = await this.getHeaders();
2878
- const response = await fetch(`${baseUrl}/api/agent/sessions/${sessionId}`, {
2879
- method: "GET",
2880
- headers
2910
+ const client = initClient(sessionsByIdContract, {
2911
+ baseUrl,
2912
+ baseHeaders: headers,
2913
+ jsonQuery: true
2881
2914
  });
2882
- if (!response.ok) {
2883
- const error = await response.json();
2884
- throw new Error(
2885
- error.error?.message || `Session not found: ${sessionId}`
2886
- );
2915
+ const result = await client.getById({
2916
+ params: { id: sessionId }
2917
+ });
2918
+ if (result.status === 200) {
2919
+ return result.body;
2887
2920
  }
2888
- return await response.json();
2921
+ const errorBody = result.body;
2922
+ const message = errorBody.error?.message || `Session not found: ${sessionId}`;
2923
+ throw new Error(message);
2889
2924
  }
2890
2925
  /**
2891
2926
  * Get checkpoint by ID
@@ -2894,20 +2929,368 @@ var ApiClient = class {
2894
2929
  async getCheckpoint(checkpointId) {
2895
2930
  const baseUrl = await this.getBaseUrl();
2896
2931
  const headers = await this.getHeaders();
2897
- const response = await fetch(
2898
- `${baseUrl}/api/agent/checkpoints/${checkpointId}`,
2899
- {
2900
- method: "GET",
2901
- headers
2932
+ const client = initClient(checkpointsByIdContract, {
2933
+ baseUrl,
2934
+ baseHeaders: headers,
2935
+ jsonQuery: true
2936
+ });
2937
+ const result = await client.getById({
2938
+ params: { id: checkpointId }
2939
+ });
2940
+ if (result.status === 200) {
2941
+ return result.body;
2942
+ }
2943
+ const errorBody = result.body;
2944
+ const message = errorBody.error?.message || `Checkpoint not found: ${checkpointId}`;
2945
+ throw new Error(message);
2946
+ }
2947
+ /**
2948
+ * Prepare storage for direct S3 upload
2949
+ */
2950
+ async prepareStorage(body) {
2951
+ const baseUrl = await this.getBaseUrl();
2952
+ const headers = await this.getHeaders();
2953
+ const client = initClient(storagesPrepareContract, {
2954
+ baseUrl,
2955
+ baseHeaders: headers,
2956
+ jsonQuery: true
2957
+ });
2958
+ const result = await client.prepare({ body });
2959
+ if (result.status === 200) {
2960
+ return result.body;
2961
+ }
2962
+ const errorBody = result.body;
2963
+ const message = errorBody.error?.message || "Failed to prepare storage";
2964
+ throw new Error(message);
2965
+ }
2966
+ /**
2967
+ * Commit storage after S3 upload
2968
+ */
2969
+ async commitStorage(body) {
2970
+ const baseUrl = await this.getBaseUrl();
2971
+ const headers = await this.getHeaders();
2972
+ const client = initClient(storagesCommitContract, {
2973
+ baseUrl,
2974
+ baseHeaders: headers,
2975
+ jsonQuery: true
2976
+ });
2977
+ const result = await client.commit({ body });
2978
+ if (result.status === 200) {
2979
+ return result.body;
2980
+ }
2981
+ const errorBody = result.body;
2982
+ const message = errorBody.error?.message || "Failed to commit storage";
2983
+ throw new Error(message);
2984
+ }
2985
+ /**
2986
+ * Get download URL for storage (volume or artifact)
2987
+ */
2988
+ async getStorageDownload(query) {
2989
+ const baseUrl = await this.getBaseUrl();
2990
+ const headers = await this.getHeaders();
2991
+ const client = initClient(storagesDownloadContract, {
2992
+ baseUrl,
2993
+ baseHeaders: headers,
2994
+ jsonQuery: true
2995
+ });
2996
+ const result = await client.download({
2997
+ query: {
2998
+ name: query.name,
2999
+ type: query.type,
3000
+ version: query.version
2902
3001
  }
2903
- );
3002
+ });
3003
+ if (result.status === 200) {
3004
+ return result.body;
3005
+ }
3006
+ const errorBody = result.body;
3007
+ const message = errorBody.error?.message || `Storage "${query.name}" not found`;
3008
+ throw new Error(message);
3009
+ }
3010
+ /**
3011
+ * List storages (volumes or artifacts)
3012
+ */
3013
+ async listStorages(query) {
3014
+ const baseUrl = await this.getBaseUrl();
3015
+ const headers = await this.getHeaders();
3016
+ const client = initClient(storagesListContract, {
3017
+ baseUrl,
3018
+ baseHeaders: headers,
3019
+ jsonQuery: true
3020
+ });
3021
+ const result = await client.list({ query });
3022
+ if (result.status === 200) {
3023
+ return result.body;
3024
+ }
3025
+ const errorBody = result.body;
3026
+ const message = errorBody.error?.message || `Failed to list ${query.type}s`;
3027
+ throw new Error(message);
3028
+ }
3029
+ /**
3030
+ * Deploy schedule (create or update)
3031
+ */
3032
+ async deploySchedule(body) {
3033
+ const baseUrl = await this.getBaseUrl();
3034
+ const headers = await this.getHeaders();
3035
+ const client = initClient(schedulesMainContract, {
3036
+ baseUrl,
3037
+ baseHeaders: headers,
3038
+ jsonQuery: true
3039
+ });
3040
+ const result = await client.deploy({ body });
3041
+ if (result.status === 200 || result.status === 201) {
3042
+ return result.body;
3043
+ }
3044
+ const errorBody = result.body;
3045
+ const message = errorBody.error?.message || "Failed to deploy schedule";
3046
+ throw new Error(message);
3047
+ }
3048
+ /**
3049
+ * List all schedules
3050
+ */
3051
+ async listSchedules() {
3052
+ const baseUrl = await this.getBaseUrl();
3053
+ const headers = await this.getHeaders();
3054
+ const client = initClient(schedulesMainContract, {
3055
+ baseUrl,
3056
+ baseHeaders: headers,
3057
+ jsonQuery: true
3058
+ });
3059
+ const result = await client.list();
3060
+ if (result.status === 200) {
3061
+ return result.body;
3062
+ }
3063
+ const errorBody = result.body;
3064
+ const message = errorBody.error?.message || "Failed to list schedules";
3065
+ throw new Error(message);
3066
+ }
3067
+ /**
3068
+ * Get schedule by name
3069
+ */
3070
+ async getScheduleByName(params) {
3071
+ const baseUrl = await this.getBaseUrl();
3072
+ const headers = await this.getHeaders();
3073
+ const client = initClient(schedulesByNameContract, {
3074
+ baseUrl,
3075
+ baseHeaders: headers,
3076
+ jsonQuery: true
3077
+ });
3078
+ const result = await client.getByName({
3079
+ params: { name: params.name },
3080
+ query: { composeId: params.composeId }
3081
+ });
3082
+ if (result.status === 200) {
3083
+ return result.body;
3084
+ }
3085
+ const errorBody = result.body;
3086
+ const message = errorBody.error?.message || `Schedule "${params.name}" not found`;
3087
+ throw new Error(message);
3088
+ }
3089
+ /**
3090
+ * Delete schedule by name
3091
+ */
3092
+ async deleteSchedule(params) {
3093
+ const baseUrl = await this.getBaseUrl();
3094
+ const headers = await this.getHeaders();
3095
+ const client = initClient(schedulesByNameContract, {
3096
+ baseUrl,
3097
+ baseHeaders: headers,
3098
+ jsonQuery: true
3099
+ });
3100
+ const result = await client.delete({
3101
+ params: { name: params.name },
3102
+ query: { composeId: params.composeId }
3103
+ });
3104
+ if (result.status === 204) {
3105
+ return;
3106
+ }
3107
+ const errorBody = result.body;
3108
+ const message = errorBody.error?.message || `Schedule "${params.name}" not found on remote`;
3109
+ throw new Error(message);
3110
+ }
3111
+ /**
3112
+ * Enable schedule
3113
+ */
3114
+ async enableSchedule(params) {
3115
+ const baseUrl = await this.getBaseUrl();
3116
+ const headers = await this.getHeaders();
3117
+ const client = initClient(schedulesEnableContract, {
3118
+ baseUrl,
3119
+ baseHeaders: headers,
3120
+ jsonQuery: true
3121
+ });
3122
+ const result = await client.enable({
3123
+ params: { name: params.name },
3124
+ body: { composeId: params.composeId }
3125
+ });
3126
+ if (result.status === 200) {
3127
+ return result.body;
3128
+ }
3129
+ const errorBody = result.body;
3130
+ const message = errorBody.error?.message || `Failed to enable schedule "${params.name}"`;
3131
+ throw new Error(message);
3132
+ }
3133
+ /**
3134
+ * Disable schedule
3135
+ */
3136
+ async disableSchedule(params) {
3137
+ const baseUrl = await this.getBaseUrl();
3138
+ const headers = await this.getHeaders();
3139
+ const client = initClient(schedulesEnableContract, {
3140
+ baseUrl,
3141
+ baseHeaders: headers,
3142
+ jsonQuery: true
3143
+ });
3144
+ const result = await client.disable({
3145
+ params: { name: params.name },
3146
+ body: { composeId: params.composeId }
3147
+ });
3148
+ if (result.status === 200) {
3149
+ return result.body;
3150
+ }
3151
+ const errorBody = result.body;
3152
+ const message = errorBody.error?.message || `Failed to disable schedule "${params.name}"`;
3153
+ throw new Error(message);
3154
+ }
3155
+ /**
3156
+ * List recent runs for a schedule
3157
+ */
3158
+ async listScheduleRuns(params) {
3159
+ const baseUrl = await this.getBaseUrl();
3160
+ const headers = await this.getHeaders();
3161
+ const client = initClient(scheduleRunsContract, {
3162
+ baseUrl,
3163
+ baseHeaders: headers,
3164
+ jsonQuery: true
3165
+ });
3166
+ const result = await client.listRuns({
3167
+ params: { name: params.name },
3168
+ query: {
3169
+ composeId: params.composeId,
3170
+ limit: params.limit ?? 5
3171
+ }
3172
+ });
3173
+ if (result.status === 200) {
3174
+ return result.body;
3175
+ }
3176
+ const errorBody = result.body;
3177
+ const message = errorBody.error?.message || `Failed to list runs for schedule "${params.name}"`;
3178
+ throw new Error(message);
3179
+ }
3180
+ /**
3181
+ * List public agents
3182
+ */
3183
+ async listPublicAgents(query) {
3184
+ const baseUrl = await this.getBaseUrl();
3185
+ const headers = await this.getHeaders();
3186
+ const client = initClient(publicAgentsListContract, {
3187
+ baseUrl,
3188
+ baseHeaders: headers,
3189
+ jsonQuery: true
3190
+ });
3191
+ const result = await client.list({ query: query ?? {} });
3192
+ if (result.status === 200) {
3193
+ return result.body;
3194
+ }
3195
+ const errorBody = result.body;
3196
+ const message = errorBody.error?.message || "Failed to list agents";
3197
+ throw new Error(message);
3198
+ }
3199
+ /**
3200
+ * List public artifacts
3201
+ */
3202
+ async listPublicArtifacts(query) {
3203
+ const baseUrl = await this.getBaseUrl();
3204
+ const headers = await this.getHeaders();
3205
+ const client = initClient(publicArtifactsListContract, {
3206
+ baseUrl,
3207
+ baseHeaders: headers,
3208
+ jsonQuery: true
3209
+ });
3210
+ const result = await client.list({ query: query ?? {} });
3211
+ if (result.status === 200) {
3212
+ return result.body;
3213
+ }
3214
+ const errorBody = result.body;
3215
+ const message = errorBody.error?.message || "Failed to list artifacts";
3216
+ throw new Error(message);
3217
+ }
3218
+ /**
3219
+ * Get public artifact by ID
3220
+ */
3221
+ async getPublicArtifact(id) {
3222
+ const baseUrl = await this.getBaseUrl();
3223
+ const headers = await this.getHeaders();
3224
+ const client = initClient(publicArtifactByIdContract, {
3225
+ baseUrl,
3226
+ baseHeaders: headers,
3227
+ jsonQuery: true
3228
+ });
3229
+ const result = await client.get({ params: { id } });
3230
+ if (result.status === 200) {
3231
+ return result.body;
3232
+ }
3233
+ const errorBody = result.body;
3234
+ const message = errorBody.error?.message || `Artifact "${id}" not found`;
3235
+ throw new Error(message);
3236
+ }
3237
+ /**
3238
+ * List public volumes
3239
+ */
3240
+ async listPublicVolumes(query) {
3241
+ const baseUrl = await this.getBaseUrl();
3242
+ const headers = await this.getHeaders();
3243
+ const client = initClient(publicVolumesListContract, {
3244
+ baseUrl,
3245
+ baseHeaders: headers,
3246
+ jsonQuery: true
3247
+ });
3248
+ const result = await client.list({ query: query ?? {} });
3249
+ if (result.status === 200) {
3250
+ return result.body;
3251
+ }
3252
+ const errorBody = result.body;
3253
+ const message = errorBody.error?.message || "Failed to list volumes";
3254
+ throw new Error(message);
3255
+ }
3256
+ /**
3257
+ * Get public volume by ID
3258
+ */
3259
+ async getPublicVolume(id) {
3260
+ const baseUrl = await this.getBaseUrl();
3261
+ const headers = await this.getHeaders();
3262
+ const client = initClient(publicVolumeByIdContract, {
3263
+ baseUrl,
3264
+ baseHeaders: headers,
3265
+ jsonQuery: true
3266
+ });
3267
+ const result = await client.get({ params: { id } });
3268
+ if (result.status === 200) {
3269
+ return result.body;
3270
+ }
3271
+ const errorBody = result.body;
3272
+ const message = errorBody.error?.message || `Volume "${id}" not found`;
3273
+ throw new Error(message);
3274
+ }
3275
+ /**
3276
+ * Get usage statistics
3277
+ */
3278
+ async getUsage(options) {
3279
+ const baseUrl = await this.getBaseUrl();
3280
+ const headers = await this.getHeaders();
3281
+ const params = new URLSearchParams({
3282
+ start_date: options.startDate,
3283
+ end_date: options.endDate
3284
+ });
3285
+ const response = await fetch(`${baseUrl}/api/usage?${params}`, {
3286
+ method: "GET",
3287
+ headers
3288
+ });
2904
3289
  if (!response.ok) {
2905
3290
  const error = await response.json();
2906
- throw new Error(
2907
- error.error?.message || `Checkpoint not found: ${checkpointId}`
2908
- );
3291
+ throw new Error(error.error?.message || "Failed to fetch usage data");
2909
3292
  }
2910
- return await response.json();
3293
+ return response.json();
2911
3294
  }
2912
3295
  /**
2913
3296
  * Generic GET request
@@ -3549,34 +3932,20 @@ async function directUpload(storageName, storageType, cwd, options) {
3549
3932
  onProgress?.("Computing file hashes...");
3550
3933
  const fileEntries = await collectFileMetadata(cwd, files, onProgress);
3551
3934
  onProgress?.("Preparing upload...");
3552
- const prepareResponse = await apiClient.post("/api/storages/prepare", {
3553
- body: JSON.stringify({
3554
- storageName,
3555
- storageType,
3556
- files: fileEntries,
3557
- force
3558
- })
3935
+ const prepareResult = await apiClient.prepareStorage({
3936
+ storageName,
3937
+ storageType,
3938
+ files: fileEntries,
3939
+ force
3559
3940
  });
3560
- if (!prepareResponse.ok) {
3561
- const error = await prepareResponse.json();
3562
- throw new Error(error.error?.message || "Prepare failed");
3563
- }
3564
- const prepareResult = await prepareResponse.json();
3565
3941
  if (prepareResult.existing) {
3566
3942
  onProgress?.("Version exists, updating HEAD...");
3567
- const commitResponse2 = await apiClient.post("/api/storages/commit", {
3568
- body: JSON.stringify({
3569
- storageName,
3570
- storageType,
3571
- versionId: prepareResult.versionId,
3572
- files: fileEntries
3573
- })
3943
+ const commitResult2 = await apiClient.commitStorage({
3944
+ storageName,
3945
+ storageType,
3946
+ versionId: prepareResult.versionId,
3947
+ files: fileEntries
3574
3948
  });
3575
- if (!commitResponse2.ok) {
3576
- const error = await commitResponse2.json();
3577
- throw new Error(error.error?.message || "Commit failed");
3578
- }
3579
- const commitResult2 = await commitResponse2.json();
3580
3949
  return {
3581
3950
  versionId: commitResult2.versionId,
3582
3951
  size: commitResult2.size,
@@ -3609,19 +3978,12 @@ async function directUpload(storageName, storageType, cwd, options) {
3609
3978
  "application/json"
3610
3979
  );
3611
3980
  onProgress?.("Committing...");
3612
- const commitResponse = await apiClient.post("/api/storages/commit", {
3613
- body: JSON.stringify({
3614
- storageName,
3615
- storageType,
3616
- versionId: prepareResult.versionId,
3617
- files: fileEntries
3618
- })
3981
+ const commitResult = await apiClient.commitStorage({
3982
+ storageName,
3983
+ storageType,
3984
+ versionId: prepareResult.versionId,
3985
+ files: fileEntries
3619
3986
  });
3620
- if (!commitResponse.ok) {
3621
- const error = await commitResponse.json();
3622
- throw new Error(error.error?.message || "Commit failed");
3623
- }
3624
- const commitResult = await commitResponse.json();
3625
3987
  return {
3626
3988
  versionId: commitResult.versionId,
3627
3989
  size: commitResult.size,
@@ -4777,7 +5139,7 @@ var runCmd = new Command2().name("run").description("Execute an agent").argument
4777
5139
  ).option(
4778
5140
  "--conversation <id>",
4779
5141
  "Resume from conversation ID (for fine-grained control)"
4780
- ).option("-v, --verbose", "Show verbose output with timing information").action(
5142
+ ).option("-v, --verbose", "Show verbose output with timing information").option("--debug-no-mock-claude").action(
4781
5143
  async (identifier, prompt, options) => {
4782
5144
  const startTimestamp = /* @__PURE__ */ new Date();
4783
5145
  const verbose = options.verbose;
@@ -4878,7 +5240,8 @@ var runCmd = new Command2().name("run").description("Execute an agent").argument
4878
5240
  artifactName: options.artifactName,
4879
5241
  artifactVersion: options.artifactVersion,
4880
5242
  volumeVersions: Object.keys(options.volumeVersion).length > 0 ? options.volumeVersion : void 0,
4881
- conversationId: options.conversation
5243
+ conversationId: options.conversation,
5244
+ debugNoMockClaude: options.debugNoMockClaude || void 0
4882
5245
  });
4883
5246
  if (response.status === "failed") {
4884
5247
  console.error(chalk5.red("\u2717 Run preparation failed"));
@@ -4943,7 +5306,7 @@ runCmd.command("resume").description("Resume an agent run from a checkpoint (use
4943
5306
  "Volume version override (repeatable)",
4944
5307
  collectVolumeVersions,
4945
5308
  {}
4946
- ).option("-v, --verbose", "Show verbose output with timing information").action(
5309
+ ).option("-v, --verbose", "Show verbose output with timing information").option("--debug-no-mock-claude").action(
4947
5310
  async (checkpointId, prompt, options, command) => {
4948
5311
  const startTimestamp = /* @__PURE__ */ new Date();
4949
5312
  const allOpts = command.optsWithGlobals();
@@ -4984,7 +5347,8 @@ runCmd.command("resume").description("Resume an agent run from a checkpoint (use
4984
5347
  prompt,
4985
5348
  vars: Object.keys(vars).length > 0 ? vars : void 0,
4986
5349
  secrets: loadedSecrets,
4987
- volumeVersions: Object.keys(allOpts.volumeVersion).length > 0 ? allOpts.volumeVersion : void 0
5350
+ volumeVersions: Object.keys(allOpts.volumeVersion).length > 0 ? allOpts.volumeVersion : void 0,
5351
+ debugNoMockClaude: options.debugNoMockClaude || allOpts.debugNoMockClaude || void 0
4988
5352
  });
4989
5353
  if (response.status === "failed") {
4990
5354
  console.error(chalk5.red("\u2717 Run preparation failed"));
@@ -5041,7 +5405,7 @@ runCmd.command("continue").description(
5041
5405
  "Volume version override (repeatable)",
5042
5406
  collectVolumeVersions,
5043
5407
  {}
5044
- ).option("-v, --verbose", "Show verbose output with timing information").action(
5408
+ ).option("-v, --verbose", "Show verbose output with timing information").option("--debug-no-mock-claude").action(
5045
5409
  async (agentSessionId, prompt, options, command) => {
5046
5410
  const startTimestamp = /* @__PURE__ */ new Date();
5047
5411
  const allOpts = command.optsWithGlobals();
@@ -5083,7 +5447,8 @@ runCmd.command("continue").description(
5083
5447
  prompt,
5084
5448
  vars: Object.keys(vars).length > 0 ? vars : void 0,
5085
5449
  secrets: loadedSecrets,
5086
- volumeVersions: Object.keys(allOpts.volumeVersion).length > 0 ? allOpts.volumeVersion : void 0
5450
+ volumeVersions: Object.keys(allOpts.volumeVersion).length > 0 ? allOpts.volumeVersion : void 0,
5451
+ debugNoMockClaude: options.debugNoMockClaude || allOpts.debugNoMockClaude || void 0
5087
5452
  });
5088
5453
  if (response.status === "failed") {
5089
5454
  console.error(chalk5.red("\u2717 Run preparation failed"));
@@ -5415,39 +5780,21 @@ var pullCommand = new Command5().name("pull").description("Pull cloud files to l
5415
5780
  console.log(`Pulling volume: ${config.name}`);
5416
5781
  }
5417
5782
  console.log(chalk9.dim("Getting download URL..."));
5418
- let url = `/api/storages/download?name=${encodeURIComponent(config.name)}&type=volume`;
5419
- if (versionId) {
5420
- const quotedVersion = JSON.stringify(versionId);
5421
- url += `&version=${encodeURIComponent(quotedVersion)}`;
5422
- }
5423
- const response = await apiClient.get(url);
5424
- if (!response.ok) {
5425
- if (response.status === 404) {
5426
- console.error(chalk9.red(`\u2717 Volume "${config.name}" not found`));
5427
- console.error(
5428
- chalk9.dim(
5429
- " Make sure the volume name is correct in .vm0/storage.yaml"
5430
- )
5431
- );
5432
- console.error(
5433
- chalk9.dim(" Or push the volume first with: vm0 volume push")
5434
- );
5435
- } else {
5436
- const error = await response.json();
5437
- throw new Error(error.error?.message || "Download failed");
5438
- }
5439
- process.exit(1);
5440
- }
5441
- const downloadInfo = await response.json();
5442
- if (downloadInfo.empty) {
5783
+ const downloadInfo = await apiClient.getStorageDownload({
5784
+ name: config.name,
5785
+ type: "volume",
5786
+ version: versionId
5787
+ });
5788
+ if ("empty" in downloadInfo) {
5443
5789
  await handleEmptyStorageResponse(cwd);
5444
5790
  return;
5445
5791
  }
5446
- if (!downloadInfo.url) {
5792
+ const downloadUrl = downloadInfo.url;
5793
+ if (!downloadUrl) {
5447
5794
  throw new Error("No download URL returned");
5448
5795
  }
5449
5796
  console.log(chalk9.dim("Downloading from S3..."));
5450
- const s3Response = await fetch(downloadInfo.url);
5797
+ const s3Response = await fetch(downloadUrl);
5451
5798
  if (!s3Response.ok) {
5452
5799
  throw new Error(`S3 download failed: ${s3Response.status}`);
5453
5800
  }
@@ -5519,21 +5866,12 @@ var statusCommand = new Command6().name("status").description("Show status of cl
5519
5866
  process.exit(1);
5520
5867
  }
5521
5868
  console.log(`Checking volume: ${config.name}`);
5522
- const url = `/api/storages/download?name=${encodeURIComponent(config.name)}&type=volume`;
5523
- const response = await apiClient.get(url);
5524
- if (!response.ok) {
5525
- if (response.status === 404) {
5526
- console.error(chalk10.red("\u2717 Not found on remote"));
5527
- console.error(chalk10.dim(" Run: vm0 volume push"));
5528
- } else {
5529
- const error = await response.json();
5530
- throw new Error(error.error?.message || "Status check failed");
5531
- }
5532
- process.exit(1);
5533
- }
5534
- const info = await response.json();
5869
+ const info = await apiClient.getStorageDownload({
5870
+ name: config.name,
5871
+ type: "volume"
5872
+ });
5535
5873
  const shortVersion = info.versionId.slice(0, 8);
5536
- if (info.empty) {
5874
+ if ("empty" in info) {
5537
5875
  console.log(chalk10.green("\u2713 Found (empty)"));
5538
5876
  console.log(chalk10.dim(` Version: ${shortVersion}`));
5539
5877
  } else {
@@ -5543,12 +5881,17 @@ var statusCommand = new Command6().name("status").description("Show status of cl
5543
5881
  console.log(chalk10.dim(` Size: ${formatBytes4(info.size)}`));
5544
5882
  }
5545
5883
  } catch (error) {
5546
- console.error(chalk10.red("\u2717 Status check failed"));
5547
- if (error instanceof Error) {
5548
- if (error.message.includes("Not authenticated")) {
5549
- console.error(chalk10.dim(" Run: vm0 auth login"));
5550
- } else {
5551
- console.error(chalk10.dim(` ${error.message}`));
5884
+ if (error instanceof Error && error.message.includes("not found")) {
5885
+ console.error(chalk10.red("\u2717 Not found on remote"));
5886
+ console.error(chalk10.dim(" Run: vm0 volume push"));
5887
+ } else {
5888
+ console.error(chalk10.red("\u2717 Status check failed"));
5889
+ if (error instanceof Error) {
5890
+ if (error.message.includes("Not authenticated")) {
5891
+ console.error(chalk10.dim(" Run: vm0 auth login"));
5892
+ } else {
5893
+ console.error(chalk10.dim(` ${error.message}`));
5894
+ }
5552
5895
  }
5553
5896
  }
5554
5897
  process.exit(1);
@@ -5558,15 +5901,9 @@ var statusCommand = new Command6().name("status").description("Show status of cl
5558
5901
  // src/commands/volume/list.ts
5559
5902
  import { Command as Command7 } from "commander";
5560
5903
  import chalk11 from "chalk";
5561
- var listCommand = new Command7().name("list").alias("ls").description("List all remote volumes").action(async () => {
5562
- try {
5563
- const url = "/api/storages/list?type=volume";
5564
- const response = await apiClient.get(url);
5565
- if (!response.ok) {
5566
- const error = await response.json();
5567
- throw new Error(error.error?.message || "List failed");
5568
- }
5569
- const items = await response.json();
5904
+ var listCommand = new Command7().name("list").alias("ls").description("List all remote volumes").action(async () => {
5905
+ try {
5906
+ const items = await apiClient.listStorages({ type: "volume" });
5570
5907
  if (items.length === 0) {
5571
5908
  console.log(chalk11.dim("No volumes found"));
5572
5909
  console.log(
@@ -5628,25 +5965,14 @@ async function cloneStorage(name, type, destination, options = {}) {
5628
5965
  throw new Error(`Directory "${destination}" already exists`);
5629
5966
  }
5630
5967
  console.log(chalk12.dim(`Checking remote ${typeLabel}...`));
5631
- let url = `/api/storages/download?name=${encodeURIComponent(name)}&type=${type}`;
5632
- if (options.version) {
5633
- const quotedVersion = JSON.stringify(options.version);
5634
- url += `&version=${encodeURIComponent(quotedVersion)}`;
5635
- }
5636
- const response = await apiClient.get(url);
5637
- if (!response.ok) {
5638
- if (response.status === 404) {
5639
- throw new Error(
5640
- `${typeLabel.charAt(0).toUpperCase() + typeLabel.slice(1)} "${name}" not found`
5641
- );
5642
- }
5643
- const error = await response.json();
5644
- throw new Error(error.error?.message || "Clone failed");
5645
- }
5646
- const downloadInfo = await response.json();
5968
+ const downloadInfo = await apiClient.getStorageDownload({
5969
+ name,
5970
+ type,
5971
+ version: options.version
5972
+ });
5647
5973
  console.log(chalk12.dim(`Creating directory: ${destination}/`));
5648
5974
  await fs7.promises.mkdir(destination, { recursive: true });
5649
- if (downloadInfo.empty) {
5975
+ if ("empty" in downloadInfo) {
5650
5976
  await writeStorageConfig(name, destination, type);
5651
5977
  console.log(chalk12.green(`\u2713 Cloned empty ${typeLabel}: ${name}`));
5652
5978
  console.log(chalk12.dim(`\u2713 Initialized .vm0/storage.yaml`));
@@ -5657,11 +5983,12 @@ async function cloneStorage(name, type, destination, options = {}) {
5657
5983
  versionId: downloadInfo.versionId
5658
5984
  };
5659
5985
  }
5660
- if (!downloadInfo.url) {
5986
+ const downloadUrl = downloadInfo.url;
5987
+ if (!downloadUrl) {
5661
5988
  throw new Error("No download URL returned");
5662
5989
  }
5663
5990
  console.log(chalk12.dim("Downloading from S3..."));
5664
- const s3Response = await fetch(downloadInfo.url);
5991
+ const s3Response = await fetch(downloadUrl);
5665
5992
  if (!s3Response.ok) {
5666
5993
  await fs7.promises.rm(destination, { recursive: true, force: true });
5667
5994
  throw new Error(`S3 download failed: ${s3Response.status}`);
@@ -5910,39 +6237,21 @@ var pullCommand2 = new Command12().name("pull").description("Pull cloud artifact
5910
6237
  console.log(`Pulling artifact: ${config.name}`);
5911
6238
  }
5912
6239
  console.log(chalk16.dim("Getting download URL..."));
5913
- let url = `/api/storages/download?name=${encodeURIComponent(config.name)}&type=artifact`;
5914
- if (versionId) {
5915
- const quotedVersion = JSON.stringify(versionId);
5916
- url += `&version=${encodeURIComponent(quotedVersion)}`;
5917
- }
5918
- const response = await apiClient.get(url);
5919
- if (!response.ok) {
5920
- if (response.status === 404) {
5921
- console.error(chalk16.red(`\u2717 Artifact "${config.name}" not found`));
5922
- console.error(
5923
- chalk16.dim(
5924
- " Make sure the artifact name is correct in .vm0/storage.yaml"
5925
- )
5926
- );
5927
- console.error(
5928
- chalk16.dim(" Or push the artifact first with: vm0 artifact push")
5929
- );
5930
- } else {
5931
- const error = await response.json();
5932
- throw new Error(error.error?.message || "Download failed");
5933
- }
5934
- process.exit(1);
5935
- }
5936
- const downloadInfo = await response.json();
5937
- if (downloadInfo.empty) {
6240
+ const downloadInfo = await apiClient.getStorageDownload({
6241
+ name: config.name,
6242
+ type: "artifact",
6243
+ version: versionId
6244
+ });
6245
+ if ("empty" in downloadInfo) {
5938
6246
  await handleEmptyStorageResponse(cwd);
5939
6247
  return;
5940
6248
  }
5941
- if (!downloadInfo.url) {
6249
+ const downloadUrl = downloadInfo.url;
6250
+ if (!downloadUrl) {
5942
6251
  throw new Error("No download URL returned");
5943
6252
  }
5944
6253
  console.log(chalk16.dim("Downloading from S3..."));
5945
- const s3Response = await fetch(downloadInfo.url);
6254
+ const s3Response = await fetch(downloadUrl);
5946
6255
  if (!s3Response.ok) {
5947
6256
  throw new Error(`S3 download failed: ${s3Response.status}`);
5948
6257
  }
@@ -6010,21 +6319,12 @@ var statusCommand2 = new Command13().name("status").description("Show status of
6010
6319
  process.exit(1);
6011
6320
  }
6012
6321
  console.log(`Checking artifact: ${config.name}`);
6013
- const url = `/api/storages/download?name=${encodeURIComponent(config.name)}&type=artifact`;
6014
- const response = await apiClient.get(url);
6015
- if (!response.ok) {
6016
- if (response.status === 404) {
6017
- console.error(chalk17.red("\u2717 Not found on remote"));
6018
- console.error(chalk17.dim(" Run: vm0 artifact push"));
6019
- } else {
6020
- const error = await response.json();
6021
- throw new Error(error.error?.message || "Status check failed");
6022
- }
6023
- process.exit(1);
6024
- }
6025
- const info = await response.json();
6322
+ const info = await apiClient.getStorageDownload({
6323
+ name: config.name,
6324
+ type: "artifact"
6325
+ });
6026
6326
  const shortVersion = info.versionId.slice(0, 8);
6027
- if (info.empty) {
6327
+ if ("empty" in info) {
6028
6328
  console.log(chalk17.green("\u2713 Found (empty)"));
6029
6329
  console.log(chalk17.dim(` Version: ${shortVersion}`));
6030
6330
  } else {
@@ -6034,9 +6334,14 @@ var statusCommand2 = new Command13().name("status").description("Show status of
6034
6334
  console.log(chalk17.dim(` Size: ${formatBytes7(info.size)}`));
6035
6335
  }
6036
6336
  } catch (error) {
6037
- console.error(chalk17.red("\u2717 Status check failed"));
6038
- if (error instanceof Error) {
6039
- console.error(chalk17.dim(` ${error.message}`));
6337
+ if (error instanceof Error && error.message.includes("not found")) {
6338
+ console.error(chalk17.red("\u2717 Not found on remote"));
6339
+ console.error(chalk17.dim(" Run: vm0 artifact push"));
6340
+ } else {
6341
+ console.error(chalk17.red("\u2717 Status check failed"));
6342
+ if (error instanceof Error) {
6343
+ console.error(chalk17.dim(` ${error.message}`));
6344
+ }
6040
6345
  }
6041
6346
  process.exit(1);
6042
6347
  }
@@ -6047,13 +6352,7 @@ import { Command as Command14 } from "commander";
6047
6352
  import chalk18 from "chalk";
6048
6353
  var listCommand2 = new Command14().name("list").alias("ls").description("List all remote artifacts").action(async () => {
6049
6354
  try {
6050
- const url = "/api/storages/list?type=artifact";
6051
- const response = await apiClient.get(url);
6052
- if (!response.ok) {
6053
- const error = await response.json();
6054
- throw new Error(error.error?.message || "List failed");
6055
- }
6056
- const items = await response.json();
6355
+ const items = await apiClient.listStorages({ type: "artifact" });
6057
6356
  if (items.length === 0) {
6058
6357
  console.log(chalk18.dim("No artifacts found"));
6059
6358
  console.log(
@@ -6471,180 +6770,186 @@ async function autoPullArtifact(runOutput, artifactDir) {
6471
6770
  }
6472
6771
  }
6473
6772
  var cookCmd = new Command17().name("cook").description("One-click agent preparation and execution from vm0.yaml");
6474
- cookCmd.argument("[prompt]", "Prompt for the agent").option("-y, --yes", "Skip confirmation prompts").action(async (prompt, options) => {
6475
- const shouldExit = await checkAndUpgrade("5.7.0", prompt);
6476
- if (shouldExit) {
6477
- process.exit(0);
6478
- }
6479
- const cwd = process.cwd();
6480
- console.log(chalk21.bold(`Reading config: ${CONFIG_FILE3}`));
6481
- if (!existsSync8(CONFIG_FILE3)) {
6482
- console.error(chalk21.red(`\u2717 Config file not found: ${CONFIG_FILE3}`));
6483
- process.exit(1);
6484
- }
6485
- let config;
6486
- try {
6487
- const content = await readFile7(CONFIG_FILE3, "utf8");
6488
- config = parseYaml4(content);
6489
- } catch (error) {
6490
- console.error(chalk21.red("\u2717 Invalid YAML format"));
6491
- if (error instanceof Error) {
6492
- console.error(chalk21.dim(` ${error.message}`));
6773
+ cookCmd.argument("[prompt]", "Prompt for the agent").option("-y, --yes", "Skip confirmation prompts").option("--debug-no-mock-claude").action(
6774
+ async (prompt, options) => {
6775
+ const shouldExit = await checkAndUpgrade("5.8.0", prompt);
6776
+ if (shouldExit) {
6777
+ process.exit(0);
6493
6778
  }
6494
- process.exit(1);
6495
- }
6496
- const validation = validateAgentCompose(config);
6497
- if (!validation.valid) {
6498
- console.error(chalk21.red(`\u2717 ${validation.error}`));
6499
- process.exit(1);
6500
- }
6501
- const agentNames = Object.keys(config.agents);
6502
- const agentName = agentNames[0];
6503
- const volumeCount = config.volumes ? Object.keys(config.volumes).length : 0;
6504
- console.log(
6505
- chalk21.green(`\u2713 Config validated: 1 agent, ${volumeCount} volume(s)`)
6506
- );
6507
- const requiredVarNames = extractRequiredVarNames(config);
6508
- if (requiredVarNames.length > 0) {
6509
- const envFilePath = path12.join(cwd, ".env");
6510
- const missingVars = checkMissingVariables(requiredVarNames, envFilePath);
6511
- if (missingVars.length > 0) {
6512
- await generateEnvPlaceholders(missingVars, envFilePath);
6513
- console.log();
6514
- console.log(
6515
- chalk21.yellow(
6516
- `\u26A0 Missing environment variables. Please fill in values in .env file:`
6517
- )
6518
- );
6519
- for (const varName of missingVars) {
6520
- console.log(chalk21.yellow(` ${varName}`));
6779
+ const cwd = process.cwd();
6780
+ console.log(chalk21.bold(`Reading config: ${CONFIG_FILE3}`));
6781
+ if (!existsSync8(CONFIG_FILE3)) {
6782
+ console.error(chalk21.red(`\u2717 Config file not found: ${CONFIG_FILE3}`));
6783
+ process.exit(1);
6784
+ }
6785
+ let config;
6786
+ try {
6787
+ const content = await readFile7(CONFIG_FILE3, "utf8");
6788
+ config = parseYaml4(content);
6789
+ } catch (error) {
6790
+ console.error(chalk21.red("\u2717 Invalid YAML format"));
6791
+ if (error instanceof Error) {
6792
+ console.error(chalk21.dim(` ${error.message}`));
6521
6793
  }
6522
6794
  process.exit(1);
6523
6795
  }
6524
- }
6525
- if (config.volumes && Object.keys(config.volumes).length > 0) {
6526
- console.log();
6527
- console.log(chalk21.bold("Processing volumes:"));
6528
- for (const volumeConfig of Object.values(config.volumes)) {
6529
- const volumeDir = path12.join(cwd, volumeConfig.name);
6530
- if (!existsSync8(volumeDir)) {
6531
- console.error(
6532
- chalk21.red(
6533
- `\u2717 Directory not found: ${volumeConfig.name}. Create the directory and add files first.`
6796
+ const validation = validateAgentCompose(config);
6797
+ if (!validation.valid) {
6798
+ console.error(chalk21.red(`\u2717 ${validation.error}`));
6799
+ process.exit(1);
6800
+ }
6801
+ const agentNames = Object.keys(config.agents);
6802
+ const agentName = agentNames[0];
6803
+ const volumeCount = config.volumes ? Object.keys(config.volumes).length : 0;
6804
+ console.log(
6805
+ chalk21.green(`\u2713 Config validated: 1 agent, ${volumeCount} volume(s)`)
6806
+ );
6807
+ const requiredVarNames = extractRequiredVarNames(config);
6808
+ if (requiredVarNames.length > 0) {
6809
+ const envFilePath = path12.join(cwd, ".env");
6810
+ const missingVars = checkMissingVariables(
6811
+ requiredVarNames,
6812
+ envFilePath
6813
+ );
6814
+ if (missingVars.length > 0) {
6815
+ await generateEnvPlaceholders(missingVars, envFilePath);
6816
+ console.log();
6817
+ console.log(
6818
+ chalk21.yellow(
6819
+ `\u26A0 Missing environment variables. Please fill in values in .env file:`
6534
6820
  )
6535
6821
  );
6822
+ for (const varName of missingVars) {
6823
+ console.log(chalk21.yellow(` ${varName}`));
6824
+ }
6536
6825
  process.exit(1);
6537
6826
  }
6538
- try {
6539
- printCommand(`cd ${volumeConfig.name}`);
6540
- const existingConfig = await readStorageConfig(volumeDir);
6541
- if (!existingConfig) {
6542
- printCommand(`vm0 volume init --name ${volumeConfig.name}`);
6543
- await execVm0Command(
6544
- ["volume", "init", "--name", volumeConfig.name],
6545
- {
6546
- cwd: volumeDir,
6547
- silent: true
6548
- }
6827
+ }
6828
+ if (config.volumes && Object.keys(config.volumes).length > 0) {
6829
+ console.log();
6830
+ console.log(chalk21.bold("Processing volumes:"));
6831
+ for (const volumeConfig of Object.values(config.volumes)) {
6832
+ const volumeDir = path12.join(cwd, volumeConfig.name);
6833
+ if (!existsSync8(volumeDir)) {
6834
+ console.error(
6835
+ chalk21.red(
6836
+ `\u2717 Directory not found: ${volumeConfig.name}. Create the directory and add files first.`
6837
+ )
6549
6838
  );
6839
+ process.exit(1);
6550
6840
  }
6551
- printCommand("vm0 volume push");
6552
- await execVm0Command(["volume", "push"], {
6553
- cwd: volumeDir,
6554
- silent: true
6555
- });
6556
- printCommand("cd ..");
6557
- } catch (error) {
6558
- console.error(chalk21.red(`\u2717 Failed`));
6559
- if (error instanceof Error) {
6560
- console.error(chalk21.dim(` ${error.message}`));
6841
+ try {
6842
+ printCommand(`cd ${volumeConfig.name}`);
6843
+ const existingConfig = await readStorageConfig(volumeDir);
6844
+ if (!existingConfig) {
6845
+ printCommand(`vm0 volume init --name ${volumeConfig.name}`);
6846
+ await execVm0Command(
6847
+ ["volume", "init", "--name", volumeConfig.name],
6848
+ {
6849
+ cwd: volumeDir,
6850
+ silent: true
6851
+ }
6852
+ );
6853
+ }
6854
+ printCommand("vm0 volume push");
6855
+ await execVm0Command(["volume", "push"], {
6856
+ cwd: volumeDir,
6857
+ silent: true
6858
+ });
6859
+ printCommand("cd ..");
6860
+ } catch (error) {
6861
+ console.error(chalk21.red(`\u2717 Failed`));
6862
+ if (error instanceof Error) {
6863
+ console.error(chalk21.dim(` ${error.message}`));
6864
+ }
6865
+ process.exit(1);
6561
6866
  }
6562
- process.exit(1);
6563
6867
  }
6564
6868
  }
6565
- }
6566
- console.log();
6567
- console.log(chalk21.bold("Processing artifact:"));
6568
- const artifactDir = path12.join(cwd, ARTIFACT_DIR);
6569
- try {
6570
- if (!existsSync8(artifactDir)) {
6571
- printCommand(`mkdir ${ARTIFACT_DIR}`);
6572
- await mkdir6(artifactDir, { recursive: true });
6573
- }
6574
- printCommand(`cd ${ARTIFACT_DIR}`);
6575
- const existingConfig = await readStorageConfig(artifactDir);
6576
- if (!existingConfig) {
6577
- printCommand(`vm0 artifact init --name ${ARTIFACT_DIR}`);
6578
- await execVm0Command(["artifact", "init", "--name", ARTIFACT_DIR], {
6869
+ console.log();
6870
+ console.log(chalk21.bold("Processing artifact:"));
6871
+ const artifactDir = path12.join(cwd, ARTIFACT_DIR);
6872
+ try {
6873
+ if (!existsSync8(artifactDir)) {
6874
+ printCommand(`mkdir ${ARTIFACT_DIR}`);
6875
+ await mkdir6(artifactDir, { recursive: true });
6876
+ }
6877
+ printCommand(`cd ${ARTIFACT_DIR}`);
6878
+ const existingConfig = await readStorageConfig(artifactDir);
6879
+ if (!existingConfig) {
6880
+ printCommand(`vm0 artifact init --name ${ARTIFACT_DIR}`);
6881
+ await execVm0Command(["artifact", "init", "--name", ARTIFACT_DIR], {
6882
+ cwd: artifactDir,
6883
+ silent: true
6884
+ });
6885
+ }
6886
+ printCommand("vm0 artifact push");
6887
+ await execVm0Command(["artifact", "push"], {
6579
6888
  cwd: artifactDir,
6580
6889
  silent: true
6581
6890
  });
6891
+ printCommand("cd ..");
6892
+ } catch (error) {
6893
+ console.error(chalk21.red(`\u2717 Failed`));
6894
+ if (error instanceof Error) {
6895
+ console.error(chalk21.dim(` ${error.message}`));
6896
+ }
6897
+ process.exit(1);
6582
6898
  }
6583
- printCommand("vm0 artifact push");
6584
- await execVm0Command(["artifact", "push"], {
6585
- cwd: artifactDir,
6586
- silent: true
6587
- });
6588
- printCommand("cd ..");
6589
- } catch (error) {
6590
- console.error(chalk21.red(`\u2717 Failed`));
6591
- if (error instanceof Error) {
6592
- console.error(chalk21.dim(` ${error.message}`));
6593
- }
6594
- process.exit(1);
6595
- }
6596
- console.log();
6597
- console.log(chalk21.bold("Composing agent:"));
6598
- const composeArgs = options.yes ? ["compose", "--yes", CONFIG_FILE3] : ["compose", CONFIG_FILE3];
6599
- printCommand(`vm0 ${composeArgs.join(" ")}`);
6600
- try {
6601
- await execVm0Command(composeArgs, {
6602
- cwd
6603
- });
6604
- } catch (error) {
6605
- console.error(chalk21.red(`\u2717 Compose failed`));
6606
- if (error instanceof Error) {
6607
- console.error(chalk21.dim(` ${error.message}`));
6608
- }
6609
- process.exit(1);
6610
- }
6611
- if (prompt) {
6612
- console.log();
6613
- console.log(chalk21.bold("Running agent:"));
6614
- printCommand(
6615
- `vm0 run ${agentName} --artifact-name ${ARTIFACT_DIR} "${prompt}"`
6616
- );
6617
6899
  console.log();
6618
- let runOutput;
6900
+ console.log(chalk21.bold("Composing agent:"));
6901
+ const composeArgs = options.yes ? ["compose", "--yes", CONFIG_FILE3] : ["compose", CONFIG_FILE3];
6902
+ printCommand(`vm0 ${composeArgs.join(" ")}`);
6619
6903
  try {
6620
- const runArgs = [
6621
- "run",
6622
- agentName,
6623
- "--artifact-name",
6624
- ARTIFACT_DIR,
6625
- prompt
6626
- ];
6627
- runOutput = await execVm0RunWithCapture(runArgs, { cwd });
6628
- } catch {
6904
+ await execVm0Command(composeArgs, {
6905
+ cwd
6906
+ });
6907
+ } catch (error) {
6908
+ console.error(chalk21.red(`\u2717 Compose failed`));
6909
+ if (error instanceof Error) {
6910
+ console.error(chalk21.dim(` ${error.message}`));
6911
+ }
6629
6912
  process.exit(1);
6630
6913
  }
6631
- const runIds = parseRunIdsFromOutput(runOutput);
6632
- if (runIds.runId || runIds.sessionId || runIds.checkpointId) {
6633
- await saveCookState({
6634
- lastRunId: runIds.runId,
6635
- lastSessionId: runIds.sessionId,
6636
- lastCheckpointId: runIds.checkpointId
6637
- });
6914
+ if (prompt) {
6915
+ console.log();
6916
+ console.log(chalk21.bold("Running agent:"));
6917
+ printCommand(
6918
+ `vm0 run ${agentName} --artifact-name ${ARTIFACT_DIR} "${prompt}"`
6919
+ );
6920
+ console.log();
6921
+ let runOutput;
6922
+ try {
6923
+ const runArgs = [
6924
+ "run",
6925
+ agentName,
6926
+ "--artifact-name",
6927
+ ARTIFACT_DIR,
6928
+ ...options.debugNoMockClaude ? ["--debug-no-mock-claude"] : [],
6929
+ prompt
6930
+ ];
6931
+ runOutput = await execVm0RunWithCapture(runArgs, { cwd });
6932
+ } catch {
6933
+ process.exit(1);
6934
+ }
6935
+ const runIds = parseRunIdsFromOutput(runOutput);
6936
+ if (runIds.runId || runIds.sessionId || runIds.checkpointId) {
6937
+ await saveCookState({
6938
+ lastRunId: runIds.runId,
6939
+ lastSessionId: runIds.sessionId,
6940
+ lastCheckpointId: runIds.checkpointId
6941
+ });
6942
+ }
6943
+ await autoPullArtifact(runOutput, artifactDir);
6944
+ } else {
6945
+ console.log();
6946
+ console.log("To run your agent:");
6947
+ printCommand(
6948
+ `vm0 run ${agentName} --artifact-name ${ARTIFACT_DIR} "your prompt"`
6949
+ );
6638
6950
  }
6639
- await autoPullArtifact(runOutput, artifactDir);
6640
- } else {
6641
- console.log();
6642
- console.log("To run your agent:");
6643
- printCommand(
6644
- `vm0 run ${agentName} --artifact-name ${ARTIFACT_DIR} "your prompt"`
6645
- );
6646
6951
  }
6647
- });
6952
+ );
6648
6953
  cookCmd.command("logs").description("View logs from the last cook run").option("-a, --agent", "Show agent events (default)").option("-s, --system", "Show system log").option("-m, --metrics", "Show metrics").option("-n, --network", "Show network logs (proxy traffic)").option(
6649
6954
  "--since <time>",
6650
6955
  "Show logs since timestamp (e.g., 5m, 2h, 1d, 2024-01-15T10:30:00Z)"
@@ -6692,7 +6997,7 @@ cookCmd.command("logs").description("View logs from the last cook run").option("
6692
6997
  );
6693
6998
  cookCmd.command("continue").description(
6694
6999
  "Continue from the last session (latest conversation and artifact)"
6695
- ).argument("<prompt>", "Prompt for the continued agent").action(async (prompt) => {
7000
+ ).argument("<prompt>", "Prompt for the continued agent").option("--debug-no-mock-claude").action(async (prompt, options) => {
6696
7001
  const state = await loadCookState();
6697
7002
  if (!state.lastSessionId) {
6698
7003
  console.error(chalk21.red("\u2717 No previous session found"));
@@ -6706,7 +7011,13 @@ cookCmd.command("continue").description(
6706
7011
  let runOutput;
6707
7012
  try {
6708
7013
  runOutput = await execVm0RunWithCapture(
6709
- ["run", "continue", state.lastSessionId, prompt],
7014
+ [
7015
+ "run",
7016
+ "continue",
7017
+ state.lastSessionId,
7018
+ ...options.debugNoMockClaude ? ["--debug-no-mock-claude"] : [],
7019
+ prompt
7020
+ ],
6710
7021
  { cwd }
6711
7022
  );
6712
7023
  } catch {
@@ -6724,7 +7035,7 @@ cookCmd.command("continue").description(
6724
7035
  });
6725
7036
  cookCmd.command("resume").description(
6726
7037
  "Resume from the last checkpoint (snapshotted conversation and artifact)"
6727
- ).argument("<prompt>", "Prompt for the resumed agent").action(async (prompt) => {
7038
+ ).argument("<prompt>", "Prompt for the resumed agent").option("--debug-no-mock-claude").action(async (prompt, options) => {
6728
7039
  const state = await loadCookState();
6729
7040
  if (!state.lastCheckpointId) {
6730
7041
  console.error(chalk21.red("\u2717 No previous checkpoint found"));
@@ -6738,7 +7049,13 @@ cookCmd.command("resume").description(
6738
7049
  let runOutput;
6739
7050
  try {
6740
7051
  runOutput = await execVm0RunWithCapture(
6741
- ["run", "resume", state.lastCheckpointId, prompt],
7052
+ [
7053
+ "run",
7054
+ "resume",
7055
+ state.lastCheckpointId,
7056
+ ...options.debugNoMockClaude ? ["--debug-no-mock-claude"] : [],
7057
+ prompt
7058
+ ],
6742
7059
  { cwd }
6743
7060
  );
6744
7061
  } catch {
@@ -7134,10 +7451,10 @@ var setCommand = new Command20().name("set").description("Set your scope slug").
7134
7451
  // src/commands/scope/index.ts
7135
7452
  var scopeCommand = new Command21().name("scope").description("Manage your scope (namespace for images)").addCommand(statusCommand3).addCommand(setCommand);
7136
7453
 
7137
- // src/commands/agents/index.ts
7454
+ // src/commands/agent/index.ts
7138
7455
  import { Command as Command24 } from "commander";
7139
7456
 
7140
- // src/commands/agents/list.ts
7457
+ // src/commands/agent/list.ts
7141
7458
  import { Command as Command22 } from "commander";
7142
7459
  import chalk25 from "chalk";
7143
7460
  var listCommand3 = new Command22().name("list").alias("ls").description("List all agent composes").option("-s, --scope <scope>", "Scope to list composes from").action(async (options) => {
@@ -7183,7 +7500,7 @@ var listCommand3 = new Command22().name("list").alias("ls").description("List al
7183
7500
  }
7184
7501
  });
7185
7502
 
7186
- // src/commands/agents/inspect.ts
7503
+ // src/commands/agent/inspect.ts
7187
7504
  import { Command as Command23 } from "commander";
7188
7505
  import chalk26 from "chalk";
7189
7506
 
@@ -7293,7 +7610,7 @@ async function deriveComposeVariableSources(content, options) {
7293
7610
  return results;
7294
7611
  }
7295
7612
 
7296
- // src/commands/agents/inspect.ts
7613
+ // src/commands/agent/inspect.ts
7297
7614
  function formatComposeOutput(name, versionId, content, variableSources) {
7298
7615
  console.log(chalk26.bold("Name:") + ` ${name}`);
7299
7616
  console.log(chalk26.bold("Version:") + ` ${versionId}`);
@@ -7375,7 +7692,7 @@ var inspectCommand = new Command23().name("inspect").description("Inspect an age
7375
7692
  } catch (error) {
7376
7693
  if (error instanceof Error && error.message.includes("not found")) {
7377
7694
  console.error(chalk26.red(`\u2717 Agent compose not found: ${name}`));
7378
- console.error(chalk26.dim(" Run: vm0 agents list"));
7695
+ console.error(chalk26.dim(" Run: vm0 agent list"));
7379
7696
  process.exit(1);
7380
7697
  }
7381
7698
  throw error;
@@ -7442,8 +7759,8 @@ var inspectCommand = new Command23().name("inspect").description("Inspect an age
7442
7759
  }
7443
7760
  );
7444
7761
 
7445
- // src/commands/agents/index.ts
7446
- var agentsCommand = new Command24().name("agents").description("Manage agent composes").addCommand(listCommand3).addCommand(inspectCommand);
7762
+ // src/commands/agent/index.ts
7763
+ var agentCommand = new Command24().name("agent").description("Manage agent composes").addCommand(listCommand3).addCommand(inspectCommand);
7447
7764
 
7448
7765
  // src/commands/init.ts
7449
7766
  import { Command as Command25 } from "commander";
@@ -8660,14 +8977,7 @@ var deployCommand = new Command28().name("deploy").description("Deploy a schedul
8660
8977
  artifactVersion: schedule.run.artifactVersion,
8661
8978
  volumeVersions: schedule.run.volumeVersions
8662
8979
  };
8663
- const response = await apiClient.post("/api/agent/schedules", {
8664
- body: JSON.stringify(body)
8665
- });
8666
- if (!response.ok) {
8667
- const error = await response.json();
8668
- throw new Error(error.error?.message || "Deploy failed");
8669
- }
8670
- const deployResult = await response.json();
8980
+ const deployResult = await apiClient.deploySchedule(body);
8671
8981
  if (deployResult.created) {
8672
8982
  console.log(
8673
8983
  chalk30.green(`\u2713 Created schedule ${chalk30.cyan(scheduleName)}`)
@@ -8714,12 +9024,7 @@ import { Command as Command29 } from "commander";
8714
9024
  import chalk31 from "chalk";
8715
9025
  var listCommand4 = new Command29().name("list").alias("ls").description("List all schedules").action(async () => {
8716
9026
  try {
8717
- const response = await apiClient.get("/api/agent/schedules");
8718
- if (!response.ok) {
8719
- const error = await response.json();
8720
- throw new Error(error.error?.message || "List failed");
8721
- }
8722
- const result = await response.json();
9027
+ const result = await apiClient.listSchedules();
8723
9028
  if (result.schedules.length === 0) {
8724
9029
  console.log(chalk31.dim("No schedules found"));
8725
9030
  console.log(
@@ -8855,14 +9160,7 @@ var statusCommand4 = new Command30().name("status").description("Show detailed s
8855
9160
  console.error(chalk32.dim(" Make sure the agent is pushed first"));
8856
9161
  process.exit(1);
8857
9162
  }
8858
- const response = await apiClient.get(
8859
- `/api/agent/schedules/${encodeURIComponent(name)}?composeId=${encodeURIComponent(composeId)}`
8860
- );
8861
- if (!response.ok) {
8862
- const error = await response.json();
8863
- throw new Error(error.error?.message || "Failed to get schedule");
8864
- }
8865
- const schedule = await response.json();
9163
+ const schedule = await apiClient.getScheduleByName({ name, composeId });
8866
9164
  console.log();
8867
9165
  console.log(`Schedule: ${chalk32.cyan(schedule.name)}`);
8868
9166
  console.log(chalk32.dim("\u2501".repeat(50)));
@@ -8905,11 +9203,12 @@ var statusCommand4 = new Command30().name("status").description("Show detailed s
8905
9203
  100
8906
9204
  );
8907
9205
  if (limit > 0) {
8908
- const runsResponse = await apiClient.get(
8909
- `/api/agent/schedules/${encodeURIComponent(name)}/runs?composeId=${encodeURIComponent(composeId)}&limit=${limit}`
8910
- );
8911
- if (runsResponse.ok) {
8912
- const { runs } = await runsResponse.json();
9206
+ try {
9207
+ const { runs } = await apiClient.listScheduleRuns({
9208
+ name,
9209
+ composeId,
9210
+ limit
9211
+ });
8913
9212
  if (runs.length > 0) {
8914
9213
  console.log();
8915
9214
  console.log("Recent Runs:");
@@ -8925,7 +9224,7 @@ var statusCommand4 = new Command30().name("status").description("Show detailed s
8925
9224
  console.log(`${id} ${status} ${created}`);
8926
9225
  }
8927
9226
  }
8928
- } else {
9227
+ } catch {
8929
9228
  console.log();
8930
9229
  console.log(chalk32.dim("Recent Runs: (unable to fetch)"));
8931
9230
  }
@@ -8968,8 +9267,8 @@ var deleteCommand = new Command31().name("delete").alias("rm").description("Dele
8968
9267
  "[name]",
8969
9268
  "Schedule name (auto-detected from schedule.yaml if omitted)"
8970
9269
  ).option("-f, --force", "Skip confirmation prompt").action(async (nameArg, options) => {
9270
+ let name = nameArg;
8971
9271
  try {
8972
- let name = nameArg;
8973
9272
  if (!name) {
8974
9273
  const scheduleResult = loadScheduleName();
8975
9274
  if (scheduleResult.error) {
@@ -9014,19 +9313,15 @@ var deleteCommand = new Command31().name("delete").alias("rm").description("Dele
9014
9313
  return;
9015
9314
  }
9016
9315
  }
9017
- const response = await apiClient.delete(
9018
- `/api/agent/schedules/${encodeURIComponent(name)}?composeId=${encodeURIComponent(composeId)}`
9019
- );
9020
- if (!response.ok) {
9021
- const error = await response.json();
9022
- throw new Error(error.error?.message || "Delete failed");
9023
- }
9316
+ await apiClient.deleteSchedule({ name, composeId });
9024
9317
  console.log(chalk33.green(`\u2713 Deleted schedule ${chalk33.cyan(name)}`));
9025
9318
  } catch (error) {
9026
9319
  console.error(chalk33.red("\u2717 Failed to delete schedule"));
9027
9320
  if (error instanceof Error) {
9028
9321
  if (error.message.includes("Not authenticated")) {
9029
9322
  console.error(chalk33.dim(" Run: vm0 auth login"));
9323
+ } else if (error.message.toLowerCase().includes("not found")) {
9324
+ console.error(chalk33.dim(` Schedule "${name}" not found`));
9030
9325
  } else {
9031
9326
  console.error(chalk33.dim(` ${error.message}`));
9032
9327
  }
@@ -9081,14 +9376,7 @@ var enableCommand = new Command32().name("enable").description("Enable a schedul
9081
9376
  console.error(chalk34.dim(" Make sure the agent is pushed first"));
9082
9377
  process.exit(1);
9083
9378
  }
9084
- const response = await apiClient.post(
9085
- `/api/agent/schedules/${encodeURIComponent(name)}/enable`,
9086
- { body: JSON.stringify({ composeId }) }
9087
- );
9088
- if (!response.ok) {
9089
- const error = await response.json();
9090
- throw new Error(error.error?.message || "Enable failed");
9091
- }
9379
+ await apiClient.enableSchedule({ name, composeId });
9092
9380
  console.log(chalk34.green(`\u2713 Enabled schedule ${chalk34.cyan(name)}`));
9093
9381
  } catch (error) {
9094
9382
  console.error(chalk34.red("\u2717 Failed to enable schedule"));
@@ -9149,14 +9437,7 @@ var disableCommand = new Command33().name("disable").description("Disable a sche
9149
9437
  console.error(chalk35.dim(" Make sure the agent is pushed first"));
9150
9438
  process.exit(1);
9151
9439
  }
9152
- const response = await apiClient.post(
9153
- `/api/agent/schedules/${encodeURIComponent(name)}/disable`,
9154
- { body: JSON.stringify({ composeId }) }
9155
- );
9156
- if (!response.ok) {
9157
- const error = await response.json();
9158
- throw new Error(error.error?.message || "Disable failed");
9159
- }
9440
+ await apiClient.disableSchedule({ name, composeId });
9160
9441
  console.log(chalk35.green(`\u2713 Disabled schedule ${chalk35.cyan(name)}`));
9161
9442
  } catch (error) {
9162
9443
  console.error(chalk35.red("\u2717 Failed to disable schedule"));
@@ -9174,11 +9455,183 @@ var disableCommand = new Command33().name("disable").description("Disable a sche
9174
9455
  // src/commands/schedule/index.ts
9175
9456
  var scheduleCommand = new Command34().name("schedule").description("Manage agent schedules").addCommand(initCommand4).addCommand(deployCommand).addCommand(listCommand4).addCommand(statusCommand4).addCommand(deleteCommand).addCommand(enableCommand).addCommand(disableCommand);
9176
9457
 
9458
+ // src/commands/usage.ts
9459
+ import { Command as Command35 } from "commander";
9460
+ import chalk36 from "chalk";
9461
+
9462
+ // src/lib/utils/duration-formatter.ts
9463
+ function formatDuration(ms) {
9464
+ if (ms === null || ms === void 0 || ms === 0) {
9465
+ return "-";
9466
+ }
9467
+ if (ms < 0) {
9468
+ return "-";
9469
+ }
9470
+ const totalSeconds = Math.floor(ms / 1e3);
9471
+ if (totalSeconds === 0) {
9472
+ return "< 1s";
9473
+ }
9474
+ const hours = Math.floor(totalSeconds / 3600);
9475
+ const minutes = Math.floor(totalSeconds % 3600 / 60);
9476
+ const seconds = totalSeconds % 60;
9477
+ const parts = [];
9478
+ if (hours > 0) {
9479
+ parts.push(`${hours}h`);
9480
+ }
9481
+ if (minutes > 0) {
9482
+ parts.push(`${minutes}m`);
9483
+ }
9484
+ if (seconds > 0) {
9485
+ parts.push(`${seconds}s`);
9486
+ }
9487
+ return parts.join(" ");
9488
+ }
9489
+
9490
+ // src/commands/usage.ts
9491
+ var MAX_RANGE_MS = 30 * 24 * 60 * 60 * 1e3;
9492
+ var DEFAULT_RANGE_MS = 7 * 24 * 60 * 60 * 1e3;
9493
+ function formatDateDisplay(dateStr) {
9494
+ const date = new Date(dateStr);
9495
+ return date.toLocaleDateString("en-US", { month: "short", day: "numeric" });
9496
+ }
9497
+ function formatDateRange(start, end) {
9498
+ const startDate = new Date(start);
9499
+ const endDate = new Date(end);
9500
+ endDate.setDate(endDate.getDate() - 1);
9501
+ const startStr = startDate.toLocaleDateString("en-US", {
9502
+ month: "short",
9503
+ day: "numeric"
9504
+ });
9505
+ const endStr = endDate.toLocaleDateString("en-US", {
9506
+ month: "short",
9507
+ day: "numeric",
9508
+ year: "numeric"
9509
+ });
9510
+ return `${startStr} - ${endStr}`;
9511
+ }
9512
+ function fillMissingDates(daily, startDate, endDate) {
9513
+ const dateMap = /* @__PURE__ */ new Map();
9514
+ for (const day of daily) {
9515
+ dateMap.set(day.date, day);
9516
+ }
9517
+ const result = [];
9518
+ const current = new Date(startDate);
9519
+ current.setUTCHours(0, 0, 0, 0);
9520
+ while (current < endDate) {
9521
+ const dateStr = current.toISOString().split("T")[0];
9522
+ const existing = dateMap.get(dateStr);
9523
+ if (existing) {
9524
+ result.push(existing);
9525
+ } else {
9526
+ result.push({ date: dateStr, run_count: 0, run_time_ms: 0 });
9527
+ }
9528
+ current.setDate(current.getDate() + 1);
9529
+ }
9530
+ result.sort((a, b) => b.date.localeCompare(a.date));
9531
+ return result;
9532
+ }
9533
+ var usageCommand = new Command35().name("usage").description("View usage statistics").option("--since <date>", "Start date (ISO format or relative: 7d, 30d)").option(
9534
+ "--until <date>",
9535
+ "End date (ISO format or relative, defaults to now)"
9536
+ ).action(async (options) => {
9537
+ try {
9538
+ const now = /* @__PURE__ */ new Date();
9539
+ let endDate;
9540
+ let startDate;
9541
+ if (options.until) {
9542
+ try {
9543
+ const untilMs = parseTime(options.until);
9544
+ endDate = new Date(untilMs);
9545
+ } catch {
9546
+ console.error(
9547
+ chalk36.red(
9548
+ "Error: Invalid --until format. Use ISO (2026-01-01) or relative (7d, 30d)"
9549
+ )
9550
+ );
9551
+ process.exit(1);
9552
+ }
9553
+ } else {
9554
+ endDate = now;
9555
+ }
9556
+ if (options.since) {
9557
+ try {
9558
+ const sinceMs = parseTime(options.since);
9559
+ startDate = new Date(sinceMs);
9560
+ } catch {
9561
+ console.error(
9562
+ chalk36.red(
9563
+ "Error: Invalid --since format. Use ISO (2026-01-01) or relative (7d, 30d)"
9564
+ )
9565
+ );
9566
+ process.exit(1);
9567
+ }
9568
+ } else {
9569
+ startDate = new Date(endDate.getTime() - DEFAULT_RANGE_MS);
9570
+ }
9571
+ if (startDate >= endDate) {
9572
+ console.error(chalk36.red("Error: --since must be before --until"));
9573
+ process.exit(1);
9574
+ }
9575
+ const rangeMs = endDate.getTime() - startDate.getTime();
9576
+ if (rangeMs > MAX_RANGE_MS) {
9577
+ console.error(
9578
+ chalk36.red(
9579
+ "Error: Time range exceeds maximum of 30 days. Use --until to specify an end date."
9580
+ )
9581
+ );
9582
+ process.exit(1);
9583
+ }
9584
+ const usage = await apiClient.getUsage({
9585
+ startDate: startDate.toISOString(),
9586
+ endDate: endDate.toISOString()
9587
+ });
9588
+ const filledDaily = fillMissingDates(
9589
+ usage.daily,
9590
+ new Date(usage.period.start),
9591
+ new Date(usage.period.end)
9592
+ );
9593
+ console.log();
9594
+ console.log(
9595
+ chalk36.bold(
9596
+ `Usage Summary (${formatDateRange(usage.period.start, usage.period.end)})`
9597
+ )
9598
+ );
9599
+ console.log();
9600
+ console.log(chalk36.dim("DATE RUNS RUN TIME"));
9601
+ for (const day of filledDaily) {
9602
+ const dateDisplay = formatDateDisplay(day.date).padEnd(10);
9603
+ const runsDisplay = String(day.run_count).padStart(6);
9604
+ const timeDisplay = formatDuration(day.run_time_ms);
9605
+ console.log(`${dateDisplay}${runsDisplay} ${timeDisplay}`);
9606
+ }
9607
+ console.log(chalk36.dim("\u2500".repeat(29)));
9608
+ const totalRunsDisplay = String(usage.summary.total_runs).padStart(6);
9609
+ const totalTimeDisplay = formatDuration(usage.summary.total_run_time_ms);
9610
+ console.log(
9611
+ `${"TOTAL".padEnd(10)}${totalRunsDisplay} ${totalTimeDisplay}`
9612
+ );
9613
+ console.log();
9614
+ } catch (error) {
9615
+ if (error instanceof Error) {
9616
+ if (error.message.includes("Not authenticated")) {
9617
+ console.error(
9618
+ chalk36.red("Error: Not authenticated. Run: vm0 auth login")
9619
+ );
9620
+ } else {
9621
+ console.error(chalk36.red(`Error: ${error.message}`));
9622
+ }
9623
+ } else {
9624
+ console.error(chalk36.red("Error: An unexpected error occurred"));
9625
+ }
9626
+ process.exit(1);
9627
+ }
9628
+ });
9629
+
9177
9630
  // src/index.ts
9178
- var program = new Command35();
9179
- program.name("vm0").description("VM0 CLI - A modern build tool").version("5.7.0");
9631
+ var program = new Command36();
9632
+ program.name("vm0").description("VM0 CLI - A modern build tool").version("5.8.0");
9180
9633
  program.command("info").description("Display environment information").action(async () => {
9181
- console.log(chalk36.bold("System Information:"));
9634
+ console.log(chalk37.bold("System Information:"));
9182
9635
  console.log(`Node Version: ${process.version}`);
9183
9636
  console.log(`Platform: ${process.platform}`);
9184
9637
  console.log(`Architecture: ${process.arch}`);
@@ -9205,10 +9658,11 @@ program.addCommand(artifactCommand);
9205
9658
  program.addCommand(cookCommand);
9206
9659
  program.addCommand(logsCommand);
9207
9660
  program.addCommand(scopeCommand);
9208
- program.addCommand(agentsCommand);
9661
+ program.addCommand(agentCommand);
9209
9662
  program.addCommand(initCommand3);
9210
9663
  program.addCommand(setupGithubCommand);
9211
9664
  program.addCommand(scheduleCommand);
9665
+ program.addCommand(usageCommand);
9212
9666
  if (process.argv[1]?.endsWith("index.js") || process.argv[1]?.endsWith("index.ts") || process.argv[1]?.endsWith("vm0")) {
9213
9667
  program.parse();
9214
9668
  }