spb/autoscholar
Public
1import { z } from "zod";2import { ArtifactDescriptor } from "./artifacts.js";3import { RunEvent } from "./events.js";4import { RunManifest, StageKey } from "./run.js";56export const WORKER_PROTOCOL_VERSION = 1;78export const WorkerStatus = z.enum(["online", "offline", "draining", "quarantined", "disabled"]);9export type WorkerStatus = z.infer<typeof WorkerStatus>;1011export const NodeMetadata = z.object({12 machineId: z.string(),13 hostname: z.string(),14 platform: z.string(),15 arch: z.string(),16 cpuCores: z.number().int(),17 memoryBytes: z.number().int(),18 osVersion: z.string(),19 workerVersion: z.string(),20 protocolVersion: z.number().int(),21 sdkVersion: z.string().optional(),22 tools: z.record(z.string(), z.string()).default({}),23 profiles: z.array(z.string()).default(["standard"]),24 user: z.string(),25 sandboxAvailable: z.boolean(),26});27export type NodeMetadata = z.infer<typeof NodeMetadata>;2829export const EnrollRequest = z.object({30 enrollmentSecret: z.string().min(16),31 name: z.string().min(2).max(64),32 lanIp: z.string().optional(),33 node: NodeMetadata,34});35export const EnrollResponse = z.object({36 workerId: z.string().uuid(),37 token: z.string(),38 protocolVersion: z.number(),39});4041export const HeartbeatRequest = z.object({42 status: WorkerStatus.default("online"),43 capacity: z.number().int().min(0).max(32),44 running: z.array(z.string().uuid()),45 load: z46 .object({ load1: z.number(), memFreeBytes: z.number(), diskFreeBytes: z.number() })47 .partial()48 .default({}),49 node: NodeMetadata.partial().optional(),50 leases: z.array(z.object({ runId: z.string().uuid(), fencingToken: z.number().int() })).default([]),51});52export const HeartbeatResponse = z.object({53 ok: z.literal(true),54 drain: z.boolean(),55 cancel: z.array(z.string().uuid()),56 revokedLeases: z.array(z.string().uuid()),57 leaseTtlSeconds: z.number().int(),58});5960export const LeaseRequest = z.object({ profiles: z.array(z.string()).default(["standard"]) });61export const LeasedJob = z.object({62 runId: z.string().uuid(),63 attemptId: z.string().uuid(),64 attemptNo: z.number().int(),65 fencingToken: z.number().int(),66 leaseExpiresAt: z.string().datetime(),67 manifest: RunManifest,68 instructionText: z.string(),69 resumeFromStage: StageKey.nullable(),70 checkpoint: z.record(z.string(), z.unknown()).nullable(),71 cancelRequested: z.boolean(),72});73export const LeaseResponse = z.object({ job: LeasedJob.nullable() });7475export const LeaseRenewRequest = z.object({ fencingToken: z.number().int() });76export const LeaseRenewResponse = z.object({77 ok: z.boolean(),78 cancelRequested: z.boolean(),79 leaseExpiresAt: z.string().datetime().optional(),80});8182export const EventsPostRequest = z.object({83 fencingToken: z.number().int(),84 events: z.array(RunEvent).min(1).max(500),85});8687export const StageReport = z.object({88 fencingToken: z.number().int(),89 stage: StageKey,90 status: z.enum(["running", "completed", "failed", "skipped"]),91 costUsd: z.number().nonnegative().default(0),92 turns: z.number().int().nonnegative().default(0),93 usage: z.record(z.string(), z.unknown()).optional(),94 error: z.string().max(4000).optional(),95 checkpoint: z.record(z.string(), z.unknown()).optional(),96});9798export const ArtifactUploadInit = z.object({ fencingToken: z.number().int(), artifact: ArtifactDescriptor });99100export const CompleteAttemptRequest = z.object({101 fencingToken: z.number().int(),102 outcome: z.enum(["completed", "failed", "cancelled", "budget_exceeded", "timeout"]),103 error: z.string().max(4000).optional(),104 summary: z.record(z.string(), z.unknown()).optional(),105 costUsd: z.number().nonnegative().default(0),106 resourceUsage: z107 .object({108 cpuSeconds: z.number().optional(),109 wallSeconds: z.number().optional(),110 diskBytes: z.number().optional(),111 peakRssBytes: z.number().optional(),112 })113 .optional(),114});115