@vm0/runner 3.7.2 → 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.
- package/index.js +56 -34
- 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
|
|
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.
|
|
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.
|
|
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(
|
|
75
|
-
}).default({ ...PROXY_DEFAULTS, ca_dir:
|
|
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
|
|
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 =
|
|
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
|
|
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 ||
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
10516
|
-
const workspacesDir =
|
|
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
|
|
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 =
|
|
10723
|
-
const workspacesDir =
|
|
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 =
|
|
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:
|
|
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.
|
|
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);
|