@vm0/runner 3.9.3 → 3.10.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.
- package/index.js +66 -35
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1352,12 +1352,15 @@ var TapPool = class {
|
|
|
1352
1352
|
}
|
|
1353
1353
|
const guestMac = generateMacAddress(vmId);
|
|
1354
1354
|
try {
|
|
1355
|
-
await
|
|
1355
|
+
await Promise.all([
|
|
1356
|
+
this.config.setMac(resource.tapDevice, guestMac),
|
|
1357
|
+
clearArpEntry(resource.guestIp)
|
|
1358
|
+
]);
|
|
1356
1359
|
} catch (err) {
|
|
1357
1360
|
if (fromPool) {
|
|
1358
1361
|
this.queue.push(resource);
|
|
1359
1362
|
logger4.log(
|
|
1360
|
-
`Returned pair to pool after MAC
|
|
1363
|
+
`Returned pair to pool after MAC/ARP failure: ${resource.tapDevice}`
|
|
1361
1364
|
);
|
|
1362
1365
|
} else {
|
|
1363
1366
|
await releaseIP(resource.guestIp).catch(() => {
|
|
@@ -1367,14 +1370,11 @@ var TapPool = class {
|
|
|
1367
1370
|
}
|
|
1368
1371
|
throw err;
|
|
1369
1372
|
}
|
|
1370
|
-
|
|
1371
|
-
try {
|
|
1372
|
-
await assignVmIdToIP(resource.guestIp, vmId);
|
|
1373
|
-
} catch (err) {
|
|
1373
|
+
assignVmIdToIP(resource.guestIp, vmId).catch((err) => {
|
|
1374
1374
|
logger4.error(
|
|
1375
1375
|
`Failed to assign vmId to IP registry: ${err instanceof Error ? err.message : "Unknown"}`
|
|
1376
1376
|
);
|
|
1377
|
-
}
|
|
1377
|
+
});
|
|
1378
1378
|
logger4.log(
|
|
1379
1379
|
`Acquired: TAP ${resource.tapDevice}, MAC ${guestMac}, IP ${resource.guestIp}`
|
|
1380
1380
|
);
|
|
@@ -1695,12 +1695,9 @@ var FirecrackerVM = class {
|
|
|
1695
1695
|
/**
|
|
1696
1696
|
* Stop the VM
|
|
1697
1697
|
*
|
|
1698
|
-
* Note: With --no-api mode, we
|
|
1699
|
-
*
|
|
1700
|
-
*
|
|
1701
|
-
* TODO(#2118): Implement graceful shutdown via vsock command to guest agent.
|
|
1702
|
-
* This would allow the guest to clean up before termination without
|
|
1703
|
-
* adding the startup latency of API mode.
|
|
1698
|
+
* Note: With --no-api mode, we force kill the Firecracker process.
|
|
1699
|
+
* Graceful shutdown (filesystem sync) should be done via vsock
|
|
1700
|
+
* before calling this method.
|
|
1704
1701
|
*/
|
|
1705
1702
|
async stop() {
|
|
1706
1703
|
if (this.state !== "running") {
|
|
@@ -1794,6 +1791,8 @@ var MSG_WRITE_FILE = 5;
|
|
|
1794
1791
|
var MSG_SPAWN_WATCH = 7;
|
|
1795
1792
|
var MSG_SPAWN_WATCH_RESULT = 8;
|
|
1796
1793
|
var MSG_PROCESS_EXIT = 9;
|
|
1794
|
+
var MSG_SHUTDOWN = 10;
|
|
1795
|
+
var MSG_SHUTDOWN_ACK = 11;
|
|
1797
1796
|
var MSG_ERROR = 255;
|
|
1798
1797
|
var FLAG_SUDO = 1;
|
|
1799
1798
|
function encode(type, seq, payload = Buffer.alloc(0)) {
|
|
@@ -2295,6 +2294,30 @@ var VsockClient = class {
|
|
|
2295
2294
|
getVsockPath() {
|
|
2296
2295
|
return this.vsockPath;
|
|
2297
2296
|
}
|
|
2297
|
+
/**
|
|
2298
|
+
* Request graceful shutdown from guest
|
|
2299
|
+
*
|
|
2300
|
+
* Sends shutdown command to guest agent, which syncs filesystems
|
|
2301
|
+
* and returns acknowledgment. Falls back gracefully on timeout.
|
|
2302
|
+
*
|
|
2303
|
+
* @param timeoutMs Maximum time to wait for acknowledgment (default: 2000ms)
|
|
2304
|
+
* @returns true if guest acknowledged, false on timeout/error
|
|
2305
|
+
*/
|
|
2306
|
+
async shutdown(timeoutMs = 2e3) {
|
|
2307
|
+
if (!this.connected || !this.socket) {
|
|
2308
|
+
return false;
|
|
2309
|
+
}
|
|
2310
|
+
try {
|
|
2311
|
+
const response = await this.request(
|
|
2312
|
+
MSG_SHUTDOWN,
|
|
2313
|
+
Buffer.alloc(0),
|
|
2314
|
+
timeoutMs
|
|
2315
|
+
);
|
|
2316
|
+
return response.type === MSG_SHUTDOWN_ACK;
|
|
2317
|
+
} catch {
|
|
2318
|
+
return false;
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2298
2321
|
/**
|
|
2299
2322
|
* Close the connection
|
|
2300
2323
|
*/
|
|
@@ -7743,7 +7766,8 @@ import { z as z17 } from "zod";
|
|
|
7743
7766
|
var c11 = initContract();
|
|
7744
7767
|
var modelProviderTypeSchema = z17.enum([
|
|
7745
7768
|
"claude-code-oauth-token",
|
|
7746
|
-
"anthropic-api-key"
|
|
7769
|
+
"anthropic-api-key",
|
|
7770
|
+
"moonshot-api-key"
|
|
7747
7771
|
]);
|
|
7748
7772
|
var modelProviderFrameworkSchema = z17.enum(["claude-code", "codex"]);
|
|
7749
7773
|
var modelProviderResponseSchema = z17.object({
|
|
@@ -7752,6 +7776,7 @@ var modelProviderResponseSchema = z17.object({
|
|
|
7752
7776
|
framework: modelProviderFrameworkSchema,
|
|
7753
7777
|
credentialName: z17.string(),
|
|
7754
7778
|
isDefault: z17.boolean(),
|
|
7779
|
+
selectedModel: z17.string().nullable(),
|
|
7755
7780
|
createdAt: z17.string(),
|
|
7756
7781
|
updatedAt: z17.string()
|
|
7757
7782
|
});
|
|
@@ -7761,7 +7786,8 @@ var modelProviderListResponseSchema = z17.object({
|
|
|
7761
7786
|
var upsertModelProviderRequestSchema = z17.object({
|
|
7762
7787
|
type: modelProviderTypeSchema,
|
|
7763
7788
|
credential: z17.string().min(1, "Credential is required"),
|
|
7764
|
-
convert: z17.boolean().optional()
|
|
7789
|
+
convert: z17.boolean().optional(),
|
|
7790
|
+
selectedModel: z17.string().optional()
|
|
7765
7791
|
});
|
|
7766
7792
|
var upsertModelProviderResponseSchema = z17.object({
|
|
7767
7793
|
provider: modelProviderResponseSchema,
|
|
@@ -10030,6 +10056,7 @@ async function executeJob(context, config, options = {}) {
|
|
|
10030
10056
|
const vmId = getVmIdFromRunId(context.runId);
|
|
10031
10057
|
let vm = null;
|
|
10032
10058
|
let guestIp = null;
|
|
10059
|
+
let vsockClient = null;
|
|
10033
10060
|
logger10.log(`Starting job ${context.runId} in VM ${vmId}`);
|
|
10034
10061
|
try {
|
|
10035
10062
|
const vmConfig = {
|
|
@@ -10050,14 +10077,12 @@ async function executeJob(context, config, options = {}) {
|
|
|
10050
10077
|
}
|
|
10051
10078
|
logger10.log(`VM ${vmId} started, guest IP: ${guestIp}`);
|
|
10052
10079
|
const vsockPath = vm.getVsockPath();
|
|
10053
|
-
|
|
10080
|
+
vsockClient = new VsockClient(vsockPath);
|
|
10081
|
+
const guest = vsockClient;
|
|
10054
10082
|
logger10.log(`Using vsock for guest communication: ${vsockPath}`);
|
|
10055
|
-
|
|
10056
|
-
|
|
10057
|
-
"guest_wait",
|
|
10058
|
-
() => guest.waitForGuestConnection(3e4)
|
|
10083
|
+
const envJson = JSON.stringify(
|
|
10084
|
+
buildEnvironmentVariables(context, config.server.url)
|
|
10059
10085
|
);
|
|
10060
|
-
logger10.log(`Guest client ready`);
|
|
10061
10086
|
const firewallConfig = context.experimentalFirewall;
|
|
10062
10087
|
if (firewallConfig?.enabled) {
|
|
10063
10088
|
const mitmEnabled = firewallConfig.experimental_mitm ?? false;
|
|
@@ -10065,19 +10090,18 @@ async function executeJob(context, config, options = {}) {
|
|
|
10065
10090
|
logger10.log(
|
|
10066
10091
|
`Setting up network security for VM ${guestIp} (mitm=${mitmEnabled}, sealSecrets=${sealSecretsEnabled})`
|
|
10067
10092
|
);
|
|
10068
|
-
|
|
10069
|
-
|
|
10070
|
-
|
|
10071
|
-
|
|
10072
|
-
context.sandboxToken,
|
|
10073
|
-
{
|
|
10074
|
-
firewallRules: firewallConfig?.rules,
|
|
10075
|
-
mitmEnabled,
|
|
10076
|
-
sealSecretsEnabled
|
|
10077
|
-
}
|
|
10078
|
-
);
|
|
10093
|
+
getVMRegistry().register(guestIp, context.runId, context.sandboxToken, {
|
|
10094
|
+
firewallRules: firewallConfig?.rules,
|
|
10095
|
+
mitmEnabled,
|
|
10096
|
+
sealSecretsEnabled
|
|
10079
10097
|
});
|
|
10080
10098
|
}
|
|
10099
|
+
logger10.log(`Waiting for guest connection...`);
|
|
10100
|
+
await withSandboxTiming(
|
|
10101
|
+
"guest_wait",
|
|
10102
|
+
() => guest.waitForGuestConnection(3e4)
|
|
10103
|
+
);
|
|
10104
|
+
logger10.log(`Guest client ready`);
|
|
10081
10105
|
if (context.storageManifest) {
|
|
10082
10106
|
await withSandboxTiming(
|
|
10083
10107
|
"storage_download",
|
|
@@ -10095,8 +10119,6 @@ async function executeJob(context, config, options = {}) {
|
|
|
10095
10119
|
)
|
|
10096
10120
|
);
|
|
10097
10121
|
}
|
|
10098
|
-
const envVars = buildEnvironmentVariables(context, config.server.url);
|
|
10099
|
-
const envJson = JSON.stringify(envVars);
|
|
10100
10122
|
logger10.log(
|
|
10101
10123
|
`Writing env JSON (${envJson.length} bytes) to ${ENV_JSON_PATH}`
|
|
10102
10124
|
);
|
|
@@ -10193,6 +10215,15 @@ async function executeJob(context, config, options = {}) {
|
|
|
10193
10215
|
}
|
|
10194
10216
|
}
|
|
10195
10217
|
if (vm) {
|
|
10218
|
+
if (vsockClient) {
|
|
10219
|
+
const acked = await vsockClient.shutdown(2e3);
|
|
10220
|
+
if (acked) {
|
|
10221
|
+
logger10.log(`Guest acknowledged shutdown`);
|
|
10222
|
+
} else {
|
|
10223
|
+
logger10.log(`Guest shutdown timeout, proceeding with SIGKILL`);
|
|
10224
|
+
}
|
|
10225
|
+
vsockClient.close();
|
|
10226
|
+
}
|
|
10196
10227
|
logger10.log(`Cleaning up VM ${vmId}...`);
|
|
10197
10228
|
await withSandboxTiming("cleanup", () => vm.kill());
|
|
10198
10229
|
}
|
|
@@ -11229,7 +11260,7 @@ var benchmarkCommand = new Command4("benchmark").description(
|
|
|
11229
11260
|
});
|
|
11230
11261
|
|
|
11231
11262
|
// src/index.ts
|
|
11232
|
-
var version = true ? "3.
|
|
11263
|
+
var version = true ? "3.10.0" : "0.1.0";
|
|
11233
11264
|
program.name("vm0-runner").version(version).description("Self-hosted runner for VM0 agents");
|
|
11234
11265
|
program.addCommand(startCommand);
|
|
11235
11266
|
program.addCommand(doctorCommand);
|