@vm0/runner 2.3.2 → 2.4.1

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 +902 -616
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -4816,25 +4816,143 @@ var apiErrorSchema = z3.object({
4816
4816
  });
4817
4817
 
4818
4818
  // ../../packages/core/src/contracts/composes.ts
4819
+ import { z as z5 } from "zod";
4820
+
4821
+ // ../../packages/core/src/contracts/runners.ts
4819
4822
  import { z as z4 } from "zod";
4820
4823
  var c = initContract();
4821
- var composeVersionQuerySchema = z4.preprocess(
4824
+ var firewallRuleSchema = z4.object({
4825
+ domain: z4.string().optional(),
4826
+ ip: z4.string().optional(),
4827
+ /** Terminal rule - value is the action (ALLOW or DENY) */
4828
+ final: z4.enum(["ALLOW", "DENY"]).optional(),
4829
+ /** Action for domain/ip rules */
4830
+ action: z4.enum(["ALLOW", "DENY"]).optional()
4831
+ });
4832
+ var experimentalFirewallSchema = z4.object({
4833
+ enabled: z4.boolean(),
4834
+ rules: z4.array(firewallRuleSchema).optional(),
4835
+ experimental_mitm: z4.boolean().optional(),
4836
+ experimental_seal_secrets: z4.boolean().optional()
4837
+ });
4838
+ var runnerGroupSchema = z4.string().regex(
4839
+ /^[a-z0-9-]+\/[a-z0-9-]+$/,
4840
+ "Runner group must be in scope/name format (e.g., acme/production)"
4841
+ );
4842
+ var jobSchema = z4.object({
4843
+ runId: z4.string().uuid(),
4844
+ prompt: z4.string(),
4845
+ agentComposeVersionId: z4.string(),
4846
+ vars: z4.record(z4.string(), z4.string()).nullable(),
4847
+ secretNames: z4.array(z4.string()).nullable(),
4848
+ checkpointId: z4.string().uuid().nullable()
4849
+ });
4850
+ var runnersPollContract = c.router({
4851
+ poll: {
4852
+ method: "POST",
4853
+ path: "/api/runners/poll",
4854
+ body: z4.object({
4855
+ group: runnerGroupSchema
4856
+ }),
4857
+ responses: {
4858
+ 200: z4.object({
4859
+ job: jobSchema.nullable()
4860
+ }),
4861
+ 400: apiErrorSchema,
4862
+ 401: apiErrorSchema,
4863
+ 500: apiErrorSchema
4864
+ },
4865
+ summary: "Poll for pending jobs (long-polling with 30s timeout)"
4866
+ }
4867
+ });
4868
+ var storageEntrySchema = z4.object({
4869
+ mountPath: z4.string(),
4870
+ archiveUrl: z4.string().nullable()
4871
+ });
4872
+ var artifactEntrySchema = z4.object({
4873
+ mountPath: z4.string(),
4874
+ archiveUrl: z4.string().nullable(),
4875
+ vasStorageName: z4.string(),
4876
+ vasVersionId: z4.string()
4877
+ });
4878
+ var storageManifestSchema = z4.object({
4879
+ storages: z4.array(storageEntrySchema),
4880
+ artifact: artifactEntrySchema.nullable()
4881
+ });
4882
+ var resumeSessionSchema = z4.object({
4883
+ sessionId: z4.string(),
4884
+ sessionHistory: z4.string()
4885
+ });
4886
+ var storedExecutionContextSchema = z4.object({
4887
+ workingDir: z4.string(),
4888
+ storageManifest: storageManifestSchema.nullable(),
4889
+ environment: z4.record(z4.string(), z4.string()).nullable(),
4890
+ resumeSession: resumeSessionSchema.nullable(),
4891
+ encryptedSecrets: z4.string().nullable(),
4892
+ // AES-256-GCM encrypted secrets
4893
+ cliAgentType: z4.string(),
4894
+ experimentalFirewall: experimentalFirewallSchema.optional()
4895
+ });
4896
+ var executionContextSchema = z4.object({
4897
+ runId: z4.string().uuid(),
4898
+ prompt: z4.string(),
4899
+ agentComposeVersionId: z4.string(),
4900
+ vars: z4.record(z4.string(), z4.string()).nullable(),
4901
+ secretNames: z4.array(z4.string()).nullable(),
4902
+ checkpointId: z4.string().uuid().nullable(),
4903
+ sandboxToken: z4.string(),
4904
+ // New fields for E2B parity:
4905
+ workingDir: z4.string(),
4906
+ storageManifest: storageManifestSchema.nullable(),
4907
+ environment: z4.record(z4.string(), z4.string()).nullable(),
4908
+ resumeSession: resumeSessionSchema.nullable(),
4909
+ secretValues: z4.array(z4.string()).nullable(),
4910
+ cliAgentType: z4.string(),
4911
+ // Experimental firewall configuration
4912
+ experimentalFirewall: experimentalFirewallSchema.optional()
4913
+ });
4914
+ var runnersJobClaimContract = c.router({
4915
+ claim: {
4916
+ method: "POST",
4917
+ path: "/api/runners/jobs/:id/claim",
4918
+ pathParams: z4.object({
4919
+ id: z4.string().uuid()
4920
+ }),
4921
+ body: z4.object({}),
4922
+ responses: {
4923
+ 200: executionContextSchema,
4924
+ 400: apiErrorSchema,
4925
+ 401: apiErrorSchema,
4926
+ 403: apiErrorSchema,
4927
+ // Job does not belong to user
4928
+ 404: apiErrorSchema,
4929
+ 409: apiErrorSchema,
4930
+ // Already claimed
4931
+ 500: apiErrorSchema
4932
+ },
4933
+ summary: "Claim a pending job for execution"
4934
+ }
4935
+ });
4936
+
4937
+ // ../../packages/core/src/contracts/composes.ts
4938
+ var c2 = initContract();
4939
+ var composeVersionQuerySchema = z5.preprocess(
4822
4940
  (val) => val === void 0 || val === null ? void 0 : String(val),
4823
- z4.string().min(1, "Missing version query parameter").regex(
4941
+ z5.string().min(1, "Missing version query parameter").regex(
4824
4942
  /^[a-f0-9]{8,64}$|^latest$/i,
4825
4943
  "Version must be 8-64 hex characters or 'latest'"
4826
4944
  )
4827
4945
  );
4828
- var agentNameSchema = z4.string().min(3, "Agent name must be at least 3 characters").max(64, "Agent name must be 64 characters or less").regex(
4946
+ var agentNameSchema = z5.string().min(3, "Agent name must be at least 3 characters").max(64, "Agent name must be 64 characters or less").regex(
4829
4947
  /^[a-zA-Z0-9][a-zA-Z0-9-]{1,62}[a-zA-Z0-9]$/,
4830
4948
  "Agent name must start and end with letter or number, and contain only letters, numbers, and hyphens"
4831
4949
  );
4832
- var volumeConfigSchema = z4.object({
4833
- name: z4.string().min(1, "Volume name is required"),
4834
- version: z4.string().min(1, "Volume version is required")
4950
+ var volumeConfigSchema = z5.object({
4951
+ name: z5.string().min(1, "Volume name is required"),
4952
+ version: z5.string().min(1, "Volume version is required")
4835
4953
  });
4836
4954
  var SUPPORTED_APPS = ["github"];
4837
- var appStringSchema = z4.string().regex(
4955
+ var appStringSchema = z5.string().regex(
4838
4956
  /^[a-z]+(:(?:latest|dev))?$/,
4839
4957
  "App must be in format 'app' or 'app:tag' (e.g., 'github', 'github:dev')"
4840
4958
  ).refine(
@@ -4844,74 +4962,80 @@ var appStringSchema = z4.string().regex(
4844
4962
  },
4845
4963
  `Unsupported app. Supported apps: ${SUPPORTED_APPS.join(", ")}`
4846
4964
  );
4847
- var agentDefinitionSchema = z4.object({
4848
- description: z4.string().optional(),
4965
+ var agentDefinitionSchema = z5.object({
4966
+ description: z5.string().optional(),
4849
4967
  /**
4850
4968
  * @deprecated Use `apps` field instead for pre-installed tools.
4851
4969
  * This field will be removed in a future version.
4852
4970
  */
4853
- image: z4.string().optional(),
4854
- provider: z4.string().min(1, "Provider is required"),
4971
+ image: z5.string().optional(),
4972
+ provider: z5.string().min(1, "Provider is required"),
4855
4973
  /**
4856
4974
  * Array of pre-installed apps/tools for the agent environment.
4857
4975
  * Format: "app" or "app:tag" (e.g., "github", "github:dev", "github:latest")
4858
4976
  * Default tag is "latest" if not specified.
4859
4977
  * Currently supported apps: "github" (includes GitHub CLI)
4860
4978
  */
4861
- apps: z4.array(appStringSchema).optional(),
4862
- volumes: z4.array(z4.string()).optional(),
4863
- working_dir: z4.string().optional(),
4979
+ apps: z5.array(appStringSchema).optional(),
4980
+ volumes: z5.array(z5.string()).optional(),
4981
+ working_dir: z5.string().optional(),
4864
4982
  // Optional when provider supports auto-config
4865
- environment: z4.record(z4.string(), z4.string()).optional(),
4983
+ environment: z5.record(z5.string(), z5.string()).optional(),
4866
4984
  /**
4867
4985
  * Enable network security mode for secrets.
4868
4986
  * When true, secrets are encrypted into proxy tokens and all traffic
4869
4987
  * is routed through mitmproxy -> VM0 Proxy for decryption.
4870
4988
  * Default: false (plaintext secrets in env vars)
4871
4989
  */
4872
- experimental_network_security: z4.boolean().optional().default(false),
4990
+ experimental_network_security: z5.boolean().optional().default(false),
4873
4991
  /**
4874
4992
  * Path to instructions file (e.g., AGENTS.md).
4875
4993
  * Auto-uploaded as volume and mounted at /home/user/.claude/CLAUDE.md
4876
4994
  */
4877
- instructions: z4.string().optional(),
4995
+ instructions: z5.string().optional(),
4878
4996
  /**
4879
4997
  * Array of GitHub tree URLs for agent skills.
4880
4998
  * Each skill is auto-downloaded and mounted at /home/user/.claude/skills/{skillName}/
4881
4999
  */
4882
- skills: z4.array(z4.string()).optional(),
5000
+ skills: z5.array(z5.string()).optional(),
4883
5001
  /**
4884
5002
  * Route this agent to a self-hosted runner instead of E2B.
4885
5003
  * When specified, runs will be queued for the specified runner group.
4886
5004
  */
4887
- experimental_runner: z4.object({
4888
- group: z4.string().regex(
5005
+ experimental_runner: z5.object({
5006
+ group: z5.string().regex(
4889
5007
  /^[a-z0-9-]+\/[a-z0-9-]+$/,
4890
5008
  "Runner group must be in scope/name format (e.g., acme/production)"
4891
5009
  )
4892
- }).optional()
5010
+ }).optional(),
5011
+ /**
5012
+ * Experimental firewall configuration for network egress control.
5013
+ * Requires experimental_runner to be configured.
5014
+ * When enabled, filters outbound traffic by domain/IP rules.
5015
+ */
5016
+ experimental_firewall: experimentalFirewallSchema.optional()
4893
5017
  });
4894
- var agentComposeContentSchema = z4.object({
4895
- version: z4.string().min(1, "Version is required"),
4896
- agents: z4.record(z4.string(), agentDefinitionSchema),
4897
- volumes: z4.record(z4.string(), volumeConfigSchema).optional()
5018
+ var agentComposeContentSchema = z5.object({
5019
+ version: z5.string().min(1, "Version is required"),
5020
+ agents: z5.record(z5.string(), agentDefinitionSchema),
5021
+ volumes: z5.record(z5.string(), volumeConfigSchema).optional()
4898
5022
  });
4899
- var composeResponseSchema = z4.object({
4900
- id: z4.string(),
4901
- name: z4.string(),
4902
- headVersionId: z4.string().nullable(),
5023
+ var composeResponseSchema = z5.object({
5024
+ id: z5.string(),
5025
+ name: z5.string(),
5026
+ headVersionId: z5.string().nullable(),
4903
5027
  content: agentComposeContentSchema.nullable(),
4904
- createdAt: z4.string(),
4905
- updatedAt: z4.string()
5028
+ createdAt: z5.string(),
5029
+ updatedAt: z5.string()
4906
5030
  });
4907
- var createComposeResponseSchema = z4.object({
4908
- composeId: z4.string(),
4909
- name: z4.string(),
4910
- versionId: z4.string(),
4911
- action: z4.enum(["created", "existing"]),
4912
- updatedAt: z4.string()
5031
+ var createComposeResponseSchema = z5.object({
5032
+ composeId: z5.string(),
5033
+ name: z5.string(),
5034
+ versionId: z5.string(),
5035
+ action: z5.enum(["created", "existing"]),
5036
+ updatedAt: z5.string()
4913
5037
  });
4914
- var composesMainContract = c.router({
5038
+ var composesMainContract = c2.router({
4915
5039
  /**
4916
5040
  * GET /api/agent/composes?name={name}&scope={scope}
4917
5041
  * Get agent compose by name with HEAD version content
@@ -4920,9 +5044,9 @@ var composesMainContract = c.router({
4920
5044
  getByName: {
4921
5045
  method: "GET",
4922
5046
  path: "/api/agent/composes",
4923
- query: z4.object({
4924
- name: z4.string().min(1, "Missing name query parameter"),
4925
- scope: z4.string().optional()
5047
+ query: z5.object({
5048
+ name: z5.string().min(1, "Missing name query parameter"),
5049
+ scope: z5.string().optional()
4926
5050
  }),
4927
5051
  responses: {
4928
5052
  200: composeResponseSchema,
@@ -4941,7 +5065,7 @@ var composesMainContract = c.router({
4941
5065
  create: {
4942
5066
  method: "POST",
4943
5067
  path: "/api/agent/composes",
4944
- body: z4.object({
5068
+ body: z5.object({
4945
5069
  content: agentComposeContentSchema
4946
5070
  }),
4947
5071
  responses: {
@@ -4953,7 +5077,7 @@ var composesMainContract = c.router({
4953
5077
  summary: "Create or update agent compose version"
4954
5078
  }
4955
5079
  });
4956
- var composesByIdContract = c.router({
5080
+ var composesByIdContract = c2.router({
4957
5081
  /**
4958
5082
  * GET /api/agent/composes/:id
4959
5083
  * Get agent compose by ID with HEAD version content
@@ -4961,8 +5085,8 @@ var composesByIdContract = c.router({
4961
5085
  getById: {
4962
5086
  method: "GET",
4963
5087
  path: "/api/agent/composes/:id",
4964
- pathParams: z4.object({
4965
- id: z4.string().min(1, "Compose ID is required")
5088
+ pathParams: z5.object({
5089
+ id: z5.string().min(1, "Compose ID is required")
4966
5090
  }),
4967
5091
  responses: {
4968
5092
  200: composeResponseSchema,
@@ -4972,7 +5096,7 @@ var composesByIdContract = c.router({
4972
5096
  summary: "Get agent compose by ID"
4973
5097
  }
4974
5098
  });
4975
- var composesVersionsContract = c.router({
5099
+ var composesVersionsContract = c2.router({
4976
5100
  /**
4977
5101
  * GET /api/agent/composes/versions?composeId={id}&version={hash|tag}
4978
5102
  * Resolve a version specifier to a full version ID
@@ -4980,14 +5104,14 @@ var composesVersionsContract = c.router({
4980
5104
  resolveVersion: {
4981
5105
  method: "GET",
4982
5106
  path: "/api/agent/composes/versions",
4983
- query: z4.object({
4984
- composeId: z4.string().min(1, "Missing composeId query parameter"),
5107
+ query: z5.object({
5108
+ composeId: z5.string().min(1, "Missing composeId query parameter"),
4985
5109
  version: composeVersionQuerySchema
4986
5110
  }),
4987
5111
  responses: {
4988
- 200: z4.object({
4989
- versionId: z4.string(),
4990
- tag: z4.string().optional()
5112
+ 200: z5.object({
5113
+ versionId: z5.string(),
5114
+ tag: z5.string().optional()
4991
5115
  }),
4992
5116
  400: apiErrorSchema,
4993
5117
  401: apiErrorSchema,
@@ -4996,12 +5120,12 @@ var composesVersionsContract = c.router({
4996
5120
  summary: "Resolve version specifier to full version ID"
4997
5121
  }
4998
5122
  });
4999
- var composeListItemSchema = z4.object({
5000
- name: z4.string(),
5001
- headVersionId: z4.string().nullable(),
5002
- updatedAt: z4.string()
5123
+ var composeListItemSchema = z5.object({
5124
+ name: z5.string(),
5125
+ headVersionId: z5.string().nullable(),
5126
+ updatedAt: z5.string()
5003
5127
  });
5004
- var composesListContract = c.router({
5128
+ var composesListContract = c2.router({
5005
5129
  /**
5006
5130
  * GET /api/agent/composes/list?scope={scope}
5007
5131
  * List all agent composes for a scope
@@ -5010,12 +5134,12 @@ var composesListContract = c.router({
5010
5134
  list: {
5011
5135
  method: "GET",
5012
5136
  path: "/api/agent/composes/list",
5013
- query: z4.object({
5014
- scope: z4.string().optional()
5137
+ query: z5.object({
5138
+ scope: z5.string().optional()
5015
5139
  }),
5016
5140
  responses: {
5017
- 200: z4.object({
5018
- composes: z4.array(composeListItemSchema)
5141
+ 200: z5.object({
5142
+ composes: z5.array(composeListItemSchema)
5019
5143
  }),
5020
5144
  400: apiErrorSchema,
5021
5145
  401: apiErrorSchema
@@ -5025,83 +5149,83 @@ var composesListContract = c.router({
5025
5149
  });
5026
5150
 
5027
5151
  // ../../packages/core/src/contracts/runs.ts
5028
- import { z as z5 } from "zod";
5029
- var c2 = initContract();
5030
- var runStatusSchema = z5.enum([
5152
+ import { z as z6 } from "zod";
5153
+ var c3 = initContract();
5154
+ var runStatusSchema = z6.enum([
5031
5155
  "pending",
5032
5156
  "running",
5033
5157
  "completed",
5034
5158
  "failed",
5035
5159
  "timeout"
5036
5160
  ]);
5037
- var unifiedRunRequestSchema = z5.object({
5161
+ var unifiedRunRequestSchema = z6.object({
5038
5162
  // High-level shortcuts (mutually exclusive with each other)
5039
- checkpointId: z5.string().optional(),
5040
- sessionId: z5.string().optional(),
5163
+ checkpointId: z6.string().optional(),
5164
+ sessionId: z6.string().optional(),
5041
5165
  // Base parameters (can be used directly or overridden after shortcut expansion)
5042
- agentComposeId: z5.string().optional(),
5043
- agentComposeVersionId: z5.string().optional(),
5044
- conversationId: z5.string().optional(),
5045
- artifactName: z5.string().optional(),
5046
- artifactVersion: z5.string().optional(),
5047
- vars: z5.record(z5.string(), z5.string()).optional(),
5048
- secrets: z5.record(z5.string(), z5.string()).optional(),
5049
- volumeVersions: z5.record(z5.string(), z5.string()).optional(),
5166
+ agentComposeId: z6.string().optional(),
5167
+ agentComposeVersionId: z6.string().optional(),
5168
+ conversationId: z6.string().optional(),
5169
+ artifactName: z6.string().optional(),
5170
+ artifactVersion: z6.string().optional(),
5171
+ vars: z6.record(z6.string(), z6.string()).optional(),
5172
+ secrets: z6.record(z6.string(), z6.string()).optional(),
5173
+ volumeVersions: z6.record(z6.string(), z6.string()).optional(),
5050
5174
  // Required
5051
- prompt: z5.string().min(1, "Missing prompt")
5175
+ prompt: z6.string().min(1, "Missing prompt")
5052
5176
  });
5053
- var createRunResponseSchema = z5.object({
5054
- runId: z5.string(),
5177
+ var createRunResponseSchema = z6.object({
5178
+ runId: z6.string(),
5055
5179
  status: runStatusSchema,
5056
- sandboxId: z5.string().optional(),
5057
- output: z5.string().optional(),
5058
- error: z5.string().optional(),
5059
- executionTimeMs: z5.number().optional(),
5060
- createdAt: z5.string()
5061
- });
5062
- var getRunResponseSchema = z5.object({
5063
- runId: z5.string(),
5064
- agentComposeVersionId: z5.string(),
5180
+ sandboxId: z6.string().optional(),
5181
+ output: z6.string().optional(),
5182
+ error: z6.string().optional(),
5183
+ executionTimeMs: z6.number().optional(),
5184
+ createdAt: z6.string()
5185
+ });
5186
+ var getRunResponseSchema = z6.object({
5187
+ runId: z6.string(),
5188
+ agentComposeVersionId: z6.string(),
5065
5189
  status: runStatusSchema,
5066
- prompt: z5.string(),
5067
- vars: z5.record(z5.string(), z5.string()).optional(),
5068
- sandboxId: z5.string().optional(),
5069
- result: z5.object({
5070
- output: z5.string(),
5071
- executionTimeMs: z5.number()
5190
+ prompt: z6.string(),
5191
+ vars: z6.record(z6.string(), z6.string()).optional(),
5192
+ sandboxId: z6.string().optional(),
5193
+ result: z6.object({
5194
+ output: z6.string(),
5195
+ executionTimeMs: z6.number()
5072
5196
  }).optional(),
5073
- error: z5.string().optional(),
5074
- createdAt: z5.string(),
5075
- startedAt: z5.string().optional(),
5076
- completedAt: z5.string().optional()
5077
- });
5078
- var runEventSchema = z5.object({
5079
- sequenceNumber: z5.number(),
5080
- eventType: z5.string(),
5081
- eventData: z5.unknown(),
5082
- createdAt: z5.string()
5083
- });
5084
- var runResultSchema = z5.object({
5085
- checkpointId: z5.string(),
5086
- agentSessionId: z5.string(),
5087
- conversationId: z5.string(),
5088
- artifact: z5.record(z5.string(), z5.string()).optional(),
5197
+ error: z6.string().optional(),
5198
+ createdAt: z6.string(),
5199
+ startedAt: z6.string().optional(),
5200
+ completedAt: z6.string().optional()
5201
+ });
5202
+ var runEventSchema = z6.object({
5203
+ sequenceNumber: z6.number(),
5204
+ eventType: z6.string(),
5205
+ eventData: z6.unknown(),
5206
+ createdAt: z6.string()
5207
+ });
5208
+ var runResultSchema = z6.object({
5209
+ checkpointId: z6.string(),
5210
+ agentSessionId: z6.string(),
5211
+ conversationId: z6.string(),
5212
+ artifact: z6.record(z6.string(), z6.string()).optional(),
5089
5213
  // optional when run has no artifact
5090
- volumes: z5.record(z5.string(), z5.string()).optional()
5214
+ volumes: z6.record(z6.string(), z6.string()).optional()
5091
5215
  });
5092
- var runStateSchema = z5.object({
5216
+ var runStateSchema = z6.object({
5093
5217
  status: runStatusSchema,
5094
5218
  result: runResultSchema.optional(),
5095
- error: z5.string().optional()
5219
+ error: z6.string().optional()
5096
5220
  });
5097
- var eventsResponseSchema = z5.object({
5098
- events: z5.array(runEventSchema),
5099
- hasMore: z5.boolean(),
5100
- nextSequence: z5.number(),
5221
+ var eventsResponseSchema = z6.object({
5222
+ events: z6.array(runEventSchema),
5223
+ hasMore: z6.boolean(),
5224
+ nextSequence: z6.number(),
5101
5225
  run: runStateSchema,
5102
- provider: z5.string()
5226
+ provider: z6.string()
5103
5227
  });
5104
- var runsMainContract = c2.router({
5228
+ var runsMainContract = c3.router({
5105
5229
  /**
5106
5230
  * POST /api/agent/runs
5107
5231
  * Create and execute a new agent run
@@ -5119,7 +5243,7 @@ var runsMainContract = c2.router({
5119
5243
  summary: "Create and execute agent run"
5120
5244
  }
5121
5245
  });
5122
- var runsByIdContract = c2.router({
5246
+ var runsByIdContract = c3.router({
5123
5247
  /**
5124
5248
  * GET /api/agent/runs/:id
5125
5249
  * Get agent run status and results
@@ -5127,8 +5251,8 @@ var runsByIdContract = c2.router({
5127
5251
  getById: {
5128
5252
  method: "GET",
5129
5253
  path: "/api/agent/runs/:id",
5130
- pathParams: z5.object({
5131
- id: z5.string().min(1, "Run ID is required")
5254
+ pathParams: z6.object({
5255
+ id: z6.string().min(1, "Run ID is required")
5132
5256
  }),
5133
5257
  responses: {
5134
5258
  200: getRunResponseSchema,
@@ -5139,7 +5263,7 @@ var runsByIdContract = c2.router({
5139
5263
  summary: "Get agent run by ID"
5140
5264
  }
5141
5265
  });
5142
- var runEventsContract = c2.router({
5266
+ var runEventsContract = c3.router({
5143
5267
  /**
5144
5268
  * GET /api/agent/runs/:id/events
5145
5269
  * Poll for agent run events with pagination
@@ -5147,12 +5271,12 @@ var runEventsContract = c2.router({
5147
5271
  getEvents: {
5148
5272
  method: "GET",
5149
5273
  path: "/api/agent/runs/:id/events",
5150
- pathParams: z5.object({
5151
- id: z5.string().min(1, "Run ID is required")
5274
+ pathParams: z6.object({
5275
+ id: z6.string().min(1, "Run ID is required")
5152
5276
  }),
5153
- query: z5.object({
5154
- since: z5.coerce.number().default(0),
5155
- limit: z5.coerce.number().default(100)
5277
+ query: z6.object({
5278
+ since: z6.coerce.number().default(0),
5279
+ limit: z6.coerce.number().default(100)
5156
5280
  }),
5157
5281
  responses: {
5158
5282
  200: eventsResponseSchema,
@@ -5162,45 +5286,45 @@ var runEventsContract = c2.router({
5162
5286
  summary: "Get agent run events"
5163
5287
  }
5164
5288
  });
5165
- var telemetryMetricSchema = z5.object({
5166
- ts: z5.string(),
5167
- cpu: z5.number(),
5168
- mem_used: z5.number(),
5169
- mem_total: z5.number(),
5170
- disk_used: z5.number(),
5171
- disk_total: z5.number()
5172
- });
5173
- var systemLogResponseSchema = z5.object({
5174
- systemLog: z5.string(),
5175
- hasMore: z5.boolean()
5176
- });
5177
- var metricsResponseSchema = z5.object({
5178
- metrics: z5.array(telemetryMetricSchema),
5179
- hasMore: z5.boolean()
5180
- });
5181
- var agentEventsResponseSchema = z5.object({
5182
- events: z5.array(runEventSchema),
5183
- hasMore: z5.boolean(),
5184
- provider: z5.string()
5185
- });
5186
- var networkLogEntrySchema = z5.object({
5187
- timestamp: z5.string(),
5188
- method: z5.string(),
5189
- url: z5.string(),
5190
- status: z5.number(),
5191
- latency_ms: z5.number(),
5192
- request_size: z5.number(),
5193
- response_size: z5.number()
5194
- });
5195
- var networkLogsResponseSchema = z5.object({
5196
- networkLogs: z5.array(networkLogEntrySchema),
5197
- hasMore: z5.boolean()
5198
- });
5199
- var telemetryResponseSchema = z5.object({
5200
- systemLog: z5.string(),
5201
- metrics: z5.array(telemetryMetricSchema)
5202
- });
5203
- var runTelemetryContract = c2.router({
5289
+ var telemetryMetricSchema = z6.object({
5290
+ ts: z6.string(),
5291
+ cpu: z6.number(),
5292
+ mem_used: z6.number(),
5293
+ mem_total: z6.number(),
5294
+ disk_used: z6.number(),
5295
+ disk_total: z6.number()
5296
+ });
5297
+ var systemLogResponseSchema = z6.object({
5298
+ systemLog: z6.string(),
5299
+ hasMore: z6.boolean()
5300
+ });
5301
+ var metricsResponseSchema = z6.object({
5302
+ metrics: z6.array(telemetryMetricSchema),
5303
+ hasMore: z6.boolean()
5304
+ });
5305
+ var agentEventsResponseSchema = z6.object({
5306
+ events: z6.array(runEventSchema),
5307
+ hasMore: z6.boolean(),
5308
+ provider: z6.string()
5309
+ });
5310
+ var networkLogEntrySchema = z6.object({
5311
+ timestamp: z6.string(),
5312
+ method: z6.string(),
5313
+ url: z6.string(),
5314
+ status: z6.number(),
5315
+ latency_ms: z6.number(),
5316
+ request_size: z6.number(),
5317
+ response_size: z6.number()
5318
+ });
5319
+ var networkLogsResponseSchema = z6.object({
5320
+ networkLogs: z6.array(networkLogEntrySchema),
5321
+ hasMore: z6.boolean()
5322
+ });
5323
+ var telemetryResponseSchema = z6.object({
5324
+ systemLog: z6.string(),
5325
+ metrics: z6.array(telemetryMetricSchema)
5326
+ });
5327
+ var runTelemetryContract = c3.router({
5204
5328
  /**
5205
5329
  * GET /api/agent/runs/:id/telemetry
5206
5330
  * Get aggregated telemetry data for a run (legacy combined format)
@@ -5208,8 +5332,8 @@ var runTelemetryContract = c2.router({
5208
5332
  getTelemetry: {
5209
5333
  method: "GET",
5210
5334
  path: "/api/agent/runs/:id/telemetry",
5211
- pathParams: z5.object({
5212
- id: z5.string().min(1, "Run ID is required")
5335
+ pathParams: z6.object({
5336
+ id: z6.string().min(1, "Run ID is required")
5213
5337
  }),
5214
5338
  responses: {
5215
5339
  200: telemetryResponseSchema,
@@ -5219,7 +5343,7 @@ var runTelemetryContract = c2.router({
5219
5343
  summary: "Get run telemetry data"
5220
5344
  }
5221
5345
  });
5222
- var runSystemLogContract = c2.router({
5346
+ var runSystemLogContract = c3.router({
5223
5347
  /**
5224
5348
  * GET /api/agent/runs/:id/telemetry/system-log
5225
5349
  * Get system log with pagination
@@ -5227,13 +5351,13 @@ var runSystemLogContract = c2.router({
5227
5351
  getSystemLog: {
5228
5352
  method: "GET",
5229
5353
  path: "/api/agent/runs/:id/telemetry/system-log",
5230
- pathParams: z5.object({
5231
- id: z5.string().min(1, "Run ID is required")
5354
+ pathParams: z6.object({
5355
+ id: z6.string().min(1, "Run ID is required")
5232
5356
  }),
5233
- query: z5.object({
5234
- since: z5.coerce.number().optional(),
5235
- limit: z5.coerce.number().min(1).max(100).default(5),
5236
- order: z5.enum(["asc", "desc"]).default("desc")
5357
+ query: z6.object({
5358
+ since: z6.coerce.number().optional(),
5359
+ limit: z6.coerce.number().min(1).max(100).default(5),
5360
+ order: z6.enum(["asc", "desc"]).default("desc")
5237
5361
  }),
5238
5362
  responses: {
5239
5363
  200: systemLogResponseSchema,
@@ -5243,7 +5367,7 @@ var runSystemLogContract = c2.router({
5243
5367
  summary: "Get system log with pagination"
5244
5368
  }
5245
5369
  });
5246
- var runMetricsContract = c2.router({
5370
+ var runMetricsContract = c3.router({
5247
5371
  /**
5248
5372
  * GET /api/agent/runs/:id/telemetry/metrics
5249
5373
  * Get metrics with pagination
@@ -5251,13 +5375,13 @@ var runMetricsContract = c2.router({
5251
5375
  getMetrics: {
5252
5376
  method: "GET",
5253
5377
  path: "/api/agent/runs/:id/telemetry/metrics",
5254
- pathParams: z5.object({
5255
- id: z5.string().min(1, "Run ID is required")
5378
+ pathParams: z6.object({
5379
+ id: z6.string().min(1, "Run ID is required")
5256
5380
  }),
5257
- query: z5.object({
5258
- since: z5.coerce.number().optional(),
5259
- limit: z5.coerce.number().min(1).max(100).default(5),
5260
- order: z5.enum(["asc", "desc"]).default("desc")
5381
+ query: z6.object({
5382
+ since: z6.coerce.number().optional(),
5383
+ limit: z6.coerce.number().min(1).max(100).default(5),
5384
+ order: z6.enum(["asc", "desc"]).default("desc")
5261
5385
  }),
5262
5386
  responses: {
5263
5387
  200: metricsResponseSchema,
@@ -5267,7 +5391,7 @@ var runMetricsContract = c2.router({
5267
5391
  summary: "Get metrics with pagination"
5268
5392
  }
5269
5393
  });
5270
- var runAgentEventsContract = c2.router({
5394
+ var runAgentEventsContract = c3.router({
5271
5395
  /**
5272
5396
  * GET /api/agent/runs/:id/telemetry/agent
5273
5397
  * Get agent events with pagination (for vm0 logs default)
@@ -5275,13 +5399,13 @@ var runAgentEventsContract = c2.router({
5275
5399
  getAgentEvents: {
5276
5400
  method: "GET",
5277
5401
  path: "/api/agent/runs/:id/telemetry/agent",
5278
- pathParams: z5.object({
5279
- id: z5.string().min(1, "Run ID is required")
5402
+ pathParams: z6.object({
5403
+ id: z6.string().min(1, "Run ID is required")
5280
5404
  }),
5281
- query: z5.object({
5282
- since: z5.coerce.number().optional(),
5283
- limit: z5.coerce.number().min(1).max(100).default(5),
5284
- order: z5.enum(["asc", "desc"]).default("desc")
5405
+ query: z6.object({
5406
+ since: z6.coerce.number().optional(),
5407
+ limit: z6.coerce.number().min(1).max(100).default(5),
5408
+ order: z6.enum(["asc", "desc"]).default("desc")
5285
5409
  }),
5286
5410
  responses: {
5287
5411
  200: agentEventsResponseSchema,
@@ -5291,7 +5415,7 @@ var runAgentEventsContract = c2.router({
5291
5415
  summary: "Get agent events with pagination"
5292
5416
  }
5293
5417
  });
5294
- var runNetworkLogsContract = c2.router({
5418
+ var runNetworkLogsContract = c3.router({
5295
5419
  /**
5296
5420
  * GET /api/agent/runs/:id/telemetry/network
5297
5421
  * Get network logs with pagination (for vm0 logs --network)
@@ -5299,13 +5423,13 @@ var runNetworkLogsContract = c2.router({
5299
5423
  getNetworkLogs: {
5300
5424
  method: "GET",
5301
5425
  path: "/api/agent/runs/:id/telemetry/network",
5302
- pathParams: z5.object({
5303
- id: z5.string().min(1, "Run ID is required")
5426
+ pathParams: z6.object({
5427
+ id: z6.string().min(1, "Run ID is required")
5304
5428
  }),
5305
- query: z5.object({
5306
- since: z5.coerce.number().optional(),
5307
- limit: z5.coerce.number().min(1).max(100).default(5),
5308
- order: z5.enum(["asc", "desc"]).default("desc")
5429
+ query: z6.object({
5430
+ since: z6.coerce.number().optional(),
5431
+ limit: z6.coerce.number().min(1).max(100).default(5),
5432
+ order: z6.enum(["asc", "desc"]).default("desc")
5309
5433
  }),
5310
5434
  responses: {
5311
5435
  200: networkLogsResponseSchema,
@@ -5317,22 +5441,22 @@ var runNetworkLogsContract = c2.router({
5317
5441
  });
5318
5442
 
5319
5443
  // ../../packages/core/src/contracts/storages.ts
5320
- import { z as z6 } from "zod";
5321
- var c3 = initContract();
5322
- var storageTypeSchema = z6.enum(["volume", "artifact"]);
5323
- var versionQuerySchema = z6.preprocess(
5444
+ import { z as z7 } from "zod";
5445
+ var c4 = initContract();
5446
+ var storageTypeSchema = z7.enum(["volume", "artifact"]);
5447
+ var versionQuerySchema = z7.preprocess(
5324
5448
  (val) => val === void 0 || val === null ? void 0 : String(val),
5325
- z6.string().regex(/^[a-f0-9]{8,64}$/i, "Version must be 8-64 hex characters").optional()
5449
+ z7.string().regex(/^[a-f0-9]{8,64}$/i, "Version must be 8-64 hex characters").optional()
5326
5450
  );
5327
- var uploadStorageResponseSchema = z6.object({
5328
- name: z6.string(),
5329
- versionId: z6.string(),
5330
- size: z6.number(),
5331
- fileCount: z6.number(),
5451
+ var uploadStorageResponseSchema = z7.object({
5452
+ name: z7.string(),
5453
+ versionId: z7.string(),
5454
+ size: z7.number(),
5455
+ fileCount: z7.number(),
5332
5456
  type: storageTypeSchema,
5333
- deduplicated: z6.boolean()
5457
+ deduplicated: z7.boolean()
5334
5458
  });
5335
- var storagesContract = c3.router({
5459
+ var storagesContract = c4.router({
5336
5460
  /**
5337
5461
  * POST /api/storages
5338
5462
  * Upload a storage (tar.gz file)
@@ -5348,7 +5472,7 @@ var storagesContract = c3.router({
5348
5472
  method: "POST",
5349
5473
  path: "/api/storages",
5350
5474
  contentType: "multipart/form-data",
5351
- body: c3.type(),
5475
+ body: c4.type(),
5352
5476
  responses: {
5353
5477
  200: uploadStorageResponseSchema,
5354
5478
  400: apiErrorSchema,
@@ -5370,15 +5494,15 @@ var storagesContract = c3.router({
5370
5494
  download: {
5371
5495
  method: "GET",
5372
5496
  path: "/api/storages",
5373
- query: z6.object({
5374
- name: z6.string().min(1, "Storage name is required"),
5497
+ query: z7.object({
5498
+ name: z7.string().min(1, "Storage name is required"),
5375
5499
  version: versionQuerySchema
5376
5500
  }),
5377
5501
  responses: {
5378
5502
  // Binary response - actual handling done at route level
5379
- 200: c3.otherResponse({
5503
+ 200: c4.otherResponse({
5380
5504
  contentType: "application/gzip",
5381
- body: c3.type()
5505
+ body: c4.type()
5382
5506
  }),
5383
5507
  400: apiErrorSchema,
5384
5508
  401: apiErrorSchema,
@@ -5388,40 +5512,40 @@ var storagesContract = c3.router({
5388
5512
  summary: "Download storage archive"
5389
5513
  }
5390
5514
  });
5391
- var fileEntryWithHashSchema = z6.object({
5392
- path: z6.string().min(1, "File path is required"),
5393
- hash: z6.string().length(64, "Hash must be SHA-256 (64 hex chars)"),
5394
- size: z6.number().int().min(0, "Size must be non-negative")
5515
+ var fileEntryWithHashSchema = z7.object({
5516
+ path: z7.string().min(1, "File path is required"),
5517
+ hash: z7.string().length(64, "Hash must be SHA-256 (64 hex chars)"),
5518
+ size: z7.number().int().min(0, "Size must be non-negative")
5395
5519
  });
5396
- var storageChangesSchema = z6.object({
5397
- added: z6.array(z6.string()),
5398
- modified: z6.array(z6.string()),
5399
- deleted: z6.array(z6.string())
5520
+ var storageChangesSchema = z7.object({
5521
+ added: z7.array(z7.string()),
5522
+ modified: z7.array(z7.string()),
5523
+ deleted: z7.array(z7.string())
5400
5524
  });
5401
- var presignedUploadSchema = z6.object({
5402
- key: z6.string(),
5403
- presignedUrl: z6.string().url()
5525
+ var presignedUploadSchema = z7.object({
5526
+ key: z7.string(),
5527
+ presignedUrl: z7.string().url()
5404
5528
  });
5405
- var storagesPrepareContract = c3.router({
5529
+ var storagesPrepareContract = c4.router({
5406
5530
  prepare: {
5407
5531
  method: "POST",
5408
5532
  path: "/api/storages/prepare",
5409
- body: z6.object({
5410
- storageName: z6.string().min(1, "Storage name is required"),
5533
+ body: z7.object({
5534
+ storageName: z7.string().min(1, "Storage name is required"),
5411
5535
  storageType: storageTypeSchema,
5412
- files: z6.array(fileEntryWithHashSchema),
5413
- force: z6.boolean().optional(),
5414
- runId: z6.string().optional(),
5536
+ files: z7.array(fileEntryWithHashSchema),
5537
+ force: z7.boolean().optional(),
5538
+ runId: z7.string().optional(),
5415
5539
  // For sandbox auth
5416
- baseVersion: z6.string().optional(),
5540
+ baseVersion: z7.string().optional(),
5417
5541
  // For incremental uploads
5418
5542
  changes: storageChangesSchema.optional()
5419
5543
  }),
5420
5544
  responses: {
5421
- 200: z6.object({
5422
- versionId: z6.string(),
5423
- existing: z6.boolean(),
5424
- uploads: z6.object({
5545
+ 200: z7.object({
5546
+ versionId: z7.string(),
5547
+ existing: z7.boolean(),
5548
+ uploads: z7.object({
5425
5549
  archive: presignedUploadSchema,
5426
5550
  manifest: presignedUploadSchema
5427
5551
  }).optional()
@@ -5434,26 +5558,26 @@ var storagesPrepareContract = c3.router({
5434
5558
  summary: "Prepare for direct S3 upload"
5435
5559
  }
5436
5560
  });
5437
- var storagesCommitContract = c3.router({
5561
+ var storagesCommitContract = c4.router({
5438
5562
  commit: {
5439
5563
  method: "POST",
5440
5564
  path: "/api/storages/commit",
5441
- body: z6.object({
5442
- storageName: z6.string().min(1, "Storage name is required"),
5565
+ body: z7.object({
5566
+ storageName: z7.string().min(1, "Storage name is required"),
5443
5567
  storageType: storageTypeSchema,
5444
- versionId: z6.string().min(1, "Version ID is required"),
5445
- files: z6.array(fileEntryWithHashSchema),
5446
- runId: z6.string().optional(),
5447
- message: z6.string().optional()
5568
+ versionId: z7.string().min(1, "Version ID is required"),
5569
+ files: z7.array(fileEntryWithHashSchema),
5570
+ runId: z7.string().optional(),
5571
+ message: z7.string().optional()
5448
5572
  }),
5449
5573
  responses: {
5450
- 200: z6.object({
5451
- success: z6.literal(true),
5452
- versionId: z6.string(),
5453
- storageName: z6.string(),
5454
- size: z6.number(),
5455
- fileCount: z6.number(),
5456
- deduplicated: z6.boolean().optional()
5574
+ 200: z7.object({
5575
+ success: z7.literal(true),
5576
+ versionId: z7.string(),
5577
+ storageName: z7.string(),
5578
+ size: z7.number(),
5579
+ fileCount: z7.number(),
5580
+ deduplicated: z7.boolean().optional()
5457
5581
  }),
5458
5582
  400: apiErrorSchema,
5459
5583
  401: apiErrorSchema,
@@ -5465,30 +5589,30 @@ var storagesCommitContract = c3.router({
5465
5589
  summary: "Commit uploaded storage"
5466
5590
  }
5467
5591
  });
5468
- var storagesDownloadContract = c3.router({
5592
+ var storagesDownloadContract = c4.router({
5469
5593
  download: {
5470
5594
  method: "GET",
5471
5595
  path: "/api/storages/download",
5472
- query: z6.object({
5473
- name: z6.string().min(1, "Storage name is required"),
5596
+ query: z7.object({
5597
+ name: z7.string().min(1, "Storage name is required"),
5474
5598
  type: storageTypeSchema,
5475
5599
  version: versionQuerySchema
5476
5600
  }),
5477
5601
  responses: {
5478
5602
  // Normal response with presigned URL
5479
- 200: z6.union([
5480
- z6.object({
5481
- url: z6.string().url(),
5482
- versionId: z6.string(),
5483
- fileCount: z6.number(),
5484
- size: z6.number()
5603
+ 200: z7.union([
5604
+ z7.object({
5605
+ url: z7.string().url(),
5606
+ versionId: z7.string(),
5607
+ fileCount: z7.number(),
5608
+ size: z7.number()
5485
5609
  }),
5486
5610
  // Empty artifact response
5487
- z6.object({
5488
- empty: z6.literal(true),
5489
- versionId: z6.string(),
5490
- fileCount: z6.literal(0),
5491
- size: z6.literal(0)
5611
+ z7.object({
5612
+ empty: z7.literal(true),
5613
+ versionId: z7.string(),
5614
+ fileCount: z7.literal(0),
5615
+ size: z7.literal(0)
5492
5616
  })
5493
5617
  ]),
5494
5618
  400: apiErrorSchema,
@@ -5499,20 +5623,20 @@ var storagesDownloadContract = c3.router({
5499
5623
  summary: "Get presigned download URL"
5500
5624
  }
5501
5625
  });
5502
- var storagesListContract = c3.router({
5626
+ var storagesListContract = c4.router({
5503
5627
  list: {
5504
5628
  method: "GET",
5505
5629
  path: "/api/storages/list",
5506
- query: z6.object({
5630
+ query: z7.object({
5507
5631
  type: storageTypeSchema
5508
5632
  }),
5509
5633
  responses: {
5510
- 200: z6.array(
5511
- z6.object({
5512
- name: z6.string(),
5513
- size: z6.number(),
5514
- fileCount: z6.number(),
5515
- updatedAt: z6.string()
5634
+ 200: z7.array(
5635
+ z7.object({
5636
+ name: z7.string(),
5637
+ size: z7.number(),
5638
+ fileCount: z7.number(),
5639
+ updatedAt: z7.string()
5516
5640
  })
5517
5641
  ),
5518
5642
  401: apiErrorSchema,
@@ -5523,20 +5647,20 @@ var storagesListContract = c3.router({
5523
5647
  });
5524
5648
 
5525
5649
  // ../../packages/core/src/contracts/webhooks.ts
5526
- import { z as z7 } from "zod";
5527
- var c4 = initContract();
5528
- var agentEventSchema = z7.object({
5529
- type: z7.string(),
5530
- sequenceNumber: z7.number().int().positive()
5650
+ import { z as z8 } from "zod";
5651
+ var c5 = initContract();
5652
+ var agentEventSchema = z8.object({
5653
+ type: z8.string(),
5654
+ sequenceNumber: z8.number().int().positive()
5531
5655
  }).passthrough();
5532
- var artifactSnapshotSchema = z7.object({
5533
- artifactName: z7.string(),
5534
- artifactVersion: z7.string()
5656
+ var artifactSnapshotSchema = z8.object({
5657
+ artifactName: z8.string(),
5658
+ artifactVersion: z8.string()
5535
5659
  });
5536
- var volumeVersionsSnapshotSchema = z7.object({
5537
- versions: z7.record(z7.string(), z7.string())
5660
+ var volumeVersionsSnapshotSchema = z8.object({
5661
+ versions: z8.record(z8.string(), z8.string())
5538
5662
  });
5539
- var webhookEventsContract = c4.router({
5663
+ var webhookEventsContract = c5.router({
5540
5664
  /**
5541
5665
  * POST /api/webhooks/agent/events
5542
5666
  * Receive agent events from E2B sandbox
@@ -5544,15 +5668,15 @@ var webhookEventsContract = c4.router({
5544
5668
  send: {
5545
5669
  method: "POST",
5546
5670
  path: "/api/webhooks/agent/events",
5547
- body: z7.object({
5548
- runId: z7.string().min(1, "runId is required"),
5549
- events: z7.array(agentEventSchema).min(1, "events array cannot be empty")
5671
+ body: z8.object({
5672
+ runId: z8.string().min(1, "runId is required"),
5673
+ events: z8.array(agentEventSchema).min(1, "events array cannot be empty")
5550
5674
  }),
5551
5675
  responses: {
5552
- 200: z7.object({
5553
- received: z7.number(),
5554
- firstSequence: z7.number(),
5555
- lastSequence: z7.number()
5676
+ 200: z8.object({
5677
+ received: z8.number(),
5678
+ firstSequence: z8.number(),
5679
+ lastSequence: z8.number()
5556
5680
  }),
5557
5681
  400: apiErrorSchema,
5558
5682
  401: apiErrorSchema,
@@ -5562,7 +5686,7 @@ var webhookEventsContract = c4.router({
5562
5686
  summary: "Receive agent events from sandbox"
5563
5687
  }
5564
5688
  });
5565
- var webhookCompleteContract = c4.router({
5689
+ var webhookCompleteContract = c5.router({
5566
5690
  /**
5567
5691
  * POST /api/webhooks/agent/complete
5568
5692
  * Handle agent run completion (success or failure)
@@ -5570,15 +5694,15 @@ var webhookCompleteContract = c4.router({
5570
5694
  complete: {
5571
5695
  method: "POST",
5572
5696
  path: "/api/webhooks/agent/complete",
5573
- body: z7.object({
5574
- runId: z7.string().min(1, "runId is required"),
5575
- exitCode: z7.number(),
5576
- error: z7.string().optional()
5697
+ body: z8.object({
5698
+ runId: z8.string().min(1, "runId is required"),
5699
+ exitCode: z8.number(),
5700
+ error: z8.string().optional()
5577
5701
  }),
5578
5702
  responses: {
5579
- 200: z7.object({
5580
- success: z7.boolean(),
5581
- status: z7.enum(["completed", "failed"])
5703
+ 200: z8.object({
5704
+ success: z8.boolean(),
5705
+ status: z8.enum(["completed", "failed"])
5582
5706
  }),
5583
5707
  400: apiErrorSchema,
5584
5708
  401: apiErrorSchema,
@@ -5588,7 +5712,7 @@ var webhookCompleteContract = c4.router({
5588
5712
  summary: "Handle agent run completion"
5589
5713
  }
5590
5714
  });
5591
- var webhookCheckpointsContract = c4.router({
5715
+ var webhookCheckpointsContract = c5.router({
5592
5716
  /**
5593
5717
  * POST /api/webhooks/agent/checkpoints
5594
5718
  * Create checkpoint for completed agent run
@@ -5596,21 +5720,21 @@ var webhookCheckpointsContract = c4.router({
5596
5720
  create: {
5597
5721
  method: "POST",
5598
5722
  path: "/api/webhooks/agent/checkpoints",
5599
- body: z7.object({
5600
- runId: z7.string().min(1, "runId is required"),
5601
- cliAgentType: z7.string().min(1, "cliAgentType is required"),
5602
- cliAgentSessionId: z7.string().min(1, "cliAgentSessionId is required"),
5603
- cliAgentSessionHistory: z7.string().min(1, "cliAgentSessionHistory is required"),
5723
+ body: z8.object({
5724
+ runId: z8.string().min(1, "runId is required"),
5725
+ cliAgentType: z8.string().min(1, "cliAgentType is required"),
5726
+ cliAgentSessionId: z8.string().min(1, "cliAgentSessionId is required"),
5727
+ cliAgentSessionHistory: z8.string().min(1, "cliAgentSessionHistory is required"),
5604
5728
  artifactSnapshot: artifactSnapshotSchema.optional(),
5605
5729
  volumeVersionsSnapshot: volumeVersionsSnapshotSchema.optional()
5606
5730
  }),
5607
5731
  responses: {
5608
- 200: z7.object({
5609
- checkpointId: z7.string(),
5610
- agentSessionId: z7.string(),
5611
- conversationId: z7.string(),
5732
+ 200: z8.object({
5733
+ checkpointId: z8.string(),
5734
+ agentSessionId: z8.string(),
5735
+ conversationId: z8.string(),
5612
5736
  artifact: artifactSnapshotSchema.optional(),
5613
- volumes: z7.record(z7.string(), z7.string()).optional()
5737
+ volumes: z8.record(z8.string(), z8.string()).optional()
5614
5738
  }),
5615
5739
  400: apiErrorSchema,
5616
5740
  401: apiErrorSchema,
@@ -5620,7 +5744,7 @@ var webhookCheckpointsContract = c4.router({
5620
5744
  summary: "Create checkpoint for agent run"
5621
5745
  }
5622
5746
  });
5623
- var webhookHeartbeatContract = c4.router({
5747
+ var webhookHeartbeatContract = c5.router({
5624
5748
  /**
5625
5749
  * POST /api/webhooks/agent/heartbeat
5626
5750
  * Receive heartbeat signals from E2B sandbox
@@ -5628,12 +5752,12 @@ var webhookHeartbeatContract = c4.router({
5628
5752
  send: {
5629
5753
  method: "POST",
5630
5754
  path: "/api/webhooks/agent/heartbeat",
5631
- body: z7.object({
5632
- runId: z7.string().min(1, "runId is required")
5755
+ body: z8.object({
5756
+ runId: z8.string().min(1, "runId is required")
5633
5757
  }),
5634
5758
  responses: {
5635
- 200: z7.object({
5636
- ok: z7.boolean()
5759
+ 200: z8.object({
5760
+ ok: z8.boolean()
5637
5761
  }),
5638
5762
  400: apiErrorSchema,
5639
5763
  401: apiErrorSchema,
@@ -5643,7 +5767,7 @@ var webhookHeartbeatContract = c4.router({
5643
5767
  summary: "Receive heartbeat from sandbox"
5644
5768
  }
5645
5769
  });
5646
- var webhookStoragesContract = c4.router({
5770
+ var webhookStoragesContract = c5.router({
5647
5771
  /**
5648
5772
  * POST /api/webhooks/agent/storages
5649
5773
  * Create a new version of a storage from sandbox
@@ -5658,13 +5782,13 @@ var webhookStoragesContract = c4.router({
5658
5782
  method: "POST",
5659
5783
  path: "/api/webhooks/agent/storages",
5660
5784
  contentType: "multipart/form-data",
5661
- body: c4.type(),
5785
+ body: c5.type(),
5662
5786
  responses: {
5663
- 200: z7.object({
5664
- versionId: z7.string(),
5665
- storageName: z7.string(),
5666
- size: z7.number(),
5667
- fileCount: z7.number()
5787
+ 200: z8.object({
5788
+ versionId: z8.string(),
5789
+ storageName: z8.string(),
5790
+ size: z8.number(),
5791
+ fileCount: z8.number()
5668
5792
  }),
5669
5793
  400: apiErrorSchema,
5670
5794
  401: apiErrorSchema,
@@ -5674,7 +5798,7 @@ var webhookStoragesContract = c4.router({
5674
5798
  summary: "Upload storage version from sandbox"
5675
5799
  }
5676
5800
  });
5677
- var webhookStoragesIncrementalContract = c4.router({
5801
+ var webhookStoragesIncrementalContract = c5.router({
5678
5802
  /**
5679
5803
  * POST /api/webhooks/agent/storages/incremental
5680
5804
  * Create a new version using incremental upload
@@ -5691,19 +5815,19 @@ var webhookStoragesIncrementalContract = c4.router({
5691
5815
  method: "POST",
5692
5816
  path: "/api/webhooks/agent/storages/incremental",
5693
5817
  contentType: "multipart/form-data",
5694
- body: c4.type(),
5818
+ body: c5.type(),
5695
5819
  responses: {
5696
- 200: z7.object({
5697
- versionId: z7.string(),
5698
- storageName: z7.string(),
5699
- size: z7.number(),
5700
- fileCount: z7.number(),
5701
- incrementalStats: z7.object({
5702
- addedFiles: z7.number(),
5703
- modifiedFiles: z7.number(),
5704
- deletedFiles: z7.number(),
5705
- unchangedFiles: z7.number(),
5706
- bytesUploaded: z7.number()
5820
+ 200: z8.object({
5821
+ versionId: z8.string(),
5822
+ storageName: z8.string(),
5823
+ size: z8.number(),
5824
+ fileCount: z8.number(),
5825
+ incrementalStats: z8.object({
5826
+ addedFiles: z8.number(),
5827
+ modifiedFiles: z8.number(),
5828
+ deletedFiles: z8.number(),
5829
+ unchangedFiles: z8.number(),
5830
+ bytesUploaded: z8.number()
5707
5831
  }).optional()
5708
5832
  }),
5709
5833
  400: apiErrorSchema,
@@ -5714,24 +5838,24 @@ var webhookStoragesIncrementalContract = c4.router({
5714
5838
  summary: "Upload storage version incrementally from sandbox"
5715
5839
  }
5716
5840
  });
5717
- var metricDataSchema = z7.object({
5718
- ts: z7.string(),
5719
- cpu: z7.number(),
5720
- mem_used: z7.number(),
5721
- mem_total: z7.number(),
5722
- disk_used: z7.number(),
5723
- disk_total: z7.number()
5724
- });
5725
- var networkLogSchema = z7.object({
5726
- timestamp: z7.string(),
5727
- method: z7.string(),
5728
- url: z7.string(),
5729
- status: z7.number(),
5730
- latency_ms: z7.number(),
5731
- request_size: z7.number(),
5732
- response_size: z7.number()
5733
- });
5734
- var webhookTelemetryContract = c4.router({
5841
+ var metricDataSchema = z8.object({
5842
+ ts: z8.string(),
5843
+ cpu: z8.number(),
5844
+ mem_used: z8.number(),
5845
+ mem_total: z8.number(),
5846
+ disk_used: z8.number(),
5847
+ disk_total: z8.number()
5848
+ });
5849
+ var networkLogSchema = z8.object({
5850
+ timestamp: z8.string(),
5851
+ method: z8.string(),
5852
+ url: z8.string(),
5853
+ status: z8.number(),
5854
+ latency_ms: z8.number(),
5855
+ request_size: z8.number(),
5856
+ response_size: z8.number()
5857
+ });
5858
+ var webhookTelemetryContract = c5.router({
5735
5859
  /**
5736
5860
  * POST /api/webhooks/agent/telemetry
5737
5861
  * Receive telemetry data (system log, metrics, and network logs) from sandbox
@@ -5739,16 +5863,16 @@ var webhookTelemetryContract = c4.router({
5739
5863
  send: {
5740
5864
  method: "POST",
5741
5865
  path: "/api/webhooks/agent/telemetry",
5742
- body: z7.object({
5743
- runId: z7.string().min(1, "runId is required"),
5744
- systemLog: z7.string().optional(),
5745
- metrics: z7.array(metricDataSchema).optional(),
5746
- networkLogs: z7.array(networkLogSchema).optional()
5866
+ body: z8.object({
5867
+ runId: z8.string().min(1, "runId is required"),
5868
+ systemLog: z8.string().optional(),
5869
+ metrics: z8.array(metricDataSchema).optional(),
5870
+ networkLogs: z8.array(networkLogSchema).optional()
5747
5871
  }),
5748
5872
  responses: {
5749
- 200: z7.object({
5750
- success: z7.boolean(),
5751
- id: z7.string()
5873
+ 200: z8.object({
5874
+ success: z8.boolean(),
5875
+ id: z8.string()
5752
5876
  }),
5753
5877
  400: apiErrorSchema,
5754
5878
  401: apiErrorSchema,
@@ -5758,25 +5882,25 @@ var webhookTelemetryContract = c4.router({
5758
5882
  summary: "Receive telemetry data from sandbox"
5759
5883
  }
5760
5884
  });
5761
- var webhookStoragesPrepareContract = c4.router({
5885
+ var webhookStoragesPrepareContract = c5.router({
5762
5886
  prepare: {
5763
5887
  method: "POST",
5764
5888
  path: "/api/webhooks/agent/storages/prepare",
5765
- body: z7.object({
5766
- runId: z7.string().min(1, "runId is required"),
5889
+ body: z8.object({
5890
+ runId: z8.string().min(1, "runId is required"),
5767
5891
  // Required for webhook auth
5768
- storageName: z7.string().min(1, "Storage name is required"),
5892
+ storageName: z8.string().min(1, "Storage name is required"),
5769
5893
  storageType: storageTypeSchema,
5770
- files: z7.array(fileEntryWithHashSchema),
5771
- force: z7.boolean().optional(),
5772
- baseVersion: z7.string().optional(),
5894
+ files: z8.array(fileEntryWithHashSchema),
5895
+ force: z8.boolean().optional(),
5896
+ baseVersion: z8.string().optional(),
5773
5897
  changes: storageChangesSchema.optional()
5774
5898
  }),
5775
5899
  responses: {
5776
- 200: z7.object({
5777
- versionId: z7.string(),
5778
- existing: z7.boolean(),
5779
- uploads: z7.object({
5900
+ 200: z8.object({
5901
+ versionId: z8.string(),
5902
+ existing: z8.boolean(),
5903
+ uploads: z8.object({
5780
5904
  archive: presignedUploadSchema,
5781
5905
  manifest: presignedUploadSchema
5782
5906
  }).optional()
@@ -5789,27 +5913,27 @@ var webhookStoragesPrepareContract = c4.router({
5789
5913
  summary: "Prepare for direct S3 upload from sandbox"
5790
5914
  }
5791
5915
  });
5792
- var webhookStoragesCommitContract = c4.router({
5916
+ var webhookStoragesCommitContract = c5.router({
5793
5917
  commit: {
5794
5918
  method: "POST",
5795
5919
  path: "/api/webhooks/agent/storages/commit",
5796
- body: z7.object({
5797
- runId: z7.string().min(1, "runId is required"),
5920
+ body: z8.object({
5921
+ runId: z8.string().min(1, "runId is required"),
5798
5922
  // Required for webhook auth
5799
- storageName: z7.string().min(1, "Storage name is required"),
5923
+ storageName: z8.string().min(1, "Storage name is required"),
5800
5924
  storageType: storageTypeSchema,
5801
- versionId: z7.string().min(1, "Version ID is required"),
5802
- files: z7.array(fileEntryWithHashSchema),
5803
- message: z7.string().optional()
5925
+ versionId: z8.string().min(1, "Version ID is required"),
5926
+ files: z8.array(fileEntryWithHashSchema),
5927
+ message: z8.string().optional()
5804
5928
  }),
5805
5929
  responses: {
5806
- 200: z7.object({
5807
- success: z7.literal(true),
5808
- versionId: z7.string(),
5809
- storageName: z7.string(),
5810
- size: z7.number(),
5811
- fileCount: z7.number(),
5812
- deduplicated: z7.boolean().optional()
5930
+ 200: z8.object({
5931
+ success: z8.literal(true),
5932
+ versionId: z8.string(),
5933
+ storageName: z8.string(),
5934
+ size: z8.number(),
5935
+ fileCount: z8.number(),
5936
+ deduplicated: z8.boolean().optional()
5813
5937
  }),
5814
5938
  400: apiErrorSchema,
5815
5939
  401: apiErrorSchema,
@@ -5823,13 +5947,13 @@ var webhookStoragesCommitContract = c4.router({
5823
5947
  });
5824
5948
 
5825
5949
  // ../../packages/core/src/contracts/cli-auth.ts
5826
- import { z as z8 } from "zod";
5827
- var c5 = initContract();
5828
- var oauthErrorSchema = z8.object({
5829
- error: z8.string(),
5830
- error_description: z8.string()
5950
+ import { z as z9 } from "zod";
5951
+ var c6 = initContract();
5952
+ var oauthErrorSchema = z9.object({
5953
+ error: z9.string(),
5954
+ error_description: z9.string()
5831
5955
  });
5832
- var cliAuthDeviceContract = c5.router({
5956
+ var cliAuthDeviceContract = c6.router({
5833
5957
  /**
5834
5958
  * POST /api/cli/auth/device
5835
5959
  * Initiate device authorization flow
@@ -5837,21 +5961,21 @@ var cliAuthDeviceContract = c5.router({
5837
5961
  create: {
5838
5962
  method: "POST",
5839
5963
  path: "/api/cli/auth/device",
5840
- body: z8.object({}).optional(),
5964
+ body: z9.object({}).optional(),
5841
5965
  responses: {
5842
- 200: z8.object({
5843
- device_code: z8.string(),
5844
- user_code: z8.string(),
5845
- verification_url: z8.string(),
5846
- expires_in: z8.number(),
5847
- interval: z8.number()
5966
+ 200: z9.object({
5967
+ device_code: z9.string(),
5968
+ user_code: z9.string(),
5969
+ verification_url: z9.string(),
5970
+ expires_in: z9.number(),
5971
+ interval: z9.number()
5848
5972
  }),
5849
5973
  500: oauthErrorSchema
5850
5974
  },
5851
5975
  summary: "Initiate device authorization flow"
5852
5976
  }
5853
5977
  });
5854
- var cliAuthTokenContract = c5.router({
5978
+ var cliAuthTokenContract = c6.router({
5855
5979
  /**
5856
5980
  * POST /api/cli/auth/token
5857
5981
  * Exchange device code for access token
@@ -5859,16 +5983,16 @@ var cliAuthTokenContract = c5.router({
5859
5983
  exchange: {
5860
5984
  method: "POST",
5861
5985
  path: "/api/cli/auth/token",
5862
- body: z8.object({
5863
- device_code: z8.string().min(1, "device_code is required")
5986
+ body: z9.object({
5987
+ device_code: z9.string().min(1, "device_code is required")
5864
5988
  }),
5865
5989
  responses: {
5866
5990
  // Success - token issued
5867
- 200: z8.object({
5868
- access_token: z8.string(),
5869
- refresh_token: z8.string(),
5870
- token_type: z8.literal("Bearer"),
5871
- expires_in: z8.number()
5991
+ 200: z9.object({
5992
+ access_token: z9.string(),
5993
+ refresh_token: z9.string(),
5994
+ token_type: z9.literal("Bearer"),
5995
+ expires_in: z9.number()
5872
5996
  }),
5873
5997
  // Authorization pending
5874
5998
  202: oauthErrorSchema,
@@ -5881,9 +6005,9 @@ var cliAuthTokenContract = c5.router({
5881
6005
  });
5882
6006
 
5883
6007
  // ../../packages/core/src/contracts/auth.ts
5884
- import { z as z9 } from "zod";
5885
- var c6 = initContract();
5886
- var authContract = c6.router({
6008
+ import { z as z10 } from "zod";
6009
+ var c7 = initContract();
6010
+ var authContract = c7.router({
5887
6011
  /**
5888
6012
  * GET /api/auth/me
5889
6013
  * Get current user information
@@ -5892,9 +6016,9 @@ var authContract = c6.router({
5892
6016
  method: "GET",
5893
6017
  path: "/api/auth/me",
5894
6018
  responses: {
5895
- 200: z9.object({
5896
- userId: z9.string(),
5897
- email: z9.string()
6019
+ 200: z10.object({
6020
+ userId: z10.string(),
6021
+ email: z10.string()
5898
6022
  }),
5899
6023
  401: apiErrorSchema,
5900
6024
  404: apiErrorSchema,
@@ -5905,20 +6029,20 @@ var authContract = c6.router({
5905
6029
  });
5906
6030
 
5907
6031
  // ../../packages/core/src/contracts/cron.ts
5908
- import { z as z10 } from "zod";
5909
- var c7 = initContract();
5910
- var cleanupResultSchema = z10.object({
5911
- runId: z10.string(),
5912
- sandboxId: z10.string().nullable(),
5913
- status: z10.enum(["cleaned", "error"]),
5914
- error: z10.string().optional()
5915
- });
5916
- var cleanupResponseSchema = z10.object({
5917
- cleaned: z10.number(),
5918
- errors: z10.number(),
5919
- results: z10.array(cleanupResultSchema)
5920
- });
5921
- var cronCleanupSandboxesContract = c7.router({
6032
+ import { z as z11 } from "zod";
6033
+ var c8 = initContract();
6034
+ var cleanupResultSchema = z11.object({
6035
+ runId: z11.string(),
6036
+ sandboxId: z11.string().nullable(),
6037
+ status: z11.enum(["cleaned", "error"]),
6038
+ error: z11.string().optional()
6039
+ });
6040
+ var cleanupResponseSchema = z11.object({
6041
+ cleaned: z11.number(),
6042
+ errors: z11.number(),
6043
+ results: z11.array(cleanupResultSchema)
6044
+ });
6045
+ var cronCleanupSandboxesContract = c8.router({
5922
6046
  /**
5923
6047
  * GET /api/cron/cleanup-sandboxes
5924
6048
  * Cron job to cleanup sandboxes that have stopped sending heartbeats
@@ -5936,48 +6060,48 @@ var cronCleanupSandboxesContract = c7.router({
5936
6060
  });
5937
6061
 
5938
6062
  // ../../packages/core/src/contracts/proxy.ts
5939
- import { z as z11 } from "zod";
5940
- var proxyErrorSchema = z11.object({
5941
- error: z11.object({
5942
- message: z11.string(),
5943
- code: z11.enum([
6063
+ import { z as z12 } from "zod";
6064
+ var proxyErrorSchema = z12.object({
6065
+ error: z12.object({
6066
+ message: z12.string(),
6067
+ code: z12.enum([
5944
6068
  "UNAUTHORIZED",
5945
6069
  "BAD_REQUEST",
5946
6070
  "BAD_GATEWAY",
5947
6071
  "INTERNAL_ERROR"
5948
6072
  ]),
5949
- targetUrl: z11.string().optional()
6073
+ targetUrl: z12.string().optional()
5950
6074
  })
5951
6075
  });
5952
6076
 
5953
6077
  // ../../packages/core/src/contracts/scopes.ts
5954
- import { z as z12 } from "zod";
5955
- var c8 = initContract();
5956
- var scopeTypeSchema = z12.enum(["personal", "organization", "system"]);
5957
- var scopeSlugSchema = z12.string().min(3, "Scope slug must be at least 3 characters").max(64, "Scope slug must be at most 64 characters").regex(
6078
+ import { z as z13 } from "zod";
6079
+ var c9 = initContract();
6080
+ var scopeTypeSchema = z13.enum(["personal", "organization", "system"]);
6081
+ var scopeSlugSchema = z13.string().min(3, "Scope slug must be at least 3 characters").max(64, "Scope slug must be at most 64 characters").regex(
5958
6082
  /^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]{1,2}$/,
5959
6083
  "Scope slug must contain only lowercase letters, numbers, and hyphens, and must start and end with an alphanumeric character"
5960
6084
  ).refine(
5961
6085
  (slug) => !slug.startsWith("vm0"),
5962
6086
  "Scope slug cannot start with 'vm0' (reserved)"
5963
6087
  ).transform((s) => s.toLowerCase());
5964
- var scopeResponseSchema = z12.object({
5965
- id: z12.string().uuid(),
5966
- slug: z12.string(),
6088
+ var scopeResponseSchema = z13.object({
6089
+ id: z13.string().uuid(),
6090
+ slug: z13.string(),
5967
6091
  type: scopeTypeSchema,
5968
- displayName: z12.string().nullable(),
5969
- createdAt: z12.string(),
5970
- updatedAt: z12.string()
6092
+ displayName: z13.string().nullable(),
6093
+ createdAt: z13.string(),
6094
+ updatedAt: z13.string()
5971
6095
  });
5972
- var createScopeRequestSchema = z12.object({
6096
+ var createScopeRequestSchema = z13.object({
5973
6097
  slug: scopeSlugSchema,
5974
- displayName: z12.string().max(128).optional()
6098
+ displayName: z13.string().max(128).optional()
5975
6099
  });
5976
- var updateScopeRequestSchema = z12.object({
6100
+ var updateScopeRequestSchema = z13.object({
5977
6101
  slug: scopeSlugSchema,
5978
- force: z12.boolean().optional().default(false)
6102
+ force: z13.boolean().optional().default(false)
5979
6103
  });
5980
- var scopeContract = c8.router({
6104
+ var scopeContract = c9.router({
5981
6105
  /**
5982
6106
  * GET /api/scope
5983
6107
  * Get current user's scope
@@ -6032,42 +6156,42 @@ var scopeContract = c8.router({
6032
6156
  });
6033
6157
 
6034
6158
  // ../../packages/core/src/contracts/sessions.ts
6035
- import { z as z13 } from "zod";
6036
- var c9 = initContract();
6037
- var sessionResponseSchema = z13.object({
6038
- id: z13.string(),
6039
- agentComposeId: z13.string(),
6040
- agentComposeVersionId: z13.string().nullable(),
6041
- conversationId: z13.string().nullable(),
6042
- artifactName: z13.string().nullable(),
6043
- vars: z13.record(z13.string(), z13.string()).nullable(),
6044
- secretNames: z13.array(z13.string()).nullable(),
6045
- volumeVersions: z13.record(z13.string(), z13.string()).nullable(),
6046
- createdAt: z13.string(),
6047
- updatedAt: z13.string()
6159
+ import { z as z14 } from "zod";
6160
+ var c10 = initContract();
6161
+ var sessionResponseSchema = z14.object({
6162
+ id: z14.string(),
6163
+ agentComposeId: z14.string(),
6164
+ agentComposeVersionId: z14.string().nullable(),
6165
+ conversationId: z14.string().nullable(),
6166
+ artifactName: z14.string().nullable(),
6167
+ vars: z14.record(z14.string(), z14.string()).nullable(),
6168
+ secretNames: z14.array(z14.string()).nullable(),
6169
+ volumeVersions: z14.record(z14.string(), z14.string()).nullable(),
6170
+ createdAt: z14.string(),
6171
+ updatedAt: z14.string()
6048
6172
  });
6049
- var agentComposeSnapshotSchema = z13.object({
6050
- agentComposeVersionId: z13.string(),
6051
- vars: z13.record(z13.string(), z13.string()).optional(),
6052
- secretNames: z13.array(z13.string()).optional()
6173
+ var agentComposeSnapshotSchema = z14.object({
6174
+ agentComposeVersionId: z14.string(),
6175
+ vars: z14.record(z14.string(), z14.string()).optional(),
6176
+ secretNames: z14.array(z14.string()).optional()
6053
6177
  });
6054
- var artifactSnapshotSchema2 = z13.object({
6055
- artifactName: z13.string(),
6056
- artifactVersion: z13.string()
6178
+ var artifactSnapshotSchema2 = z14.object({
6179
+ artifactName: z14.string(),
6180
+ artifactVersion: z14.string()
6057
6181
  });
6058
- var volumeVersionsSnapshotSchema2 = z13.object({
6059
- versions: z13.record(z13.string(), z13.string())
6182
+ var volumeVersionsSnapshotSchema2 = z14.object({
6183
+ versions: z14.record(z14.string(), z14.string())
6060
6184
  });
6061
- var checkpointResponseSchema = z13.object({
6062
- id: z13.string(),
6063
- runId: z13.string(),
6064
- conversationId: z13.string(),
6185
+ var checkpointResponseSchema = z14.object({
6186
+ id: z14.string(),
6187
+ runId: z14.string(),
6188
+ conversationId: z14.string(),
6065
6189
  agentComposeSnapshot: agentComposeSnapshotSchema,
6066
6190
  artifactSnapshot: artifactSnapshotSchema2.nullable(),
6067
6191
  volumeVersionsSnapshot: volumeVersionsSnapshotSchema2.nullable(),
6068
- createdAt: z13.string()
6192
+ createdAt: z14.string()
6069
6193
  });
6070
- var sessionsByIdContract = c9.router({
6194
+ var sessionsByIdContract = c10.router({
6071
6195
  /**
6072
6196
  * GET /api/agent/sessions/:id
6073
6197
  * Get session by ID
@@ -6075,8 +6199,8 @@ var sessionsByIdContract = c9.router({
6075
6199
  getById: {
6076
6200
  method: "GET",
6077
6201
  path: "/api/agent/sessions/:id",
6078
- pathParams: z13.object({
6079
- id: z13.string().min(1, "Session ID is required")
6202
+ pathParams: z14.object({
6203
+ id: z14.string().min(1, "Session ID is required")
6080
6204
  }),
6081
6205
  responses: {
6082
6206
  200: sessionResponseSchema,
@@ -6087,7 +6211,7 @@ var sessionsByIdContract = c9.router({
6087
6211
  summary: "Get session by ID"
6088
6212
  }
6089
6213
  });
6090
- var checkpointsByIdContract = c9.router({
6214
+ var checkpointsByIdContract = c10.router({
6091
6215
  /**
6092
6216
  * GET /api/agent/checkpoints/:id
6093
6217
  * Get checkpoint by ID
@@ -6095,8 +6219,8 @@ var checkpointsByIdContract = c9.router({
6095
6219
  getById: {
6096
6220
  method: "GET",
6097
6221
  path: "/api/agent/checkpoints/:id",
6098
- pathParams: z13.object({
6099
- id: z13.string().min(1, "Checkpoint ID is required")
6222
+ pathParams: z14.object({
6223
+ id: z14.string().min(1, "Checkpoint ID is required")
6100
6224
  }),
6101
6225
  responses: {
6102
6226
  200: checkpointResponseSchema,
@@ -6108,108 +6232,6 @@ var checkpointsByIdContract = c9.router({
6108
6232
  }
6109
6233
  });
6110
6234
 
6111
- // ../../packages/core/src/contracts/runners.ts
6112
- import { z as z14 } from "zod";
6113
- var c10 = initContract();
6114
- var runnerGroupSchema = z14.string().regex(
6115
- /^[a-z0-9-]+\/[a-z0-9-]+$/,
6116
- "Runner group must be in scope/name format (e.g., acme/production)"
6117
- );
6118
- var jobSchema = z14.object({
6119
- runId: z14.string().uuid(),
6120
- prompt: z14.string(),
6121
- agentComposeVersionId: z14.string(),
6122
- vars: z14.record(z14.string(), z14.string()).nullable(),
6123
- secretNames: z14.array(z14.string()).nullable(),
6124
- checkpointId: z14.string().uuid().nullable()
6125
- });
6126
- var runnersPollContract = c10.router({
6127
- poll: {
6128
- method: "POST",
6129
- path: "/api/runners/poll",
6130
- body: z14.object({
6131
- group: runnerGroupSchema
6132
- }),
6133
- responses: {
6134
- 200: z14.object({
6135
- job: jobSchema.nullable()
6136
- }),
6137
- 400: apiErrorSchema,
6138
- 401: apiErrorSchema,
6139
- 500: apiErrorSchema
6140
- },
6141
- summary: "Poll for pending jobs (long-polling with 30s timeout)"
6142
- }
6143
- });
6144
- var storageEntrySchema = z14.object({
6145
- mountPath: z14.string(),
6146
- archiveUrl: z14.string().nullable()
6147
- });
6148
- var artifactEntrySchema = z14.object({
6149
- mountPath: z14.string(),
6150
- archiveUrl: z14.string().nullable(),
6151
- vasStorageName: z14.string(),
6152
- vasVersionId: z14.string()
6153
- });
6154
- var storageManifestSchema = z14.object({
6155
- storages: z14.array(storageEntrySchema),
6156
- artifact: artifactEntrySchema.nullable()
6157
- });
6158
- var resumeSessionSchema = z14.object({
6159
- sessionId: z14.string(),
6160
- sessionHistory: z14.string()
6161
- });
6162
- var storedExecutionContextSchema = z14.object({
6163
- workingDir: z14.string(),
6164
- storageManifest: storageManifestSchema.nullable(),
6165
- environment: z14.record(z14.string(), z14.string()).nullable(),
6166
- resumeSession: resumeSessionSchema.nullable(),
6167
- encryptedSecrets: z14.string().nullable(),
6168
- // AES-256-GCM encrypted secrets
6169
- cliAgentType: z14.string(),
6170
- experimentalNetworkSecurity: z14.boolean().optional()
6171
- });
6172
- var executionContextSchema = z14.object({
6173
- runId: z14.string().uuid(),
6174
- prompt: z14.string(),
6175
- agentComposeVersionId: z14.string(),
6176
- vars: z14.record(z14.string(), z14.string()).nullable(),
6177
- secretNames: z14.array(z14.string()).nullable(),
6178
- checkpointId: z14.string().uuid().nullable(),
6179
- sandboxToken: z14.string(),
6180
- // New fields for E2B parity:
6181
- workingDir: z14.string(),
6182
- storageManifest: storageManifestSchema.nullable(),
6183
- environment: z14.record(z14.string(), z14.string()).nullable(),
6184
- resumeSession: resumeSessionSchema.nullable(),
6185
- secretValues: z14.array(z14.string()).nullable(),
6186
- cliAgentType: z14.string(),
6187
- // Network security mode flag
6188
- experimentalNetworkSecurity: z14.boolean().optional()
6189
- });
6190
- var runnersJobClaimContract = c10.router({
6191
- claim: {
6192
- method: "POST",
6193
- path: "/api/runners/jobs/:id/claim",
6194
- pathParams: z14.object({
6195
- id: z14.string().uuid()
6196
- }),
6197
- body: z14.object({}),
6198
- responses: {
6199
- 200: executionContextSchema,
6200
- 400: apiErrorSchema,
6201
- 401: apiErrorSchema,
6202
- 403: apiErrorSchema,
6203
- // Job does not belong to user
6204
- 404: apiErrorSchema,
6205
- 409: apiErrorSchema,
6206
- // Already claimed
6207
- 500: apiErrorSchema
6208
- },
6209
- summary: "Claim a pending job for execution"
6210
- }
6211
- });
6212
-
6213
6235
  // ../../packages/core/src/contracts/public/common.ts
6214
6236
  import { z as z15 } from "zod";
6215
6237
  var publicApiErrorTypeSchema = z15.enum([
@@ -6285,18 +6307,21 @@ var updateAgentRequestSchema = z16.object({
6285
6307
  config: z16.unknown()
6286
6308
  // New agent configuration (creates new version)
6287
6309
  });
6310
+ var agentListQuerySchema = listQuerySchema.extend({
6311
+ name: z16.string().optional()
6312
+ });
6288
6313
  var publicAgentsListContract = c11.router({
6289
6314
  list: {
6290
6315
  method: "GET",
6291
6316
  path: "/v1/agents",
6292
- query: listQuerySchema,
6317
+ query: agentListQuerySchema,
6293
6318
  responses: {
6294
6319
  200: paginatedAgentsSchema,
6295
6320
  401: publicApiErrorSchema,
6296
6321
  500: publicApiErrorSchema
6297
6322
  },
6298
6323
  summary: "List agents",
6299
- description: "List all agents in the current scope with pagination"
6324
+ description: "List all agents in the current scope with pagination. Use the `name` query parameter to filter by agent name."
6300
6325
  },
6301
6326
  create: {
6302
6327
  method: "POST",
@@ -9551,14 +9576,21 @@ var VMRegistry = class {
9551
9576
  /**
9552
9577
  * Register a VM with its IP address
9553
9578
  */
9554
- register(vmIp, runId, sandboxToken) {
9579
+ register(vmIp, runId, sandboxToken, options) {
9555
9580
  this.data.vms[vmIp] = {
9556
9581
  runId,
9557
9582
  sandboxToken,
9558
- registeredAt: Date.now()
9583
+ registeredAt: Date.now(),
9584
+ firewallRules: options?.firewallRules,
9585
+ mitmEnabled: options?.mitmEnabled,
9586
+ sealSecretsEnabled: options?.sealSecretsEnabled
9559
9587
  };
9560
9588
  this.save();
9561
- console.log(`[VMRegistry] Registered VM ${vmIp} for run ${runId}`);
9589
+ const firewallInfo = options?.firewallRules ? ` with ${options.firewallRules.length} firewall rules` : "";
9590
+ const mitmInfo = options?.mitmEnabled ? ", MITM enabled" : "";
9591
+ console.log(
9592
+ `[VMRegistry] Registered VM ${vmIp} for run ${runId}${firewallInfo}${mitmInfo}`
9593
+ );
9562
9594
  }
9563
9595
  /**
9564
9596
  * Unregister a VM by IP address
@@ -9624,16 +9656,19 @@ mitmproxy addon for VM0 runner-level network security mode.
9624
9656
 
9625
9657
  This addon runs on the runner HOST (not inside VMs) and:
9626
9658
  1. Intercepts all HTTPS requests from VMs
9627
- 2. Looks up the source VM's runId from the VM registry
9628
- 3. Rewrites requests to go through VM0 Proxy endpoint
9629
- 4. Preserves all original headers (including encrypted tokens)
9630
- 5. Logs network activity per-run to JSONL files
9659
+ 2. Looks up the source VM's runId and firewall rules from the VM registry
9660
+ 3. Evaluates firewall rules (first-match-wins) to ALLOW or DENY
9661
+ 4. For MITM mode: Rewrites requests to go through VM0 Proxy endpoint
9662
+ 5. For SNI-only mode: Passes through or blocks without decryption
9663
+ 6. Logs network activity per-run to JSONL files
9631
9664
  """
9632
9665
  import os
9633
9666
  import json
9634
9667
  import time
9635
9668
  import urllib.parse
9636
- from mitmproxy import http, ctx
9669
+ import ipaddress
9670
+ import socket
9671
+ from mitmproxy import http, ctx, tls
9637
9672
 
9638
9673
 
9639
9674
  # VM0 Proxy configuration from environment
@@ -9716,12 +9751,189 @@ def get_original_url(flow: http.HTTPFlow) -> str:
9716
9751
  return f"{scheme}://{host_with_port}{path}"
9717
9752
 
9718
9753
 
9719
- def request(flow: http.HTTPFlow) -> None:
9754
+ # ============================================================================
9755
+ # Firewall Rule Matching
9756
+ # ============================================================================
9757
+
9758
+ def match_domain(pattern: str, hostname: str) -> bool:
9759
+ """
9760
+ Match hostname against domain pattern.
9761
+ Supports exact match and wildcard prefix (*.example.com).
9762
+ """
9763
+ if not pattern or not hostname:
9764
+ return False
9765
+
9766
+ pattern = pattern.lower()
9767
+ hostname = hostname.lower()
9768
+
9769
+ if pattern.startswith("*."):
9770
+ # Wildcard: *.example.com matches sub.example.com, www.example.com
9771
+ # Also matches example.com itself (without subdomain)
9772
+ suffix = pattern[1:] # .example.com
9773
+ base = pattern[2:] # example.com
9774
+ return hostname.endswith(suffix) or hostname == base
9775
+
9776
+ return hostname == pattern
9777
+
9778
+
9779
+ def match_ip(cidr: str, ip_str: str) -> bool:
9720
9780
  """
9721
- Intercept request and rewrite to VM0 Proxy.
9781
+ Match IP address against CIDR range.
9782
+ Supports single IPs (1.2.3.4) and ranges (10.0.0.0/8).
9783
+ """
9784
+ if not cidr or not ip_str:
9785
+ return False
9786
+
9787
+ try:
9788
+ # Parse CIDR (automatically handles single IPs as /32)
9789
+ if "/" not in cidr:
9790
+ cidr = f"{cidr}/32"
9791
+ network = ipaddress.ip_network(cidr, strict=False)
9792
+ ip = ipaddress.ip_address(ip_str)
9793
+ return ip in network
9794
+ except ValueError:
9795
+ return False
9796
+
9722
9797
 
9723
- Identifies the source VM by client IP and looks up the associated
9724
- runId and sandboxToken from the VM registry.
9798
+ def resolve_hostname_to_ip(hostname: str) -> str | None:
9799
+ """Resolve hostname to IP address for IP-based rule matching."""
9800
+ try:
9801
+ return socket.gethostbyname(hostname)
9802
+ except socket.gaierror:
9803
+ return None
9804
+
9805
+
9806
+ def evaluate_rules(rules: list, hostname: str, ip_str: str = None) -> tuple[str, str | None]:
9807
+ """
9808
+ Evaluate firewall rules against hostname/IP.
9809
+ Returns (action, matched_rule_description).
9810
+
9811
+ Rule evaluation is first-match-wins (top to bottom).
9812
+
9813
+ Rule formats:
9814
+ - Domain/IP rule: { domain: "*.example.com", action: "ALLOW" }
9815
+ - Terminal rule: { final: "DENY" }
9816
+ """
9817
+ if not rules:
9818
+ return ("ALLOW", None) # No rules = allow all
9819
+
9820
+ for rule in rules:
9821
+ # Final/terminal rule - value is the action
9822
+ final_action = rule.get("final")
9823
+ if final_action:
9824
+ return (final_action, "final")
9825
+
9826
+ # Domain rule
9827
+ domain = rule.get("domain")
9828
+ if domain and match_domain(domain, hostname):
9829
+ return (rule.get("action", "DENY"), f"domain:{domain}")
9830
+
9831
+ # IP rule
9832
+ ip_pattern = rule.get("ip")
9833
+ if ip_pattern:
9834
+ target_ip = ip_str
9835
+ if not target_ip:
9836
+ target_ip = resolve_hostname_to_ip(hostname)
9837
+ if target_ip and match_ip(ip_pattern, target_ip):
9838
+ return (rule.get("action", "DENY"), f"ip:{ip_pattern}")
9839
+
9840
+ # No rule matched - default deny (zero-trust)
9841
+ return ("DENY", "default")
9842
+
9843
+
9844
+ # ============================================================================
9845
+ # TLS ClientHello Handler (SNI-only mode)
9846
+ # ============================================================================
9847
+
9848
+ def tls_clienthello(data: tls.ClientHelloData) -> None:
9849
+ """
9850
+ Handle TLS ClientHello for SNI-based filtering.
9851
+ This is called BEFORE TLS decryption, allowing SNI-only filtering.
9852
+ """
9853
+ client_ip = data.context.client.peername[0] if data.context.client.peername else None
9854
+ if not client_ip:
9855
+ return
9856
+
9857
+ vm_info = get_vm_info(client_ip)
9858
+ if not vm_info:
9859
+ return # Not a registered VM, let it through
9860
+
9861
+ # If MITM is enabled, let the normal flow handle it
9862
+ if vm_info.get("mitmEnabled", False):
9863
+ return
9864
+
9865
+ # SNI-only mode: check rules based on SNI
9866
+ sni = data.context.client.sni
9867
+ run_id = vm_info.get("runId", "")
9868
+ rules = vm_info.get("firewallRules", [])
9869
+
9870
+ # Auto-allow VM0 API requests - the agent MUST be able to communicate with VM0
9871
+ if API_URL and sni:
9872
+ parsed_api = urllib.parse.urlparse(API_URL)
9873
+ api_hostname = parsed_api.hostname.lower() if parsed_api.hostname else ""
9874
+ sni_lower = sni.lower()
9875
+ if api_hostname and (sni_lower == api_hostname or sni_lower.endswith(f".{api_hostname}")):
9876
+ ctx.log.info(f"[{run_id}] SNI-only auto-allow VM0 API: {sni}")
9877
+ log_network_entry(run_id, {
9878
+ "timestamp": time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime()),
9879
+ "mode": "sni",
9880
+ "action": "ALLOW",
9881
+ "host": sni,
9882
+ "port": 443,
9883
+ "rule_matched": "vm0-api",
9884
+ })
9885
+ data.ignore_connection = True # Pass through without MITM
9886
+ return
9887
+
9888
+ if not sni:
9889
+ # No SNI, can't determine target - block for security
9890
+ ctx.log.warn(f"[{run_id}] SNI-only: No SNI in ClientHello, blocking")
9891
+ log_network_entry(run_id, {
9892
+ "timestamp": time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime()),
9893
+ "mode": "sni",
9894
+ "action": "DENY",
9895
+ "host": "",
9896
+ "port": 443,
9897
+ "rule_matched": "no-sni",
9898
+ })
9899
+ # Don't set ignore_connection - mitmproxy will attempt MITM handshake
9900
+ # Since VM doesn't have CA cert (SNI-only mode), TLS will fail immediately
9901
+ return
9902
+
9903
+ # Evaluate rules
9904
+ action, matched_rule = evaluate_rules(rules, sni)
9905
+
9906
+ # Log the connection
9907
+ log_network_entry(run_id, {
9908
+ "timestamp": time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime()),
9909
+ "mode": "sni",
9910
+ "action": action,
9911
+ "host": sni,
9912
+ "port": 443,
9913
+ "rule_matched": matched_rule,
9914
+ })
9915
+
9916
+ if action == "ALLOW":
9917
+ # Pass through without MITM - mitmproxy will relay without decryption
9918
+ ctx.log.info(f"[{run_id}] SNI-only ALLOW: {sni} (rule: {matched_rule})")
9919
+ data.ignore_connection = True
9920
+ else:
9921
+ # Block the connection by NOT setting ignore_connection
9922
+ # mitmproxy will attempt MITM handshake, but since VM doesn't have
9923
+ # our CA certificate installed (SNI-only mode), the TLS handshake
9924
+ # will fail immediately with a certificate error.
9925
+ ctx.log.warn(f"[{run_id}] SNI-only DENY: {sni} (rule: {matched_rule})")
9926
+ # Client will see: SSL certificate problem / certificate verify failed
9927
+
9928
+
9929
+ # ============================================================================
9930
+ # HTTP Request Handler (MITM mode)
9931
+ # ============================================================================
9932
+
9933
+ def request(flow: http.HTTPFlow) -> None:
9934
+ """
9935
+ Intercept request and apply firewall rules.
9936
+ For MITM mode, rewrites allowed requests to VM0 Proxy.
9725
9937
  """
9726
9938
  # Track request start time
9727
9939
  request_start_times[flow.id] = time.time()
@@ -9738,16 +9950,52 @@ def request(flow: http.HTTPFlow) -> None:
9738
9950
 
9739
9951
  if not vm_info:
9740
9952
  # Not a registered VM, pass through without proxying
9741
- # This allows non-VM traffic to work normally
9742
9953
  ctx.log.info(f"No VM registration for {client_ip}, passing through")
9743
9954
  return
9744
9955
 
9745
9956
  run_id = vm_info.get("runId", "")
9746
9957
  sandbox_token = vm_info.get("sandboxToken", "")
9958
+ mitm_enabled = vm_info.get("mitmEnabled", False)
9959
+ rules = vm_info.get("firewallRules", [])
9747
9960
 
9748
9961
  # Store info for response handler
9749
9962
  flow.metadata["vm_run_id"] = run_id
9750
9963
  flow.metadata["vm_client_ip"] = client_ip
9964
+ flow.metadata["vm_mitm_enabled"] = mitm_enabled
9965
+
9966
+ # Get target hostname
9967
+ hostname = flow.request.pretty_host.lower()
9968
+
9969
+ # Auto-allow VM0 API requests - the agent MUST be able to communicate with VM0
9970
+ # This is checked before user firewall rules to ensure agent functionality
9971
+ if API_URL:
9972
+ parsed_api = urllib.parse.urlparse(API_URL)
9973
+ api_hostname = parsed_api.hostname.lower() if parsed_api.hostname else ""
9974
+ if api_hostname and (hostname == api_hostname or hostname.endswith(f".{api_hostname}")):
9975
+ ctx.log.info(f"[{run_id}] Auto-allow VM0 API: {hostname}")
9976
+ flow.metadata["firewall_action"] = "ALLOW"
9977
+ flow.metadata["firewall_rule"] = "vm0-api"
9978
+ # Continue to skip rewrite check below
9979
+ flow.metadata["original_url"] = get_original_url(flow)
9980
+ flow.metadata["skip_rewrite"] = True
9981
+ return
9982
+
9983
+ # Evaluate firewall rules
9984
+ action, matched_rule = evaluate_rules(rules, hostname)
9985
+ flow.metadata["firewall_action"] = action
9986
+ flow.metadata["firewall_rule"] = matched_rule
9987
+
9988
+ if action == "DENY":
9989
+ ctx.log.warn(f"[{run_id}] Firewall DENY: {hostname} (rule: {matched_rule})")
9990
+ # Kill the flow and return error response
9991
+ flow.response = http.Response.make(
9992
+ 403,
9993
+ b"Blocked by firewall",
9994
+ {"Content-Type": "text/plain"}
9995
+ )
9996
+ return
9997
+
9998
+ # Request is ALLOWED - proceed with processing
9751
9999
 
9752
10000
  # Skip if no API URL configured
9753
10001
  if not API_URL:
@@ -9760,9 +10008,8 @@ def request(flow: http.HTTPFlow) -> None:
9760
10008
  flow.metadata["skip_rewrite"] = True
9761
10009
  return
9762
10010
 
9763
- # Skip rewriting requests to trusted domains (S3, etc.)
10011
+ # Skip rewriting requests to trusted storage domains (S3, etc.)
9764
10012
  # S3 presigned URLs have signatures that break when proxied
9765
- host = flow.request.pretty_host.lower()
9766
10013
  TRUSTED_DOMAINS = [
9767
10014
  ".s3.amazonaws.com",
9768
10015
  ".s3-", # Regional S3 endpoints like s3-us-west-2.amazonaws.com
@@ -9771,8 +10018,8 @@ def request(flow: http.HTTPFlow) -> None:
9771
10018
  ".storage.googleapis.com",
9772
10019
  ]
9773
10020
  for domain in TRUSTED_DOMAINS:
9774
- if domain in host or host.endswith(domain.lstrip(".")):
9775
- ctx.log.info(f"[{run_id}] Skipping trusted domain: {host}")
10021
+ if domain in hostname or hostname.endswith(domain.lstrip(".")):
10022
+ ctx.log.info(f"[{run_id}] Skipping trusted storage domain: {hostname}")
9776
10023
  flow.metadata["original_url"] = get_original_url(flow)
9777
10024
  flow.metadata["skip_rewrite"] = True
9778
10025
  return
@@ -9781,7 +10028,13 @@ def request(flow: http.HTTPFlow) -> None:
9781
10028
  original_url = get_original_url(flow)
9782
10029
  flow.metadata["original_url"] = original_url
9783
10030
 
9784
- ctx.log.info(f"[{run_id}] Proxying: {original_url}")
10031
+ # If MITM is not enabled, just allow the request through without rewriting
10032
+ if not mitm_enabled:
10033
+ ctx.log.info(f"[{run_id}] Firewall ALLOW (no MITM): {hostname}")
10034
+ return
10035
+
10036
+ # MITM mode: rewrite to VM0 Proxy
10037
+ ctx.log.info(f"[{run_id}] Proxying via MITM: {original_url}")
9785
10038
 
9786
10039
  # Parse proxy URL
9787
10040
  parsed = urllib.parse.urlparse(PROXY_URL)
@@ -9822,34 +10075,58 @@ def response(flow: http.HTTPFlow) -> None:
9822
10075
  # Get stored info
9823
10076
  run_id = flow.metadata.get("vm_run_id", "")
9824
10077
  original_url = flow.metadata.get("original_url", flow.request.pretty_url)
10078
+ mitm_enabled = flow.metadata.get("vm_mitm_enabled", False)
10079
+ firewall_action = flow.metadata.get("firewall_action", "ALLOW")
10080
+ firewall_rule = flow.metadata.get("firewall_rule")
9825
10081
 
9826
10082
  # Calculate sizes
9827
10083
  request_size = len(flow.request.content) if flow.request.content else 0
9828
10084
  response_size = len(flow.response.content) if flow.response and flow.response.content else 0
9829
10085
  status_code = flow.response.status_code if flow.response else 0
9830
10086
 
10087
+ # Parse URL for host
10088
+ try:
10089
+ parsed_url = urllib.parse.urlparse(original_url)
10090
+ host = parsed_url.hostname or flow.request.pretty_host
10091
+ port = parsed_url.port or (443 if parsed_url.scheme == "https" else 80)
10092
+ except:
10093
+ host = flow.request.pretty_host
10094
+ port = flow.request.port
10095
+
9831
10096
  # Log network entry for this run
9832
10097
  if run_id:
9833
10098
  log_entry = {
9834
10099
  "timestamp": time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime()),
9835
- "method": flow.request.method,
9836
- "url": original_url,
9837
- "status": status_code,
9838
- "latency_ms": latency_ms,
9839
- "request_size": request_size,
9840
- "response_size": response_size,
10100
+ "mode": "mitm" if mitm_enabled else "filter",
10101
+ "action": firewall_action,
10102
+ "host": host,
10103
+ "port": port,
10104
+ "rule_matched": firewall_rule,
9841
10105
  }
10106
+
10107
+ # Add HTTP details only in MITM mode
10108
+ if mitm_enabled:
10109
+ log_entry.update({
10110
+ "method": flow.request.method,
10111
+ "path": flow.request.path.split("?")[0], # Path without query
10112
+ "url": original_url,
10113
+ "status": status_code,
10114
+ "latency_ms": latency_ms,
10115
+ "request_size": request_size,
10116
+ "response_size": response_size,
10117
+ })
10118
+
9842
10119
  log_network_entry(run_id, log_entry)
9843
10120
 
9844
10121
  # Log errors to mitmproxy console
9845
10122
  if flow.response and flow.response.status_code >= 400:
9846
10123
  ctx.log.warn(
9847
- f"[{run_id}] Proxy response {flow.response.status_code}: {original_url}"
10124
+ f"[{run_id}] Response {flow.response.status_code}: {original_url}"
9848
10125
  )
9849
10126
 
9850
10127
 
9851
10128
  # mitmproxy addon registration
9852
- addons = [request, response]
10129
+ addons = [tls_clienthello, request, response]
9853
10130
  `;
9854
10131
 
9855
10132
  // src/lib/proxy/proxy-manager.ts
@@ -10087,7 +10364,7 @@ function buildEnvironmentVariables(context, apiUrl) {
10087
10364
  envVars[key] = value;
10088
10365
  }
10089
10366
  }
10090
- if (context.experimentalNetworkSecurity) {
10367
+ if (context.experimentalFirewall?.experimental_mitm) {
10091
10368
  envVars.NODE_EXTRA_CA_CERTS = "/usr/local/share/ca-certificates/vm0-proxy-ca.crt";
10092
10369
  }
10093
10370
  return envVars;
@@ -10261,13 +10538,22 @@ async function executeJob(context, config) {
10261
10538
  console.log(`[Executor] Waiting for SSH on ${guestIp}...`);
10262
10539
  await ssh.waitUntilReachable(12e4, 2e3);
10263
10540
  console.log(`[Executor] SSH ready on ${guestIp}`);
10264
- if (context.experimentalNetworkSecurity) {
10541
+ const firewallConfig = context.experimentalFirewall;
10542
+ if (firewallConfig?.enabled) {
10543
+ const mitmEnabled = firewallConfig.experimental_mitm ?? false;
10544
+ const sealSecretsEnabled = firewallConfig.experimental_seal_secrets ?? false;
10265
10545
  console.log(
10266
- `[Executor] Setting up network security mode for VM ${guestIp}`
10546
+ `[Executor] Setting up network security for VM ${guestIp} (mitm=${mitmEnabled}, sealSecrets=${sealSecretsEnabled})`
10267
10547
  );
10268
10548
  await setupVMProxyRules(guestIp, config.proxy.port);
10269
- getVMRegistry().register(guestIp, context.runId, context.sandboxToken);
10270
- await installProxyCA(ssh);
10549
+ getVMRegistry().register(guestIp, context.runId, context.sandboxToken, {
10550
+ firewallRules: firewallConfig?.rules,
10551
+ mitmEnabled,
10552
+ sealSecretsEnabled
10553
+ });
10554
+ if (mitmEnabled) {
10555
+ await installProxyCA(ssh);
10556
+ }
10271
10557
  }
10272
10558
  console.log(`[Executor] Configuring DNS...`);
10273
10559
  await configureDNS(ssh);
@@ -10341,7 +10627,7 @@ async function executeJob(context, config) {
10341
10627
  error: errorMsg
10342
10628
  };
10343
10629
  } finally {
10344
- if (context.experimentalNetworkSecurity && guestIp) {
10630
+ if (context.experimentalFirewall?.enabled && guestIp) {
10345
10631
  console.log(`[Executor] Cleaning up network security for VM ${guestIp}`);
10346
10632
  try {
10347
10633
  await removeVMProxyRules(guestIp, config.proxy.port);
@@ -10438,7 +10724,7 @@ var startCommand = new Command("start").description("Start the runner").option("
10438
10724
  `Network proxy not available: ${err instanceof Error ? err.message : "Unknown error"}`
10439
10725
  );
10440
10726
  console.warn(
10441
- "Jobs with experimentalNetworkSecurity enabled will run without network interception"
10727
+ "Jobs with experimentalFirewall enabled will run without network interception"
10442
10728
  );
10443
10729
  }
10444
10730
  const statusFilePath = join(dirname(options.config), "status.json");
@@ -10587,7 +10873,7 @@ var statusCommand = new Command2("status").description("Check runner connectivit
10587
10873
  });
10588
10874
 
10589
10875
  // src/index.ts
10590
- var version = true ? "2.3.2" : "0.1.0";
10876
+ var version = true ? "2.4.1" : "0.1.0";
10591
10877
  program.name("vm0-runner").version(version).description("Self-hosted runner for VM0 agents");
10592
10878
  program.addCommand(startCommand);
10593
10879
  program.addCommand(statusCommand);