@vm0/runner 3.11.2 → 3.12.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 +1026 -607
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -44,7 +44,7 @@ var runnerPaths = {
44
44
  /** Runner status file */
45
45
  statusFile: (baseDir) => path.join(baseDir, "status.json"),
46
46
  /** Snapshot generation work directory */
47
- snapshotWorkDir: (baseDir) => path.join(baseDir, "workspaces", ".snapshot-work"),
47
+ snapshotWorkDir: (baseDir) => path.join(baseDir, "workspaces", "snapshot"),
48
48
  /** Check if a directory name is a VM workspace */
49
49
  isVmWorkspace: (dirname) => dirname.startsWith(VM_WORKSPACE_PREFIX),
50
50
  /** Extract vmId from workspace directory name */
@@ -62,6 +62,14 @@ var vmPaths = {
62
62
  /** Overlay filesystem for VM writes */
63
63
  overlay: (workDir) => path.join(workDir, "overlay.ext4")
64
64
  };
65
+ var snapshotOutputPaths = {
66
+ /** VM state snapshot */
67
+ snapshot: (outputDir) => path.join(outputDir, "snapshot.bin"),
68
+ /** VM memory snapshot */
69
+ memory: (outputDir) => path.join(outputDir, "memory.bin"),
70
+ /** Golden overlay with guest state */
71
+ overlay: (outputDir) => path.join(outputDir, "overlay.ext4")
72
+ };
65
73
  var tempPaths = {
66
74
  /** Default proxy CA directory */
67
75
  proxyDir: `${VM0_TMP_PREFIX}-proxy`,
@@ -102,7 +110,12 @@ var runnerConfigSchema = z.object({
102
110
  firecracker: z.object({
103
111
  binary: z.string().min(1, "Firecracker binary path is required"),
104
112
  kernel: z.string().min(1, "Kernel path is required"),
105
- rootfs: z.string().min(1, "Rootfs path is required")
113
+ rootfs: z.string().min(1, "Rootfs path is required"),
114
+ snapshot: z.object({
115
+ snapshot: z.string().min(1, "Snapshot state file path is required"),
116
+ memory: z.string().min(1, "Snapshot memory file path is required"),
117
+ overlay: z.string().min(1, "Snapshot overlay file path is required")
118
+ }).optional()
106
119
  }),
107
120
  proxy: z.object({
108
121
  // TODO: Allow 0 to auto-find available port
@@ -131,7 +144,12 @@ var debugConfigSchema = z.object({
131
144
  firecracker: z.object({
132
145
  binary: z.string().min(1, "Firecracker binary path is required"),
133
146
  kernel: z.string().min(1, "Kernel path is required"),
134
- rootfs: z.string().min(1, "Rootfs path is required")
147
+ rootfs: z.string().min(1, "Rootfs path is required"),
148
+ snapshot: z.object({
149
+ snapshot: z.string().min(1, "Snapshot state file path is required"),
150
+ memory: z.string().min(1, "Snapshot memory file path is required"),
151
+ overlay: z.string().min(1, "Snapshot overlay file path is required")
152
+ }).optional()
135
153
  }),
136
154
  proxy: z.object({
137
155
  port: z.number().int().min(1024).max(65535).default(PROXY_DEFAULTS.port),
@@ -172,6 +190,13 @@ function validateFirecrackerPaths(config) {
172
190
  { path: config.kernel, name: "Kernel" },
173
191
  { path: config.rootfs, name: "Rootfs" }
174
192
  ];
193
+ if (config.snapshot) {
194
+ checks.push(
195
+ { path: config.snapshot.snapshot, name: "Snapshot state file" },
196
+ { path: config.snapshot.memory, name: "Snapshot memory file" },
197
+ { path: config.snapshot.overlay, name: "Snapshot overlay file" }
198
+ );
199
+ }
175
200
  for (const check of checks) {
176
201
  if (!fs.existsSync(check.path)) {
177
202
  throw new Error(`${check.name} not found: ${check.path}`);
@@ -337,10 +362,13 @@ async function subscribeToJobs(server, group, onJob, onConnectionChange) {
337
362
 
338
363
  // src/lib/executor.ts
339
364
  import fs9 from "fs";
365
+ import path6 from "path";
340
366
 
341
367
  // src/lib/firecracker/vm.ts
342
368
  import { spawn } from "child_process";
343
369
  import fs4 from "fs";
370
+ import os from "os";
371
+ import path4 from "path";
344
372
  import readline from "readline";
345
373
 
346
374
  // src/lib/firecracker/netns-pool.ts
@@ -379,8 +407,8 @@ var DEFAULT_OPTIONS = {
379
407
  maxTimeout: 1e3
380
408
  }
381
409
  };
382
- async function withFileLock(path7, fn, options) {
383
- const release = await lockfile.lock(path7, { ...DEFAULT_OPTIONS, ...options });
410
+ async function withFileLock(path9, fn, options) {
411
+ const release = await lockfile.lock(path9, { ...DEFAULT_OPTIONS, ...options });
384
412
  try {
385
413
  return await fn();
386
414
  } finally {
@@ -436,6 +464,10 @@ async function createNetnsWithTap(nsName, tap) {
436
464
  await execCommand(`ip netns exec ${nsName} ip link set ${tap.tapName} up`);
437
465
  await execCommand(`ip netns exec ${nsName} ip link set lo up`);
438
466
  }
467
+ async function deleteNetns(nsName) {
468
+ await execCommand(`ip netns del ${nsName}`).catch(() => {
469
+ });
470
+ }
439
471
 
440
472
  // src/lib/firecracker/netns-pool.ts
441
473
  var logger = createLogger("NetnsPool");
@@ -1077,10 +1109,10 @@ import * as http from "http";
1077
1109
  import * as fs3 from "fs";
1078
1110
  var logger3 = createLogger("FirecrackerClient");
1079
1111
  var FirecrackerApiError = class extends Error {
1080
- constructor(statusCode, path7, faultMessage) {
1081
- super(`Firecracker API error ${statusCode} on ${path7}: ${faultMessage}`);
1112
+ constructor(statusCode, path9, faultMessage) {
1113
+ super(`Firecracker API error ${statusCode} on ${path9}: ${faultMessage}`);
1082
1114
  this.statusCode = statusCode;
1083
- this.path = path7;
1115
+ this.path = path9;
1084
1116
  this.faultMessage = faultMessage;
1085
1117
  this.name = "FirecrackerApiError";
1086
1118
  }
@@ -1193,27 +1225,27 @@ var FirecrackerClient = class {
1193
1225
  /**
1194
1226
  * GET request
1195
1227
  */
1196
- async get(path7) {
1197
- return this.request("GET", path7);
1228
+ async get(path9) {
1229
+ return this.request("GET", path9);
1198
1230
  }
1199
1231
  /**
1200
1232
  * PATCH request
1201
1233
  */
1202
- async patch(path7, body) {
1203
- return this.request("PATCH", path7, body);
1234
+ async patch(path9, body) {
1235
+ return this.request("PATCH", path9, body);
1204
1236
  }
1205
1237
  /**
1206
1238
  * PUT request
1207
1239
  */
1208
- async put(path7, body) {
1209
- return this.request("PUT", path7, body);
1240
+ async put(path9, body) {
1241
+ return this.request("PUT", path9, body);
1210
1242
  }
1211
1243
  /**
1212
1244
  * Make an HTTP request to Firecracker API
1213
1245
  *
1214
1246
  * @param timeoutMs Request timeout in milliseconds (default: 30000ms)
1215
1247
  */
1216
- request(method, path7, body, timeoutMs = 3e4) {
1248
+ request(method, path9, body, timeoutMs = 3e4) {
1217
1249
  return new Promise((resolve, reject) => {
1218
1250
  const bodyStr = body !== void 0 ? JSON.stringify(body) : void 0;
1219
1251
  const headers = {
@@ -1227,7 +1259,7 @@ var FirecrackerClient = class {
1227
1259
  }
1228
1260
  const options = {
1229
1261
  socketPath: this.socketPath,
1230
- path: path7,
1262
+ path: path9,
1231
1263
  method,
1232
1264
  headers,
1233
1265
  timeout: timeoutMs,
@@ -1235,7 +1267,7 @@ var FirecrackerClient = class {
1235
1267
  // Firecracker's single-threaded API can have issues with pipelined requests
1236
1268
  agent: false
1237
1269
  };
1238
- logger3.log(`${method} ${path7}${bodyStr ? " " + bodyStr : ""}`);
1270
+ logger3.log(`${method} ${path9}${bodyStr ? " " + bodyStr : ""}`);
1239
1271
  const req = http.request(options, (res) => {
1240
1272
  let data = "";
1241
1273
  res.on("data", (chunk) => {
@@ -1252,14 +1284,14 @@ var FirecrackerClient = class {
1252
1284
  faultMessage = errorBody.fault_message || data;
1253
1285
  } catch {
1254
1286
  }
1255
- reject(new FirecrackerApiError(statusCode, path7, faultMessage));
1287
+ reject(new FirecrackerApiError(statusCode, path9, faultMessage));
1256
1288
  }
1257
1289
  });
1258
1290
  });
1259
1291
  req.on("timeout", () => {
1260
1292
  req.destroy();
1261
1293
  reject(
1262
- new Error(`Request timeout after ${timeoutMs}ms: ${method} ${path7}`)
1294
+ new Error(`Request timeout after ${timeoutMs}ms: ${method} ${path9}`)
1263
1295
  );
1264
1296
  });
1265
1297
  req.on("error", (err) => {
@@ -1352,7 +1384,7 @@ var FirecrackerVM = class {
1352
1384
  this.workDir = config.workDir;
1353
1385
  this.vsockPath = vmPaths.vsock(this.workDir);
1354
1386
  this.configPath = vmPaths.config(this.workDir);
1355
- this.apiSocketPath = `${this.workDir}/api.sock`;
1387
+ this.apiSocketPath = vmPaths.apiSock(this.workDir);
1356
1388
  }
1357
1389
  /**
1358
1390
  * Get current VM state
@@ -1451,6 +1483,7 @@ var FirecrackerVM = class {
1451
1483
  const config = this.buildConfig();
1452
1484
  fs4.writeFileSync(this.configPath, JSON.stringify(config, null, 2));
1453
1485
  logger4.log(`[VM ${this.config.vmId}] Starting Firecracker (fresh boot)...`);
1486
+ const currentUser = os.userInfo().username;
1454
1487
  this.process = spawn(
1455
1488
  "sudo",
1456
1489
  [
@@ -1458,6 +1491,9 @@ var FirecrackerVM = class {
1458
1491
  "netns",
1459
1492
  "exec",
1460
1493
  this.netns.name,
1494
+ "sudo",
1495
+ "-u",
1496
+ currentUser,
1461
1497
  this.config.firecrackerBinary,
1462
1498
  "--config-file",
1463
1499
  this.configPath,
@@ -1475,25 +1511,58 @@ var FirecrackerVM = class {
1475
1511
  * Start VM from snapshot
1476
1512
  * Uses --api-sock to load snapshot via API
1477
1513
  *
1478
- * Drive configuration must be done before loading snapshot
1479
- * because our overlay path differs from the snapshot's original path.
1514
+ * Snapshot contains original absolute paths for drives. We use mount namespace
1515
+ * isolation to bind mount our actual overlay file to the path expected by the snapshot.
1516
+ * This allows concurrent VMs to each have their own overlay while restoring from
1517
+ * the same snapshot.
1480
1518
  */
1481
1519
  async startFromSnapshot(snapshot) {
1482
1520
  logger4.log(
1483
1521
  `[VM ${this.config.vmId}] Starting Firecracker (snapshot restore)...`
1484
1522
  );
1485
- logger4.log(`[VM ${this.config.vmId}] Snapshot: ${snapshot.snapshotPath}`);
1486
- logger4.log(`[VM ${this.config.vmId}] Memory: ${snapshot.memoryPath}`);
1523
+ logger4.log(`[VM ${this.config.vmId}] Snapshot: ${snapshot.snapshot}`);
1524
+ logger4.log(`[VM ${this.config.vmId}] Memory: ${snapshot.memory}`);
1525
+ const actualVsockDir = vmPaths.vsockDir(this.workDir);
1526
+ logger4.log(
1527
+ `[VM ${this.config.vmId}] Snapshot vsock: ${snapshot.snapshotVsockDir}`
1528
+ );
1529
+ logger4.log(
1530
+ `[VM ${this.config.vmId}] Snapshot overlay: ${snapshot.snapshotOverlay}`
1531
+ );
1532
+ logger4.log(`[VM ${this.config.vmId}] Actual vsock: ${actualVsockDir}`);
1533
+ logger4.log(
1534
+ `[VM ${this.config.vmId}] Actual overlay: ${this.vmOverlayPath}`
1535
+ );
1536
+ fs4.mkdirSync(snapshot.snapshotVsockDir, { recursive: true });
1537
+ fs4.mkdirSync(path4.dirname(snapshot.snapshotOverlay), {
1538
+ recursive: true
1539
+ });
1540
+ if (!fs4.existsSync(snapshot.snapshotOverlay)) {
1541
+ fs4.writeFileSync(snapshot.snapshotOverlay, "");
1542
+ }
1543
+ const currentUser = os.userInfo().username;
1544
+ const bindMountVsock = `mount --bind "${actualVsockDir}" "${snapshot.snapshotVsockDir}"`;
1545
+ const bindMountOverlay = `mount --bind "${this.vmOverlayPath}" "${snapshot.snapshotOverlay}"`;
1546
+ const firecrackerCmd = [
1547
+ "ip",
1548
+ "netns",
1549
+ "exec",
1550
+ this.netns.name,
1551
+ "sudo",
1552
+ "-u",
1553
+ currentUser,
1554
+ this.config.firecrackerBinary,
1555
+ "--api-sock",
1556
+ this.apiSocketPath
1557
+ ].join(" ");
1487
1558
  this.process = spawn(
1488
1559
  "sudo",
1489
1560
  [
1490
- "ip",
1491
- "netns",
1492
- "exec",
1493
- this.netns.name,
1494
- this.config.firecrackerBinary,
1495
- "--api-sock",
1496
- this.apiSocketPath
1561
+ "unshare",
1562
+ "--mount",
1563
+ "bash",
1564
+ "-c",
1565
+ `${bindMountVsock} && ${bindMountOverlay} && ${firecrackerCmd}`
1497
1566
  ],
1498
1567
  {
1499
1568
  cwd: this.workDir,
@@ -1504,26 +1573,11 @@ var FirecrackerVM = class {
1504
1573
  this.setupProcessHandlers();
1505
1574
  const client = new FirecrackerClient(this.apiSocketPath);
1506
1575
  await this.waitForApiReady(client);
1507
- logger4.log(`[VM ${this.config.vmId}] Configuring drives...`);
1508
- await Promise.all([
1509
- client.configureDrive({
1510
- drive_id: "rootfs",
1511
- path_on_host: this.config.rootfsPath,
1512
- is_root_device: true,
1513
- is_read_only: true
1514
- }),
1515
- client.configureDrive({
1516
- drive_id: "overlay",
1517
- path_on_host: this.vmOverlayPath,
1518
- is_root_device: false,
1519
- is_read_only: false
1520
- })
1521
- ]);
1522
1576
  logger4.log(`[VM ${this.config.vmId}] Loading snapshot...`);
1523
1577
  await client.loadSnapshot({
1524
- snapshot_path: snapshot.snapshotPath,
1578
+ snapshot_path: snapshot.snapshot,
1525
1579
  mem_backend: {
1526
- backend_path: snapshot.memoryPath,
1580
+ backend_path: snapshot.memory,
1527
1581
  backend_type: "File"
1528
1582
  },
1529
1583
  resume_vm: true
@@ -1756,8 +1810,8 @@ function encodeExecPayload(command, timeoutMs) {
1756
1810
  cmdBuf.copy(payload, 8);
1757
1811
  return payload;
1758
1812
  }
1759
- function encodeWriteFilePayload(path7, content, sudo) {
1760
- const pathBuf = Buffer.from(path7, "utf-8");
1813
+ function encodeWriteFilePayload(path9, content, sudo) {
1814
+ const pathBuf = Buffer.from(path9, "utf-8");
1761
1815
  if (pathBuf.length > 65535) {
1762
1816
  throw new Error(`Path too long: ${pathBuf.length} bytes (max 65535)`);
1763
1817
  }
@@ -2285,6 +2339,20 @@ var VsockClient = class {
2285
2339
  }
2286
2340
  this.cachedExits.clear();
2287
2341
  }
2342
+ /**
2343
+ * Send raw bytes directly to the socket (for testing only)
2344
+ *
2345
+ * This allows tests to send malformed messages to verify the agent's
2346
+ * protocol error handling (e.g., oversized messages).
2347
+ *
2348
+ * @internal This method is only for testing purposes
2349
+ */
2350
+ sendRawForTesting(data) {
2351
+ if (!this.connected || !this.socket) {
2352
+ throw new Error("Not connected - call waitForGuestConnection() first");
2353
+ }
2354
+ this.socket.write(data);
2355
+ }
2288
2356
  };
2289
2357
 
2290
2358
  // ../../packages/core/src/contracts/base.ts
@@ -2638,8 +2706,8 @@ function getErrorMap() {
2638
2706
  return overrideErrorMap;
2639
2707
  }
2640
2708
  var makeIssue = (params) => {
2641
- const { data, path: path7, errorMaps, issueData } = params;
2642
- const fullPath = [...path7, ...issueData.path || []];
2709
+ const { data, path: path9, errorMaps, issueData } = params;
2710
+ const fullPath = [...path9, ...issueData.path || []];
2643
2711
  const fullIssue = {
2644
2712
  ...issueData,
2645
2713
  path: fullPath
@@ -2738,11 +2806,11 @@ var errorUtil;
2738
2806
  errorUtil2.toString = (message) => typeof message === "string" ? message : message === null || message === void 0 ? void 0 : message.message;
2739
2807
  })(errorUtil || (errorUtil = {}));
2740
2808
  var ParseInputLazyPath = class {
2741
- constructor(parent, value, path7, key) {
2809
+ constructor(parent, value, path9, key) {
2742
2810
  this._cachedPath = [];
2743
2811
  this.parent = parent;
2744
2812
  this.data = value;
2745
- this._path = path7;
2813
+ this._path = path9;
2746
2814
  this._key = key;
2747
2815
  }
2748
2816
  get path() {
@@ -7705,58 +7773,159 @@ var credentialsByNameContract = c10.router({
7705
7773
  }
7706
7774
  });
7707
7775
 
7708
- // ../../packages/core/src/contracts/model-providers.ts
7776
+ // ../../packages/core/src/contracts/secrets.ts
7709
7777
  import { z as z18 } from "zod";
7710
7778
  var c11 = initContract();
7711
- var modelProviderTypeSchema = z18.enum([
7779
+ var secretNameSchema = z18.string().min(1, "Secret name is required").max(255, "Secret name must be at most 255 characters").regex(
7780
+ /^[A-Z][A-Z0-9_]*$/,
7781
+ "Secret name must contain only uppercase letters, numbers, and underscores, and must start with a letter (e.g., MY_API_KEY)"
7782
+ );
7783
+ var secretTypeSchema = z18.enum(["user", "model-provider"]);
7784
+ var secretResponseSchema = z18.object({
7785
+ id: z18.string().uuid(),
7786
+ name: z18.string(),
7787
+ description: z18.string().nullable(),
7788
+ type: secretTypeSchema,
7789
+ createdAt: z18.string(),
7790
+ updatedAt: z18.string()
7791
+ });
7792
+ var secretListResponseSchema = z18.object({
7793
+ secrets: z18.array(secretResponseSchema)
7794
+ });
7795
+ var setSecretRequestSchema = z18.object({
7796
+ name: secretNameSchema,
7797
+ value: z18.string().min(1, "Secret value is required"),
7798
+ description: z18.string().max(1e3).optional()
7799
+ });
7800
+ var secretsMainContract = c11.router({
7801
+ /**
7802
+ * GET /api/secrets
7803
+ * List all secrets for the current user's scope (metadata only)
7804
+ */
7805
+ list: {
7806
+ method: "GET",
7807
+ path: "/api/secrets",
7808
+ headers: authHeadersSchema,
7809
+ responses: {
7810
+ 200: secretListResponseSchema,
7811
+ 401: apiErrorSchema,
7812
+ 500: apiErrorSchema
7813
+ },
7814
+ summary: "List all secrets (metadata only)"
7815
+ },
7816
+ /**
7817
+ * PUT /api/secrets
7818
+ * Create or update a secret
7819
+ */
7820
+ set: {
7821
+ method: "PUT",
7822
+ path: "/api/secrets",
7823
+ headers: authHeadersSchema,
7824
+ body: setSecretRequestSchema,
7825
+ responses: {
7826
+ 200: secretResponseSchema,
7827
+ 201: secretResponseSchema,
7828
+ 400: apiErrorSchema,
7829
+ 401: apiErrorSchema,
7830
+ 500: apiErrorSchema
7831
+ },
7832
+ summary: "Create or update a secret"
7833
+ }
7834
+ });
7835
+ var secretsByNameContract = c11.router({
7836
+ /**
7837
+ * GET /api/secrets/:name
7838
+ * Get a secret by name (metadata only)
7839
+ */
7840
+ get: {
7841
+ method: "GET",
7842
+ path: "/api/secrets/:name",
7843
+ headers: authHeadersSchema,
7844
+ pathParams: z18.object({
7845
+ name: secretNameSchema
7846
+ }),
7847
+ responses: {
7848
+ 200: secretResponseSchema,
7849
+ 401: apiErrorSchema,
7850
+ 404: apiErrorSchema,
7851
+ 500: apiErrorSchema
7852
+ },
7853
+ summary: "Get secret metadata by name"
7854
+ },
7855
+ /**
7856
+ * DELETE /api/secrets/:name
7857
+ * Delete a secret by name
7858
+ */
7859
+ delete: {
7860
+ method: "DELETE",
7861
+ path: "/api/secrets/:name",
7862
+ headers: authHeadersSchema,
7863
+ pathParams: z18.object({
7864
+ name: secretNameSchema
7865
+ }),
7866
+ responses: {
7867
+ 204: c11.noBody(),
7868
+ 401: apiErrorSchema,
7869
+ 404: apiErrorSchema,
7870
+ 500: apiErrorSchema
7871
+ },
7872
+ summary: "Delete a secret"
7873
+ }
7874
+ });
7875
+
7876
+ // ../../packages/core/src/contracts/model-providers.ts
7877
+ import { z as z19 } from "zod";
7878
+ var c12 = initContract();
7879
+ var modelProviderTypeSchema = z19.enum([
7712
7880
  "claude-code-oauth-token",
7713
7881
  "anthropic-api-key",
7714
7882
  "openrouter-api-key",
7715
7883
  "moonshot-api-key",
7716
7884
  "minimax-api-key",
7717
7885
  "deepseek-api-key",
7886
+ "zai-api-key",
7718
7887
  "aws-bedrock"
7719
7888
  ]);
7720
- var modelProviderFrameworkSchema = z18.enum(["claude-code", "codex"]);
7721
- var modelProviderResponseSchema = z18.object({
7722
- id: z18.string().uuid(),
7889
+ var modelProviderFrameworkSchema = z19.enum(["claude-code", "codex"]);
7890
+ var modelProviderResponseSchema = z19.object({
7891
+ id: z19.string().uuid(),
7723
7892
  type: modelProviderTypeSchema,
7724
7893
  framework: modelProviderFrameworkSchema,
7725
- credentialName: z18.string().nullable(),
7894
+ credentialName: z19.string().nullable(),
7726
7895
  // Legacy single-credential (deprecated for multi-auth)
7727
- authMethod: z18.string().nullable(),
7896
+ authMethod: z19.string().nullable(),
7728
7897
  // For multi-auth providers
7729
- credentialNames: z18.array(z18.string()).nullable(),
7898
+ credentialNames: z19.array(z19.string()).nullable(),
7730
7899
  // For multi-auth providers
7731
- isDefault: z18.boolean(),
7732
- selectedModel: z18.string().nullable(),
7733
- createdAt: z18.string(),
7734
- updatedAt: z18.string()
7900
+ isDefault: z19.boolean(),
7901
+ selectedModel: z19.string().nullable(),
7902
+ createdAt: z19.string(),
7903
+ updatedAt: z19.string()
7735
7904
  });
7736
- var modelProviderListResponseSchema = z18.object({
7737
- modelProviders: z18.array(modelProviderResponseSchema)
7905
+ var modelProviderListResponseSchema = z19.object({
7906
+ modelProviders: z19.array(modelProviderResponseSchema)
7738
7907
  });
7739
- var upsertModelProviderRequestSchema = z18.object({
7908
+ var upsertModelProviderRequestSchema = z19.object({
7740
7909
  type: modelProviderTypeSchema,
7741
- credential: z18.string().min(1).optional(),
7910
+ credential: z19.string().min(1).optional(),
7742
7911
  // Legacy single credential
7743
- authMethod: z18.string().optional(),
7912
+ authMethod: z19.string().optional(),
7744
7913
  // For multi-auth providers
7745
- credentials: z18.record(z18.string(), z18.string()).optional(),
7914
+ credentials: z19.record(z19.string(), z19.string()).optional(),
7746
7915
  // For multi-auth providers
7747
- convert: z18.boolean().optional(),
7748
- selectedModel: z18.string().optional()
7916
+ convert: z19.boolean().optional(),
7917
+ selectedModel: z19.string().optional()
7749
7918
  });
7750
- var upsertModelProviderResponseSchema = z18.object({
7919
+ var upsertModelProviderResponseSchema = z19.object({
7751
7920
  provider: modelProviderResponseSchema,
7752
- created: z18.boolean()
7921
+ created: z19.boolean()
7753
7922
  });
7754
- var checkCredentialResponseSchema = z18.object({
7755
- exists: z18.boolean(),
7756
- credentialName: z18.string(),
7757
- currentType: z18.enum(["user", "model-provider"]).optional()
7923
+ var checkCredentialResponseSchema = z19.object({
7924
+ exists: z19.boolean(),
7925
+ credentialName: z19.string(),
7926
+ currentType: z19.enum(["user", "model-provider"]).optional()
7758
7927
  });
7759
- var modelProvidersMainContract = c11.router({
7928
+ var modelProvidersMainContract = c12.router({
7760
7929
  list: {
7761
7930
  method: "GET",
7762
7931
  path: "/api/model-providers",
@@ -7784,12 +7953,12 @@ var modelProvidersMainContract = c11.router({
7784
7953
  summary: "Create or update a model provider"
7785
7954
  }
7786
7955
  });
7787
- var modelProvidersCheckContract = c11.router({
7956
+ var modelProvidersCheckContract = c12.router({
7788
7957
  check: {
7789
7958
  method: "GET",
7790
7959
  path: "/api/model-providers/check/:type",
7791
7960
  headers: authHeadersSchema,
7792
- pathParams: z18.object({
7961
+ pathParams: z19.object({
7793
7962
  type: modelProviderTypeSchema
7794
7963
  }),
7795
7964
  responses: {
@@ -7800,16 +7969,16 @@ var modelProvidersCheckContract = c11.router({
7800
7969
  summary: "Check if credential exists for a model provider type"
7801
7970
  }
7802
7971
  });
7803
- var modelProvidersByTypeContract = c11.router({
7972
+ var modelProvidersByTypeContract = c12.router({
7804
7973
  delete: {
7805
7974
  method: "DELETE",
7806
7975
  path: "/api/model-providers/:type",
7807
7976
  headers: authHeadersSchema,
7808
- pathParams: z18.object({
7977
+ pathParams: z19.object({
7809
7978
  type: modelProviderTypeSchema
7810
7979
  }),
7811
7980
  responses: {
7812
- 204: c11.noBody(),
7981
+ 204: c12.noBody(),
7813
7982
  401: apiErrorSchema,
7814
7983
  404: apiErrorSchema,
7815
7984
  500: apiErrorSchema
@@ -7817,15 +7986,15 @@ var modelProvidersByTypeContract = c11.router({
7817
7986
  summary: "Delete a model provider"
7818
7987
  }
7819
7988
  });
7820
- var modelProvidersConvertContract = c11.router({
7989
+ var modelProvidersConvertContract = c12.router({
7821
7990
  convert: {
7822
7991
  method: "POST",
7823
7992
  path: "/api/model-providers/:type/convert",
7824
7993
  headers: authHeadersSchema,
7825
- pathParams: z18.object({
7994
+ pathParams: z19.object({
7826
7995
  type: modelProviderTypeSchema
7827
7996
  }),
7828
- body: z18.undefined(),
7997
+ body: z19.undefined(),
7829
7998
  responses: {
7830
7999
  200: modelProviderResponseSchema,
7831
8000
  400: apiErrorSchema,
@@ -7836,15 +8005,15 @@ var modelProvidersConvertContract = c11.router({
7836
8005
  summary: "Convert existing user credential to model provider"
7837
8006
  }
7838
8007
  });
7839
- var modelProvidersSetDefaultContract = c11.router({
8008
+ var modelProvidersSetDefaultContract = c12.router({
7840
8009
  setDefault: {
7841
8010
  method: "POST",
7842
8011
  path: "/api/model-providers/:type/set-default",
7843
8012
  headers: authHeadersSchema,
7844
- pathParams: z18.object({
8013
+ pathParams: z19.object({
7845
8014
  type: modelProviderTypeSchema
7846
8015
  }),
7847
- body: z18.undefined(),
8016
+ body: z19.undefined(),
7848
8017
  responses: {
7849
8018
  200: modelProviderResponseSchema,
7850
8019
  401: apiErrorSchema,
@@ -7854,15 +8023,15 @@ var modelProvidersSetDefaultContract = c11.router({
7854
8023
  summary: "Set a model provider as default for its framework"
7855
8024
  }
7856
8025
  });
7857
- var updateModelRequestSchema = z18.object({
7858
- selectedModel: z18.string().optional()
8026
+ var updateModelRequestSchema = z19.object({
8027
+ selectedModel: z19.string().optional()
7859
8028
  });
7860
- var modelProvidersUpdateModelContract = c11.router({
8029
+ var modelProvidersUpdateModelContract = c12.router({
7861
8030
  updateModel: {
7862
8031
  method: "PATCH",
7863
8032
  path: "/api/model-providers/:type/model",
7864
8033
  headers: authHeadersSchema,
7865
- pathParams: z18.object({
8034
+ pathParams: z19.object({
7866
8035
  type: modelProviderTypeSchema
7867
8036
  }),
7868
8037
  body: updateModelRequestSchema,
@@ -7877,42 +8046,42 @@ var modelProvidersUpdateModelContract = c11.router({
7877
8046
  });
7878
8047
 
7879
8048
  // ../../packages/core/src/contracts/sessions.ts
7880
- import { z as z19 } from "zod";
7881
- var c12 = initContract();
7882
- var sessionResponseSchema = z19.object({
7883
- id: z19.string(),
7884
- agentComposeId: z19.string(),
7885
- agentComposeVersionId: z19.string().nullable(),
7886
- conversationId: z19.string().nullable(),
7887
- artifactName: z19.string().nullable(),
7888
- vars: z19.record(z19.string(), z19.string()).nullable(),
7889
- secretNames: z19.array(z19.string()).nullable(),
7890
- volumeVersions: z19.record(z19.string(), z19.string()).nullable(),
7891
- createdAt: z19.string(),
7892
- updatedAt: z19.string()
8049
+ import { z as z20 } from "zod";
8050
+ var c13 = initContract();
8051
+ var sessionResponseSchema = z20.object({
8052
+ id: z20.string(),
8053
+ agentComposeId: z20.string(),
8054
+ agentComposeVersionId: z20.string().nullable(),
8055
+ conversationId: z20.string().nullable(),
8056
+ artifactName: z20.string().nullable(),
8057
+ vars: z20.record(z20.string(), z20.string()).nullable(),
8058
+ secretNames: z20.array(z20.string()).nullable(),
8059
+ volumeVersions: z20.record(z20.string(), z20.string()).nullable(),
8060
+ createdAt: z20.string(),
8061
+ updatedAt: z20.string()
7893
8062
  });
7894
- var agentComposeSnapshotSchema = z19.object({
7895
- agentComposeVersionId: z19.string(),
7896
- vars: z19.record(z19.string(), z19.string()).optional(),
7897
- secretNames: z19.array(z19.string()).optional()
8063
+ var agentComposeSnapshotSchema = z20.object({
8064
+ agentComposeVersionId: z20.string(),
8065
+ vars: z20.record(z20.string(), z20.string()).optional(),
8066
+ secretNames: z20.array(z20.string()).optional()
7898
8067
  });
7899
- var artifactSnapshotSchema2 = z19.object({
7900
- artifactName: z19.string(),
7901
- artifactVersion: z19.string()
8068
+ var artifactSnapshotSchema2 = z20.object({
8069
+ artifactName: z20.string(),
8070
+ artifactVersion: z20.string()
7902
8071
  });
7903
- var volumeVersionsSnapshotSchema2 = z19.object({
7904
- versions: z19.record(z19.string(), z19.string())
8072
+ var volumeVersionsSnapshotSchema2 = z20.object({
8073
+ versions: z20.record(z20.string(), z20.string())
7905
8074
  });
7906
- var checkpointResponseSchema = z19.object({
7907
- id: z19.string(),
7908
- runId: z19.string(),
7909
- conversationId: z19.string(),
8075
+ var checkpointResponseSchema = z20.object({
8076
+ id: z20.string(),
8077
+ runId: z20.string(),
8078
+ conversationId: z20.string(),
7910
8079
  agentComposeSnapshot: agentComposeSnapshotSchema,
7911
8080
  artifactSnapshot: artifactSnapshotSchema2.nullable(),
7912
8081
  volumeVersionsSnapshot: volumeVersionsSnapshotSchema2.nullable(),
7913
- createdAt: z19.string()
8082
+ createdAt: z20.string()
7914
8083
  });
7915
- var sessionsByIdContract = c12.router({
8084
+ var sessionsByIdContract = c13.router({
7916
8085
  /**
7917
8086
  * GET /api/agent/sessions/:id
7918
8087
  * Get session by ID
@@ -7921,8 +8090,8 @@ var sessionsByIdContract = c12.router({
7921
8090
  method: "GET",
7922
8091
  path: "/api/agent/sessions/:id",
7923
8092
  headers: authHeadersSchema,
7924
- pathParams: z19.object({
7925
- id: z19.string().min(1, "Session ID is required")
8093
+ pathParams: z20.object({
8094
+ id: z20.string().min(1, "Session ID is required")
7926
8095
  }),
7927
8096
  responses: {
7928
8097
  200: sessionResponseSchema,
@@ -7933,7 +8102,7 @@ var sessionsByIdContract = c12.router({
7933
8102
  summary: "Get session by ID"
7934
8103
  }
7935
8104
  });
7936
- var checkpointsByIdContract = c12.router({
8105
+ var checkpointsByIdContract = c13.router({
7937
8106
  /**
7938
8107
  * GET /api/agent/checkpoints/:id
7939
8108
  * Get checkpoint by ID
@@ -7942,8 +8111,8 @@ var checkpointsByIdContract = c12.router({
7942
8111
  method: "GET",
7943
8112
  path: "/api/agent/checkpoints/:id",
7944
8113
  headers: authHeadersSchema,
7945
- pathParams: z19.object({
7946
- id: z19.string().min(1, "Checkpoint ID is required")
8114
+ pathParams: z20.object({
8115
+ id: z20.string().min(1, "Checkpoint ID is required")
7947
8116
  }),
7948
8117
  responses: {
7949
8118
  200: checkpointResponseSchema,
@@ -7956,93 +8125,93 @@ var checkpointsByIdContract = c12.router({
7956
8125
  });
7957
8126
 
7958
8127
  // ../../packages/core/src/contracts/schedules.ts
7959
- import { z as z20 } from "zod";
7960
- var c13 = initContract();
7961
- var scheduleTriggerSchema = z20.object({
7962
- cron: z20.string().optional(),
7963
- at: z20.string().optional(),
7964
- timezone: z20.string().default("UTC")
8128
+ import { z as z21 } from "zod";
8129
+ var c14 = initContract();
8130
+ var scheduleTriggerSchema = z21.object({
8131
+ cron: z21.string().optional(),
8132
+ at: z21.string().optional(),
8133
+ timezone: z21.string().default("UTC")
7965
8134
  }).refine((data) => data.cron && !data.at || !data.cron && data.at, {
7966
8135
  message: "Exactly one of 'cron' or 'at' must be specified"
7967
8136
  });
7968
- var scheduleRunConfigSchema = z20.object({
7969
- agent: z20.string().min(1, "Agent reference required"),
7970
- prompt: z20.string().min(1, "Prompt required"),
7971
- vars: z20.record(z20.string(), z20.string()).optional(),
7972
- secrets: z20.record(z20.string(), z20.string()).optional(),
7973
- artifactName: z20.string().optional(),
7974
- artifactVersion: z20.string().optional(),
7975
- volumeVersions: z20.record(z20.string(), z20.string()).optional()
8137
+ var scheduleRunConfigSchema = z21.object({
8138
+ agent: z21.string().min(1, "Agent reference required"),
8139
+ prompt: z21.string().min(1, "Prompt required"),
8140
+ vars: z21.record(z21.string(), z21.string()).optional(),
8141
+ secrets: z21.record(z21.string(), z21.string()).optional(),
8142
+ artifactName: z21.string().optional(),
8143
+ artifactVersion: z21.string().optional(),
8144
+ volumeVersions: z21.record(z21.string(), z21.string()).optional()
7976
8145
  });
7977
- var scheduleDefinitionSchema = z20.object({
8146
+ var scheduleDefinitionSchema = z21.object({
7978
8147
  on: scheduleTriggerSchema,
7979
8148
  run: scheduleRunConfigSchema
7980
8149
  });
7981
- var scheduleYamlSchema = z20.object({
7982
- version: z20.literal("1.0"),
7983
- schedules: z20.record(z20.string(), scheduleDefinitionSchema)
7984
- });
7985
- var deployScheduleRequestSchema = z20.object({
7986
- name: z20.string().min(1).max(64, "Schedule name max 64 chars"),
7987
- cronExpression: z20.string().optional(),
7988
- atTime: z20.string().optional(),
7989
- timezone: z20.string().default("UTC"),
7990
- prompt: z20.string().min(1, "Prompt required"),
7991
- vars: z20.record(z20.string(), z20.string()).optional(),
7992
- secrets: z20.record(z20.string(), z20.string()).optional(),
7993
- artifactName: z20.string().optional(),
7994
- artifactVersion: z20.string().optional(),
7995
- volumeVersions: z20.record(z20.string(), z20.string()).optional(),
8150
+ var scheduleYamlSchema = z21.object({
8151
+ version: z21.literal("1.0"),
8152
+ schedules: z21.record(z21.string(), scheduleDefinitionSchema)
8153
+ });
8154
+ var deployScheduleRequestSchema = z21.object({
8155
+ name: z21.string().min(1).max(64, "Schedule name max 64 chars"),
8156
+ cronExpression: z21.string().optional(),
8157
+ atTime: z21.string().optional(),
8158
+ timezone: z21.string().default("UTC"),
8159
+ prompt: z21.string().min(1, "Prompt required"),
8160
+ vars: z21.record(z21.string(), z21.string()).optional(),
8161
+ secrets: z21.record(z21.string(), z21.string()).optional(),
8162
+ artifactName: z21.string().optional(),
8163
+ artifactVersion: z21.string().optional(),
8164
+ volumeVersions: z21.record(z21.string(), z21.string()).optional(),
7996
8165
  // Resolved agent compose ID (CLI resolves scope/name:version → composeId)
7997
- composeId: z20.string().uuid("Invalid compose ID")
8166
+ composeId: z21.string().uuid("Invalid compose ID")
7998
8167
  }).refine(
7999
8168
  (data) => data.cronExpression && !data.atTime || !data.cronExpression && data.atTime,
8000
8169
  {
8001
8170
  message: "Exactly one of 'cronExpression' or 'atTime' must be specified"
8002
8171
  }
8003
8172
  );
8004
- var scheduleResponseSchema = z20.object({
8005
- id: z20.string().uuid(),
8006
- composeId: z20.string().uuid(),
8007
- composeName: z20.string(),
8008
- scopeSlug: z20.string(),
8009
- name: z20.string(),
8010
- cronExpression: z20.string().nullable(),
8011
- atTime: z20.string().nullable(),
8012
- timezone: z20.string(),
8013
- prompt: z20.string(),
8014
- vars: z20.record(z20.string(), z20.string()).nullable(),
8173
+ var scheduleResponseSchema = z21.object({
8174
+ id: z21.string().uuid(),
8175
+ composeId: z21.string().uuid(),
8176
+ composeName: z21.string(),
8177
+ scopeSlug: z21.string(),
8178
+ name: z21.string(),
8179
+ cronExpression: z21.string().nullable(),
8180
+ atTime: z21.string().nullable(),
8181
+ timezone: z21.string(),
8182
+ prompt: z21.string(),
8183
+ vars: z21.record(z21.string(), z21.string()).nullable(),
8015
8184
  // Secret names only (values are never returned)
8016
- secretNames: z20.array(z20.string()).nullable(),
8017
- artifactName: z20.string().nullable(),
8018
- artifactVersion: z20.string().nullable(),
8019
- volumeVersions: z20.record(z20.string(), z20.string()).nullable(),
8020
- enabled: z20.boolean(),
8021
- nextRunAt: z20.string().nullable(),
8022
- lastRunAt: z20.string().nullable(),
8023
- retryStartedAt: z20.string().nullable(),
8024
- createdAt: z20.string(),
8025
- updatedAt: z20.string()
8026
- });
8027
- var runSummarySchema = z20.object({
8028
- id: z20.string().uuid(),
8029
- status: z20.enum(["pending", "running", "completed", "failed", "timeout"]),
8030
- createdAt: z20.string(),
8031
- completedAt: z20.string().nullable(),
8032
- error: z20.string().nullable()
8033
- });
8034
- var scheduleRunsResponseSchema = z20.object({
8035
- runs: z20.array(runSummarySchema)
8036
- });
8037
- var scheduleListResponseSchema = z20.object({
8038
- schedules: z20.array(scheduleResponseSchema)
8039
- });
8040
- var deployScheduleResponseSchema = z20.object({
8185
+ secretNames: z21.array(z21.string()).nullable(),
8186
+ artifactName: z21.string().nullable(),
8187
+ artifactVersion: z21.string().nullable(),
8188
+ volumeVersions: z21.record(z21.string(), z21.string()).nullable(),
8189
+ enabled: z21.boolean(),
8190
+ nextRunAt: z21.string().nullable(),
8191
+ lastRunAt: z21.string().nullable(),
8192
+ retryStartedAt: z21.string().nullable(),
8193
+ createdAt: z21.string(),
8194
+ updatedAt: z21.string()
8195
+ });
8196
+ var runSummarySchema = z21.object({
8197
+ id: z21.string().uuid(),
8198
+ status: z21.enum(["pending", "running", "completed", "failed", "timeout"]),
8199
+ createdAt: z21.string(),
8200
+ completedAt: z21.string().nullable(),
8201
+ error: z21.string().nullable()
8202
+ });
8203
+ var scheduleRunsResponseSchema = z21.object({
8204
+ runs: z21.array(runSummarySchema)
8205
+ });
8206
+ var scheduleListResponseSchema = z21.object({
8207
+ schedules: z21.array(scheduleResponseSchema)
8208
+ });
8209
+ var deployScheduleResponseSchema = z21.object({
8041
8210
  schedule: scheduleResponseSchema,
8042
- created: z20.boolean()
8211
+ created: z21.boolean()
8043
8212
  // true if created, false if updated
8044
8213
  });
8045
- var schedulesMainContract = c13.router({
8214
+ var schedulesMainContract = c14.router({
8046
8215
  /**
8047
8216
  * POST /api/agent/schedules
8048
8217
  * Deploy (create or update) a schedule
@@ -8080,7 +8249,7 @@ var schedulesMainContract = c13.router({
8080
8249
  summary: "List all schedules"
8081
8250
  }
8082
8251
  });
8083
- var schedulesByNameContract = c13.router({
8252
+ var schedulesByNameContract = c14.router({
8084
8253
  /**
8085
8254
  * GET /api/agent/schedules/:name
8086
8255
  * Get schedule by name
@@ -8089,11 +8258,11 @@ var schedulesByNameContract = c13.router({
8089
8258
  method: "GET",
8090
8259
  path: "/api/agent/schedules/:name",
8091
8260
  headers: authHeadersSchema,
8092
- pathParams: z20.object({
8093
- name: z20.string().min(1, "Schedule name required")
8261
+ pathParams: z21.object({
8262
+ name: z21.string().min(1, "Schedule name required")
8094
8263
  }),
8095
- query: z20.object({
8096
- composeId: z20.string().uuid("Compose ID required")
8264
+ query: z21.object({
8265
+ composeId: z21.string().uuid("Compose ID required")
8097
8266
  }),
8098
8267
  responses: {
8099
8268
  200: scheduleResponseSchema,
@@ -8110,21 +8279,21 @@ var schedulesByNameContract = c13.router({
8110
8279
  method: "DELETE",
8111
8280
  path: "/api/agent/schedules/:name",
8112
8281
  headers: authHeadersSchema,
8113
- pathParams: z20.object({
8114
- name: z20.string().min(1, "Schedule name required")
8282
+ pathParams: z21.object({
8283
+ name: z21.string().min(1, "Schedule name required")
8115
8284
  }),
8116
- query: z20.object({
8117
- composeId: z20.string().uuid("Compose ID required")
8285
+ query: z21.object({
8286
+ composeId: z21.string().uuid("Compose ID required")
8118
8287
  }),
8119
8288
  responses: {
8120
- 204: c13.noBody(),
8289
+ 204: c14.noBody(),
8121
8290
  401: apiErrorSchema,
8122
8291
  404: apiErrorSchema
8123
8292
  },
8124
8293
  summary: "Delete schedule"
8125
8294
  }
8126
8295
  });
8127
- var schedulesEnableContract = c13.router({
8296
+ var schedulesEnableContract = c14.router({
8128
8297
  /**
8129
8298
  * POST /api/agent/schedules/:name/enable
8130
8299
  * Enable a disabled schedule
@@ -8133,11 +8302,11 @@ var schedulesEnableContract = c13.router({
8133
8302
  method: "POST",
8134
8303
  path: "/api/agent/schedules/:name/enable",
8135
8304
  headers: authHeadersSchema,
8136
- pathParams: z20.object({
8137
- name: z20.string().min(1, "Schedule name required")
8305
+ pathParams: z21.object({
8306
+ name: z21.string().min(1, "Schedule name required")
8138
8307
  }),
8139
- body: z20.object({
8140
- composeId: z20.string().uuid("Compose ID required")
8308
+ body: z21.object({
8309
+ composeId: z21.string().uuid("Compose ID required")
8141
8310
  }),
8142
8311
  responses: {
8143
8312
  200: scheduleResponseSchema,
@@ -8154,11 +8323,11 @@ var schedulesEnableContract = c13.router({
8154
8323
  method: "POST",
8155
8324
  path: "/api/agent/schedules/:name/disable",
8156
8325
  headers: authHeadersSchema,
8157
- pathParams: z20.object({
8158
- name: z20.string().min(1, "Schedule name required")
8326
+ pathParams: z21.object({
8327
+ name: z21.string().min(1, "Schedule name required")
8159
8328
  }),
8160
- body: z20.object({
8161
- composeId: z20.string().uuid("Compose ID required")
8329
+ body: z21.object({
8330
+ composeId: z21.string().uuid("Compose ID required")
8162
8331
  }),
8163
8332
  responses: {
8164
8333
  200: scheduleResponseSchema,
@@ -8168,7 +8337,7 @@ var schedulesEnableContract = c13.router({
8168
8337
  summary: "Disable schedule"
8169
8338
  }
8170
8339
  });
8171
- var scheduleRunsContract = c13.router({
8340
+ var scheduleRunsContract = c14.router({
8172
8341
  /**
8173
8342
  * GET /api/agent/schedules/:name/runs
8174
8343
  * List recent runs for a schedule
@@ -8177,12 +8346,12 @@ var scheduleRunsContract = c13.router({
8177
8346
  method: "GET",
8178
8347
  path: "/api/agent/schedules/:name/runs",
8179
8348
  headers: authHeadersSchema,
8180
- pathParams: z20.object({
8181
- name: z20.string().min(1, "Schedule name required")
8349
+ pathParams: z21.object({
8350
+ name: z21.string().min(1, "Schedule name required")
8182
8351
  }),
8183
- query: z20.object({
8184
- composeId: z20.string().uuid("Compose ID required"),
8185
- limit: z20.coerce.number().min(0).max(100).default(5)
8352
+ query: z21.object({
8353
+ composeId: z21.string().uuid("Compose ID required"),
8354
+ limit: z21.coerce.number().min(0).max(100).default(5)
8186
8355
  }),
8187
8356
  responses: {
8188
8357
  200: scheduleRunsResponseSchema,
@@ -8194,18 +8363,18 @@ var scheduleRunsContract = c13.router({
8194
8363
  });
8195
8364
 
8196
8365
  // ../../packages/core/src/contracts/realtime.ts
8197
- import { z as z21 } from "zod";
8198
- var c14 = initContract();
8199
- var ablyTokenRequestSchema = z21.object({
8200
- keyName: z21.string(),
8201
- ttl: z21.number().optional(),
8202
- timestamp: z21.number(),
8203
- capability: z21.string(),
8204
- clientId: z21.string().optional(),
8205
- nonce: z21.string(),
8206
- mac: z21.string()
8366
+ import { z as z22 } from "zod";
8367
+ var c15 = initContract();
8368
+ var ablyTokenRequestSchema = z22.object({
8369
+ keyName: z22.string(),
8370
+ ttl: z22.number().optional(),
8371
+ timestamp: z22.number(),
8372
+ capability: z22.string(),
8373
+ clientId: z22.string().optional(),
8374
+ nonce: z22.string(),
8375
+ mac: z22.string()
8207
8376
  });
8208
- var realtimeTokenContract = c14.router({
8377
+ var realtimeTokenContract = c15.router({
8209
8378
  /**
8210
8379
  * POST /api/realtime/token
8211
8380
  * Get an Ably token to subscribe to a run's events channel
@@ -8214,8 +8383,8 @@ var realtimeTokenContract = c14.router({
8214
8383
  method: "POST",
8215
8384
  path: "/api/realtime/token",
8216
8385
  headers: authHeadersSchema,
8217
- body: z21.object({
8218
- runId: z21.string().uuid("runId must be a valid UUID")
8386
+ body: z22.object({
8387
+ runId: z22.string().uuid("runId must be a valid UUID")
8219
8388
  }),
8220
8389
  responses: {
8221
8390
  200: ablyTokenRequestSchema,
@@ -8227,7 +8396,7 @@ var realtimeTokenContract = c14.router({
8227
8396
  summary: "Get Ably token for run event subscription"
8228
8397
  }
8229
8398
  });
8230
- var runnerRealtimeTokenContract = c14.router({
8399
+ var runnerRealtimeTokenContract = c15.router({
8231
8400
  /**
8232
8401
  * POST /api/runners/realtime/token
8233
8402
  * Get an Ably token to subscribe to a runner group's job notification channel
@@ -8236,7 +8405,7 @@ var runnerRealtimeTokenContract = c14.router({
8236
8405
  method: "POST",
8237
8406
  path: "/api/runners/realtime/token",
8238
8407
  headers: authHeadersSchema,
8239
- body: z21.object({
8408
+ body: z22.object({
8240
8409
  group: runnerGroupSchema
8241
8410
  }),
8242
8411
  responses: {
@@ -8250,11 +8419,11 @@ var runnerRealtimeTokenContract = c14.router({
8250
8419
  });
8251
8420
 
8252
8421
  // ../../packages/core/src/contracts/platform.ts
8253
- import { z as z23 } from "zod";
8422
+ import { z as z24 } from "zod";
8254
8423
 
8255
8424
  // ../../packages/core/src/contracts/public/common.ts
8256
- import { z as z22 } from "zod";
8257
- var publicApiErrorTypeSchema = z22.enum([
8425
+ import { z as z23 } from "zod";
8426
+ var publicApiErrorTypeSchema = z23.enum([
8258
8427
  "api_error",
8259
8428
  // Internal server error (5xx)
8260
8429
  "invalid_request_error",
@@ -8268,40 +8437,40 @@ var publicApiErrorTypeSchema = z22.enum([
8268
8437
  "rate_limit_error"
8269
8438
  // Rate limit exceeded (429)
8270
8439
  ]);
8271
- var publicApiErrorSchema = z22.object({
8272
- error: z22.object({
8440
+ var publicApiErrorSchema = z23.object({
8441
+ error: z23.object({
8273
8442
  type: publicApiErrorTypeSchema,
8274
- code: z22.string(),
8275
- message: z22.string(),
8276
- param: z22.string().optional(),
8277
- docUrl: z22.string().url().optional()
8443
+ code: z23.string(),
8444
+ message: z23.string(),
8445
+ param: z23.string().optional(),
8446
+ docUrl: z23.string().url().optional()
8278
8447
  })
8279
8448
  });
8280
- var paginationSchema = z22.object({
8281
- hasMore: z22.boolean(),
8282
- nextCursor: z22.string().nullable()
8449
+ var paginationSchema = z23.object({
8450
+ hasMore: z23.boolean(),
8451
+ nextCursor: z23.string().nullable()
8283
8452
  });
8284
8453
  function createPaginatedResponseSchema(dataSchema) {
8285
- return z22.object({
8286
- data: z22.array(dataSchema),
8454
+ return z23.object({
8455
+ data: z23.array(dataSchema),
8287
8456
  pagination: paginationSchema
8288
8457
  });
8289
8458
  }
8290
- var listQuerySchema = z22.object({
8291
- cursor: z22.string().optional(),
8292
- limit: z22.coerce.number().min(1).max(100).default(20)
8459
+ var listQuerySchema = z23.object({
8460
+ cursor: z23.string().optional(),
8461
+ limit: z23.coerce.number().min(1).max(100).default(20)
8293
8462
  });
8294
- var requestIdSchema = z22.string().uuid();
8295
- var timestampSchema = z22.string().datetime();
8463
+ var requestIdSchema = z23.string().uuid();
8464
+ var timestampSchema = z23.string().datetime();
8296
8465
 
8297
8466
  // ../../packages/core/src/contracts/platform.ts
8298
- var c15 = initContract();
8299
- var platformPaginationSchema = z23.object({
8300
- hasMore: z23.boolean(),
8301
- nextCursor: z23.string().nullable(),
8302
- totalPages: z23.number()
8467
+ var c16 = initContract();
8468
+ var platformPaginationSchema = z24.object({
8469
+ hasMore: z24.boolean(),
8470
+ nextCursor: z24.string().nullable(),
8471
+ totalPages: z24.number()
8303
8472
  });
8304
- var platformLogStatusSchema = z23.enum([
8473
+ var platformLogStatusSchema = z24.enum([
8305
8474
  "pending",
8306
8475
  "running",
8307
8476
  "completed",
@@ -8309,41 +8478,41 @@ var platformLogStatusSchema = z23.enum([
8309
8478
  "timeout",
8310
8479
  "cancelled"
8311
8480
  ]);
8312
- var platformLogEntrySchema = z23.object({
8313
- id: z23.string().uuid(),
8314
- sessionId: z23.string().nullable(),
8315
- agentName: z23.string(),
8316
- framework: z23.string().nullable(),
8481
+ var platformLogEntrySchema = z24.object({
8482
+ id: z24.string().uuid(),
8483
+ sessionId: z24.string().nullable(),
8484
+ agentName: z24.string(),
8485
+ framework: z24.string().nullable(),
8317
8486
  status: platformLogStatusSchema,
8318
- createdAt: z23.string()
8487
+ createdAt: z24.string()
8319
8488
  });
8320
- var platformLogsListResponseSchema = z23.object({
8321
- data: z23.array(platformLogEntrySchema),
8489
+ var platformLogsListResponseSchema = z24.object({
8490
+ data: z24.array(platformLogEntrySchema),
8322
8491
  pagination: platformPaginationSchema
8323
8492
  });
8324
- var artifactSchema = z23.object({
8325
- name: z23.string().nullable(),
8326
- version: z23.string().nullable()
8493
+ var artifactSchema = z24.object({
8494
+ name: z24.string().nullable(),
8495
+ version: z24.string().nullable()
8327
8496
  });
8328
- var platformLogDetailSchema = z23.object({
8329
- id: z23.string().uuid(),
8330
- sessionId: z23.string().nullable(),
8331
- agentName: z23.string(),
8332
- framework: z23.string().nullable(),
8497
+ var platformLogDetailSchema = z24.object({
8498
+ id: z24.string().uuid(),
8499
+ sessionId: z24.string().nullable(),
8500
+ agentName: z24.string(),
8501
+ framework: z24.string().nullable(),
8333
8502
  status: platformLogStatusSchema,
8334
- prompt: z23.string(),
8335
- error: z23.string().nullable(),
8336
- createdAt: z23.string(),
8337
- startedAt: z23.string().nullable(),
8338
- completedAt: z23.string().nullable(),
8503
+ prompt: z24.string(),
8504
+ error: z24.string().nullable(),
8505
+ createdAt: z24.string(),
8506
+ startedAt: z24.string().nullable(),
8507
+ completedAt: z24.string().nullable(),
8339
8508
  artifact: artifactSchema
8340
8509
  });
8341
- var platformLogsListContract = c15.router({
8510
+ var platformLogsListContract = c16.router({
8342
8511
  list: {
8343
8512
  method: "GET",
8344
8513
  path: "/api/platform/logs",
8345
8514
  query: listQuerySchema.extend({
8346
- search: z23.string().optional()
8515
+ search: z24.string().optional()
8347
8516
  }),
8348
8517
  responses: {
8349
8518
  200: platformLogsListResponseSchema,
@@ -8352,12 +8521,12 @@ var platformLogsListContract = c15.router({
8352
8521
  summary: "List agent run logs with pagination"
8353
8522
  }
8354
8523
  });
8355
- var platformLogsByIdContract = c15.router({
8524
+ var platformLogsByIdContract = c16.router({
8356
8525
  getById: {
8357
8526
  method: "GET",
8358
8527
  path: "/api/platform/logs/:id",
8359
- pathParams: z23.object({
8360
- id: z23.string().uuid("Invalid log ID")
8528
+ pathParams: z24.object({
8529
+ id: z24.string().uuid("Invalid log ID")
8361
8530
  }),
8362
8531
  responses: {
8363
8532
  200: platformLogDetailSchema,
@@ -8367,17 +8536,17 @@ var platformLogsByIdContract = c15.router({
8367
8536
  summary: "Get agent run log details by ID"
8368
8537
  }
8369
8538
  });
8370
- var artifactDownloadResponseSchema = z23.object({
8371
- url: z23.string().url(),
8372
- expiresAt: z23.string()
8539
+ var artifactDownloadResponseSchema = z24.object({
8540
+ url: z24.string().url(),
8541
+ expiresAt: z24.string()
8373
8542
  });
8374
- var platformArtifactDownloadContract = c15.router({
8543
+ var platformArtifactDownloadContract = c16.router({
8375
8544
  getDownloadUrl: {
8376
8545
  method: "GET",
8377
8546
  path: "/api/platform/artifacts/download",
8378
- query: z23.object({
8379
- name: z23.string().min(1, "Artifact name is required"),
8380
- version: z23.string().optional()
8547
+ query: z24.object({
8548
+ name: z24.string().min(1, "Artifact name is required"),
8549
+ version: z24.string().optional()
8381
8550
  }),
8382
8551
  responses: {
8383
8552
  200: artifactDownloadResponseSchema,
@@ -8389,29 +8558,29 @@ var platformArtifactDownloadContract = c15.router({
8389
8558
  });
8390
8559
 
8391
8560
  // ../../packages/core/src/contracts/llm.ts
8392
- import { z as z24 } from "zod";
8393
- var c16 = initContract();
8394
- var messageRoleSchema = z24.enum(["user", "assistant", "system"]);
8395
- var chatMessageSchema = z24.object({
8561
+ import { z as z25 } from "zod";
8562
+ var c17 = initContract();
8563
+ var messageRoleSchema = z25.enum(["user", "assistant", "system"]);
8564
+ var chatMessageSchema = z25.object({
8396
8565
  role: messageRoleSchema,
8397
- content: z24.string()
8566
+ content: z25.string()
8398
8567
  });
8399
- var tokenUsageSchema = z24.object({
8400
- promptTokens: z24.number(),
8401
- completionTokens: z24.number(),
8402
- totalTokens: z24.number()
8568
+ var tokenUsageSchema = z25.object({
8569
+ promptTokens: z25.number(),
8570
+ completionTokens: z25.number(),
8571
+ totalTokens: z25.number()
8403
8572
  });
8404
- var llmChatRequestSchema = z24.object({
8405
- model: z24.string().min(1).optional(),
8406
- messages: z24.array(chatMessageSchema).min(1, "At least one message is required"),
8407
- stream: z24.boolean().optional().default(false)
8573
+ var llmChatRequestSchema = z25.object({
8574
+ model: z25.string().min(1).optional(),
8575
+ messages: z25.array(chatMessageSchema).min(1, "At least one message is required"),
8576
+ stream: z25.boolean().optional().default(false)
8408
8577
  });
8409
- var llmChatResponseSchema = z24.object({
8410
- content: z24.string(),
8411
- model: z24.string(),
8578
+ var llmChatResponseSchema = z25.object({
8579
+ content: z25.string(),
8580
+ model: z25.string(),
8412
8581
  usage: tokenUsageSchema
8413
8582
  });
8414
- var llmChatContract = c16.router({
8583
+ var llmChatContract = c17.router({
8415
8584
  chat: {
8416
8585
  method: "POST",
8417
8586
  path: "/api/llm/chat",
@@ -8427,28 +8596,28 @@ var llmChatContract = c16.router({
8427
8596
  });
8428
8597
 
8429
8598
  // ../../packages/core/src/contracts/public/agents.ts
8430
- import { z as z25 } from "zod";
8431
- var c17 = initContract();
8432
- var publicAgentSchema = z25.object({
8433
- id: z25.string(),
8434
- name: z25.string(),
8435
- currentVersionId: z25.string().nullable(),
8599
+ import { z as z26 } from "zod";
8600
+ var c18 = initContract();
8601
+ var publicAgentSchema = z26.object({
8602
+ id: z26.string(),
8603
+ name: z26.string(),
8604
+ currentVersionId: z26.string().nullable(),
8436
8605
  createdAt: timestampSchema,
8437
8606
  updatedAt: timestampSchema
8438
8607
  });
8439
- var agentVersionSchema = z25.object({
8440
- id: z25.string(),
8441
- agentId: z25.string(),
8442
- versionNumber: z25.number(),
8608
+ var agentVersionSchema = z26.object({
8609
+ id: z26.string(),
8610
+ agentId: z26.string(),
8611
+ versionNumber: z26.number(),
8443
8612
  createdAt: timestampSchema
8444
8613
  });
8445
8614
  var publicAgentDetailSchema = publicAgentSchema;
8446
8615
  var paginatedAgentsSchema = createPaginatedResponseSchema(publicAgentSchema);
8447
8616
  var paginatedAgentVersionsSchema = createPaginatedResponseSchema(agentVersionSchema);
8448
8617
  var agentListQuerySchema = listQuerySchema.extend({
8449
- name: z25.string().optional()
8618
+ name: z26.string().optional()
8450
8619
  });
8451
- var publicAgentsListContract = c17.router({
8620
+ var publicAgentsListContract = c18.router({
8452
8621
  list: {
8453
8622
  method: "GET",
8454
8623
  path: "/v1/agents",
@@ -8463,13 +8632,13 @@ var publicAgentsListContract = c17.router({
8463
8632
  description: "List all agents in the current scope with pagination. Use the `name` query parameter to filter by agent name."
8464
8633
  }
8465
8634
  });
8466
- var publicAgentByIdContract = c17.router({
8635
+ var publicAgentByIdContract = c18.router({
8467
8636
  get: {
8468
8637
  method: "GET",
8469
8638
  path: "/v1/agents/:id",
8470
8639
  headers: authHeadersSchema,
8471
- pathParams: z25.object({
8472
- id: z25.string().min(1, "Agent ID is required")
8640
+ pathParams: z26.object({
8641
+ id: z26.string().min(1, "Agent ID is required")
8473
8642
  }),
8474
8643
  responses: {
8475
8644
  200: publicAgentDetailSchema,
@@ -8481,13 +8650,13 @@ var publicAgentByIdContract = c17.router({
8481
8650
  description: "Get agent details by ID"
8482
8651
  }
8483
8652
  });
8484
- var publicAgentVersionsContract = c17.router({
8653
+ var publicAgentVersionsContract = c18.router({
8485
8654
  list: {
8486
8655
  method: "GET",
8487
8656
  path: "/v1/agents/:id/versions",
8488
8657
  headers: authHeadersSchema,
8489
- pathParams: z25.object({
8490
- id: z25.string().min(1, "Agent ID is required")
8658
+ pathParams: z26.object({
8659
+ id: z26.string().min(1, "Agent ID is required")
8491
8660
  }),
8492
8661
  query: listQuerySchema,
8493
8662
  responses: {
@@ -8502,9 +8671,9 @@ var publicAgentVersionsContract = c17.router({
8502
8671
  });
8503
8672
 
8504
8673
  // ../../packages/core/src/contracts/public/runs.ts
8505
- import { z as z26 } from "zod";
8506
- var c18 = initContract();
8507
- var publicRunStatusSchema = z26.enum([
8674
+ import { z as z27 } from "zod";
8675
+ var c19 = initContract();
8676
+ var publicRunStatusSchema = z27.enum([
8508
8677
  "pending",
8509
8678
  "running",
8510
8679
  "completed",
@@ -8512,54 +8681,54 @@ var publicRunStatusSchema = z26.enum([
8512
8681
  "timeout",
8513
8682
  "cancelled"
8514
8683
  ]);
8515
- var publicRunSchema = z26.object({
8516
- id: z26.string(),
8517
- agentId: z26.string(),
8518
- agentName: z26.string(),
8684
+ var publicRunSchema = z27.object({
8685
+ id: z27.string(),
8686
+ agentId: z27.string(),
8687
+ agentName: z27.string(),
8519
8688
  status: publicRunStatusSchema,
8520
- prompt: z26.string(),
8689
+ prompt: z27.string(),
8521
8690
  createdAt: timestampSchema,
8522
8691
  startedAt: timestampSchema.nullable(),
8523
8692
  completedAt: timestampSchema.nullable()
8524
8693
  });
8525
8694
  var publicRunDetailSchema = publicRunSchema.extend({
8526
- error: z26.string().nullable(),
8527
- executionTimeMs: z26.number().nullable(),
8528
- checkpointId: z26.string().nullable(),
8529
- sessionId: z26.string().nullable(),
8530
- artifactName: z26.string().nullable(),
8531
- artifactVersion: z26.string().nullable(),
8532
- volumes: z26.record(z26.string(), z26.string()).optional()
8695
+ error: z27.string().nullable(),
8696
+ executionTimeMs: z27.number().nullable(),
8697
+ checkpointId: z27.string().nullable(),
8698
+ sessionId: z27.string().nullable(),
8699
+ artifactName: z27.string().nullable(),
8700
+ artifactVersion: z27.string().nullable(),
8701
+ volumes: z27.record(z27.string(), z27.string()).optional()
8533
8702
  });
8534
8703
  var paginatedRunsSchema = createPaginatedResponseSchema(publicRunSchema);
8535
- var createRunRequestSchema = z26.object({
8704
+ var createRunRequestSchema = z27.object({
8536
8705
  // Agent identification (one of: agent, agentId, sessionId, checkpointId)
8537
- agent: z26.string().optional(),
8706
+ agent: z27.string().optional(),
8538
8707
  // Agent name
8539
- agentId: z26.string().optional(),
8708
+ agentId: z27.string().optional(),
8540
8709
  // Agent ID
8541
- agentVersion: z26.string().optional(),
8710
+ agentVersion: z27.string().optional(),
8542
8711
  // Version specifier (e.g., "latest", "v1", specific ID)
8543
8712
  // Continue session
8544
- sessionId: z26.string().optional(),
8713
+ sessionId: z27.string().optional(),
8545
8714
  // Resume from checkpoint
8546
- checkpointId: z26.string().optional(),
8715
+ checkpointId: z27.string().optional(),
8547
8716
  // Required
8548
- prompt: z26.string().min(1, "Prompt is required"),
8717
+ prompt: z27.string().min(1, "Prompt is required"),
8549
8718
  // Optional configuration
8550
- variables: z26.record(z26.string(), z26.string()).optional(),
8551
- secrets: z26.record(z26.string(), z26.string()).optional(),
8552
- artifactName: z26.string().optional(),
8719
+ variables: z27.record(z27.string(), z27.string()).optional(),
8720
+ secrets: z27.record(z27.string(), z27.string()).optional(),
8721
+ artifactName: z27.string().optional(),
8553
8722
  // Artifact name to mount
8554
- artifactVersion: z26.string().optional(),
8723
+ artifactVersion: z27.string().optional(),
8555
8724
  // Artifact version (defaults to latest)
8556
- volumes: z26.record(z26.string(), z26.string()).optional()
8725
+ volumes: z27.record(z27.string(), z27.string()).optional()
8557
8726
  // volume_name -> version
8558
8727
  });
8559
8728
  var runListQuerySchema = listQuerySchema.extend({
8560
8729
  status: publicRunStatusSchema.optional()
8561
8730
  });
8562
- var publicRunsListContract = c18.router({
8731
+ var publicRunsListContract = c19.router({
8563
8732
  list: {
8564
8733
  method: "GET",
8565
8734
  path: "/v1/runs",
@@ -8591,13 +8760,13 @@ var publicRunsListContract = c18.router({
8591
8760
  description: "Create and execute a new agent run. Returns 202 Accepted as runs execute asynchronously."
8592
8761
  }
8593
8762
  });
8594
- var publicRunByIdContract = c18.router({
8763
+ var publicRunByIdContract = c19.router({
8595
8764
  get: {
8596
8765
  method: "GET",
8597
8766
  path: "/v1/runs/:id",
8598
8767
  headers: authHeadersSchema,
8599
- pathParams: z26.object({
8600
- id: z26.string().min(1, "Run ID is required")
8768
+ pathParams: z27.object({
8769
+ id: z27.string().min(1, "Run ID is required")
8601
8770
  }),
8602
8771
  responses: {
8603
8772
  200: publicRunDetailSchema,
@@ -8609,15 +8778,15 @@ var publicRunByIdContract = c18.router({
8609
8778
  description: "Get run details by ID"
8610
8779
  }
8611
8780
  });
8612
- var publicRunCancelContract = c18.router({
8781
+ var publicRunCancelContract = c19.router({
8613
8782
  cancel: {
8614
8783
  method: "POST",
8615
8784
  path: "/v1/runs/:id/cancel",
8616
8785
  headers: authHeadersSchema,
8617
- pathParams: z26.object({
8618
- id: z26.string().min(1, "Run ID is required")
8786
+ pathParams: z27.object({
8787
+ id: z27.string().min(1, "Run ID is required")
8619
8788
  }),
8620
- body: z26.undefined(),
8789
+ body: z27.undefined(),
8621
8790
  responses: {
8622
8791
  200: publicRunDetailSchema,
8623
8792
  400: publicApiErrorSchema,
@@ -8630,27 +8799,27 @@ var publicRunCancelContract = c18.router({
8630
8799
  description: "Cancel a pending or running execution"
8631
8800
  }
8632
8801
  });
8633
- var logEntrySchema = z26.object({
8802
+ var logEntrySchema = z27.object({
8634
8803
  timestamp: timestampSchema,
8635
- type: z26.enum(["agent", "system", "network"]),
8636
- level: z26.enum(["debug", "info", "warn", "error"]),
8637
- message: z26.string(),
8638
- metadata: z26.record(z26.string(), z26.unknown()).optional()
8804
+ type: z27.enum(["agent", "system", "network"]),
8805
+ level: z27.enum(["debug", "info", "warn", "error"]),
8806
+ message: z27.string(),
8807
+ metadata: z27.record(z27.string(), z27.unknown()).optional()
8639
8808
  });
8640
8809
  var paginatedLogsSchema = createPaginatedResponseSchema(logEntrySchema);
8641
8810
  var logsQuerySchema = listQuerySchema.extend({
8642
- type: z26.enum(["agent", "system", "network", "all"]).default("all"),
8811
+ type: z27.enum(["agent", "system", "network", "all"]).default("all"),
8643
8812
  since: timestampSchema.optional(),
8644
8813
  until: timestampSchema.optional(),
8645
- order: z26.enum(["asc", "desc"]).default("asc")
8814
+ order: z27.enum(["asc", "desc"]).default("asc")
8646
8815
  });
8647
- var publicRunLogsContract = c18.router({
8816
+ var publicRunLogsContract = c19.router({
8648
8817
  getLogs: {
8649
8818
  method: "GET",
8650
8819
  path: "/v1/runs/:id/logs",
8651
8820
  headers: authHeadersSchema,
8652
- pathParams: z26.object({
8653
- id: z26.string().min(1, "Run ID is required")
8821
+ pathParams: z27.object({
8822
+ id: z27.string().min(1, "Run ID is required")
8654
8823
  }),
8655
8824
  query: logsQuerySchema,
8656
8825
  responses: {
@@ -8663,30 +8832,30 @@ var publicRunLogsContract = c18.router({
8663
8832
  description: "Get unified logs for a run. Combines agent, system, and network logs."
8664
8833
  }
8665
8834
  });
8666
- var metricPointSchema = z26.object({
8835
+ var metricPointSchema = z27.object({
8667
8836
  timestamp: timestampSchema,
8668
- cpuPercent: z26.number(),
8669
- memoryUsedMb: z26.number(),
8670
- memoryTotalMb: z26.number(),
8671
- diskUsedMb: z26.number(),
8672
- diskTotalMb: z26.number()
8673
- });
8674
- var metricsSummarySchema = z26.object({
8675
- avgCpuPercent: z26.number(),
8676
- maxMemoryUsedMb: z26.number(),
8677
- totalDurationMs: z26.number().nullable()
8678
- });
8679
- var metricsResponseSchema2 = z26.object({
8680
- data: z26.array(metricPointSchema),
8837
+ cpuPercent: z27.number(),
8838
+ memoryUsedMb: z27.number(),
8839
+ memoryTotalMb: z27.number(),
8840
+ diskUsedMb: z27.number(),
8841
+ diskTotalMb: z27.number()
8842
+ });
8843
+ var metricsSummarySchema = z27.object({
8844
+ avgCpuPercent: z27.number(),
8845
+ maxMemoryUsedMb: z27.number(),
8846
+ totalDurationMs: z27.number().nullable()
8847
+ });
8848
+ var metricsResponseSchema2 = z27.object({
8849
+ data: z27.array(metricPointSchema),
8681
8850
  summary: metricsSummarySchema
8682
8851
  });
8683
- var publicRunMetricsContract = c18.router({
8852
+ var publicRunMetricsContract = c19.router({
8684
8853
  getMetrics: {
8685
8854
  method: "GET",
8686
8855
  path: "/v1/runs/:id/metrics",
8687
8856
  headers: authHeadersSchema,
8688
- pathParams: z26.object({
8689
- id: z26.string().min(1, "Run ID is required")
8857
+ pathParams: z27.object({
8858
+ id: z27.string().min(1, "Run ID is required")
8690
8859
  }),
8691
8860
  responses: {
8692
8861
  200: metricsResponseSchema2,
@@ -8698,7 +8867,7 @@ var publicRunMetricsContract = c18.router({
8698
8867
  description: "Get CPU, memory, and disk metrics for a run"
8699
8868
  }
8700
8869
  });
8701
- var sseEventTypeSchema = z26.enum([
8870
+ var sseEventTypeSchema = z27.enum([
8702
8871
  "status",
8703
8872
  // Run status change
8704
8873
  "output",
@@ -8710,26 +8879,26 @@ var sseEventTypeSchema = z26.enum([
8710
8879
  "heartbeat"
8711
8880
  // Keep-alive
8712
8881
  ]);
8713
- var sseEventSchema = z26.object({
8882
+ var sseEventSchema = z27.object({
8714
8883
  event: sseEventTypeSchema,
8715
- data: z26.unknown(),
8716
- id: z26.string().optional()
8884
+ data: z27.unknown(),
8885
+ id: z27.string().optional()
8717
8886
  // For Last-Event-ID reconnection
8718
8887
  });
8719
- var publicRunEventsContract = c18.router({
8888
+ var publicRunEventsContract = c19.router({
8720
8889
  streamEvents: {
8721
8890
  method: "GET",
8722
8891
  path: "/v1/runs/:id/events",
8723
8892
  headers: authHeadersSchema,
8724
- pathParams: z26.object({
8725
- id: z26.string().min(1, "Run ID is required")
8893
+ pathParams: z27.object({
8894
+ id: z27.string().min(1, "Run ID is required")
8726
8895
  }),
8727
- query: z26.object({
8728
- lastEventId: z26.string().optional()
8896
+ query: z27.object({
8897
+ lastEventId: z27.string().optional()
8729
8898
  // For reconnection
8730
8899
  }),
8731
8900
  responses: {
8732
- 200: z26.any(),
8901
+ 200: z27.any(),
8733
8902
  // SSE stream - actual content is text/event-stream
8734
8903
  401: publicApiErrorSchema,
8735
8904
  404: publicApiErrorSchema,
@@ -8741,28 +8910,28 @@ var publicRunEventsContract = c18.router({
8741
8910
  });
8742
8911
 
8743
8912
  // ../../packages/core/src/contracts/public/artifacts.ts
8744
- import { z as z27 } from "zod";
8745
- var c19 = initContract();
8746
- var publicArtifactSchema = z27.object({
8747
- id: z27.string(),
8748
- name: z27.string(),
8749
- currentVersionId: z27.string().nullable(),
8750
- size: z27.number(),
8913
+ import { z as z28 } from "zod";
8914
+ var c20 = initContract();
8915
+ var publicArtifactSchema = z28.object({
8916
+ id: z28.string(),
8917
+ name: z28.string(),
8918
+ currentVersionId: z28.string().nullable(),
8919
+ size: z28.number(),
8751
8920
  // Total size in bytes
8752
- fileCount: z27.number(),
8921
+ fileCount: z28.number(),
8753
8922
  createdAt: timestampSchema,
8754
8923
  updatedAt: timestampSchema
8755
8924
  });
8756
- var artifactVersionSchema = z27.object({
8757
- id: z27.string(),
8925
+ var artifactVersionSchema = z28.object({
8926
+ id: z28.string(),
8758
8927
  // SHA-256 content hash
8759
- artifactId: z27.string(),
8760
- size: z27.number(),
8928
+ artifactId: z28.string(),
8929
+ size: z28.number(),
8761
8930
  // Size in bytes
8762
- fileCount: z27.number(),
8763
- message: z27.string().nullable(),
8931
+ fileCount: z28.number(),
8932
+ message: z28.string().nullable(),
8764
8933
  // Optional commit message
8765
- createdBy: z27.string(),
8934
+ createdBy: z28.string(),
8766
8935
  createdAt: timestampSchema
8767
8936
  });
8768
8937
  var publicArtifactDetailSchema = publicArtifactSchema.extend({
@@ -8772,7 +8941,7 @@ var paginatedArtifactsSchema = createPaginatedResponseSchema(publicArtifactSchem
8772
8941
  var paginatedArtifactVersionsSchema = createPaginatedResponseSchema(
8773
8942
  artifactVersionSchema
8774
8943
  );
8775
- var publicArtifactsListContract = c19.router({
8944
+ var publicArtifactsListContract = c20.router({
8776
8945
  list: {
8777
8946
  method: "GET",
8778
8947
  path: "/v1/artifacts",
@@ -8787,13 +8956,13 @@ var publicArtifactsListContract = c19.router({
8787
8956
  description: "List all artifacts in the current scope with pagination"
8788
8957
  }
8789
8958
  });
8790
- var publicArtifactByIdContract = c19.router({
8959
+ var publicArtifactByIdContract = c20.router({
8791
8960
  get: {
8792
8961
  method: "GET",
8793
8962
  path: "/v1/artifacts/:id",
8794
8963
  headers: authHeadersSchema,
8795
- pathParams: z27.object({
8796
- id: z27.string().min(1, "Artifact ID is required")
8964
+ pathParams: z28.object({
8965
+ id: z28.string().min(1, "Artifact ID is required")
8797
8966
  }),
8798
8967
  responses: {
8799
8968
  200: publicArtifactDetailSchema,
@@ -8805,13 +8974,13 @@ var publicArtifactByIdContract = c19.router({
8805
8974
  description: "Get artifact details by ID"
8806
8975
  }
8807
8976
  });
8808
- var publicArtifactVersionsContract = c19.router({
8977
+ var publicArtifactVersionsContract = c20.router({
8809
8978
  list: {
8810
8979
  method: "GET",
8811
8980
  path: "/v1/artifacts/:id/versions",
8812
8981
  headers: authHeadersSchema,
8813
- pathParams: z27.object({
8814
- id: z27.string().min(1, "Artifact ID is required")
8982
+ pathParams: z28.object({
8983
+ id: z28.string().min(1, "Artifact ID is required")
8815
8984
  }),
8816
8985
  query: listQuerySchema,
8817
8986
  responses: {
@@ -8824,20 +8993,20 @@ var publicArtifactVersionsContract = c19.router({
8824
8993
  description: "List all versions of an artifact with pagination"
8825
8994
  }
8826
8995
  });
8827
- var publicArtifactDownloadContract = c19.router({
8996
+ var publicArtifactDownloadContract = c20.router({
8828
8997
  download: {
8829
8998
  method: "GET",
8830
8999
  path: "/v1/artifacts/:id/download",
8831
9000
  headers: authHeadersSchema,
8832
- pathParams: z27.object({
8833
- id: z27.string().min(1, "Artifact ID is required")
9001
+ pathParams: z28.object({
9002
+ id: z28.string().min(1, "Artifact ID is required")
8834
9003
  }),
8835
- query: z27.object({
8836
- versionId: z27.string().optional()
9004
+ query: z28.object({
9005
+ versionId: z28.string().optional()
8837
9006
  // Defaults to current version
8838
9007
  }),
8839
9008
  responses: {
8840
- 302: z27.undefined(),
9009
+ 302: z28.undefined(),
8841
9010
  // Redirect to presigned URL
8842
9011
  401: publicApiErrorSchema,
8843
9012
  404: publicApiErrorSchema,
@@ -8849,28 +9018,28 @@ var publicArtifactDownloadContract = c19.router({
8849
9018
  });
8850
9019
 
8851
9020
  // ../../packages/core/src/contracts/public/volumes.ts
8852
- import { z as z28 } from "zod";
8853
- var c20 = initContract();
8854
- var publicVolumeSchema = z28.object({
8855
- id: z28.string(),
8856
- name: z28.string(),
8857
- currentVersionId: z28.string().nullable(),
8858
- size: z28.number(),
9021
+ import { z as z29 } from "zod";
9022
+ var c21 = initContract();
9023
+ var publicVolumeSchema = z29.object({
9024
+ id: z29.string(),
9025
+ name: z29.string(),
9026
+ currentVersionId: z29.string().nullable(),
9027
+ size: z29.number(),
8859
9028
  // Total size in bytes
8860
- fileCount: z28.number(),
9029
+ fileCount: z29.number(),
8861
9030
  createdAt: timestampSchema,
8862
9031
  updatedAt: timestampSchema
8863
9032
  });
8864
- var volumeVersionSchema = z28.object({
8865
- id: z28.string(),
9033
+ var volumeVersionSchema = z29.object({
9034
+ id: z29.string(),
8866
9035
  // SHA-256 content hash
8867
- volumeId: z28.string(),
8868
- size: z28.number(),
9036
+ volumeId: z29.string(),
9037
+ size: z29.number(),
8869
9038
  // Size in bytes
8870
- fileCount: z28.number(),
8871
- message: z28.string().nullable(),
9039
+ fileCount: z29.number(),
9040
+ message: z29.string().nullable(),
8872
9041
  // Optional commit message
8873
- createdBy: z28.string(),
9042
+ createdBy: z29.string(),
8874
9043
  createdAt: timestampSchema
8875
9044
  });
8876
9045
  var publicVolumeDetailSchema = publicVolumeSchema.extend({
@@ -8878,7 +9047,7 @@ var publicVolumeDetailSchema = publicVolumeSchema.extend({
8878
9047
  });
8879
9048
  var paginatedVolumesSchema = createPaginatedResponseSchema(publicVolumeSchema);
8880
9049
  var paginatedVolumeVersionsSchema = createPaginatedResponseSchema(volumeVersionSchema);
8881
- var publicVolumesListContract = c20.router({
9050
+ var publicVolumesListContract = c21.router({
8882
9051
  list: {
8883
9052
  method: "GET",
8884
9053
  path: "/v1/volumes",
@@ -8893,13 +9062,13 @@ var publicVolumesListContract = c20.router({
8893
9062
  description: "List all volumes in the current scope with pagination"
8894
9063
  }
8895
9064
  });
8896
- var publicVolumeByIdContract = c20.router({
9065
+ var publicVolumeByIdContract = c21.router({
8897
9066
  get: {
8898
9067
  method: "GET",
8899
9068
  path: "/v1/volumes/:id",
8900
9069
  headers: authHeadersSchema,
8901
- pathParams: z28.object({
8902
- id: z28.string().min(1, "Volume ID is required")
9070
+ pathParams: z29.object({
9071
+ id: z29.string().min(1, "Volume ID is required")
8903
9072
  }),
8904
9073
  responses: {
8905
9074
  200: publicVolumeDetailSchema,
@@ -8911,13 +9080,13 @@ var publicVolumeByIdContract = c20.router({
8911
9080
  description: "Get volume details by ID"
8912
9081
  }
8913
9082
  });
8914
- var publicVolumeVersionsContract = c20.router({
9083
+ var publicVolumeVersionsContract = c21.router({
8915
9084
  list: {
8916
9085
  method: "GET",
8917
9086
  path: "/v1/volumes/:id/versions",
8918
9087
  headers: authHeadersSchema,
8919
- pathParams: z28.object({
8920
- id: z28.string().min(1, "Volume ID is required")
9088
+ pathParams: z29.object({
9089
+ id: z29.string().min(1, "Volume ID is required")
8921
9090
  }),
8922
9091
  query: listQuerySchema,
8923
9092
  responses: {
@@ -8930,20 +9099,20 @@ var publicVolumeVersionsContract = c20.router({
8930
9099
  description: "List all versions of a volume with pagination"
8931
9100
  }
8932
9101
  });
8933
- var publicVolumeDownloadContract = c20.router({
9102
+ var publicVolumeDownloadContract = c21.router({
8934
9103
  download: {
8935
9104
  method: "GET",
8936
9105
  path: "/v1/volumes/:id/download",
8937
9106
  headers: authHeadersSchema,
8938
- pathParams: z28.object({
8939
- id: z28.string().min(1, "Volume ID is required")
9107
+ pathParams: z29.object({
9108
+ id: z29.string().min(1, "Volume ID is required")
8940
9109
  }),
8941
- query: z28.object({
8942
- versionId: z28.string().optional()
9110
+ query: z29.object({
9111
+ versionId: z29.string().optional()
8943
9112
  // Defaults to current version
8944
9113
  }),
8945
9114
  responses: {
8946
- 302: z28.undefined(),
9115
+ 302: z29.undefined(),
8947
9116
  // Redirect to presigned URL
8948
9117
  401: publicApiErrorSchema,
8949
9118
  404: publicApiErrorSchema,
@@ -9105,7 +9274,7 @@ function initVMRegistry(registryPath) {
9105
9274
  // src/lib/proxy/proxy-manager.ts
9106
9275
  import { spawn as spawn2 } from "child_process";
9107
9276
  import fs7 from "fs";
9108
- import path4 from "path";
9277
+ import path5 from "path";
9109
9278
 
9110
9279
  // src/lib/proxy/mitm-addon-script.ts
9111
9280
  var RUNNER_MITM_ADDON_SCRIPT = `#!/usr/bin/env python3
@@ -9601,7 +9770,7 @@ var ProxyManager = class {
9601
9770
  process = null;
9602
9771
  isRunning = false;
9603
9772
  constructor(config) {
9604
- const addonPath = path4.join(config.caDir, "mitm_addon.py");
9773
+ const addonPath = path5.join(config.caDir, "mitm_addon.py");
9605
9774
  this.config = {
9606
9775
  ...DEFAULT_PROXY_OPTIONS,
9607
9776
  ...config,
@@ -9628,7 +9797,7 @@ var ProxyManager = class {
9628
9797
  * Ensure the addon script exists at the configured path
9629
9798
  */
9630
9799
  ensureAddonScript() {
9631
- const addonDir = path4.dirname(this.config.addonPath);
9800
+ const addonDir = path5.dirname(this.config.addonPath);
9632
9801
  if (!fs7.existsSync(addonDir)) {
9633
9802
  fs7.mkdirSync(addonDir, { recursive: true });
9634
9803
  }
@@ -9644,7 +9813,7 @@ var ProxyManager = class {
9644
9813
  if (!fs7.existsSync(this.config.caDir)) {
9645
9814
  throw new Error(`Proxy CA directory not found: ${this.config.caDir}`);
9646
9815
  }
9647
- const caCertPath = path4.join(this.config.caDir, "mitmproxy-ca.pem");
9816
+ const caCertPath = path5.join(this.config.caDir, "mitmproxy-ca.pem");
9648
9817
  if (!fs7.existsSync(caCertPath)) {
9649
9818
  throw new Error(`Proxy CA certificate not found: ${caCertPath}`);
9650
9819
  }
@@ -10097,7 +10266,21 @@ async function executeJob(context, config, options = {}) {
10097
10266
  const guestConnectionPromise = guest.waitForGuestConnection(3e4);
10098
10267
  logger9.log(`Creating VM ${vmId}...`);
10099
10268
  vm = new FirecrackerVM(vmConfig);
10100
- await withSandboxTiming("vm_create", () => vm.start());
10269
+ const snapshotConfig = config.firecracker.snapshot;
10270
+ let snapshotPaths;
10271
+ if (snapshotConfig) {
10272
+ const snapshotDir = path6.dirname(snapshotConfig.snapshot);
10273
+ const originalBaseDir = path6.dirname(snapshotDir);
10274
+ const snapshotBaseDir = runnerPaths.snapshotBaseDir(originalBaseDir);
10275
+ const snapshotWorkDir = runnerPaths.snapshotWorkDir(snapshotBaseDir);
10276
+ snapshotPaths = {
10277
+ snapshot: snapshotConfig.snapshot,
10278
+ memory: snapshotConfig.memory,
10279
+ snapshotOverlay: vmPaths.overlay(snapshotWorkDir),
10280
+ snapshotVsockDir: vmPaths.vsockDir(snapshotWorkDir)
10281
+ };
10282
+ }
10283
+ await withSandboxTiming("vm_create", () => vm.start(snapshotPaths));
10101
10284
  guestIp = vm.getGuestIp();
10102
10285
  vethNsIp = vm.getNetns()?.vethNsIp ?? null;
10103
10286
  if (!guestIp || !vethNsIp) {
@@ -10125,6 +10308,10 @@ async function executeJob(context, config, options = {}) {
10125
10308
  logger9.log(`Waiting for guest connection...`);
10126
10309
  await withSandboxTiming("guest_wait", () => guestConnectionPromise);
10127
10310
  logger9.log(`Guest client ready`);
10311
+ if (config.firecracker.snapshot) {
10312
+ const timestamp = (Date.now() / 1e3).toFixed(3);
10313
+ await guest.exec(`date -s "@${timestamp}"`);
10314
+ }
10128
10315
  if (context.storageManifest) {
10129
10316
  await withSandboxTiming(
10130
10317
  "storage_download",
@@ -10327,7 +10514,7 @@ async function isPortInUse(port) {
10327
10514
 
10328
10515
  // src/lib/runner/runner-lock.ts
10329
10516
  import fs10 from "fs";
10330
- import path5 from "path";
10517
+ import path7 from "path";
10331
10518
  var logger11 = createLogger("RunnerLock");
10332
10519
  var DEFAULT_PID_FILE = runtimePaths.runnerPid;
10333
10520
  var currentPidFile = null;
@@ -10344,7 +10531,7 @@ function isProcessRunning(pid) {
10344
10531
  }
10345
10532
  function acquireRunnerLock(options = {}) {
10346
10533
  const pidFile = options.pidFile ?? DEFAULT_PID_FILE;
10347
- const runDir = path5.dirname(pidFile);
10534
+ const runDir = path7.dirname(pidFile);
10348
10535
  fs10.mkdirSync(runDir, { recursive: true });
10349
10536
  if (fs10.existsSync(pidFile)) {
10350
10537
  const pidStr = fs10.readFileSync(pidFile, "utf-8").trim();
@@ -10378,7 +10565,7 @@ function releaseRunnerLock() {
10378
10565
  var logger12 = createLogger("Runner");
10379
10566
  async function setupEnvironment(options) {
10380
10567
  const { config } = options;
10381
- await acquireRunnerLock();
10568
+ acquireRunnerLock();
10382
10569
  const networkCheck = checkNetworkPrerequisites();
10383
10570
  if (!networkCheck.ok) {
10384
10571
  logger12.error("Network prerequisites not met:");
@@ -10408,10 +10595,16 @@ async function setupEnvironment(options) {
10408
10595
  );
10409
10596
  }
10410
10597
  logger12.log("Initializing overlay pool...");
10598
+ const snapshotConfig = config.firecracker.snapshot;
10411
10599
  await initOverlayPool({
10412
10600
  size: config.sandbox.max_concurrent + 2,
10413
10601
  replenishThreshold: config.sandbox.max_concurrent,
10414
- poolDir: runnerPaths.overlayPool(config.base_dir)
10602
+ poolDir: runnerPaths.overlayPool(config.base_dir),
10603
+ createFile: snapshotConfig ? (filePath) => execCommand(
10604
+ `cp --sparse=always "${snapshotConfig.overlay}" "${filePath}"`,
10605
+ false
10606
+ ).then(() => {
10607
+ }) : void 0
10415
10608
  });
10416
10609
  logger12.log("Initializing namespace pool...");
10417
10610
  await initNetnsPool({
@@ -10719,7 +10912,7 @@ import { existsSync as existsSync5, readFileSync as readFileSync3, readdirSync a
10719
10912
 
10720
10913
  // src/lib/firecracker/process.ts
10721
10914
  import { readdirSync, readFileSync as readFileSync2, existsSync as existsSync4 } from "fs";
10722
- import path6 from "path";
10915
+ import path8 from "path";
10723
10916
  function parseFirecrackerCmdline(cmdline) {
10724
10917
  const args = cmdline.split("\0");
10725
10918
  if (!args[0]?.includes("firecracker")) return null;
@@ -10752,7 +10945,7 @@ function findFirecrackerProcesses() {
10752
10945
  for (const entry of entries) {
10753
10946
  if (!/^\d+$/.test(entry)) continue;
10754
10947
  const pid = parseInt(entry, 10);
10755
- const cmdlinePath = path6.join(procDir, entry, "cmdline");
10948
+ const cmdlinePath = path8.join(procDir, entry, "cmdline");
10756
10949
  if (!existsSync4(cmdlinePath)) continue;
10757
10950
  try {
10758
10951
  const cmdline = readFileSync2(cmdlinePath, "utf-8");
@@ -10810,7 +11003,7 @@ function findMitmproxyProcess() {
10810
11003
  for (const entry of entries) {
10811
11004
  if (!/^\d+$/.test(entry)) continue;
10812
11005
  const pid = parseInt(entry, 10);
10813
- const cmdlinePath = path6.join(procDir, entry, "cmdline");
11006
+ const cmdlinePath = path8.join(procDir, entry, "cmdline");
10814
11007
  if (!existsSync4(cmdlinePath)) continue;
10815
11008
  try {
10816
11009
  const cmdline = readFileSync2(cmdlinePath, "utf-8");
@@ -10826,142 +11019,127 @@ function findMitmproxyProcess() {
10826
11019
  }
10827
11020
 
10828
11021
  // src/commands/doctor.ts
10829
- var doctorCommand = new Command2("doctor").description("Diagnose runner health, check network, and detect issues").option("--config <path>", "Config file path", "./runner.yaml").action(
10830
- // eslint-disable-next-line complexity -- TODO: refactor complex function
10831
- async (options) => {
10832
- try {
10833
- const config = loadConfig(options.config);
10834
- const statusFilePath = runnerPaths.statusFile(config.base_dir);
10835
- const workspacesDir = runnerPaths.workspacesDir(config.base_dir);
10836
- console.log(`Runner: ${config.name}`);
10837
- let status = null;
10838
- if (existsSync5(statusFilePath)) {
10839
- try {
10840
- status = JSON.parse(
10841
- readFileSync3(statusFilePath, "utf-8")
10842
- );
10843
- console.log(`Mode: ${status.mode}`);
10844
- if (status.started_at) {
10845
- const started = new Date(status.started_at);
10846
- const uptime = formatUptime(Date.now() - started.getTime());
10847
- console.log(
10848
- `Started: ${started.toLocaleString()} (uptime: ${uptime})`
10849
- );
10850
- }
10851
- } catch {
10852
- console.log("Mode: unknown (status.json unreadable)");
10853
- }
10854
- } else {
10855
- console.log("Mode: unknown (no status.json)");
10856
- }
10857
- console.log("");
10858
- console.log("API Connectivity:");
10859
- try {
10860
- await pollForJob(config.server, config.group);
10861
- console.log(` \u2713 Connected to ${config.server.url}`);
10862
- console.log(" \u2713 Authentication: OK");
10863
- } catch (error) {
10864
- console.log(` \u2717 Cannot connect to ${config.server.url}`);
10865
- console.log(
10866
- ` Error: ${error instanceof Error ? error.message : "Unknown error"}`
10867
- );
10868
- }
10869
- console.log("");
10870
- console.log("Network:");
10871
- const warnings = [];
10872
- const proxyPort = config.proxy.port;
10873
- const mitmProc = findMitmproxyProcess();
10874
- const portInUse = await isPortInUse(proxyPort);
10875
- if (mitmProc) {
10876
- console.log(
10877
- ` \u2713 Proxy mitmproxy (PID ${mitmProc.pid}) on :${proxyPort}`
10878
- );
10879
- } else if (portInUse) {
10880
- console.log(
10881
- ` \u26A0\uFE0F Proxy port :${proxyPort} in use but mitmproxy process not found`
10882
- );
10883
- warnings.push({
10884
- message: `Port ${proxyPort} is in use but mitmproxy process not detected`
10885
- });
10886
- } else {
10887
- console.log(` \u2717 Proxy mitmproxy not running`);
10888
- warnings.push({ message: "Proxy mitmproxy is not running" });
10889
- }
10890
- console.log(
10891
- ` \u2139 Namespaces: each VM runs in isolated namespace with IP ${SNAPSHOT_NETWORK.guestIp}`
10892
- );
10893
- console.log("");
10894
- const processes = findFirecrackerProcesses();
10895
- const workspaces = existsSync5(workspacesDir) ? readdirSync2(workspacesDir).filter(runnerPaths.isVmWorkspace) : [];
10896
- const jobs = [];
10897
- const statusVmIds = /* @__PURE__ */ new Set();
10898
- if (status?.active_run_ids) {
10899
- for (const runId of status.active_run_ids) {
10900
- const vmId = createVmId(runId);
10901
- statusVmIds.add(vmId);
10902
- const proc = processes.find((p) => p.vmId === vmId);
10903
- jobs.push({
10904
- runId,
10905
- vmId,
10906
- hasProcess: !!proc,
10907
- pid: proc?.pid
10908
- });
10909
- }
10910
- }
10911
- const maxConcurrent = config.sandbox.max_concurrent;
10912
- console.log(`Runs (${jobs.length} active, max ${maxConcurrent}):`);
10913
- if (jobs.length === 0) {
10914
- console.log(" No active runs");
10915
- } else {
10916
- console.log(
10917
- " Run ID VM ID Status"
10918
- );
10919
- for (const job of jobs) {
10920
- const statusText = job.hasProcess ? `\u2713 Running (PID ${job.pid})` : "\u26A0\uFE0F No process";
10921
- console.log(` ${job.runId} ${job.vmId} ${statusText}`);
10922
- }
10923
- }
10924
- console.log("");
10925
- for (const job of jobs) {
10926
- if (!job.hasProcess) {
10927
- warnings.push({
10928
- message: `Run ${job.vmId} in status.json but no Firecracker process running`
10929
- });
10930
- }
10931
- }
10932
- const processVmIds = new Set(processes.map((p) => p.vmId));
10933
- for (const proc of processes) {
10934
- if (!statusVmIds.has(proc.vmId)) {
10935
- warnings.push({
10936
- message: `Orphan process: PID ${proc.pid} (vmId ${proc.vmId}) not in status.json`
10937
- });
10938
- }
10939
- }
10940
- for (const ws of workspaces) {
10941
- const vmId = runnerPaths.extractVmId(ws);
10942
- if (!processVmIds.has(vmId) && !statusVmIds.has(vmId)) {
10943
- warnings.push({
10944
- message: `Orphan workspace: ${ws} (no matching job or process)`
10945
- });
10946
- }
10947
- }
10948
- console.log("Warnings:");
10949
- if (warnings.length === 0) {
10950
- console.log(" None");
10951
- } else {
10952
- for (const w of warnings) {
10953
- console.log(` - ${w.message}`);
10954
- }
10955
- }
10956
- process.exit(warnings.length > 0 ? 1 : 0);
10957
- } catch (error) {
10958
- console.error(
10959
- `Error: ${error instanceof Error ? error.message : "Unknown error"}`
10960
- );
10961
- process.exit(1);
11022
+ function displayRunnerStatus(statusFilePath) {
11023
+ if (!existsSync5(statusFilePath)) {
11024
+ console.log("Mode: unknown (no status.json)");
11025
+ return null;
11026
+ }
11027
+ try {
11028
+ const status = JSON.parse(
11029
+ readFileSync3(statusFilePath, "utf-8")
11030
+ );
11031
+ console.log(`Mode: ${status.mode}`);
11032
+ if (status.started_at) {
11033
+ const started = new Date(status.started_at);
11034
+ const uptime = formatUptime(Date.now() - started.getTime());
11035
+ console.log(`Started: ${started.toLocaleString()} (uptime: ${uptime})`);
10962
11036
  }
11037
+ return status;
11038
+ } catch {
11039
+ console.log("Mode: unknown (status.json unreadable)");
11040
+ return null;
10963
11041
  }
10964
- );
11042
+ }
11043
+ async function checkApiConnectivity(config) {
11044
+ console.log("API Connectivity:");
11045
+ try {
11046
+ await pollForJob(config.server, config.group);
11047
+ console.log(` \u2713 Connected to ${config.server.url}`);
11048
+ console.log(" \u2713 Authentication: OK");
11049
+ } catch (error) {
11050
+ console.log(` \u2717 Cannot connect to ${config.server.url}`);
11051
+ console.log(
11052
+ ` Error: ${error instanceof Error ? error.message : "Unknown error"}`
11053
+ );
11054
+ }
11055
+ }
11056
+ async function checkNetwork(config, warnings) {
11057
+ console.log("Network:");
11058
+ const proxyPort = config.proxy.port;
11059
+ const mitmProc = findMitmproxyProcess();
11060
+ const portInUse = await isPortInUse(proxyPort);
11061
+ if (mitmProc) {
11062
+ console.log(` \u2713 Proxy mitmproxy (PID ${mitmProc.pid}) on :${proxyPort}`);
11063
+ } else if (portInUse) {
11064
+ console.log(
11065
+ ` \u26A0\uFE0F Proxy port :${proxyPort} in use but mitmproxy process not found`
11066
+ );
11067
+ warnings.push({
11068
+ message: `Port ${proxyPort} is in use but mitmproxy process not detected`
11069
+ });
11070
+ } else {
11071
+ console.log(` \u2717 Proxy mitmproxy not running`);
11072
+ warnings.push({ message: "Proxy mitmproxy is not running" });
11073
+ }
11074
+ console.log(
11075
+ ` \u2139 Namespaces: each VM runs in isolated namespace with IP ${SNAPSHOT_NETWORK.guestIp}`
11076
+ );
11077
+ }
11078
+ function buildJobInfo(status, processes) {
11079
+ const jobs = [];
11080
+ const statusVmIds = /* @__PURE__ */ new Set();
11081
+ if (status?.active_run_ids) {
11082
+ for (const runId of status.active_run_ids) {
11083
+ const vmId = createVmId(runId);
11084
+ statusVmIds.add(vmId);
11085
+ const proc = processes.find((p) => p.vmId === vmId);
11086
+ jobs.push({
11087
+ runId,
11088
+ vmId,
11089
+ hasProcess: !!proc,
11090
+ pid: proc?.pid
11091
+ });
11092
+ }
11093
+ }
11094
+ return { jobs, statusVmIds };
11095
+ }
11096
+ function displayRuns(jobs, maxConcurrent) {
11097
+ console.log(`Runs (${jobs.length} active, max ${maxConcurrent}):`);
11098
+ if (jobs.length === 0) {
11099
+ console.log(" No active runs");
11100
+ return;
11101
+ }
11102
+ console.log(" Run ID VM ID Status");
11103
+ for (const job of jobs) {
11104
+ const statusText = job.hasProcess ? `\u2713 Running (PID ${job.pid})` : "\u26A0\uFE0F No process";
11105
+ console.log(` ${job.runId} ${job.vmId} ${statusText}`);
11106
+ }
11107
+ }
11108
+ function detectOrphanResources(jobs, processes, workspaces, statusVmIds, warnings) {
11109
+ for (const job of jobs) {
11110
+ if (!job.hasProcess) {
11111
+ warnings.push({
11112
+ message: `Run ${job.vmId} in status.json but no Firecracker process running`
11113
+ });
11114
+ }
11115
+ }
11116
+ const processVmIds = new Set(processes.map((p) => p.vmId));
11117
+ for (const proc of processes) {
11118
+ if (!statusVmIds.has(proc.vmId)) {
11119
+ warnings.push({
11120
+ message: `Orphan process: PID ${proc.pid} (vmId ${proc.vmId}) not in status.json`
11121
+ });
11122
+ }
11123
+ }
11124
+ for (const ws of workspaces) {
11125
+ const vmId = runnerPaths.extractVmId(ws);
11126
+ if (!processVmIds.has(vmId) && !statusVmIds.has(vmId)) {
11127
+ warnings.push({
11128
+ message: `Orphan workspace: ${ws} (no matching job or process)`
11129
+ });
11130
+ }
11131
+ }
11132
+ }
11133
+ function displayWarnings(warnings) {
11134
+ console.log("Warnings:");
11135
+ if (warnings.length === 0) {
11136
+ console.log(" None");
11137
+ } else {
11138
+ for (const w of warnings) {
11139
+ console.log(` - ${w.message}`);
11140
+ }
11141
+ }
11142
+ }
10965
11143
  function formatUptime(ms) {
10966
11144
  const seconds = Math.floor(ms / 1e3);
10967
11145
  const minutes = Math.floor(seconds / 60);
@@ -10972,6 +11150,34 @@ function formatUptime(ms) {
10972
11150
  if (minutes > 0) return `${minutes}m`;
10973
11151
  return `${seconds}s`;
10974
11152
  }
11153
+ var doctorCommand = new Command2("doctor").description("Diagnose runner health, check network, and detect issues").option("--config <path>", "Config file path", "./runner.yaml").action(async (options) => {
11154
+ try {
11155
+ const config = loadConfig(options.config);
11156
+ const statusFilePath = runnerPaths.statusFile(config.base_dir);
11157
+ const workspacesDir = runnerPaths.workspacesDir(config.base_dir);
11158
+ const warnings = [];
11159
+ console.log(`Runner: ${config.name}`);
11160
+ const status = displayRunnerStatus(statusFilePath);
11161
+ console.log("");
11162
+ await checkApiConnectivity(config);
11163
+ console.log("");
11164
+ await checkNetwork(config, warnings);
11165
+ console.log("");
11166
+ const processes = findFirecrackerProcesses();
11167
+ const workspaces = existsSync5(workspacesDir) ? readdirSync2(workspacesDir).filter(runnerPaths.isVmWorkspace) : [];
11168
+ const { jobs, statusVmIds } = buildJobInfo(status, processes);
11169
+ displayRuns(jobs, config.sandbox.max_concurrent);
11170
+ console.log("");
11171
+ detectOrphanResources(jobs, processes, workspaces, statusVmIds, warnings);
11172
+ displayWarnings(warnings);
11173
+ process.exit(warnings.length > 0 ? 1 : 0);
11174
+ } catch (error) {
11175
+ console.error(
11176
+ `Error: ${error instanceof Error ? error.message : "Unknown error"}`
11177
+ );
11178
+ process.exit(1);
11179
+ }
11180
+ });
10975
11181
 
10976
11182
  // src/commands/kill.ts
10977
11183
  import { Command as Command3 } from "commander";
@@ -11208,10 +11414,16 @@ var benchmarkCommand = new Command4("benchmark").description(
11208
11414
  process.exit(1);
11209
11415
  }
11210
11416
  timer.log("Initializing pools...");
11417
+ const snapshotConfig = config.firecracker.snapshot;
11211
11418
  await initOverlayPool({
11212
11419
  size: 2,
11213
11420
  replenishThreshold: 1,
11214
- poolDir: runnerPaths.overlayPool(config.base_dir)
11421
+ poolDir: runnerPaths.overlayPool(config.base_dir),
11422
+ createFile: snapshotConfig ? (filePath) => execCommand(
11423
+ `cp --sparse=always "${snapshotConfig.overlay}" "${filePath}"`,
11424
+ false
11425
+ ).then(() => {
11426
+ }) : void 0
11215
11427
  });
11216
11428
  await initNetnsPool({ name: config.name, size: 2 });
11217
11429
  poolsInitialized = true;
@@ -11239,12 +11451,219 @@ var benchmarkCommand = new Command4("benchmark").description(
11239
11451
  process.exit(exitCode);
11240
11452
  });
11241
11453
 
11454
+ // src/commands/snapshot.ts
11455
+ import { Command as Command5 } from "commander";
11456
+ import { spawn as spawn3 } from "child_process";
11457
+ import fs11 from "fs";
11458
+ import os2 from "os";
11459
+ import readline3 from "readline";
11460
+ var logger15 = createLogger("Snapshot");
11461
+ function startFirecracker(nsName, firecrackerBinary, apiSocketPath, workDir) {
11462
+ logger15.log("Starting Firecracker with API socket...");
11463
+ const currentUser = os2.userInfo().username;
11464
+ const fcProcess = spawn3(
11465
+ "sudo",
11466
+ [
11467
+ "ip",
11468
+ "netns",
11469
+ "exec",
11470
+ nsName,
11471
+ "sudo",
11472
+ "-u",
11473
+ currentUser,
11474
+ firecrackerBinary,
11475
+ "--api-sock",
11476
+ apiSocketPath
11477
+ ],
11478
+ {
11479
+ cwd: workDir,
11480
+ stdio: ["ignore", "pipe", "pipe"],
11481
+ detached: false
11482
+ }
11483
+ );
11484
+ if (fcProcess.stdout) {
11485
+ const stdoutRL = readline3.createInterface({ input: fcProcess.stdout });
11486
+ stdoutRL.on("line", (line) => {
11487
+ if (line.trim()) logger15.log(`[FC] ${line}`);
11488
+ });
11489
+ }
11490
+ if (fcProcess.stderr) {
11491
+ const stderrRL = readline3.createInterface({ input: fcProcess.stderr });
11492
+ stderrRL.on("line", (line) => {
11493
+ if (line.trim()) logger15.log(`[FC stderr] ${line}`);
11494
+ });
11495
+ }
11496
+ fcProcess.on("error", (err) => logger15.log(`Firecracker error: ${err}`));
11497
+ fcProcess.on(
11498
+ "exit",
11499
+ (code, signal) => logger15.log(`Firecracker exited: code=${code}, signal=${signal}`)
11500
+ );
11501
+ return fcProcess;
11502
+ }
11503
+ var snapshotCommand = new Command5("snapshot").description("Generate a Firecracker snapshot for fast VM startup").argument("<output-dir>", "Output directory for snapshot files").option("--config <path>", "Config file path", "./runner.yaml").action(
11504
+ async (outputDir, opts) => {
11505
+ const options = {
11506
+ config: opts.config ?? "./runner.yaml",
11507
+ output: outputDir
11508
+ };
11509
+ const timer = new Timer();
11510
+ setGlobalLogger(timer.log.bind(timer));
11511
+ logger15.log("Loading configuration...");
11512
+ const config = loadDebugConfig(options.config);
11513
+ validateFirecrackerPaths(config.firecracker);
11514
+ const nsName = "vm0-snapshot";
11515
+ const workDir = runnerPaths.snapshotWorkDir(config.base_dir);
11516
+ const overlayPath = vmPaths.overlay(workDir);
11517
+ const vsockPath = vmPaths.vsock(workDir);
11518
+ const apiSocketPath = vmPaths.apiSock(workDir);
11519
+ const outputSnapshot = snapshotOutputPaths.snapshot(options.output);
11520
+ const outputMemory = snapshotOutputPaths.memory(options.output);
11521
+ const outputOverlay = snapshotOutputPaths.overlay(options.output);
11522
+ let fcProcess = null;
11523
+ let vsockClient = null;
11524
+ let exitCode = 0;
11525
+ try {
11526
+ if (fs11.existsSync(workDir)) {
11527
+ logger15.log("Cleaning up stale work directory...");
11528
+ fs11.rmSync(workDir, { recursive: true, force: true });
11529
+ }
11530
+ logger15.log(`Creating directories...`);
11531
+ fs11.mkdirSync(options.output, { recursive: true });
11532
+ fs11.mkdirSync(workDir, { recursive: true });
11533
+ fs11.mkdirSync(vmPaths.vsockDir(workDir), { recursive: true });
11534
+ logger15.log("Creating overlay filesystem...");
11535
+ await createOverlayFile(overlayPath);
11536
+ logger15.log(`Overlay created: ${overlayPath}`);
11537
+ logger15.log(`Creating network namespace: ${nsName}`);
11538
+ await deleteNetns(nsName);
11539
+ await createNetnsWithTap(nsName, {
11540
+ tapName: SNAPSHOT_NETWORK.tapName,
11541
+ gatewayIpWithPrefix: `${SNAPSHOT_NETWORK.gatewayIp}/${SNAPSHOT_NETWORK.prefixLen}`
11542
+ });
11543
+ logger15.log("Network namespace created");
11544
+ fcProcess = startFirecracker(
11545
+ nsName,
11546
+ config.firecracker.binary,
11547
+ apiSocketPath,
11548
+ workDir
11549
+ );
11550
+ const apiClient = new FirecrackerClient(apiSocketPath);
11551
+ logger15.log("Waiting for API to be ready...");
11552
+ await apiClient.waitForReady();
11553
+ logger15.log("API ready");
11554
+ logger15.log("Configuring VM via API...");
11555
+ await Promise.all([
11556
+ apiClient.configureMachine({
11557
+ vcpu_count: config.sandbox.vcpu,
11558
+ mem_size_mib: config.sandbox.memory_mb
11559
+ }),
11560
+ apiClient.configureBootSource({
11561
+ kernel_image_path: config.firecracker.kernel,
11562
+ boot_args: buildBootArgs()
11563
+ }),
11564
+ apiClient.configureDrive({
11565
+ drive_id: "rootfs",
11566
+ path_on_host: config.firecracker.rootfs,
11567
+ is_root_device: true,
11568
+ is_read_only: true
11569
+ }),
11570
+ apiClient.configureDrive({
11571
+ drive_id: "overlay",
11572
+ path_on_host: overlayPath,
11573
+ is_root_device: false,
11574
+ is_read_only: false
11575
+ }),
11576
+ apiClient.configureNetworkInterface({
11577
+ iface_id: "eth0",
11578
+ guest_mac: SNAPSHOT_NETWORK.guestMac,
11579
+ host_dev_name: SNAPSHOT_NETWORK.tapName
11580
+ }),
11581
+ apiClient.configureVsock({
11582
+ guest_cid: 3,
11583
+ uds_path: vsockPath
11584
+ })
11585
+ ]);
11586
+ logger15.log("VM configured");
11587
+ logger15.log("Starting vsock listener...");
11588
+ vsockClient = new VsockClient(vsockPath);
11589
+ const guestConnectionPromise = vsockClient.waitForGuestConnection(6e4);
11590
+ logger15.log("Starting VM...");
11591
+ await apiClient.startInstance();
11592
+ logger15.log("VM started");
11593
+ logger15.log("Waiting for guest connection...");
11594
+ await guestConnectionPromise;
11595
+ logger15.log("Guest connected");
11596
+ logger15.log("Verifying guest is responsive...");
11597
+ const reachable = await vsockClient.isReachable();
11598
+ if (!reachable) {
11599
+ throw new Error("Guest is not responsive");
11600
+ }
11601
+ logger15.log("Guest is responsive");
11602
+ logger15.log("Pausing VM...");
11603
+ await apiClient.pause();
11604
+ logger15.log("VM paused");
11605
+ logger15.log("Creating snapshot...");
11606
+ await apiClient.createSnapshot({
11607
+ snapshot_type: "Full",
11608
+ snapshot_path: outputSnapshot,
11609
+ mem_file_path: outputMemory
11610
+ });
11611
+ logger15.log("Snapshot created");
11612
+ logger15.log("Copying overlay as golden overlay...");
11613
+ await execCommand(
11614
+ `cp --sparse=always "${overlayPath}" "${outputOverlay}"`,
11615
+ false
11616
+ );
11617
+ logger15.log("Golden overlay created");
11618
+ logger15.log("=".repeat(40));
11619
+ logger15.log("Snapshot generation complete!");
11620
+ logger15.log("Files (logical size):");
11621
+ const lsOutput = await execCommand(`ls -lh "${options.output}"`, false);
11622
+ logger15.log(lsOutput);
11623
+ logger15.log("Actual disk usage:");
11624
+ const duOutput = await execCommand(
11625
+ `du -h "${options.output}"/*`,
11626
+ false
11627
+ );
11628
+ logger15.log(duOutput);
11629
+ logger15.log("=".repeat(40));
11630
+ } catch (error) {
11631
+ logger15.error(
11632
+ `Error: ${error instanceof Error ? error.message : "Unknown error"}`
11633
+ );
11634
+ exitCode = 1;
11635
+ } finally {
11636
+ logger15.log("Cleaning up...");
11637
+ if (vsockClient) {
11638
+ vsockClient.close();
11639
+ }
11640
+ if (fcProcess && !fcProcess.killed) {
11641
+ fcProcess.kill("SIGKILL");
11642
+ }
11643
+ await execCommand(
11644
+ `pkill -9 -f "firecracker.*${apiSocketPath}"`,
11645
+ true
11646
+ ).catch(() => {
11647
+ });
11648
+ await deleteNetns(nsName);
11649
+ if (fs11.existsSync(workDir)) {
11650
+ fs11.rmSync(workDir, { recursive: true, force: true });
11651
+ }
11652
+ logger15.log("Cleanup complete");
11653
+ }
11654
+ if (exitCode !== 0) {
11655
+ process.exit(exitCode);
11656
+ }
11657
+ }
11658
+ );
11659
+
11242
11660
  // src/index.ts
11243
- var version = true ? "3.11.2" : "0.1.0";
11661
+ var version = true ? "3.12.0" : "0.1.0";
11244
11662
  program.name("vm0-runner").version(version).description("Self-hosted runner for VM0 agents");
11245
11663
  program.addCommand(startCommand);
11246
11664
  program.addCommand(doctorCommand);
11247
11665
  program.addCommand(killCommand);
11248
11666
  program.addCommand(benchmarkCommand);
11667
+ program.addCommand(snapshotCommand);
11249
11668
  program.parse();
11250
11669
  //# sourceMappingURL=index.js.map