UNPKG

@audin.ai/operator-sdk

Version:

Headless browser SDK for the Audin operator softphone — make and receive calls over the Audin operator WebSockets.

92 lines (91 loc) 3.75 kB
/** * PresenceClient — the signalling channel. * * A single WebSocket to the Audin presence endpoint over which the operator: * - announces availability on a set of phone numbers (`set_available`), * - keeps the connection alive (`ping` heartbeat), * - answers / declines inbound offers (`accept` / `reject`), * - starts outbound calls (`start_outbound`). * * and over which the server pushes: `connected`, `available_set`, * `incoming_call`, `call_taken`, `call_assigned`, `outbound_started`, `pong`, * `error`. * * Resilience built in here so the higher-level `AudinOperator` doesn't have to: * - HEARTBEAT: a `ping` every `heartbeatIntervalMs`; the server reaps idle * connections at ~90s. * - RECONNECT: on unexpected close, walk a backoff schedule, fetch a FRESH * token via `getToken` (the old one may have expired), reconnect, and * re-send the last `set_available` so the operator comes back online * automatically. * * This class is transport-only: it parses/dispatches messages and owns the * socket lifecycle. It holds NO call state and names NO providers. */ import type { OperatorLogger } from "./types.js"; /** A server→client message. Loosely typed; the handler narrows on `type`. */ export interface PresenceServerMessage { type: string; [key: string]: unknown; } export interface PresenceClientOptions { /** Base WS URL WITHOUT query, e.g. `wss://host/operator-presence/ws`. */ presenceWsUrl: string; /** * Resolve a valid session token (cached & shared with REST/audio). Returns * the raw JWT string. Should re-mint internally once expired. */ ensureToken: () => Promise<string>; /** * Drop the cached token so the next {@link ensureToken} re-mints. Invoked on * an auth-related socket close so a reconnect uses a fresh token. */ invalidateToken: () => void; heartbeatIntervalMs: number; reconnectBackoffMs: number[]; logger: OperatorLogger; /** Invoked for every parsed server message. */ onMessage: (msg: PresenceServerMessage) => void; /** Connection lifecycle for the higher layer (state, errors). */ onOpen: () => void; onReconnecting: () => void; onError: (code: string, message: string, cause?: unknown) => void; } export declare class PresenceClient { private readonly opts; private ws; private heartbeatTimer; private reconnectTimer; private reconnectAttempt; /** True between `connect()` and `disconnect()` — gates auto-reconnect. */ private desiredOnline; /** Last availability we announced, replayed after a reconnect. */ private lastAvailability; constructor(opts: PresenceClientOptions); /** Open the presence socket and keep it open (auto-reconnecting). */ connect(): Promise<void>; /** * Close the socket and stop reconnecting. Best-effort `set_unavailable` * first so the server drops presence immediately rather than waiting for the * reaper. */ disconnect(): void; /** Announce availability on `phoneNumberIds`; remembered for reconnects. */ setAvailable(phoneNumberIds: string[]): void; /** Drop availability (stays connected). */ setUnavailable(): void; /** Answer an inbound offer. */ accept(callSid: string): void; /** Decline an inbound offer. */ reject(callSid: string): void; /** Request an outbound call. The server replies with `outbound_started`. */ startOutbound(to: string, callerId: string): void; /** Whether the socket is currently open. */ get isOpen(): boolean; private openOnce; private scheduleReconnect; private startHeartbeat; private stopHeartbeat; private clearTimers; private sendRaw; }