import type { DaemonControlRouteHandlers } from './context.js'; import type { AuthenticatedPrincipal } from './http-policy.js'; import { type JsonRecord } from './route-helpers.js'; interface GatewayMethodDescriptorLike { readonly access?: 'public' | 'authenticated' | 'admin' | 'remote-peer' | undefined; } interface GatewayMethodCatalogLike { list(options?: Record): unknown; listEvents(options?: Record): unknown; get(methodId: string): GatewayMethodDescriptorLike | null | undefined; } interface ControlPlaneGatewayLike { getSnapshot(): unknown; renderWebUi(): Response; listRecentEvents(limit: number): unknown; listSurfaceMessages(): unknown; listClients(): unknown; createEventStream(req: Request, input: Record): Response | Promise; } interface ControlRouteContext { readonly authToken: string | null; /** * The PLATFORM build this daemon is composed from, the SDK's own version. * * It is not the version of the product a person installed, and it used to be * the only thing /status reported. A daemon shipped from its own repository * on its own release line answered every version read with the SDK version * it happened to be built against, so an updater comparing release tags and a * client checking whether the daemon was new enough were both reading a * number about a different artifact. See {@link buildVersion}. */ readonly version: string; /** * The RUNNING ARTIFACT's own release version, when the host stated one * (`updateArtifact.version`, the same value the auto-update loop compares * against release tags). Absent on an embedded daemon that ships no artifact * of its own, where the platform build is the only version there is. */ readonly buildVersion?: (() => string | null) | undefined; readonly sessionCookieName: string; readonly controlPlaneGateway: ControlPlaneGatewayLike; readonly extractAuthToken: (req: Request) => string; readonly resolveAuthenticatedPrincipal: (req: Request) => AuthenticatedPrincipal | null; readonly gatewayMethods: GatewayMethodCatalogLike; readonly getOperatorContract: () => unknown; readonly invokeGatewayMethodCall: (input: { readonly authToken: string; readonly methodId: string; readonly query?: Record | undefined; readonly body?: unknown | undefined; readonly context?: { readonly principalId?: string | undefined; readonly principalKind?: 'user' | 'bot' | 'service' | 'token' | 'remote-peer' | undefined; readonly admin?: boolean | undefined; readonly scopes?: readonly string[] | undefined; readonly clientKind?: string | undefined; }; /** * How many synthesized dispatches already produced this call. Passed on so * a request that re-enters the dispatcher is refused as a loop instead of * consuming the concurrent-call budget one nesting level at a time. */ readonly synthesizedDepth?: number | undefined; }) => Promise<{ status: number; ok: boolean; body: unknown; }>; readonly parseOptionalJsonBody: (req: Request) => Promise; readonly requireAdmin: (req: Request) => Response | null; readonly requireAuthenticatedSession: (req: Request) => { username: string; roles: readonly string[]; } | null; readonly login?: ((req: Request) => Promise | Response) | undefined; /** * Undelivered daemon receipts ("updated from X to Y at HH:MM", * "restarted after a crash at HH:MM"). Invoked ONLY when a /status reader * explicitly opts in with `?receipts=consume`; the provider marks the * returned receipts delivered, so consumption stays exactly-once across * consuming readers. A plain /status read (identity probe, keepalive, * version poll) neither receives nor consumes receipts. */ readonly collectReceipts?: (() => readonly { id: string; text: string; at: number; }[]) | undefined; /** * Which node on the local network currently consumes inbound channel * messages, and how it got there. * * Inspection ONLY, and unconditional on every /status read (unlike receipts, * reading it consumes nothing). Leader election is otherwise invisible by * design: it produces no notification, no transcript line and no message on * any surface, so a `/status` read is the one place an operator can see who * holds the role. Absent on a host with no coordinator. */ readonly collectClusterStatus?: (() => unknown) | undefined; /** * The minimum CLIENT build this daemon accepts as a full participant, * announced on /status as a response header (the body has a closed schema, * and a header is additive and ignorable by older clients). * * A daemon update does not restart the clients attached to it, so without * this a process started days ago keeps executing shared-session work under * whatever rules its build shipped with. Supplying it lets every client * check itself on the liveness probe it already performs. */ readonly clientCompatibilityFloor?: string | undefined; /** Header name carrying {@link clientCompatibilityFloor}. */ readonly clientCompatibilityFloorHeader?: string | undefined; } /** * The HTTP form of "a person asked for this right now". * * A header rather than a body field, so it applies uniformly to the * GET-shaped REST verbs and the POST invoke envelope without either schema * having to carry it. Only the literal `true` counts: absent, malformed, or * anything else means the caller cannot claim it, which is the honest default * for scheduled work, triggers and channel-driven calls. */ export declare const EXPLICIT_USER_REQUEST_HEADER = "x-goodvibes-explicit-user-request"; export declare function createDaemonControlRouteHandlers(context: ControlRouteContext): DaemonControlRouteHandlers; export {}; //# sourceMappingURL=control-routes.d.ts.map