UNPKG

@audin.ai/operator-sdk

Version:

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

131 lines (130 loc) 6.22 kB
/** * AudinOperator — the public entry point of `@audin.ai/operator-sdk`. * * A headless (no-UI) controller an operator's browser app drives to make and * receive phone calls through Audin. It hides: * - the signalling channel (PresenceClient: availability, ringing, dialing), * - the media channel (AudioBridge: microphone, μ-law transcoding, playback), * - reconnection, heartbeats and token refresh. * * Credentials never enter the browser: the app supplies a `getToken` callback * that calls ITS OWN backend (which holds the account key) to mint a * short-lived session token. The SDK only ever sees that token. * * MVP concurrency: ONE active call at a time. While a call is live, an inbound * offer is auto-declined (`reject`) and `dial()` rejects with an error. This * keeps the audio graph and state machine simple; multi-line can layer on top * later without changing the public surface. * * Naming is provider-neutral throughout — this is "the Audin operator * softphone", nothing more. */ import { TypedEmitter } from "./emitter.js"; import type { AudinOperatorConfig, AudinOperatorEventMap, AudinOperatorEventName, AudinOperatorListener, DialOptions, OperatorCall, OperatorPhoneNumber, PresenceState } from "./types.js"; export declare class AudinOperator extends TypedEmitter<AudinOperatorEventMap> { private readonly presence; private readonly coreWsHost; private readonly coreHttpBase; private readonly tokens; /** * Microphone constraints handed to the next {@link AudioBridge} when a call's * audio leg opens. Mutable so {@link setAudioConstraints} can switch the input * device at runtime; read fresh in {@link openAudioBridge} (never captured * earlier), so the change applies from the next call. */ private audioConstraints; private readonly logger; private presenceState; /** The single active/ringing call (MVP: one at a time). */ private activeCall; private activeBridge; /** Pending outbound dial awaiting its `outbound_started` ack. */ private pendingOutbound; constructor(config: AudinOperatorConfig); on<K extends AudinOperatorEventName>(event: K, listener: AudinOperatorListener<K>): () => void; off<K extends AudinOperatorEventName>(event: K, listener: AudinOperatorListener<K>): void; once<K extends AudinOperatorEventName>(event: K, listener: AudinOperatorListener<K>): () => void; /** * List the phone numbers this account owns and may go online on / dial from. * * Fetches over REST using the SAME short-lived session token the WebSockets * use (obtained through `getToken`) — the Account API Key never enters the * browser. On a 401 the cached token is invalidated and the request is * retried ONCE with a fresh token; a second 401 throws an * {@link OperatorRequestError} with code `UNAUTHORIZED`. Other non-OK * responses throw with code `REQUEST_FAILED`. * * Use a returned number's `id` for {@link goOnline} and its `phoneNumber` * (E.164) as the `callerId` for {@link dial}. */ listPhoneNumbers(): Promise<OperatorPhoneNumber[]>; private fetchPhoneNumbers; /** * Go online and start accepting inbound calls on `phoneNumberIds`. Connects * the presence channel (fetching a token via `getToken`) and announces * availability. Safe to call again to change the number set. */ goOnline(phoneNumberIds: string[]): Promise<void>; /** * Go offline: drop availability, tear down any active call, and close the * presence channel (no auto-reconnect until `goOnline` again). */ goOffline(): Promise<void>; /** Current presence channel state. */ get state(): PresenceState; /** The current active/ringing call, if any. */ get currentCall(): OperatorCall | null; /** * Switch the microphone constraints used to capture the operator's audio — * typically to pick a specific input device, e.g. * `setAudioConstraints({ deviceId: { exact: id }, echoCancellation: true })`. * * Takes effect from the NEXT call: a call already in progress keeps the * device its {@link AudioBridge} opened with (this does not re-open the * microphone mid-call). The new constraints are read when the next * `dial`/accepted-inbound opens its audio leg. */ setAudioConstraints(constraints: MediaTrackConstraints): void; /** * Place an outbound call to `to` (E.164), presenting `options.callerId` * (which must be an active number your account owns). Resolves once the call * is accepted by the platform and the audio bridge is opening; rejects on * validation failure, a busy operator, or if the platform doesn't ack in * time. */ dial(to: string, options: DialOptions): Promise<OperatorCall>; private handlePresenceMessage; private handleIncomingCall; private handleCallTaken; private handleCallAssigned; private handleOutboundStarted; private openAudioBridge; private onBridgeClosed; private makeCall; private endCallWithReason; private teardownActiveCall; private failPendingOutbound; private setPresenceState; private emitError; /** Public-facing emit wrapper (the base `emit` is protected). */ private emitEvent; } /** * Normalise a base URL into a `ws(s)://host` origin with no trailing slash and * no path. Accepts `http(s)` or `ws(s)`; upgrades the scheme accordingly. */ export declare function toWsBase(coreUrl: string): string; /** * Normalise a base URL into an `http(s)://host` origin with no trailing slash * and no path. Accepts `http(s)` or `ws(s)`; downgrades the WS scheme to the * matching HTTP scheme for REST calls. */ export declare function toHttpBase(coreUrl: string): string; /** Error thrown by the SDK's REST helpers (e.g. {@link AudinOperator.listPhoneNumbers}). */ export declare class OperatorRequestError extends Error { /** Stable machine code: `UNAUTHORIZED` | `REQUEST_FAILED`. */ readonly code: string; /** Original error / response context, if any. */ readonly cause?: unknown; constructor(code: string, message: string, cause?: unknown); }