@vm0/runner 2.0.0 → 2.0.2

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 +51 -54
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -105,7 +105,7 @@ async function claimJob(server, runId) {
105
105
  }
106
106
  return response.json();
107
107
  }
108
- async function completeJob(context, exitCode, error) {
108
+ async function completeJob(apiUrl, context, exitCode, error) {
109
109
  const headers = {
110
110
  Authorization: `Bearer ${context.sandboxToken}`,
111
111
  "Content-Type": "application/json"
@@ -114,18 +114,15 @@ async function completeJob(context, exitCode, error) {
114
114
  if (bypassSecret) {
115
115
  headers["x-vercel-protection-bypass"] = bypassSecret;
116
116
  }
117
- const response = await fetch(
118
- `${context.apiUrl}/api/webhooks/agent/complete`,
119
- {
120
- method: "POST",
121
- headers,
122
- body: JSON.stringify({
123
- runId: context.runId,
124
- exitCode,
125
- error
126
- })
127
- }
128
- );
117
+ const response = await fetch(`${apiUrl}/api/webhooks/agent/complete`, {
118
+ method: "POST",
119
+ headers,
120
+ body: JSON.stringify({
121
+ runId: context.runId,
122
+ exitCode,
123
+ error
124
+ })
125
+ });
129
126
  if (!response.ok) {
130
127
  const errorData = await response.json();
131
128
  throw new Error(
@@ -654,10 +651,12 @@ var FirecrackerVM = class {
654
651
  console.log(`[VM ${this.config.vmId}] Stopping...`);
655
652
  try {
656
653
  if (this.client) {
657
- try {
658
- await this.client.sendCtrlAltDel();
659
- } catch {
660
- }
654
+ await this.client.sendCtrlAltDel().catch((error) => {
655
+ console.log(
656
+ `[VM ${this.config.vmId}] Graceful shutdown signal failed (VM may already be stopping):`,
657
+ error instanceof Error ? error.message : error
658
+ );
659
+ });
661
660
  }
662
661
  } finally {
663
662
  await this.cleanup();
@@ -851,14 +850,9 @@ var SSHClient = class {
851
850
  */
852
851
  async waitUntilReachable(timeoutMs = 12e4, intervalMs = 2e3) {
853
852
  const start = Date.now();
854
- let lastError = null;
855
853
  while (Date.now() - start < timeoutMs) {
856
- try {
857
- if (await this.isReachable()) {
858
- return;
859
- }
860
- } catch (err) {
861
- lastError = err instanceof Error ? err : new Error(String(err));
854
+ if (await this.isReachable()) {
855
+ return;
862
856
  }
863
857
  await new Promise((resolve) => {
864
858
  const remaining = timeoutMs - (Date.now() - start);
@@ -870,7 +864,7 @@ var SSHClient = class {
870
864
  });
871
865
  }
872
866
  throw new Error(
873
- `SSH not reachable after ${timeoutMs}ms at ${this.config.host}: ${lastError?.message || "timeout"}`
867
+ `SSH not reachable after ${timeoutMs}ms at ${this.config.host}`
874
868
  );
875
869
  }
876
870
  /**
@@ -6172,7 +6166,6 @@ var executionContextSchema = z15.object({
6172
6166
  secretNames: z15.array(z15.string()).nullable(),
6173
6167
  checkpointId: z15.string().uuid().nullable(),
6174
6168
  sandboxToken: z15.string(),
6175
- apiUrl: z15.string(),
6176
6169
  // New fields for E2B parity:
6177
6170
  workingDir: z15.string(),
6178
6171
  storageManifest: storageManifestSchema.nullable(),
@@ -9013,9 +9006,9 @@ function getAllScripts() {
9013
9006
  function getVmIdFromRunId(runId) {
9014
9007
  return runId.split("-")[0] || runId.substring(0, 8);
9015
9008
  }
9016
- function buildEnvironmentVariables(context) {
9009
+ function buildEnvironmentVariables(context, apiUrl) {
9017
9010
  const envVars = {
9018
- VM0_API_URL: context.apiUrl,
9011
+ VM0_API_URL: apiUrl,
9019
9012
  VM0_RUN_ID: context.runId,
9020
9013
  VM0_API_TOKEN: context.sandboxToken,
9021
9014
  VM0_PROMPT: context.prompt,
@@ -9155,23 +9148,41 @@ async function executeJob(context, config) {
9155
9148
  context.cliAgentType || "claude-code"
9156
9149
  );
9157
9150
  }
9158
- const envVars = buildEnvironmentVariables(context);
9151
+ const envVars = buildEnvironmentVariables(context, config.server.url);
9159
9152
  const envJson = JSON.stringify(envVars);
9160
9153
  console.log(
9161
9154
  `[Executor] Writing env JSON (${envJson.length} bytes) to ${ENV_JSON_PATH}`
9162
9155
  );
9163
9156
  await ssh.writeFile(ENV_JSON_PATH, envJson);
9164
9157
  const systemLogFile = `/tmp/vm0-main-${context.runId}.log`;
9165
- console.log(`[Executor] Running agent via env-loader...`);
9158
+ const exitCodeFile = `/tmp/vm0-exit-${context.runId}`;
9159
+ console.log(`[Executor] Running agent via env-loader (background)...`);
9166
9160
  const startTime = Date.now();
9167
- const result = await ssh.exec(
9168
- `{ python3 -u ${ENV_LOADER_PATH}; } > ${systemLogFile} 2>&1; echo "EXIT_CODE:$?"`
9161
+ await ssh.exec(
9162
+ `nohup sh -c 'python3 -u ${ENV_LOADER_PATH}; echo $? > ${exitCodeFile}' > ${systemLogFile} 2>&1 &`
9169
9163
  );
9170
- const lines = result.stdout.trim().split("\n");
9171
- const exitCodeLine = lines[lines.length - 1] ?? "";
9172
- const exitCodeMatch = exitCodeLine.match(/EXIT_CODE:(\d+)/);
9173
- const exitCode = exitCodeMatch && exitCodeMatch[1] ? parseInt(exitCodeMatch[1], 10) : 1;
9164
+ console.log(`[Executor] Agent started in background`);
9165
+ const pollIntervalMs = 2e3;
9166
+ const maxWaitMs = 24 * 60 * 60 * 1e3;
9167
+ let exitCode = 1;
9168
+ let completed = false;
9169
+ while (Date.now() - startTime < maxWaitMs) {
9170
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
9171
+ const checkResult = await ssh.exec(`cat ${exitCodeFile} 2>/dev/null`);
9172
+ if (checkResult.exitCode === 0 && checkResult.stdout.trim()) {
9173
+ exitCode = parseInt(checkResult.stdout.trim(), 10) || 1;
9174
+ completed = true;
9175
+ break;
9176
+ }
9177
+ }
9174
9178
  const duration = Math.round((Date.now() - startTime) / 1e3);
9179
+ if (!completed) {
9180
+ console.log(`[Executor] Agent timed out after ${duration}s`);
9181
+ return {
9182
+ exitCode: 1,
9183
+ error: `Agent execution timed out after ${duration}s`
9184
+ };
9185
+ }
9175
9186
  console.log(
9176
9187
  `[Executor] Agent finished in ${duration}s with exit code ${exitCode}`
9177
9188
  );
@@ -9195,14 +9206,7 @@ async function executeJob(context, config) {
9195
9206
  } finally {
9196
9207
  if (vm) {
9197
9208
  console.log(`[Executor] Cleaning up VM ${vmId}...`);
9198
- try {
9199
- await vm.kill();
9200
- } catch (error) {
9201
- console.error(
9202
- `[Executor] Failed to cleanup VM ${vmId}:`,
9203
- error instanceof Error ? error.message : error
9204
- );
9205
- }
9209
+ await vm.kill();
9206
9210
  }
9207
9211
  }
9208
9212
  }
@@ -9224,15 +9228,8 @@ async function executeJob2(context, config) {
9224
9228
  } catch (err) {
9225
9229
  const error = err instanceof Error ? err.message : "Unknown execution error";
9226
9230
  console.error(` Job ${context.runId} execution failed: ${error}`);
9227
- try {
9228
- const result = await completeJob(context, 1, error);
9229
- console.log(` Job ${context.runId} reported as ${result.status}`);
9230
- } catch (reportErr) {
9231
- console.error(
9232
- ` Failed to report job ${context.runId} completion:`,
9233
- reportErr instanceof Error ? reportErr.message : "Unknown error"
9234
- );
9235
- }
9231
+ const result = await completeJob(config.server.url, context, 1, error);
9232
+ console.log(` Job ${context.runId} reported as ${result.status}`);
9236
9233
  }
9237
9234
  }
9238
9235
  var startCommand = new Command("start").description("Start the runner").option("--config <path>", "Config file path", "./runner.yaml").action(async (options) => {
@@ -9336,7 +9333,7 @@ var statusCommand = new Command2("status").description("Show runner status").act
9336
9333
  });
9337
9334
 
9338
9335
  // src/index.ts
9339
- var version = true ? "2.0.0" : "0.1.0";
9336
+ var version = true ? "2.0.2" : "0.1.0";
9340
9337
  program.name("vm0-runner").version(version).description("Self-hosted runner for VM0 agents");
9341
9338
  program.addCommand(startCommand);
9342
9339
  program.addCommand(statusCommand);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vm0/runner",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "Self-hosted runner for VM0 agents",
5
5
  "repository": {
6
6
  "type": "git",