59 lines
3.0 KiB
TypeScript
59 lines
3.0 KiB
TypeScript
import { RunnerManagerApi, failureKindFromError, terminalStatusForFailure, errorMessage } from "./manager-api.js";
|
|
import { runBackendTurn, type BackendAdapterOptions } from "../backend/adapter.js";
|
|
import type { BackendProfile, JsonRecord, RunRecord, RunnerRecord } from "../common/types.js";
|
|
|
|
export interface RunnerOnceOptions extends BackendAdapterOptions {
|
|
managerUrl: string;
|
|
runId: string;
|
|
commandId?: string;
|
|
runnerId?: string;
|
|
attemptId?: string;
|
|
leaseMs?: number;
|
|
backendProfile?: BackendProfile;
|
|
placement?: "host-process" | "kubernetes-job";
|
|
sourceCommit?: string;
|
|
jobName?: string;
|
|
podName?: string;
|
|
logPath?: string;
|
|
}
|
|
|
|
export async function runOnce(options: RunnerOnceOptions): Promise<JsonRecord> {
|
|
const api = new RunnerManagerApi(options.managerUrl);
|
|
const leaseMs = options.leaseMs ?? 60_000;
|
|
const attemptId = options.attemptId ?? `attempt_${Date.now().toString(36)}`;
|
|
const runner = await api.register({
|
|
runId: options.runId,
|
|
attemptId,
|
|
backendProfile: options.backendProfile ?? "codex",
|
|
placement: options.placement ?? "host-process",
|
|
sourceCommit: options.sourceCommit ?? process.env.AGENTRUN_SOURCE_COMMIT ?? "unknown",
|
|
...(options.runnerId ? { runnerId: options.runnerId } : {}),
|
|
...(options.jobName ? { jobName: options.jobName } : {}),
|
|
...(options.podName ? { podName: options.podName } : {}),
|
|
...(options.logPath ? { logPath: options.logPath } : {}),
|
|
}) as RunnerRecord;
|
|
let claimed: RunRecord;
|
|
try {
|
|
claimed = await api.claim(options.runId, runner.id, leaseMs);
|
|
await api.heartbeat(options.runId, runner.id, leaseMs);
|
|
} catch (error) {
|
|
const failureKind = failureKindFromError(error);
|
|
if (failureKind !== "runner-lease-conflict") {
|
|
await api.reportFailure(options.runId, { terminalStatus: terminalStatusForFailure(failureKind), failureKind, failureMessage: errorMessage(error) });
|
|
}
|
|
throw error;
|
|
}
|
|
const commandsResponse = await api.pollCommands(options.runId, { afterSeq: 0, limit: 20, ...(options.commandId ? { commandId: options.commandId } : {}) });
|
|
const command = commandsResponse.selected;
|
|
if (!command) {
|
|
await api.reportStatus(options.runId, { terminalStatus: "blocked", failureKind: "schema-invalid", failureMessage: "no pending turn command" });
|
|
return { runner, claimed, terminalStatus: "blocked", failureKind: "schema-invalid", polledCommands: commandsResponse.items.length };
|
|
}
|
|
await api.ackCommand(command.id);
|
|
const result = await runBackendTurn(claimed, command, options);
|
|
for (const event of result.events) await api.appendEvent(options.runId, event);
|
|
await api.reportCommandStatus(command.id, { terminalStatus: result.terminalStatus, failureKind: result.failureKind, failureMessage: result.failureMessage });
|
|
const finalRun = await api.reportStatus(options.runId, { terminalStatus: result.terminalStatus, failureKind: result.failureKind, failureMessage: result.failureMessage }) as RunRecord;
|
|
return { runner, commandId: command.id, terminalStatus: result.terminalStatus, failureKind: result.failureKind, run: finalRun } as JsonRecord;
|
|
}
|