@vm0/runner 3.7.1 → 3.7.3

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 +56 -34
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -5,12 +5,41 @@ import { program } from "commander";
5
5
 
6
6
  // src/commands/start.ts
7
7
  import { Command } from "commander";
8
- import { dirname, join as join2 } from "path";
8
+ import { dirname, join } from "path";
9
9
 
10
10
  // src/lib/config.ts
11
11
  import { z } from "zod";
12
12
  import fs from "fs";
13
13
  import yaml from "yaml";
14
+
15
+ // src/lib/paths.ts
16
+ import path from "path";
17
+ var VM0_RUN_DIR = "/var/run/vm0";
18
+ var VM0_TMP_PREFIX = "/tmp/vm0";
19
+ var paths = {
20
+ /** Runner PID file for single-instance lock */
21
+ runnerPid: path.join(VM0_RUN_DIR, "runner.pid"),
22
+ /** IP pool lock file */
23
+ ipPoolLock: path.join(VM0_RUN_DIR, "ip-pool.lock.active"),
24
+ /** IP allocation registry */
25
+ ipRegistry: path.join(VM0_RUN_DIR, "ip-registry.json")
26
+ };
27
+ var dataPaths = {
28
+ /** Overlay pool directory for pre-warmed VM overlays */
29
+ overlayPool: (dataDir) => path.join(dataDir, "overlay-pool")
30
+ };
31
+ var tempPaths = {
32
+ /** Default proxy CA directory */
33
+ proxyDir: `${VM0_TMP_PREFIX}-proxy`,
34
+ /** VM registry for proxy */
35
+ vmRegistry: `${VM0_TMP_PREFIX}-vm-registry.json`,
36
+ /** VM work directory (fallback when not using workspaces) */
37
+ vmWorkDir: (vmId) => `${VM0_TMP_PREFIX}-vm-${vmId}`,
38
+ /** Network log file for a run */
39
+ networkLog: (runId) => `${VM0_TMP_PREFIX}-network-${runId}.jsonl`
40
+ };
41
+
42
+ // src/lib/config.ts
14
43
  var SANDBOX_DEFAULTS = {
15
44
  max_concurrent: 1,
16
45
  vcpu: 2,
@@ -27,8 +56,9 @@ var runnerConfigSchema = z.object({
27
56
  /^[a-z0-9-]+\/[a-z0-9-]+$/,
28
57
  "Group must be in format 'scope/name' (lowercase, hyphens allowed)"
29
58
  ),
59
+ data_dir: z.string().min(1, "Data directory is required"),
30
60
  server: z.object({
31
- url: z.string().url("Server URL must be a valid URL"),
61
+ url: z.url({ message: "Server URL must be a valid URL" }),
32
62
  token: z.string().min(1, "Server token is required")
33
63
  }),
34
64
  sandbox: z.object({
@@ -54,8 +84,9 @@ var DEBUG_SERVER_DEFAULTS = {
54
84
  var debugConfigSchema = z.object({
55
85
  name: z.string().default("debug-runner"),
56
86
  group: z.string().default("debug/local"),
87
+ data_dir: z.string().min(1, "Data directory is required"),
57
88
  server: z.object({
58
- url: z.string().url().default(DEBUG_SERVER_DEFAULTS.url),
89
+ url: z.url().default(DEBUG_SERVER_DEFAULTS.url),
59
90
  token: z.string().default(DEBUG_SERVER_DEFAULTS.token)
60
91
  }).default(DEBUG_SERVER_DEFAULTS),
61
92
  sandbox: z.object({
@@ -71,8 +102,8 @@ var debugConfigSchema = z.object({
71
102
  }),
72
103
  proxy: z.object({
73
104
  port: z.number().int().min(1024).max(65535).default(PROXY_DEFAULTS.port),
74
- ca_dir: z.string().default("/tmp/vm0-proxy")
75
- }).default({ ...PROXY_DEFAULTS, ca_dir: "/tmp/vm0-proxy" })
105
+ ca_dir: z.string().default(tempPaths.proxyDir)
106
+ }).default({ ...PROXY_DEFAULTS, ca_dir: tempPaths.proxyDir })
76
107
  });
77
108
  function loadDebugConfig(configPath) {
78
109
  if (!fs.existsSync(configPath)) {
@@ -448,7 +479,6 @@ import { promisify as promisify2 } from "util";
448
479
  import { exec } from "child_process";
449
480
  import { promisify } from "util";
450
481
  import * as fs2 from "fs";
451
- import * as path from "path";
452
482
 
453
483
  // src/lib/logger.ts
454
484
  var _log = null;
@@ -473,8 +503,7 @@ function createLogger(prefix) {
473
503
  // src/lib/firecracker/ip-pool.ts
474
504
  var execAsync = promisify(exec);
475
505
  var logger = createLogger("IP Pool");
476
- var VM0_RUN_DIR = "/var/run/vm0";
477
- var REGISTRY_FILE_PATH = path.join(VM0_RUN_DIR, "ip-registry.json");
506
+ var REGISTRY_FILE_PATH = paths.ipRegistry;
478
507
  var BRIDGE_NAME = "vm0br0";
479
508
  var IP_PREFIX = "172.16.0.";
480
509
  var IP_START = 2;
@@ -490,7 +519,7 @@ async function ensureRunDir() {
490
519
  }
491
520
  async function withLock(fn) {
492
521
  await ensureRunDir();
493
- const lockMarker = path.join(VM0_RUN_DIR, "ip-pool.lock.active");
522
+ const lockMarker = paths.ipPoolLock;
494
523
  const startTime = Date.now();
495
524
  let lockAcquired = false;
496
525
  while (Date.now() - startTime < LOCK_TIMEOUT_MS) {
@@ -1071,8 +1100,6 @@ import path2 from "path";
1071
1100
  import { promisify as promisify3 } from "util";
1072
1101
  var execAsync3 = promisify3(exec3);
1073
1102
  var logger3 = createLogger("OverlayPool");
1074
- var VM0_RUN_DIR2 = "/var/run/vm0";
1075
- var DEFAULT_POOL_DIR = path2.join(VM0_RUN_DIR2, "overlay-pool");
1076
1103
  var OVERLAY_SIZE = 2 * 1024 * 1024 * 1024;
1077
1104
  async function defaultCreateFile(filePath) {
1078
1105
  const fd = fs3.openSync(filePath, "w");
@@ -1089,7 +1116,7 @@ var OverlayPool = class {
1089
1116
  this.config = {
1090
1117
  size: config.size,
1091
1118
  replenishThreshold: config.replenishThreshold,
1092
- poolDir: config.poolDir ?? DEFAULT_POOL_DIR,
1119
+ poolDir: config.poolDir,
1093
1120
  createFile: config.createFile ?? defaultCreateFile
1094
1121
  };
1095
1122
  }
@@ -1276,7 +1303,7 @@ var FirecrackerVM = class {
1276
1303
  // Vsock UDS path for host-guest communication
1277
1304
  constructor(config) {
1278
1305
  this.config = config;
1279
- this.workDir = config.workDir || `/tmp/vm0-vm-${config.vmId}`;
1306
+ this.workDir = config.workDir || tempPaths.vmWorkDir(config.vmId);
1280
1307
  this.socketPath = path3.join(this.workDir, "firecracker.sock");
1281
1308
  this.vsockPath = path3.join(this.workDir, "vsock.sock");
1282
1309
  }
@@ -8694,10 +8721,6 @@ var FEATURE_SWITCHES = {
8694
8721
  ["platformApiKeys" /* PlatformApiKeys */]: {
8695
8722
  maintainer: "ethan@vm0.ai",
8696
8723
  enabled: false
8697
- },
8698
- ["platformLogs" /* PlatformLogs */]: {
8699
- maintainer: "ethan@vm0.ai",
8700
- enabled: false
8701
8724
  }
8702
8725
  };
8703
8726
 
@@ -8707,7 +8730,7 @@ var ENV_LOADER_PATH = "/usr/local/bin/vm0-agent/env-loader.mjs";
8707
8730
  // src/lib/proxy/vm-registry.ts
8708
8731
  import fs6 from "fs";
8709
8732
  var logger5 = createLogger("VMRegistry");
8710
- var DEFAULT_REGISTRY_PATH = "/tmp/vm0-vm-registry.json";
8733
+ var DEFAULT_REGISTRY_PATH = tempPaths.vmRegistry;
8711
8734
  var VMRegistry = class {
8712
8735
  registryPath;
8713
8736
  data;
@@ -9299,8 +9322,7 @@ addons = [tls_clienthello, request, response]
9299
9322
  var logger6 = createLogger("ProxyManager");
9300
9323
  var DEFAULT_PROXY_OPTIONS = {
9301
9324
  port: 8080,
9302
- registryPath: DEFAULT_REGISTRY_PATH,
9303
- apiUrl: process.env.VM0_API_URL || "https://www.vm0.ai"
9325
+ registryPath: DEFAULT_REGISTRY_PATH
9304
9326
  };
9305
9327
  var ProxyManager = class {
9306
9328
  config;
@@ -9699,7 +9721,7 @@ function buildEnvironmentVariables(context, apiUrl) {
9699
9721
  import fs8 from "fs";
9700
9722
  var logger8 = createLogger("NetworkLogs");
9701
9723
  function getNetworkLogPath(runId) {
9702
- return `/tmp/vm0-network-${runId}.jsonl`;
9724
+ return tempPaths.networkLog(runId);
9703
9725
  }
9704
9726
  function readNetworkLogs(runId) {
9705
9727
  const logPath = getNetworkLogPath(runId);
@@ -9992,8 +10014,7 @@ import path6 from "path";
9992
10014
  import { promisify as promisify4 } from "util";
9993
10015
  var execAsync4 = promisify4(exec4);
9994
10016
  var logger11 = createLogger("RunnerLock");
9995
- var DEFAULT_RUN_DIR = "/var/run/vm0";
9996
- var DEFAULT_PID_FILE = `${DEFAULT_RUN_DIR}/runner.pid`;
10017
+ var DEFAULT_PID_FILE = paths.runnerPid;
9997
10018
  var currentPidFile = null;
9998
10019
  async function ensureRunDir2(dirPath, skipSudo) {
9999
10020
  if (!fs9.existsSync(dirPath)) {
@@ -10073,7 +10094,8 @@ async function setupEnvironment(options) {
10073
10094
  logger12.log("Initializing overlay pool...");
10074
10095
  await initOverlayPool({
10075
10096
  size: config.sandbox.max_concurrent + 2,
10076
- replenishThreshold: config.sandbox.max_concurrent
10097
+ replenishThreshold: config.sandbox.max_concurrent,
10098
+ poolDir: dataPaths.overlayPool(config.data_dir)
10077
10099
  });
10078
10100
  logger12.log("Initializing network proxy...");
10079
10101
  initVMRegistry();
@@ -10380,7 +10402,7 @@ var startCommand = new Command("start").description("Start the runner").option("
10380
10402
  const config = loadConfig(options.config);
10381
10403
  validateFirecrackerPaths(config.firecracker);
10382
10404
  console.log("Config valid");
10383
- const statusFilePath = join2(dirname(options.config), "status.json");
10405
+ const statusFilePath = join(dirname(options.config), "status.json");
10384
10406
  const runner = new Runner(config, statusFilePath);
10385
10407
  await runner.start();
10386
10408
  } catch (error) {
@@ -10396,7 +10418,7 @@ var startCommand = new Command("start").description("Start the runner").option("
10396
10418
  // src/commands/doctor.ts
10397
10419
  import { Command as Command2 } from "commander";
10398
10420
  import { existsSync as existsSync4, readFileSync as readFileSync3, readdirSync as readdirSync2 } from "fs";
10399
- import { dirname as dirname2, join as join3 } from "path";
10421
+ import { dirname as dirname2, join as join2 } from "path";
10400
10422
 
10401
10423
  // src/lib/firecracker/process.ts
10402
10424
  import { readdirSync, readFileSync as readFileSync2, existsSync as existsSync3 } from "fs";
@@ -10512,8 +10534,8 @@ var doctorCommand = new Command2("doctor").description("Diagnose runner health,
10512
10534
  try {
10513
10535
  const config = loadConfig(options.config);
10514
10536
  const configDir = dirname2(options.config);
10515
- const statusFilePath = join3(configDir, "status.json");
10516
- const workspacesDir = join3(configDir, "workspaces");
10537
+ const statusFilePath = join2(configDir, "status.json");
10538
+ const workspacesDir = join2(configDir, "workspaces");
10517
10539
  console.log(`Runner: ${config.name}`);
10518
10540
  let status = null;
10519
10541
  if (existsSync4(statusFilePath)) {
@@ -10711,7 +10733,7 @@ function formatUptime(ms) {
10711
10733
  // src/commands/kill.ts
10712
10734
  import { Command as Command3 } from "commander";
10713
10735
  import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3, rmSync } from "fs";
10714
- import { dirname as dirname3, join as join4 } from "path";
10736
+ import { dirname as dirname3, join as join3 } from "path";
10715
10737
  import * as readline2 from "readline";
10716
10738
  var killCommand = new Command3("kill").description("Force terminate a run and clean up all resources").argument("<run-id>", "Run ID (full UUID or short 8-char vmId)").option("--config <path>", "Config file path", "./runner.yaml").option("--force", "Skip confirmation prompt").action(
10717
10739
  // eslint-disable-next-line complexity -- TODO: refactor complex function
@@ -10719,13 +10741,13 @@ var killCommand = new Command3("kill").description("Force terminate a run and cl
10719
10741
  try {
10720
10742
  loadConfig(options.config);
10721
10743
  const configDir = dirname3(options.config);
10722
- const statusFilePath = join4(configDir, "status.json");
10723
- const workspacesDir = join4(configDir, "workspaces");
10744
+ const statusFilePath = join3(configDir, "status.json");
10745
+ const workspacesDir = join3(configDir, "workspaces");
10724
10746
  const { vmId, runId } = resolveRunId(runIdArg, statusFilePath);
10725
10747
  console.log(`Killing run ${vmId}...`);
10726
10748
  const proc = findProcessByVmId(vmId);
10727
10749
  const tapDevice = `tap${vmId}`;
10728
- const workspaceDir = join4(workspacesDir, `vm0-${vmId}`);
10750
+ const workspaceDir = join3(workspacesDir, `vm0-${vmId}`);
10729
10751
  console.log("");
10730
10752
  console.log("Resources to clean up:");
10731
10753
  if (proc) {
@@ -10966,7 +10988,7 @@ var benchmarkCommand = new Command4("benchmark").description(
10966
10988
  await initOverlayPool({
10967
10989
  size: 2,
10968
10990
  replenishThreshold: 1,
10969
- poolDir: "/var/run/vm0/overlay-pool-benchmark"
10991
+ poolDir: dataPaths.overlayPool(config.data_dir)
10970
10992
  });
10971
10993
  timer.log(`Executing command: ${prompt}`);
10972
10994
  const context = createBenchmarkContext(prompt, options);
@@ -10988,7 +11010,7 @@ var benchmarkCommand = new Command4("benchmark").description(
10988
11010
  });
10989
11011
 
10990
11012
  // src/index.ts
10991
- var version = true ? "3.7.1" : "0.1.0";
11013
+ var version = true ? "3.7.3" : "0.1.0";
10992
11014
  program.name("vm0-runner").version(version).description("Self-hosted runner for VM0 agents");
10993
11015
  program.addCommand(startCommand);
10994
11016
  program.addCommand(doctorCommand);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vm0/runner",
3
- "version": "3.7.1",
3
+ "version": "3.7.3",
4
4
  "description": "Self-hosted runner for VM0 agents",
5
5
  "repository": {
6
6
  "type": "git",