@vm0/runner 2.0.3 → 2.1.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 +72 -4
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -5,6 +5,8 @@ import { program } from "commander";
5
5
 
6
6
  // src/commands/start.ts
7
7
  import { Command } from "commander";
8
+ import { writeFileSync } from "fs";
9
+ import { dirname, join } from "path";
8
10
 
9
11
  // src/lib/config.ts
10
12
  import { z } from "zod";
@@ -4766,6 +4768,13 @@ var apiErrorSchema = z3.object({
4766
4768
  // ../../packages/core/src/contracts/composes.ts
4767
4769
  import { z as z4 } from "zod";
4768
4770
  var c = initContract();
4771
+ var composeVersionQuerySchema = z4.preprocess(
4772
+ (val) => val === void 0 || val === null ? void 0 : String(val),
4773
+ z4.string().min(1, "Missing version query parameter").regex(
4774
+ /^[a-f0-9]{8,64}$|^latest$/i,
4775
+ "Version must be 8-64 hex characters or 'latest'"
4776
+ )
4777
+ );
4769
4778
  var agentNameSchema = z4.string().min(3, "Agent name must be at least 3 characters").max(64, "Agent name must be 64 characters or less").regex(
4770
4779
  /^[a-zA-Z0-9][a-zA-Z0-9-]{1,62}[a-zA-Z0-9]$/,
4771
4780
  "Agent name must start and end with letter or number, and contain only letters, numbers, and hyphens"
@@ -4902,7 +4911,7 @@ var composesVersionsContract = c.router({
4902
4911
  path: "/api/agent/composes/versions",
4903
4912
  query: z4.object({
4904
4913
  composeId: z4.string().min(1, "Missing composeId query parameter"),
4905
- version: z4.string().min(1, "Missing version query parameter")
4914
+ version: composeVersionQuerySchema
4906
4915
  }),
4907
4916
  responses: {
4908
4917
  200: z4.object({
@@ -5213,6 +5222,10 @@ var runNetworkLogsContract = c2.router({
5213
5222
  import { z as z6 } from "zod";
5214
5223
  var c3 = initContract();
5215
5224
  var storageTypeSchema = z6.enum(["volume", "artifact"]);
5225
+ var versionQuerySchema = z6.preprocess(
5226
+ (val) => val === void 0 || val === null ? void 0 : String(val),
5227
+ z6.string().regex(/^[a-f0-9]{8,64}$/i, "Version must be 8-64 hex characters").optional()
5228
+ );
5216
5229
  var uploadStorageResponseSchema = z6.object({
5217
5230
  name: z6.string(),
5218
5231
  versionId: z6.string(),
@@ -5261,7 +5274,7 @@ var storagesContract = c3.router({
5261
5274
  path: "/api/storages",
5262
5275
  query: z6.object({
5263
5276
  name: z6.string().min(1, "Storage name is required"),
5264
- version: z6.string().optional()
5277
+ version: versionQuerySchema
5265
5278
  }),
5266
5279
  responses: {
5267
5280
  // Binary response - actual handling done at route level
@@ -5361,7 +5374,7 @@ var storagesDownloadContract = c3.router({
5361
5374
  query: z6.object({
5362
5375
  name: z6.string().min(1, "Storage name is required"),
5363
5376
  type: storageTypeSchema,
5364
- version: z6.string().optional()
5377
+ version: versionQuerySchema
5365
5378
  }),
5366
5379
  responses: {
5367
5380
  // Normal response with presigned URL
@@ -9235,6 +9248,22 @@ async function executeJob(context, config) {
9235
9248
 
9236
9249
  // src/commands/start.ts
9237
9250
  var activeJobs = /* @__PURE__ */ new Set();
9251
+ function writeStatusFile(statusFilePath, mode, startedAt) {
9252
+ const status = {
9253
+ mode,
9254
+ active_jobs: activeJobs.size,
9255
+ active_job_ids: Array.from(activeJobs),
9256
+ started_at: startedAt.toISOString(),
9257
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
9258
+ };
9259
+ try {
9260
+ writeFileSync(statusFilePath, JSON.stringify(status, null, 2));
9261
+ } catch (err) {
9262
+ console.error(
9263
+ `Failed to write status file: ${err instanceof Error ? err.message : "Unknown error"}`
9264
+ );
9265
+ }
9266
+ }
9238
9267
  async function executeJob2(context, config) {
9239
9268
  console.log(` Executing job ${context.runId}...`);
9240
9269
  console.log(` Prompt: ${context.prompt.substring(0, 100)}...`);
@@ -9269,26 +9298,61 @@ var startCommand = new Command("start").description("Start the runner").option("
9269
9298
  }
9270
9299
  console.log("Setting up network bridge...");
9271
9300
  await setupBridge();
9301
+ const statusFilePath = join(dirname(options.config), "status.json");
9302
+ const startedAt = /* @__PURE__ */ new Date();
9303
+ const state = { mode: "running" };
9304
+ const updateStatus = () => {
9305
+ writeStatusFile(statusFilePath, state.mode, startedAt);
9306
+ };
9272
9307
  console.log(
9273
9308
  `Starting runner '${config.name}' for group '${config.group}'...`
9274
9309
  );
9275
9310
  console.log(`Max concurrent jobs: ${config.sandbox.max_concurrent}`);
9311
+ console.log(`Status file: ${statusFilePath}`);
9276
9312
  console.log("Press Ctrl+C to stop");
9277
9313
  console.log("");
9314
+ updateStatus();
9278
9315
  let running = true;
9279
9316
  process.on("SIGINT", () => {
9280
9317
  console.log("\nShutting down...");
9281
9318
  running = false;
9319
+ state.mode = "stopped";
9320
+ updateStatus();
9282
9321
  });
9283
9322
  process.on("SIGTERM", () => {
9284
9323
  console.log("\nShutting down...");
9285
9324
  running = false;
9325
+ state.mode = "stopped";
9326
+ updateStatus();
9327
+ });
9328
+ process.on("SIGUSR1", () => {
9329
+ if (state.mode === "running") {
9330
+ console.log("\n[Maintenance] Entering drain mode...");
9331
+ console.log(
9332
+ `[Maintenance] Active jobs: ${activeJobs.size} (will wait for completion)`
9333
+ );
9334
+ state.mode = "draining";
9335
+ updateStatus();
9336
+ }
9286
9337
  });
9287
9338
  const jobPromises = /* @__PURE__ */ new Set();
9288
9339
  while (running) {
9340
+ if (state.mode === "draining") {
9341
+ if (activeJobs.size === 0) {
9342
+ console.log("[Maintenance] All jobs completed, exiting drain mode");
9343
+ running = false;
9344
+ break;
9345
+ }
9346
+ if (jobPromises.size > 0) {
9347
+ await Promise.race(jobPromises);
9348
+ updateStatus();
9349
+ }
9350
+ continue;
9351
+ }
9289
9352
  if (activeJobs.size >= config.sandbox.max_concurrent) {
9290
9353
  if (jobPromises.size > 0) {
9291
9354
  await Promise.race(jobPromises);
9355
+ updateStatus();
9292
9356
  }
9293
9357
  continue;
9294
9358
  }
@@ -9305,6 +9369,7 @@ var startCommand = new Command("start").description("Start the runner").option("
9305
9369
  const context = await claimJob(config.server, job.runId);
9306
9370
  console.log(`Claimed job: ${context.runId}`);
9307
9371
  activeJobs.add(context.runId);
9372
+ updateStatus();
9308
9373
  const jobPromise = executeJob2(context, config).catch((error) => {
9309
9374
  console.error(
9310
9375
  `Job ${context.runId} failed:`,
@@ -9313,6 +9378,7 @@ var startCommand = new Command("start").description("Start the runner").option("
9313
9378
  }).finally(() => {
9314
9379
  activeJobs.delete(context.runId);
9315
9380
  jobPromises.delete(jobPromise);
9381
+ updateStatus();
9316
9382
  });
9317
9383
  jobPromises.add(jobPromise);
9318
9384
  } catch (error) {
@@ -9335,6 +9401,8 @@ var startCommand = new Command("start").description("Start the runner").option("
9335
9401
  );
9336
9402
  await Promise.all(jobPromises);
9337
9403
  }
9404
+ state.mode = "stopped";
9405
+ updateStatus();
9338
9406
  console.log("Runner stopped");
9339
9407
  process.exit(0);
9340
9408
  } catch (error) {
@@ -9355,7 +9423,7 @@ var statusCommand = new Command2("status").description("Show runner status").act
9355
9423
  });
9356
9424
 
9357
9425
  // src/index.ts
9358
- var version = true ? "2.0.3" : "0.1.0";
9426
+ var version = true ? "2.1.0" : "0.1.0";
9359
9427
  program.name("vm0-runner").version(version).description("Self-hosted runner for VM0 agents");
9360
9428
  program.addCommand(startCommand);
9361
9429
  program.addCommand(statusCommand);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vm0/runner",
3
- "version": "2.0.3",
3
+ "version": "2.1.0",
4
4
  "description": "Self-hosted runner for VM0 agents",
5
5
  "repository": {
6
6
  "type": "git",