UNPKG

@audin.ai/operator-sdk

Version:

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

168 lines (167 loc) 6.99 kB
/** * Public + protocol types for `@audin.ai/operator-sdk`. * * Naming is deliberately neutral: this SDK is shipped to partners and never * names the underlying telephony or AI providers. It speaks only of "calls", * "phone numbers" and "audio". */ /** * The token your application's backend returns for a softphone session. * * Your backend calls the Audin REST API `POST /operator-sessions/token` * (authenticated with your Account API Key — which NEVER leaves your server) * and returns at least the `token` here. The SDK presents this token to the * Audin WebSockets; it is short-lived (~1h) and the SDK requests a fresh one * via {@link AudinOperatorConfig.getToken} whenever it (re)connects. */ export interface OperatorSessionToken { /** Short-lived session JWT the SDK presents to the Audin WebSockets. */ token: string; /** Optional ISO-8601 expiry. If provided, the SDK refreshes ahead of it. */ expiresAt?: string; } /** Async provider of a fresh {@link OperatorSessionToken}. */ export type GetTokenFn = () => Promise<OperatorSessionToken>; /** * A phone number the operator's account owns and may go online on / dial from. * Returned by {@link AudinOperator.listPhoneNumbers}. */ export interface OperatorPhoneNumber { /** Stable identifier — pass to `goOnline([...])`. */ id: string; /** E.164 number — present as `callerId` when dialling. */ phoneNumber: string; /** Human label, when set. */ displayName: string | null; } /** Direction of a call from the operator's point of view. */ export type CallDirection = "inbound" | "outbound"; /** Lifecycle state of a {@link OperatorCall}. */ export type CallState = "ringing" | "connecting" | "active" | "ended"; /** Why a call ended — surfaced on the `callEnded` event. */ export type CallEndReason = "hangup" | "remote_hangup" | "taken_by_other" | "rejected" | "no_answer" | "failed" | "offline"; /** * Configuration for {@link AudinOperator}. */ export interface AudinOperatorConfig { /** * Base URL of the Audin operator service, e.g. `https://core.<audin-host>`. * `http(s)` is accepted and upgraded to the matching `ws(s)` scheme * internally. Trailing slashes are tolerated. */ coreUrl: string; /** * Returns a fresh session token. Called on initial connect AND on every * reconnect, so always fetch a NEW token (do not cache an expired one). * This is the single seam through which credentials enter the SDK — the * Account API Key stays on your backend. */ getToken: GetTokenFn; /** * Heartbeat interval (ms) for the presence connection. Defaults to 25000. * The server reaps connections idle for ~90s, so keep this well under that. */ heartbeatIntervalMs?: number; /** * Reconnect backoff schedule (ms) for the presence connection. The SDK * walks this array on consecutive failures and stays on the last value * thereafter. Defaults to `[1000, 2000, 5000, 10000, 30000]`. */ reconnectBackoffMs?: number[]; /** * Constraints passed to `getUserMedia({ audio })` for microphone capture. * Defaults enable echo cancellation, noise suppression and auto gain. */ audioConstraints?: MediaTrackConstraints; /** Optional sink for SDK diagnostics. Defaults to `console`. */ logger?: OperatorLogger; } /** Minimal logger surface the SDK writes to (defaults to `console`). */ export interface OperatorLogger { debug(...args: unknown[]): void; info(...args: unknown[]): void; warn(...args: unknown[]): void; error(...args: unknown[]): void; } /** Connection state of the presence channel. */ export type PresenceState = "offline" | "connecting" | "online" | "reconnecting"; /** * Events emitted by {@link AudinOperator}. Subscribe with `op.on(name, cb)`. */ export interface AudinOperatorEventMap { /** Presence channel state changed. */ presenceStateChanged: PresenceState; /** The set of phone numbers the operator is online on was (re)confirmed. */ availabilityChanged: { accepted: string[]; rejected: string[]; }; /** An inbound call is ringing. Call `call.accept()` or `call.reject()`. */ incomingCall: OperatorCall; /** A call's audio is being established (after accept / dial). */ callStarted: OperatorCall; /** A call ended. Inspect `call.endReason`. */ callEnded: OperatorCall; /** A non-fatal error. The SDK keeps running where it can. */ error: OperatorError; } /** Names of the events in {@link AudinOperatorEventMap}. */ export type AudinOperatorEventName = keyof AudinOperatorEventMap; /** Listener signature for a given event. */ export type AudinOperatorListener<K extends AudinOperatorEventName> = (payload: AudinOperatorEventMap[K]) => void; /** Structured error surfaced on the `error` event. */ export interface OperatorError { /** Stable machine code, e.g. `MIC_PERMISSION_DENIED`, `WS_ERROR`. */ code: string; /** Human-readable description. */ message: string; /** Original error / server payload, if any. */ cause?: unknown; } /** Options for {@link AudinOperator.dial}. */ export interface DialOptions { /** * Caller ID to present — MUST be a phone number your account owns and that * is active. Required by the platform for outbound calls. */ callerId: string; } /** * Handle to a single call. Obtained from the `incomingCall` / `callStarted` * events or returned by {@link AudinOperator.dial}. */ export interface OperatorCall { /** Platform call identifier. */ readonly callSid: string; /** inbound | outbound. */ readonly direction: CallDirection; /** Remote party number (E.164) when known. */ readonly from?: string; /** Local/called number (E.164) when known. */ readonly to?: string; /** Current lifecycle state. */ readonly state: CallState; /** Populated once `state === "ended"`. */ readonly endReason?: CallEndReason; /** Answer an inbound offer. No-op if not ringing. */ accept(): void; /** Decline an inbound offer. No-op if not ringing. */ reject(): void; /** Mute / unmute the operator microphone toward the far end. */ mute(on: boolean): void; /** Whether the operator microphone is currently muted. */ readonly muted: boolean; /** * Send a DTMF digit (`0-9`, `*`, `#`). * * NOT YET SUPPORTED END-TO-END. The digit is validated and emitted as a * control message on the call channel, but the server does not yet forward * the tones onto the telephone network — so today this is effectively a * functional no-op for the far end. The method is kept (and the wire message * remains valid) so that enabling it server-side in a future version requires * no SDK change, but do NOT rely on the remote party hearing the tones yet. */ sendDtmf(digit: string): void; /** Hang up the call. */ hangup(): void; }