UNPKG

aiwg

Version:

Deployment tool and support utility for AI context. Copies agents, skills, commands, rules, and behaviors into the paths each AI platform reads (Claude Code, Codex, Copilot, Cursor, Warp, OpenClaw, and 6 more) so one source of truth works across 10 platfo

229 lines 8.19 kB
/** * Executor Registry * * Manages registered executor instances per the executor contract v1 spec * (docs/contracts/executor.v1.md). Each executor registers with * `aiwg serve` via POST /api/v1/executors/register and then pushes * real-time events over WebSocket at /ws/executors/:executorId. * * This is the AIWG-side dispatch surface for the executor contract epic. * Executor implementations (sandbox adapter, local executor) are in sibling issues. * * @issue #1179 * @see #1177 — executor-contract epic * @see #1178 — JSON Schema + conformance fixtures (schemas/executor-v1.json) * @see docs/contracts/executor.v1.md — authoritative prose spec */ import { EventEmitter } from 'events'; export declare function validateRegisterPayload(data: unknown): { valid: boolean; errors: string; }; export declare function validateEventEnvelope(data: unknown): { valid: boolean; errors: string; }; export declare function validateDispatchPayload(data: unknown): { valid: boolean; errors: string; }; /** Transport endpoints as declared in the schema. */ export interface TransportEndpoints { rest: string; ws: string; grpc?: string; } /** One active mission tracked by aiwg serve. */ export interface MissionRecord { missionId: string; executorId: string; state: MissionState; createdAt: string; updatedAt: string; completedAt?: string; recentEvents: EventEnvelope[]; ptySessionRef?: string; exitCode?: number; error?: string; } export type MissionState = 'queued' | 'assigned' | 'running' | 'paused' | 'hitl-required' | 'suspended' | 'done' | 'failed' | 'aborted'; export interface EventEnvelope { event: string; executor_id: string; mission_id?: string; ts: string; data?: Record<string, unknown>; } /** Per-executor registration record (in-memory). */ export interface ExecutorRegistration { /** Stable executor UUID — declared by the executor at registration time. */ executorId: string; /** Optional default A2A sandbox instance id. May differ from executorId. */ a2aInstanceId?: string; name: string; version: string; specVersion: string; transportEndpoints: TransportEndpoints; capabilities: string[]; /** Bearer token issued at first registration; preserved across re-registers. */ token: string; connected: boolean; lastEventTs?: string; registeredAt: string; disconnectedAt?: string; /** Non-terminal missions currently owned by this executor. */ currentMissions: Set<string>; /** Live WS connection handle (set by serve.ts on WS upgrade). */ wsConn?: WebSocketConn; } /** Minimal WS connection interface (duck-typed from the `ws` package). */ export interface WebSocketConn { readyState: number; send(data: string): void; close(code?: number, reason?: string): void; } /** Serializable executor summary (used in API responses). */ export interface ExecutorSummary { executor_id: string; a2a_instance_id?: string; name: string; version: string; spec_version: string; transport_endpoints: TransportEndpoints; capabilities: string[]; connected: boolean; last_event_ts?: string; active_mission_count: number; registered_at: string; disconnected_at?: string; } /** Request body for POST /api/v1/executors/register (wire shape). */ export interface ExecutorRegisterRequest { executor_id: string; a2a_instance_id?: string; name: string; version: string; spec_version: string; transport_endpoints: TransportEndpoints; capabilities: string[]; } /** Response body for 201 Created. */ export interface ExecutorRegisterResponse { executor_id: string; token: string; registered_at: string; } /** Filter criteria for executor selection (mirrors ExecutorFilter schema). */ export interface ExecutorFilter { /** Pin to specific executor. null = any. */ executor_id?: string | null; /** Executor must advertise ALL listed capabilities. */ capabilities?: string[]; } /** Result of picking an executor via pickByFilter(). */ export interface ExecutorPickResult { executor: ExecutorRegistration; reason: string; rejected: Array<{ executorId: string; reason: string; }>; } export declare function resolveExecutorIdentityStorePath(projectRootOverride?: string): string; /** * ExecutorRegistry — state, auth, and identity store integration for * registered executor instances. Parallel to SandboxRegistry. * * Emits EventEmitter events: * 'executor:registered' — { executorId, name } * 'executor:deregistered' — { executorId, reason } * 'mission:assigned' — { missionId, executorId } * 'mission:state_change' — { missionId, executorId, state, prevState } */ export declare class ExecutorRegistry extends EventEmitter { private executors; private missions; /** Persistent token store — executorId → identity record */ private identityStore; constructor(); /** * Register an executor. Validates payload against `register_payload` schema. * Returns 400-level error string on invalid payload. * On re-register with same executor_id: reclaims prior token (per ADR §9). */ register(req: ExecutorRegisterRequest): ExecutorRegisterResponse | { error: string; status: 400; }; /** * Deregister an executor by ID. Auth must be checked by the caller. * Emits 'executor:deregistered'. */ deregister(executorId: string, reason?: 'graceful_shutdown' | 'operator_deleted' | 'timeout'): boolean; /** * Validate bearer token for an executor. */ authenticate(executorId: string, token: string): boolean; /** * Mark the WS event stream as connected or disconnected. */ setConnected(executorId: string, connected: boolean, wsConn?: WebSocketConn): void; /** * Push a message to the executor's live WS connection. * Returns true if sent, false if the executor has no open connection. */ pushToExecutor(executorId: string, event: EventEnvelope): boolean; /** * Handle an inbound event from an executor WS connection. * Validates against event_envelope schema; updates mission state. */ handleEvent(envelope: EventEnvelope): void; private appendMissionEvent; /** * Create a mission record and associate it with an executor. * Called by the dispatch route after forwarding succeeds. */ assignMission(missionId: string, executorId: string): MissionRecord; /** * Mark a mission as failed (e.g. executor unreachable on forward). */ failMission(missionId: string, error: string): void; /** * Get a mission record by ID. */ getMission(missionId: string): MissionRecord | undefined; /** * Transition a mission to a requested operator state. * Returns false if the mission is in a terminal state or not found. */ transitionMission(missionId: string, targetState: 'paused' | 'running' | 'aborted'): boolean; /** * List all executors as serializable summaries. */ list(): ExecutorSummary[]; /** * Get a single executor summary. */ get(executorId: string): ExecutorSummary | undefined; /** * Get the raw registration record (internal use by dispatch). */ getRegistration(executorId: string): ExecutorRegistration | undefined; /** * Pick the best executor matching the given filter. * * Default-selection policy (per ADR §3): * 1. Sandbox-first: prefer any executor with isolation:vm or isolation:container * 2. Local fallback: isolation:none or isolation:host * 3. 503 if no executor available * * `long_running: true` requires a 'resumable' capability. */ pickByFilter(filter: ExecutorFilter, longRunning?: boolean): ExecutorPickResult | null; /** Total registered executor count. */ get size(): number; /** Shut down — clear all state. */ shutdown(): void; } export declare const executorRegistry: ExecutorRegistry; //# sourceMappingURL=executor-registry.d.ts.map