/** * rest-transport.ts, the session-spine's raw-REST `SpineTransport`. * * A hand-rolled REST mirror of the daemon's `sessions.register` / * `sessions.close` HTTP routes (`POST /api/sessions/register`, * `POST /api/sessions/{sessionId}/close`, see * `method-catalog-control-core.ts`), written request/response only with a * Bearer token and an `AbortController` timeout, deliberately NOT the typed * operator SDK client. "Version-tolerant" here means tolerant of the ADOPTED * DAEMON's version, not this package's: a consumer can attach to a daemon * that predates one of these routes, and every response is classified from * its actual HTTP status rather than assumed present, a 404 becomes an * honest `connected_host_route_unavailable` rather than a thrown parse error. * * ── Hoist provenance (2026-07-30 daemon/TUI split) ────────────────────────── * * Both the TUI (`session-spine-transport.ts`, a thin fold over the SDK's own * typed operator client) and the agent (`session-spine-rest-transport.ts`, * this raw-REST mirror plus a probe and a receipt consumer) carried a * `SpineTransport` implementation. The agent's is the superset adopted here: * it folds failures into THREE outcomes (`ok`/`offline`/`rejected`) instead * of the TUI's two (`ok`/`offline`, which treats every failure, including a * durable auth/route rejection, as a transient connectivity fault the spine * client will retry forever), and it supplies a reachability probe and a * daemon-receipt consumer the TUI's thinner adapter did not need because it * already had a live, in-process typed client for those concerns. Kept out * of this hoist: the agent's `createSpineConnectionResolver` (reads a * connected-host token file from a specific home directory, a consumer * trust-boundary concern the SDK core deliberately never reaches into; a * consumer builds its own `resolveConnection` and passes it in here). */ import type { RegisterSharedSessionInput, SharedSessionRecord } from '../../control-plane/session-types.js'; import type { DaemonReceipt } from '../../daemon/receipts.js'; import type { SpineTransport } from './client.js'; export interface SessionSpineRestConnection { readonly baseUrl: string; readonly token: string | null; readonly tokenPath?: string; } export type SessionSpineRestFailureKind = 'auth_required' | 'connected_host_unavailable' | 'connected_host_route_unavailable' | 'connected_host_error'; export interface SessionSpineRestFailure { readonly ok: false; readonly kind: SessionSpineRestFailureKind; readonly status?: number; readonly error: string; } export interface SessionSpineRegisterSuccess { readonly ok: true; readonly reopened: boolean; readonly conflict?: { readonly status: 'closed'; }; readonly session?: SharedSessionRecord; } export type SessionSpineRegisterResult = SessionSpineRegisterSuccess | SessionSpineRestFailure; export type SessionSpineCloseResult = { readonly ok: true; readonly session: SharedSessionRecord | null; } | SessionSpineRestFailure; /** Idempotently register (or heartbeat) a shared session over `POST /api/sessions/register`. */ export declare function postSessionSpineRegister(connection: SessionSpineRestConnection, input: RegisterSharedSessionInput, options?: { readonly timeoutMs?: number; }): Promise; /** Close a shared session over `POST /api/sessions/{sessionId}/close`. */ export declare function postSessionSpineClose(connection: SessionSpineRestConnection, sessionId: string, options?: { readonly timeoutMs?: number; }): Promise; export interface SessionSpineRestTransportOptions { readonly resolveConnection: () => SessionSpineRestConnection; readonly registerTimeoutMs?: number; readonly closeTimeoutMs?: number; } /** Build the `SpineTransport` injected into `SessionSpineClient` at construction (live-immediately mode). */ export declare function createSessionSpineRestTransport(options: SessionSpineRestTransportOptions): SpineTransport; export interface SessionSpineRestProbeOptions { readonly resolveConnection: () => SessionSpineRestConnection; readonly probeTimeoutMs?: number; /** Override for tests; default does a short plain GET {baseUrl}/status. */ readonly probeImpl?: (connection: SessionSpineRestConnection, timeoutMs: number, onDaemonFloor?: (floor: string | undefined) => void) => Promise; /** * Receives the minimum client build the daemon announced on this read * (`X-Goodvibes-Client-Floor`), or undefined when it announced none. Wire * this to a client-build compatibility guard that pauses shared-session * work when this process is below the floor. */ readonly onDaemonFloor?: ((floor: string | undefined) => void) | undefined; } /** * Builds the zero-argument `probe` `SessionSpineClient.probeReachability()` * calls directly (its injected-probe shape takes no parameters, the client * has no connection to hand it). Liveness only, receipt consumption is * {@link createSessionSpineReceiptConsumer}, not this. */ export declare function createSessionSpineRestProbe(options: SessionSpineRestProbeOptions): () => Promise; /** Parse the `receipts` array off a `/status` response body; `[]` when absent/malformed. */ export declare function extractSessionSpineReceipts(body: unknown): DaemonReceipt[]; export interface SessionSpineReceiptConsumerOptions { readonly resolveConnection: () => SessionSpineRestConnection; readonly consumeTimeoutMs?: number; /** Override for tests; default does a short GET {baseUrl}/status?receipts=consume. */ readonly consumeImpl?: (connection: SessionSpineRestConnection, timeoutMs: number) => Promise; } /** * Builds the zero-argument receipt consumer a consumer invokes once per * attach. Delivery is destructive at the daemon (served exactly once), so * this must be called on attach only, never on the frequent liveness probe * cadence above. */ export declare function createSessionSpineReceiptConsumer(options: SessionSpineReceiptConsumerOptions): () => Promise; //# sourceMappingURL=rest-transport.d.ts.map