#!/usr/bin/env bun import type { ExecutorResult, Loop, LoopRun, WorkflowSpec } from "../types.js"; import { type RunnerEpisodeRecorder } from "./episodes.js"; export { LoopsApiError, RunnerRefusalError } from "./errors.js"; export { classifyRunnerFailure, createRunnerEpisodeRecorder, runnerEpisodesStatePath, runnerEventsOutboxPath, type RunnerEpisodeEvent, type RunnerEpisodeRecorder, type RunnerEpisodeRecorderOptions, type RunnerEpisodeStreak, type RunnerEpisodesFile, type RunnerFailureClass, } from "./episodes.js"; import { type StorageConnectionReport } from "../lib/runtime-status.js"; /** * Runner authority, derived from the client connection only. Deployment modes * are removed: the runner is authoritative over the local file (the daemon), * or ready when a fully configured API connection (URL + key) exists. * Partial API configuration fails closed — the runner never claims work unless * the whole API connection is configured. */ export type RunnerState = "file_authoritative" | "api_ready" | "missing_api_url" | "missing_api_key"; export interface RunnerStatus { ok: boolean; service: "loops-runner"; machineId?: string; state: RunnerState; storageConnection: StorageConnectionReport; /** Claim scope from the runner env file or environment, when configured. */ claimScope?: RunnerClaimScope; /** Service-unit state when `loops-runner install` has run. */ serviceState: { installed: boolean; active: boolean | null; unitPath?: string; }; } export declare function runnerStatus(machineId?: string | undefined): RunnerStatus; export interface RunnerApiClaim { loop: Loop; run: LoopRun; claimToken: string; workflow?: WorkflowSpec; } export interface RunnerOnceResult { ok: boolean; claimed: number; completed: LoopRun[]; } export interface RunnerLoopResult extends RunnerOnceResult { iterations: number; errors: number; idle: boolean; stopped: boolean; /** True when the loop terminated because of a PERMANENT control-plane denial (wrong_token_kind, expired, ...). */ permanent: boolean; /** Actionable message for the permanent denial, when one stopped the loop. */ permanentMessage?: string; } /** Exit code for a permanent credential/config denial, distinct from a generic transient failure (1). */ export declare const RUNNER_PERMANENT_DENIAL_EXIT_CODE = 4; /** * `fleet` claims machine-unbound loops as well as loops pinned to this runner; * `bound` claims only pinned loops. The wire default is `fleet`, so a runner * that sends nothing behaves exactly as every runner did before this existed. */ export type RunnerClaimScope = "fleet" | "bound"; export declare const RUNNER_CLAIM_SCOPES: readonly RunnerClaimScope[]; /** Advertised on the open `/version` probe by a control plane that enforces claimScope. */ export declare const RUNNER_CLAIM_SCOPE_CAPABILITY = "runner.claimScope"; export interface RunRunnerOnceOptions { apiUrl?: string; apiKey?: string; runnerId?: string; machineId?: string; claimScope?: RunnerClaimScope; now?: Date; heartbeatIntervalMs?: number; fetchImpl?: typeof fetch; execute?: (loop: Loop, run: LoopRun, opts?: { signal?: AbortSignal; }) => Promise; env?: NodeJS.ProcessEnv; } export interface RunRunnerLoopOptions extends RunRunnerOnceOptions { pollIntervalMs?: number; maxIterations?: number; idleExitAfterMs?: number; signal?: AbortSignal; sleep?: (ms: number, signal?: AbortSignal) => Promise; nowMs?: () => number; onError?: (error: unknown) => void; /** Failure-episode tracker; the CLI wires the default persisted recorder. */ episodeRecorder?: RunnerEpisodeRecorder; } /** Typed error for a permanent control-plane denial. The message is a known-safe, generated string (never provider output). */ export declare class RunnerPermanentDenialError extends Error { readonly reason: string; readonly route: string; constructor(reason: string, route: string); } /** * Convert a thrown server error into a {@link RunnerPermanentDenialError} when * it carries a known permanent denial reason; otherwise return undefined. */ export declare function runnerPermanentDenial(error: unknown): RunnerPermanentDenialError | undefined; export declare function runRunnerOnce(opts?: RunRunnerOnceOptions): Promise; export declare function runRunnerLoop(opts?: RunRunnerLoopOptions): Promise; export declare function main(argv?: string[]): Promise; export declare function logRunnerCommandFailure(error: unknown): void;