@vm0/cli 4.5.1 → 4.6.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 +169 -29
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -391,6 +391,28 @@ var ApiClient = class {
391
391
  }
392
392
  return await response.json();
393
393
  }
394
+ async getNetworkLogs(runId, options) {
395
+ const baseUrl = await this.getBaseUrl();
396
+ const headers = await this.getHeaders();
397
+ const params = new URLSearchParams();
398
+ if (options?.since !== void 0) {
399
+ params.set("since", String(options.since));
400
+ }
401
+ if (options?.limit !== void 0) {
402
+ params.set("limit", String(options.limit));
403
+ }
404
+ const queryString = params.toString();
405
+ const url2 = `${baseUrl}/api/agent/runs/${runId}/telemetry/network${queryString ? `?${queryString}` : ""}`;
406
+ const response = await fetch(url2, {
407
+ method: "GET",
408
+ headers
409
+ });
410
+ if (!response.ok) {
411
+ const error43 = await response.json();
412
+ throw new Error(error43.error?.message || "Failed to fetch network logs");
413
+ }
414
+ return await response.json();
415
+ }
394
416
  async createImage(body) {
395
417
  const baseUrl = await this.getBaseUrl();
396
418
  const headers = await this.getHeaders();
@@ -813,35 +835,34 @@ var EventRenderer = class {
813
835
  return `${(elapsedMs / 1e3).toFixed(1)}s`;
814
836
  }
815
837
  /**
816
- * Format timestamp for display
838
+ * Format timestamp for display (without milliseconds, matching metrics format)
817
839
  */
818
840
  static formatTimestamp(timestamp) {
819
- return timestamp.toISOString();
841
+ return timestamp.toISOString().replace(/\.\d{3}Z$/, "Z");
820
842
  }
821
843
  /**
822
844
  * Render a parsed event to console
823
845
  */
824
846
  static render(event, options) {
825
- const timestampPrefix = options?.showTimestamp ? chalk3.gray(`[${this.formatTimestamp(event.timestamp)}] `) : "";
826
- const elapsedPrefix = options?.verbose && options?.previousTimestamp ? chalk3.gray(
847
+ const timestampPrefix = options?.showTimestamp ? `[${this.formatTimestamp(event.timestamp)}] ` : "";
848
+ const elapsedSuffix = options?.verbose && options?.previousTimestamp ? " " + chalk3.gray(
827
849
  this.formatElapsed(options.previousTimestamp, event.timestamp)
828
850
  ) : "";
829
- const prefix = timestampPrefix + elapsedPrefix;
830
851
  switch (event.type) {
831
852
  case "init":
832
- this.renderInit(event, prefix);
853
+ this.renderInit(event, timestampPrefix, elapsedSuffix);
833
854
  break;
834
855
  case "text":
835
- this.renderText(event, prefix);
856
+ this.renderText(event, timestampPrefix, elapsedSuffix);
836
857
  break;
837
858
  case "tool_use":
838
- this.renderToolUse(event, prefix);
859
+ this.renderToolUse(event, timestampPrefix, elapsedSuffix);
839
860
  break;
840
861
  case "tool_result":
841
- this.renderToolResult(event, prefix);
862
+ this.renderToolResult(event, timestampPrefix, elapsedSuffix);
842
863
  break;
843
864
  case "result":
844
- this.renderResult(event, prefix);
865
+ this.renderResult(event, timestampPrefix, elapsedSuffix);
845
866
  break;
846
867
  }
847
868
  }
@@ -888,8 +909,10 @@ var EventRenderer = class {
888
909
  console.log(chalk3.red("\u2717 Run failed"));
889
910
  console.log(` Error: ${chalk3.red(error43 || "Unknown error")}`);
890
911
  }
891
- static renderInit(event, prefix) {
892
- console.log(chalk3.cyan("[init]") + prefix + " Starting Claude Code agent");
912
+ static renderInit(event, prefix, suffix) {
913
+ console.log(
914
+ prefix + chalk3.cyan("[init]") + suffix + " Starting Claude Code agent"
915
+ );
893
916
  console.log(` Session: ${chalk3.gray(String(event.data.sessionId || ""))}`);
894
917
  console.log(` Model: ${chalk3.gray(String(event.data.model || ""))}`);
895
918
  console.log(
@@ -898,13 +921,13 @@ var EventRenderer = class {
898
921
  )}`
899
922
  );
900
923
  }
901
- static renderText(event, prefix) {
924
+ static renderText(event, prefix, suffix) {
902
925
  const text = String(event.data.text || "");
903
- console.log(chalk3.blue("[text]") + prefix + " " + text);
926
+ console.log(prefix + chalk3.blue("[text]") + suffix + " " + text);
904
927
  }
905
- static renderToolUse(event, prefix) {
928
+ static renderToolUse(event, prefix, suffix) {
906
929
  const tool = String(event.data.tool || "");
907
- console.log(chalk3.yellow("[tool_use]") + prefix + " " + tool);
930
+ console.log(prefix + chalk3.yellow("[tool_use]") + suffix + " " + tool);
908
931
  const input = event.data.input;
909
932
  if (input && typeof input === "object") {
910
933
  for (const [key, value] of Object.entries(input)) {
@@ -915,19 +938,19 @@ var EventRenderer = class {
915
938
  }
916
939
  }
917
940
  }
918
- static renderToolResult(event, prefix) {
941
+ static renderToolResult(event, prefix, suffix) {
919
942
  const isError = Boolean(event.data.isError);
920
943
  const status = isError ? "Error" : "Completed";
921
944
  const color = isError ? chalk3.red : chalk3.green;
922
- console.log(color("[tool_result]") + prefix + " " + status);
945
+ console.log(prefix + color("[tool_result]") + suffix + " " + status);
923
946
  const result = String(event.data.result || "");
924
947
  console.log(` ${chalk3.gray(result)}`);
925
948
  }
926
- static renderResult(event, prefix) {
949
+ static renderResult(event, prefix, suffix) {
927
950
  const success2 = Boolean(event.data.success);
928
951
  const status = success2 ? "\u2713 completed successfully" : "\u2717 failed";
929
952
  const color = success2 ? chalk3.green : chalk3.red;
930
- console.log(color("[result]") + prefix + " " + status);
953
+ console.log(prefix + color("[result]") + suffix + " " + status);
931
954
  const durationMs = Number(event.data.durationMs || 0);
932
955
  const durationSec = (durationMs / 1e3).toFixed(1);
933
956
  console.log(` Duration: ${chalk3.gray(durationSec + "s")}`);
@@ -14216,7 +14239,14 @@ var agentDefinitionSchema = external_exports.object({
14216
14239
  provider: external_exports.string().min(1, "Provider is required"),
14217
14240
  volumes: external_exports.array(external_exports.string()).optional(),
14218
14241
  working_dir: external_exports.string().min(1, "Working directory is required"),
14219
- environment: external_exports.record(external_exports.string(), external_exports.string()).optional()
14242
+ environment: external_exports.record(external_exports.string(), external_exports.string()).optional(),
14243
+ /**
14244
+ * Enable network security mode for secrets.
14245
+ * When true, secrets are encrypted into proxy tokens and all traffic
14246
+ * is routed through mitmproxy -> VM0 Proxy for decryption.
14247
+ * Default: false (plaintext secrets in env vars)
14248
+ */
14249
+ beta_network_security: external_exports.boolean().optional().default(false)
14220
14250
  });
14221
14251
  var agentComposeContentSchema = external_exports.object({
14222
14252
  version: external_exports.string().min(1, "Version is required"),
@@ -14476,6 +14506,19 @@ var agentEventsResponseSchema = external_exports.object({
14476
14506
  events: external_exports.array(runEventSchema),
14477
14507
  hasMore: external_exports.boolean()
14478
14508
  });
14509
+ var networkLogEntrySchema = external_exports.object({
14510
+ timestamp: external_exports.string(),
14511
+ method: external_exports.string(),
14512
+ url: external_exports.string(),
14513
+ status: external_exports.number(),
14514
+ latency_ms: external_exports.number(),
14515
+ request_size: external_exports.number(),
14516
+ response_size: external_exports.number()
14517
+ });
14518
+ var networkLogsResponseSchema = external_exports.object({
14519
+ networkLogs: external_exports.array(networkLogEntrySchema),
14520
+ hasMore: external_exports.boolean()
14521
+ });
14479
14522
  var telemetryResponseSchema = external_exports.object({
14480
14523
  systemLog: external_exports.string(),
14481
14524
  metrics: external_exports.array(telemetryMetricSchema)
@@ -14568,6 +14611,29 @@ var runAgentEventsContract = c3.router({
14568
14611
  summary: "Get agent events with pagination"
14569
14612
  }
14570
14613
  });
14614
+ var runNetworkLogsContract = c3.router({
14615
+ /**
14616
+ * GET /api/agent/runs/:id/telemetry/network
14617
+ * Get network logs with pagination (for vm0 logs --network)
14618
+ */
14619
+ getNetworkLogs: {
14620
+ method: "GET",
14621
+ path: "/api/agent/runs/:id/telemetry/network",
14622
+ pathParams: external_exports.object({
14623
+ id: external_exports.string().min(1, "Run ID is required")
14624
+ }),
14625
+ query: external_exports.object({
14626
+ since: external_exports.coerce.number().optional(),
14627
+ limit: external_exports.coerce.number().min(1).max(100).default(5)
14628
+ }),
14629
+ responses: {
14630
+ 200: networkLogsResponseSchema,
14631
+ 401: apiErrorSchema,
14632
+ 404: apiErrorSchema
14633
+ },
14634
+ summary: "Get network logs with pagination"
14635
+ }
14636
+ });
14571
14637
 
14572
14638
  // ../../packages/core/src/contracts/sessions.ts
14573
14639
  var c4 = initContract();
@@ -14917,10 +14983,19 @@ var metricDataSchema = external_exports.object({
14917
14983
  disk_used: external_exports.number(),
14918
14984
  disk_total: external_exports.number()
14919
14985
  });
14986
+ var networkLogSchema = external_exports.object({
14987
+ timestamp: external_exports.string(),
14988
+ method: external_exports.string(),
14989
+ url: external_exports.string(),
14990
+ status: external_exports.number(),
14991
+ latency_ms: external_exports.number(),
14992
+ request_size: external_exports.number(),
14993
+ response_size: external_exports.number()
14994
+ });
14920
14995
  var webhookTelemetryContract = c6.router({
14921
14996
  /**
14922
14997
  * POST /api/webhooks/agent/telemetry
14923
- * Receive telemetry data (system log and metrics) from sandbox
14998
+ * Receive telemetry data (system log, metrics, and network logs) from sandbox
14924
14999
  */
14925
15000
  send: {
14926
15001
  method: "POST",
@@ -14928,7 +15003,8 @@ var webhookTelemetryContract = c6.router({
14928
15003
  body: external_exports.object({
14929
15004
  runId: external_exports.string().min(1, "runId is required"),
14930
15005
  systemLog: external_exports.string().optional(),
14931
- metrics: external_exports.array(metricDataSchema).optional()
15006
+ metrics: external_exports.array(metricDataSchema).optional(),
15007
+ networkLogs: external_exports.array(networkLogSchema).optional()
14932
15008
  }),
14933
15009
  responses: {
14934
15010
  200: external_exports.object({
@@ -15171,6 +15247,20 @@ var cronCleanupSandboxesContract = c10.router({
15171
15247
  }
15172
15248
  });
15173
15249
 
15250
+ // ../../packages/core/src/contracts/proxy.ts
15251
+ var proxyErrorSchema = external_exports.object({
15252
+ error: external_exports.object({
15253
+ message: external_exports.string(),
15254
+ code: external_exports.enum([
15255
+ "UNAUTHORIZED",
15256
+ "BAD_REQUEST",
15257
+ "BAD_GATEWAY",
15258
+ "INTERNAL_ERROR"
15259
+ ]),
15260
+ targetUrl: external_exports.string().optional()
15261
+ })
15262
+ });
15263
+
15174
15264
  // src/lib/secrets-client.ts
15175
15265
  async function getClientConfig() {
15176
15266
  const baseUrl = await getApiUrl();
@@ -15836,6 +15926,27 @@ function formatMetric(metric) {
15836
15926
  const diskPercent = (metric.disk_used / metric.disk_total * 100).toFixed(1);
15837
15927
  return `[${metric.ts}] CPU: ${metric.cpu.toFixed(1)}% | Mem: ${formatBytes5(metric.mem_used)}/${formatBytes5(metric.mem_total)} (${memPercent}%) | Disk: ${formatBytes5(metric.disk_used)}/${formatBytes5(metric.disk_total)} (${diskPercent}%)`;
15838
15928
  }
15929
+ function formatNetworkLog(entry) {
15930
+ let statusColor;
15931
+ if (entry.status >= 200 && entry.status < 300) {
15932
+ statusColor = chalk18.green;
15933
+ } else if (entry.status >= 300 && entry.status < 400) {
15934
+ statusColor = chalk18.yellow;
15935
+ } else if (entry.status >= 400) {
15936
+ statusColor = chalk18.red;
15937
+ } else {
15938
+ statusColor = chalk18.gray;
15939
+ }
15940
+ let latencyColor;
15941
+ if (entry.latency_ms < 500) {
15942
+ latencyColor = chalk18.green;
15943
+ } else if (entry.latency_ms < 2e3) {
15944
+ latencyColor = chalk18.yellow;
15945
+ } else {
15946
+ latencyColor = chalk18.red;
15947
+ }
15948
+ return `[${entry.timestamp}] ${chalk18.cyan(entry.method.padEnd(6))} ${statusColor(entry.status)} ${latencyColor(entry.latency_ms + "ms")} ${formatBytes5(entry.request_size)}/${formatBytes5(entry.response_size)} ${chalk18.gray(entry.url)}`;
15949
+ }
15839
15950
  function renderAgentEvent(event) {
15840
15951
  const parsed = ClaudeEventParser.parse(
15841
15952
  event.eventData
@@ -15846,22 +15957,26 @@ function renderAgentEvent(event) {
15846
15957
  }
15847
15958
  }
15848
15959
  function getLogType(options) {
15849
- const selected = [options.agent, options.system, options.metrics].filter(
15850
- Boolean
15851
- ).length;
15960
+ const selected = [
15961
+ options.agent,
15962
+ options.system,
15963
+ options.metrics,
15964
+ options.network
15965
+ ].filter(Boolean).length;
15852
15966
  if (selected > 1) {
15853
15967
  console.error(
15854
15968
  chalk18.red(
15855
- "Options --agent, --system, and --metrics are mutually exclusive"
15969
+ "Options --agent, --system, --metrics, and --network are mutually exclusive"
15856
15970
  )
15857
15971
  );
15858
15972
  process.exit(1);
15859
15973
  }
15860
15974
  if (options.system) return "system";
15861
15975
  if (options.metrics) return "metrics";
15976
+ if (options.network) return "network";
15862
15977
  return "agent";
15863
15978
  }
15864
- var logsCommand = new Command20().name("logs").description("View logs for an agent run").argument("<runId>", "Run ID to fetch logs for").option("-a, --agent", "Show agent events (default)").option("-s, --system", "Show system log").option("-m, --metrics", "Show metrics").option(
15979
+ var logsCommand = new Command20().name("logs").description("View logs for an agent run").argument("<runId>", "Run ID to fetch logs for").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(
15865
15980
  "--since <time>",
15866
15981
  "Show logs since timestamp (e.g., 5m, 2h, 1d, 2024-01-15T10:30:00Z, 1705312200)"
15867
15982
  ).option(
@@ -15890,6 +16005,9 @@ var logsCommand = new Command20().name("logs").description("View logs for an age
15890
16005
  case "metrics":
15891
16006
  await showMetrics(runId, { since, limit });
15892
16007
  break;
16008
+ case "network":
16009
+ await showNetworkLogs(runId, { since, limit });
16010
+ break;
15893
16011
  }
15894
16012
  } catch (error43) {
15895
16013
  handleError(error43, runId);
@@ -15947,6 +16065,28 @@ async function showMetrics(runId, options) {
15947
16065
  );
15948
16066
  }
15949
16067
  }
16068
+ async function showNetworkLogs(runId, options) {
16069
+ const response = await apiClient.getNetworkLogs(runId, options);
16070
+ if (response.networkLogs.length === 0) {
16071
+ console.log(
16072
+ chalk18.yellow(
16073
+ "No network logs found for this run. Network logs are only captured when beta_network_security is enabled."
16074
+ )
16075
+ );
16076
+ return;
16077
+ }
16078
+ for (const entry of response.networkLogs) {
16079
+ console.log(formatNetworkLog(entry));
16080
+ }
16081
+ if (response.hasMore) {
16082
+ console.log();
16083
+ console.log(
16084
+ chalk18.gray(
16085
+ `Showing ${response.networkLogs.length} network logs. Use --limit to see more.`
16086
+ )
16087
+ );
16088
+ }
16089
+ }
15950
16090
  function handleError(error43, runId) {
15951
16091
  if (error43 instanceof Error) {
15952
16092
  if (error43.message.includes("Not authenticated")) {
@@ -15966,7 +16106,7 @@ function handleError(error43, runId) {
15966
16106
 
15967
16107
  // src/index.ts
15968
16108
  var program = new Command21();
15969
- program.name("vm0").description("VM0 CLI - A modern build tool").version("4.5.1");
16109
+ program.name("vm0").description("VM0 CLI - A modern build tool").version("4.6.0");
15970
16110
  program.command("info").description("Display environment information").action(async () => {
15971
16111
  console.log(chalk19.cyan("System Information:"));
15972
16112
  console.log(`Node Version: ${process.version}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vm0/cli",
3
- "version": "4.5.1",
3
+ "version": "4.6.0",
4
4
  "description": "CLI application",
5
5
  "repository": {
6
6
  "type": "git",