@sayna-ai/node-sdk 0.0.8 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -152,10 +152,10 @@ Sets or updates SIP webhook hooks in the runtime cache. Hooks with matching host
152
152
 
153
153
  Each `SipHook` object contains:
154
154
 
155
- | field | type | description |
156
- | ------ | -------- | ---------------------------------------- |
157
- | `host` | `string` | SIP domain pattern (case-insensitive). |
158
- | `url` | `string` | HTTPS URL to forward webhook events to. |
155
+ | field | type | description |
156
+ | ------ | -------- | --------------------------------------- |
157
+ | `host` | `string` | SIP domain pattern (case-insensitive). |
158
+ | `url` | `string` | HTTPS URL to forward webhook events to. |
159
159
 
160
160
  **Returns**: `Promise<SipHooksResponse>` - Object containing the merged list of all configured hooks.
161
161
 
@@ -284,8 +284,8 @@ The SDK includes a `WebhookReceiver` class for securely receiving and verifying
284
284
 
285
285
  Creates a new webhook receiver instance.
286
286
 
287
- | parameter | type | purpose |
288
- | --------- | -------- | ---------------------------------------------------------------------------- |
287
+ | parameter | type | purpose |
288
+ | --------- | -------- | --------------------------------------------------------------------------- |
289
289
  | `secret` | `string` | HMAC signing secret (min 16 chars). Defaults to `SAYNA_WEBHOOK_SECRET` env. |
290
290
 
291
291
  **Example**:
@@ -305,10 +305,10 @@ const receiver = new WebhookReceiver();
305
305
 
306
306
  Verifies and parses an incoming SIP webhook.
307
307
 
308
- | parameter | type | purpose |
309
- | --------- | ------------------------------------------- | -------------------------------------------- |
310
- | `headers` | `Record<string, string \| string[] \| undefined>` | HTTP request headers (case-insensitive). |
311
- | `body` | `string` | Raw request body as string (not parsed JSON). |
308
+ | parameter | type | purpose |
309
+ | --------- | ------------------------------------------------- | --------------------------------------------- |
310
+ | `headers` | `Record<string, string \| string[] \| undefined>` | HTTP request headers (case-insensitive). |
311
+ | `body` | `string` | Raw request body as string (not parsed JSON). |
312
312
 
313
313
  **Returns**: `WebhookSIPOutput` - Parsed and validated webhook payload.
314
314
 
@@ -380,19 +380,19 @@ fastify.post(
380
380
 
381
381
  The `receive` method returns a `WebhookSIPOutput` object with the following structure:
382
382
 
383
- | field | type | description |
384
- | ------------------- | ----------------------- | -------------------------------------------------- |
385
- | `participant` | `WebhookSIPParticipant` | SIP participant information. |
386
- | `participant.identity` | `string` | Unique identity assigned to the participant. |
387
- | `participant.sid` | `string` | Participant session ID from LiveKit. |
388
- | `participant.name` | `string?` | Display name (optional). |
389
- | `room` | `WebhookSIPRoom` | LiveKit room information. |
390
- | `room.name` | `string` | Name of the LiveKit room. |
391
- | `room.sid` | `string` | Room session ID from LiveKit. |
392
- | `from_phone_number` | `string` | Caller's phone number (E.164 format). |
393
- | `to_phone_number` | `string` | Called phone number (E.164 format). |
394
- | `room_prefix` | `string` | Room name prefix configured in Sayna. |
395
- | `sip_host` | `string` | SIP domain extracted from the To header. |
383
+ | field | type | description |
384
+ | ---------------------- | ----------------------- | -------------------------------------------- |
385
+ | `participant` | `WebhookSIPParticipant` | SIP participant information. |
386
+ | `participant.identity` | `string` | Unique identity assigned to the participant. |
387
+ | `participant.sid` | `string` | Participant session ID from LiveKit. |
388
+ | `participant.name` | `string?` | Display name (optional). |
389
+ | `room` | `WebhookSIPRoom` | LiveKit room information. |
390
+ | `room.name` | `string` | Name of the LiveKit room. |
391
+ | `room.sid` | `string` | Room session ID from LiveKit. |
392
+ | `from_phone_number` | `string` | Caller's phone number (E.164 format). |
393
+ | `to_phone_number` | `string` | Called phone number (E.164 format). |
394
+ | `room_prefix` | `string` | Room name prefix configured in Sayna. |
395
+ | `sip_host` | `string` | SIP domain extracted from the To header. |
396
396
 
397
397
  ## Development
398
398
 
package/dist/index.js CHANGED
@@ -64,6 +64,8 @@ class SaynaClient {
64
64
  _livekitUrl;
65
65
  _saynaParticipantIdentity;
66
66
  _saynaParticipantName;
67
+ _streamId;
68
+ inputStreamId;
67
69
  sttCallback;
68
70
  ttsCallback;
69
71
  errorCallback;
@@ -72,7 +74,7 @@ class SaynaClient {
72
74
  ttsPlaybackCompleteCallback;
73
75
  readyPromiseResolve;
74
76
  readyPromiseReject;
75
- constructor(url, sttConfig, ttsConfig, livekitConfig, withoutAudio = false, apiKey) {
77
+ constructor(url, sttConfig, ttsConfig, livekitConfig, withoutAudio = false, apiKey, streamId) {
76
78
  if (!url || typeof url !== "string") {
77
79
  throw new SaynaValidationError("URL must be a non-empty string");
78
80
  }
@@ -90,6 +92,7 @@ class SaynaClient {
90
92
  this.livekitConfig = livekitConfig;
91
93
  this.withoutAudio = withoutAudio;
92
94
  this.apiKey = apiKey ?? process.env.SAYNA_API_KEY;
95
+ this.inputStreamId = streamId;
93
96
  }
94
97
  async connect() {
95
98
  if (this.isConnected) {
@@ -107,6 +110,7 @@ class SaynaClient {
107
110
  this.isConnected = true;
108
111
  const configMessage = {
109
112
  type: "config",
113
+ stream_id: this.inputStreamId,
110
114
  stt_config: this.sttConfig,
111
115
  tts_config: this.ttsConfig,
112
116
  livekit: this.livekitConfig,
@@ -177,6 +181,7 @@ class SaynaClient {
177
181
  this._livekitUrl = readyMsg.livekit_url;
178
182
  this._saynaParticipantIdentity = readyMsg.sayna_participant_identity;
179
183
  this._saynaParticipantName = readyMsg.sayna_participant_name;
184
+ this._streamId = readyMsg.stream_id;
180
185
  if (this.readyPromiseResolve) {
181
186
  this.readyPromiseResolve();
182
187
  }
@@ -237,6 +242,7 @@ class SaynaClient {
237
242
  this._livekitUrl = undefined;
238
243
  this._saynaParticipantIdentity = undefined;
239
244
  this._saynaParticipantName = undefined;
245
+ this._streamId = undefined;
240
246
  }
241
247
  getHttpUrl() {
242
248
  return this.url.replace(/^ws:\/\//, "http://").replace(/^wss:\/\//, "https://");
@@ -435,6 +441,12 @@ class SaynaClient {
435
441
  })
436
442
  });
437
443
  }
444
+ async getRecording(streamId) {
445
+ if (!streamId || streamId.trim().length === 0) {
446
+ throw new SaynaValidationError("streamId cannot be empty");
447
+ }
448
+ return this.fetchFromSayna(`recording/${encodeURIComponent(streamId)}`, { method: "GET" }, "arrayBuffer");
449
+ }
438
450
  async getSipHooks() {
439
451
  return this.fetchFromSayna("sip/hooks");
440
452
  }
@@ -493,6 +505,9 @@ class SaynaClient {
493
505
  get saynaParticipantName() {
494
506
  return this._saynaParticipantName;
495
507
  }
508
+ get streamId() {
509
+ return this._streamId;
510
+ }
496
511
  }
497
512
  // src/webhook-receiver.ts
498
513
  import { createHmac, timingSafeEqual } from "crypto";
@@ -636,4 +651,4 @@ export {
636
651
  SaynaClient
637
652
  };
638
653
 
639
- //# debugId=DAB85213A282150564756E2164756E21
654
+ //# debugId=439FAA862268544264756E2164756E21
package/dist/index.js.map CHANGED
@@ -3,11 +3,11 @@
3
3
  "sources": ["../src/errors.ts", "../src/sayna-client.ts", "../src/webhook-receiver.ts", "../src/index.ts"],
4
4
  "sourcesContent": [
5
5
  "/**\n * Base error class for all Sayna SDK errors.\n */\nexport class SaynaError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"SaynaError\";\n Object.setPrototypeOf(this, SaynaError.prototype);\n }\n}\n\n/**\n * Error thrown when attempting to use the client before it's connected.\n */\nexport class SaynaNotConnectedError extends SaynaError {\n constructor(message: string = \"Not connected to Sayna WebSocket\") {\n super(message);\n this.name = \"SaynaNotConnectedError\";\n Object.setPrototypeOf(this, SaynaNotConnectedError.prototype);\n }\n}\n\n/**\n * Error thrown when attempting operations before the client is ready.\n */\nexport class SaynaNotReadyError extends SaynaError {\n constructor(\n message: string = \"Sayna voice providers are not ready. Wait for the connection to be established.\"\n ) {\n super(message);\n this.name = \"SaynaNotReadyError\";\n Object.setPrototypeOf(this, SaynaNotReadyError.prototype);\n }\n}\n\n/**\n * Error thrown when WebSocket connection fails.\n */\nexport class SaynaConnectionError extends SaynaError {\n public override readonly cause?: unknown;\n\n constructor(message: string, cause?: unknown) {\n super(message);\n this.name = \"SaynaConnectionError\";\n this.cause = cause;\n Object.setPrototypeOf(this, SaynaConnectionError.prototype);\n }\n}\n\n/**\n * Error thrown when invalid parameters are provided.\n */\nexport class SaynaValidationError extends SaynaError {\n constructor(message: string) {\n super(message);\n this.name = \"SaynaValidationError\";\n Object.setPrototypeOf(this, SaynaValidationError.prototype);\n }\n}\n\n/**\n * Error thrown when the server returns an error.\n */\nexport class SaynaServerError extends SaynaError {\n constructor(message: string) {\n super(message);\n this.name = \"SaynaServerError\";\n Object.setPrototypeOf(this, SaynaServerError.prototype);\n }\n}\n",
6
- "import type {\n STTConfig,\n TTSConfig,\n LiveKitConfig,\n ConfigMessage,\n SpeakMessage,\n ClearMessage,\n SendMessageMessage,\n STTResultMessage,\n ErrorMessage,\n OutgoingMessage,\n SaynaMessage,\n Participant,\n VoicesResponse,\n HealthResponse,\n LiveKitTokenResponse,\n SipHook,\n SipHooksResponse,\n} from \"./types\";\nimport {\n SaynaNotConnectedError,\n SaynaNotReadyError,\n SaynaConnectionError,\n SaynaValidationError,\n SaynaServerError,\n} from \"./errors\";\n\n// Node.js 18+ has native fetch support\ndeclare const fetch: typeof globalThis.fetch;\n\n/**\n * Event handler for speech-to-text results.\n */\nexport type STTResultHandler = (\n result: STTResultMessage\n) => void | Promise<void>;\n\n/**\n * Event handler for text-to-speech audio data.\n */\nexport type TTSAudioHandler = (audio: ArrayBuffer) => void | Promise<void>;\n\n/**\n * Event handler for error messages.\n */\nexport type ErrorHandler = (error: ErrorMessage) => void | Promise<void>;\n\n/**\n * Event handler for participant messages.\n */\nexport type MessageHandler = (message: SaynaMessage) => void | Promise<void>;\n\n/**\n * Event handler for participant disconnections.\n */\nexport type ParticipantDisconnectedHandler = (\n participant: Participant\n) => void | Promise<void>;\n\n/**\n * Event handler for TTS playback completion.\n */\nexport type TTSPlaybackCompleteHandler = (\n timestamp: number\n) => void | Promise<void>;\n\n/**\n * Client for connecting to Sayna WebSocket server for real-time voice interactions.\n *\n * @example\n * ```typescript\n * const client = await saynaConnect(\n * \"https://api.sayna.ai\",\n * sttConfig,\n * ttsConfig\n * );\n *\n * client.registerOnSttResult((result) => {\n * console.log(\"Transcription:\", result.transcript);\n * });\n *\n * await client.speak(\"Hello, world!\");\n * ```\n */\nexport class SaynaClient {\n private url: string;\n private sttConfig?: STTConfig;\n private ttsConfig?: TTSConfig;\n private livekitConfig?: LiveKitConfig;\n private withoutAudio: boolean;\n private apiKey?: string;\n private websocket?: WebSocket;\n private isConnected: boolean = false;\n private isReady: boolean = false;\n private _livekitRoomName?: string;\n private _livekitUrl?: string;\n private _saynaParticipantIdentity?: string;\n private _saynaParticipantName?: string;\n private sttCallback?: STTResultHandler;\n private ttsCallback?: TTSAudioHandler;\n private errorCallback?: ErrorHandler;\n private messageCallback?: MessageHandler;\n private participantDisconnectedCallback?: ParticipantDisconnectedHandler;\n private ttsPlaybackCompleteCallback?: TTSPlaybackCompleteHandler;\n private readyPromiseResolve?: () => void;\n private readyPromiseReject?: (error: Error) => void;\n\n /**\n * Creates a new SaynaClient instance.\n *\n * @param url - The Sayna server URL (e.g., \"https://api.sayna.ai\")\n * @param sttConfig - Speech-to-text configuration (required when withoutAudio=false)\n * @param ttsConfig - Text-to-speech configuration (required when withoutAudio=false)\n * @param livekitConfig - Optional LiveKit room configuration\n * @param withoutAudio - If true, disables audio streaming (default: false)\n * @param apiKey - Optional API key used to authorize HTTP and WebSocket calls (defaults to SAYNA_API_KEY env)\n *\n * @throws {SaynaValidationError} If URL is invalid or if audio configs are missing when audio is enabled\n */\n constructor(\n url: string,\n sttConfig?: STTConfig,\n ttsConfig?: TTSConfig,\n livekitConfig?: LiveKitConfig,\n withoutAudio: boolean = false,\n apiKey?: string\n ) {\n // Validate URL\n if (!url || typeof url !== \"string\") {\n throw new SaynaValidationError(\"URL must be a non-empty string\");\n }\n if (\n !url.startsWith(\"http://\") &&\n !url.startsWith(\"https://\") &&\n !url.startsWith(\"ws://\") &&\n !url.startsWith(\"wss://\")\n ) {\n throw new SaynaValidationError(\n \"URL must start with http://, https://, ws://, or wss://\"\n );\n }\n\n // Validate audio config requirements\n if (!withoutAudio) {\n if (!sttConfig || !ttsConfig) {\n throw new SaynaValidationError(\n \"sttConfig and ttsConfig are required when withoutAudio=false (audio streaming enabled). \" +\n \"Either provide both configs or set withoutAudio=true for non-audio use cases.\"\n );\n }\n }\n\n this.url = url;\n this.sttConfig = sttConfig;\n this.ttsConfig = ttsConfig;\n this.livekitConfig = livekitConfig;\n this.withoutAudio = withoutAudio;\n this.apiKey = apiKey ?? process.env.SAYNA_API_KEY;\n }\n\n /**\n * Establishes connection to the Sayna WebSocket server.\n *\n * @throws {SaynaConnectionError} If connection fails\n * @returns Promise that resolves when the connection is ready\n */\n async connect(): Promise<void> {\n if (this.isConnected) {\n return;\n }\n\n // Convert HTTP(S) URL to WebSocket URL\n const wsUrl =\n this.url.replace(/^https:\\/\\//, \"wss://\").replace(/^http:\\/\\//, \"ws://\") +\n (this.url.endsWith(\"/\") ? \"ws\" : \"/ws\");\n\n return new Promise((resolve, reject) => {\n this.readyPromiseResolve = resolve;\n this.readyPromiseReject = reject;\n\n try {\n const headers = this.apiKey\n ? { Authorization: `Bearer ${this.apiKey}` }\n : undefined;\n\n const WebSocketConstructor = WebSocket as unknown as {\n new (\n url: string,\n protocols?: string | string[],\n options?: { headers?: Record<string, string> }\n ): WebSocket;\n };\n\n this.websocket = headers\n ? new WebSocketConstructor(wsUrl, undefined, { headers })\n : new WebSocket(wsUrl);\n\n this.websocket.onopen = () => {\n this.isConnected = true;\n\n // Send initial configuration\n const configMessage: ConfigMessage = {\n type: \"config\",\n stt_config: this.sttConfig,\n tts_config: this.ttsConfig,\n livekit: this.livekitConfig,\n audio: !this.withoutAudio,\n };\n\n try {\n if (this.websocket) {\n this.websocket.send(JSON.stringify(configMessage));\n }\n } catch (error) {\n this.cleanup();\n const err = new SaynaConnectionError(\n \"Failed to send configuration\",\n error\n );\n if (this.readyPromiseReject) {\n this.readyPromiseReject(err);\n }\n }\n };\n\n this.websocket.onmessage = async (event) => {\n try {\n if (\n event.data instanceof Blob ||\n event.data instanceof ArrayBuffer\n ) {\n // Binary TTS audio data\n const buffer =\n event.data instanceof Blob\n ? await event.data.arrayBuffer()\n : event.data;\n if (this.ttsCallback) {\n await this.ttsCallback(buffer);\n }\n } else {\n // JSON control messages\n if (typeof event.data !== \"string\") {\n throw new Error(\"Expected string data for JSON messages\");\n }\n const data = JSON.parse(event.data) as OutgoingMessage;\n await this.handleJsonMessage(data);\n }\n } catch (error) {\n // Log parse errors but don't break the connection\n if (this.errorCallback) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n await this.errorCallback({\n type: \"error\",\n message: `Failed to process message: ${errorMessage}`,\n });\n }\n }\n };\n\n this.websocket.onerror = () => {\n const error = new SaynaConnectionError(\"WebSocket connection error\");\n if (this.readyPromiseReject && !this.isReady) {\n this.readyPromiseReject(error);\n }\n };\n\n this.websocket.onclose = (event) => {\n const wasReady = this.isReady;\n this.cleanup();\n\n // If connection closed before ready, reject the promise\n if (!wasReady && this.readyPromiseReject) {\n this.readyPromiseReject(\n new SaynaConnectionError(\n `WebSocket closed before ready (code: ${event.code}, reason: ${event.reason || \"none\"})`\n )\n );\n }\n };\n } catch (error) {\n reject(new SaynaConnectionError(\"Failed to create WebSocket\", error));\n }\n });\n }\n\n /**\n * Handles incoming JSON messages from the WebSocket.\n * @internal\n */\n private async handleJsonMessage(data: OutgoingMessage): Promise<void> {\n const messageType = data.type;\n\n try {\n switch (messageType) {\n case \"ready\": {\n const readyMsg = data;\n this.isReady = true;\n this._livekitRoomName = readyMsg.livekit_room_name;\n this._livekitUrl = readyMsg.livekit_url;\n this._saynaParticipantIdentity = readyMsg.sayna_participant_identity;\n this._saynaParticipantName = readyMsg.sayna_participant_name;\n if (this.readyPromiseResolve) {\n this.readyPromiseResolve();\n }\n break;\n }\n\n case \"stt_result\": {\n const sttResult = data;\n if (this.sttCallback) {\n await this.sttCallback(sttResult);\n }\n break;\n }\n\n case \"error\": {\n const errorMsg = data;\n if (this.errorCallback) {\n await this.errorCallback(errorMsg);\n }\n if (this.readyPromiseReject && !this.isReady) {\n this.readyPromiseReject(new SaynaServerError(errorMsg.message));\n }\n break;\n }\n\n case \"message\": {\n const messageData = data;\n if (this.messageCallback) {\n await this.messageCallback(messageData.message);\n }\n break;\n }\n\n case \"participant_disconnected\": {\n const participantMsg = data;\n if (this.participantDisconnectedCallback) {\n await this.participantDisconnectedCallback(\n participantMsg.participant\n );\n }\n break;\n }\n\n case \"tts_playback_complete\": {\n const ttsPlaybackCompleteMsg = data;\n if (this.ttsPlaybackCompleteCallback) {\n await this.ttsPlaybackCompleteCallback(\n ttsPlaybackCompleteMsg.timestamp\n );\n }\n break;\n }\n }\n } catch (error) {\n // Notify error callback if handler fails\n if (this.errorCallback) {\n await this.errorCallback({\n type: \"error\",\n message: `Handler error: ${error instanceof Error ? error.message : String(error)}`,\n });\n }\n }\n }\n\n /**\n * Cleans up internal state.\n * @internal\n */\n private cleanup(): void {\n this.isConnected = false;\n this.isReady = false;\n this._livekitRoomName = undefined;\n this._livekitUrl = undefined;\n this._saynaParticipantIdentity = undefined;\n this._saynaParticipantName = undefined;\n }\n\n /**\n * Converts WebSocket URL to HTTP URL for REST API calls.\n * @internal\n */\n private getHttpUrl(): string {\n return this.url\n .replace(/^ws:\\/\\//, \"http://\")\n .replace(/^wss:\\/\\//, \"https://\");\n }\n\n /**\n * Generic fetch helper for making REST API calls to Sayna server.\n * Handles URL construction, headers, error responses, and type conversion.\n * @internal\n *\n * @param endpoint - API endpoint path (e.g., \"/voices\", \"/speak\")\n * @param options - Fetch options including method, body, headers, etc.\n * @param responseType - Expected response type: \"json\" or \"arrayBuffer\"\n * @returns Promise resolving to the parsed response\n * @throws {SaynaConnectionError} If the network request fails\n * @throws {SaynaServerError} If the server returns an error response\n */\n private async fetchFromSayna<T>(\n endpoint: string,\n options: RequestInit = {},\n responseType: \"json\" | \"arrayBuffer\" = \"json\"\n ): Promise<T> {\n const httpUrl = this.getHttpUrl();\n const url = `${httpUrl}${httpUrl.endsWith(\"/\") ? \"\" : \"/\"}${endpoint.startsWith(\"/\") ? endpoint.slice(1) : endpoint}`;\n\n // Merge default headers with user-provided headers\n const headers: Record<string, string> = {\n ...(options.headers as Record<string, string>),\n };\n\n // Add Authorization header when an API key is provided, unless user supplied one\n const hasAuthHeader = Object.keys(headers).some(\n (key) => key.toLowerCase() === \"authorization\"\n );\n if (this.apiKey && !hasAuthHeader) {\n headers[\"Authorization\"] = `Bearer ${this.apiKey}`;\n }\n\n // Add Content-Type for JSON requests if not already set\n if (options.method === \"POST\" && options.body && !headers[\"Content-Type\"]) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n try {\n const response = await fetch(url, {\n ...options,\n headers,\n });\n\n if (!response.ok) {\n // Try to parse error message from JSON response\n let errorMessage: string;\n try {\n const errorData: unknown = await response.json();\n errorMessage =\n errorData &&\n typeof errorData === \"object\" &&\n \"error\" in errorData &&\n typeof errorData.error === \"string\"\n ? errorData.error\n : `Request failed: ${response.status} ${response.statusText}`;\n } catch {\n errorMessage = `Request failed: ${response.status} ${response.statusText}`;\n }\n throw new SaynaServerError(errorMessage);\n }\n\n // Parse response based on expected type\n if (responseType === \"arrayBuffer\") {\n return (await response.arrayBuffer()) as T;\n } else {\n return (await response.json()) as T;\n }\n } catch (error) {\n // Re-throw SaynaServerError as-is\n if (error instanceof SaynaServerError) {\n throw error;\n }\n // Wrap other errors in SaynaConnectionError\n throw new SaynaConnectionError(`Failed to fetch from ${endpoint}`, error);\n }\n }\n\n /**\n * Disconnects from the Sayna WebSocket server and cleans up resources.\n */\n disconnect(): void {\n if (this.websocket) {\n // Remove event handlers to prevent memory leaks\n this.websocket.onopen = null;\n this.websocket.onmessage = null;\n this.websocket.onerror = null;\n this.websocket.onclose = null;\n\n if (this.websocket.readyState === WebSocket.OPEN) {\n this.websocket.close(1000, \"Client disconnect\");\n }\n\n this.websocket = undefined;\n }\n\n this.cleanup();\n }\n\n /**\n * Sends audio data to the server for speech recognition.\n *\n * @param audioData - Raw audio data as ArrayBuffer\n * @throws {SaynaNotConnectedError} If not connected\n * @throws {SaynaNotReadyError} If connection is not ready\n */\n onAudioInput(audioData: ArrayBuffer): void {\n if (!this.isConnected || !this.websocket) {\n throw new SaynaNotConnectedError();\n }\n\n if (!this.isReady) {\n throw new SaynaNotReadyError();\n }\n\n if (!(audioData instanceof ArrayBuffer)) {\n throw new SaynaValidationError(\"audioData must be an ArrayBuffer\");\n }\n\n if (audioData.byteLength === 0) {\n throw new SaynaValidationError(\"audioData cannot be empty\");\n }\n\n try {\n this.websocket.send(audioData);\n } catch (error) {\n throw new SaynaConnectionError(\"Failed to send audio data\", error);\n }\n }\n\n /**\n * Registers a callback for speech-to-text results.\n *\n * @param callback - Function to call when STT results are received\n */\n registerOnSttResult(callback: STTResultHandler): void {\n this.sttCallback = callback;\n }\n\n /**\n * Registers a callback for text-to-speech audio data.\n *\n * @param callback - Function to call when TTS audio is received\n */\n registerOnTtsAudio(callback: TTSAudioHandler): void {\n this.ttsCallback = callback;\n }\n\n /**\n * Registers a callback for error messages.\n *\n * @param callback - Function to call when errors occur\n */\n registerOnError(callback: ErrorHandler): void {\n this.errorCallback = callback;\n }\n\n /**\n * Registers a callback for participant messages.\n *\n * @param callback - Function to call when messages are received\n */\n registerOnMessage(callback: MessageHandler): void {\n this.messageCallback = callback;\n }\n\n /**\n * Registers a callback for participant disconnection events.\n *\n * @param callback - Function to call when a participant disconnects\n */\n registerOnParticipantDisconnected(\n callback: ParticipantDisconnectedHandler\n ): void {\n this.participantDisconnectedCallback = callback;\n }\n\n /**\n * Registers a callback for TTS playback completion.\n *\n * @param callback - Function to call when TTS playback is complete\n */\n registerOnTtsPlaybackComplete(callback: TTSPlaybackCompleteHandler): void {\n this.ttsPlaybackCompleteCallback = callback;\n }\n\n /**\n * Sends text to be synthesized as speech.\n *\n * @param text - Text to synthesize\n * @param flush - Whether to flush the TTS queue before speaking (default: true)\n * @param allowInterruption - Whether this speech can be interrupted (default: true)\n * @throws {SaynaNotConnectedError} If not connected\n * @throws {SaynaNotReadyError} If connection is not ready\n * @throws {SaynaValidationError} If text is not a string\n */\n speak(\n text: string,\n flush: boolean = true,\n allowInterruption: boolean = true\n ): void {\n if (!this.isConnected || !this.websocket) {\n throw new SaynaNotConnectedError();\n }\n\n if (!this.isReady) {\n throw new SaynaNotReadyError();\n }\n\n if (typeof text !== \"string\") {\n throw new SaynaValidationError(\"text must be a string\");\n }\n\n try {\n const speakMessage: SpeakMessage = {\n type: \"speak\",\n text,\n flush,\n allow_interruption: allowInterruption,\n };\n this.websocket.send(JSON.stringify(speakMessage));\n } catch (error) {\n throw new SaynaConnectionError(\"Failed to send speak command\", error);\n }\n }\n\n /**\n * Clears the text-to-speech queue.\n *\n * @throws {SaynaNotConnectedError} If not connected\n * @throws {SaynaNotReadyError} If connection is not ready\n */\n clear(): void {\n if (!this.isConnected || !this.websocket) {\n throw new SaynaNotConnectedError();\n }\n\n if (!this.isReady) {\n throw new SaynaNotReadyError();\n }\n\n try {\n const clearMessage: ClearMessage = {\n type: \"clear\",\n };\n this.websocket.send(JSON.stringify(clearMessage));\n } catch (error) {\n throw new SaynaConnectionError(\"Failed to send clear command\", error);\n }\n }\n\n /**\n * Flushes the TTS queue by sending an empty speak command.\n *\n * @param allowInterruption - Whether the flush can be interrupted (default: true)\n * @throws {SaynaNotConnectedError} If not connected\n * @throws {SaynaNotReadyError} If connection is not ready\n */\n ttsFlush(allowInterruption: boolean = true): void {\n this.speak(\"\", true, allowInterruption);\n }\n\n /**\n * Sends a message to the Sayna session.\n *\n * @param message - Message content\n * @param role - Message role (e.g., \"user\", \"assistant\")\n * @param topic - Optional topic identifier\n * @param debug - Optional debug metadata\n * @throws {SaynaNotConnectedError} If not connected\n * @throws {SaynaNotReadyError} If connection is not ready\n * @throws {SaynaValidationError} If parameters are invalid\n */\n sendMessage(\n message: string,\n role: string,\n topic?: string,\n debug?: Record<string, unknown>\n ): void {\n if (!this.isConnected || !this.websocket) {\n throw new SaynaNotConnectedError();\n }\n\n if (!this.isReady) {\n throw new SaynaNotReadyError();\n }\n\n if (typeof message !== \"string\") {\n throw new SaynaValidationError(\"message must be a string\");\n }\n\n if (typeof role !== \"string\") {\n throw new SaynaValidationError(\"role must be a string\");\n }\n\n try {\n const sendMsg: SendMessageMessage = {\n type: \"send_message\",\n message,\n role,\n topic,\n debug,\n };\n this.websocket.send(JSON.stringify(sendMsg));\n } catch (error) {\n throw new SaynaConnectionError(\"Failed to send message\", error);\n }\n }\n\n /**\n * Performs a health check on the Sayna server.\n *\n * @returns Promise that resolves with the health status\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error\n *\n * @example\n * ```typescript\n * const health = await client.health();\n * console.log(health.status); // \"OK\"\n * ```\n */\n async health(): Promise<HealthResponse> {\n return this.fetchFromSayna<HealthResponse>(\"\");\n }\n\n /**\n * Retrieves the catalogue of text-to-speech voices grouped by provider.\n *\n * @returns Promise that resolves with voices organized by provider\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error\n *\n * @example\n * ```typescript\n * const voices = await client.getVoices();\n * for (const [provider, voiceList] of Object.entries(voices)) {\n * console.log(`${provider}:`, voiceList.map(v => v.name));\n * }\n * ```\n */\n async getVoices(): Promise<VoicesResponse> {\n return this.fetchFromSayna<VoicesResponse>(\"voices\");\n }\n\n /**\n * Synthesizes text into audio using the REST API endpoint.\n * This is a standalone synthesis method that doesn't require an active WebSocket connection.\n *\n * @param text - Text to synthesize\n * @param ttsConfig - Text-to-speech configuration\n * @returns Promise that resolves with the audio data as ArrayBuffer\n * @throws {SaynaValidationError} If text is empty\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error\n *\n * @example\n * ```typescript\n * const audioBuffer = await client.speakRest(\"Hello, world!\", {\n * provider: \"elevenlabs\",\n * voice_id: \"21m00Tcm4TlvDq8ikWAM\",\n * model: \"eleven_turbo_v2\",\n * speaking_rate: 1.0,\n * audio_format: \"mp3\",\n * sample_rate: 24000,\n * connection_timeout: 30,\n * request_timeout: 60,\n * pronunciations: []\n * });\n * ```\n */\n async speakRest(text: string, ttsConfig: TTSConfig): Promise<ArrayBuffer> {\n if (!text || text.trim().length === 0) {\n throw new SaynaValidationError(\"Text cannot be empty\");\n }\n\n return this.fetchFromSayna<ArrayBuffer>(\n \"speak\",\n {\n method: \"POST\",\n body: JSON.stringify({\n text,\n tts_config: ttsConfig,\n }),\n },\n \"arrayBuffer\"\n );\n }\n\n /**\n * Issues a LiveKit access token for a participant.\n *\n * @param roomName - LiveKit room to join or create\n * @param participantName - Display name assigned to the participant\n * @param participantIdentity - Unique identifier for the participant\n * @returns Promise that resolves with the LiveKit token and connection details\n * @throws {SaynaValidationError} If any parameter is empty\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error\n *\n * @example\n * ```typescript\n * const tokenInfo = await client.getLiveKitToken(\n * \"my-room\",\n * \"John Doe\",\n * \"user-123\"\n * );\n * console.log(\"Token:\", tokenInfo.token);\n * console.log(\"LiveKit URL:\", tokenInfo.livekit_url);\n * ```\n */\n async getLiveKitToken(\n roomName: string,\n participantName: string,\n participantIdentity: string\n ): Promise<LiveKitTokenResponse> {\n if (!roomName || roomName.trim().length === 0) {\n throw new SaynaValidationError(\"room_name cannot be empty\");\n }\n\n if (!participantName || participantName.trim().length === 0) {\n throw new SaynaValidationError(\"participant_name cannot be empty\");\n }\n\n if (!participantIdentity || participantIdentity.trim().length === 0) {\n throw new SaynaValidationError(\"participant_identity cannot be empty\");\n }\n\n return this.fetchFromSayna<LiveKitTokenResponse>(\"livekit/token\", {\n method: \"POST\",\n body: JSON.stringify({\n room_name: roomName,\n participant_name: participantName,\n participant_identity: participantIdentity,\n }),\n });\n }\n\n /**\n * Retrieves all configured SIP webhook hooks from the runtime cache.\n *\n * @returns Promise that resolves with the list of configured SIP hooks\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error (e.g., 500 if reading cache fails)\n *\n * @example\n * ```typescript\n * const response = await client.getSipHooks();\n * for (const hook of response.hooks) {\n * console.log(`Host: ${hook.host}, URL: ${hook.url}`);\n * }\n * ```\n */\n async getSipHooks(): Promise<SipHooksResponse> {\n return this.fetchFromSayna<SipHooksResponse>(\"sip/hooks\");\n }\n\n /**\n * Sets or updates SIP webhook hooks in the runtime cache.\n *\n * Hooks with matching hosts will be replaced; new hosts will be added.\n * The response contains the merged list of all hooks (existing + new).\n *\n * @param hooks - Array of SIP hook configurations to add or replace\n * @returns Promise that resolves with the merged list of all configured hooks\n * @throws {SaynaValidationError} If hooks array is empty or contains invalid entries\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error (e.g., 400 for duplicate hosts, 500 for cache errors)\n *\n * @example\n * ```typescript\n * const response = await client.setSipHooks([\n * { host: \"example.com\", url: \"https://webhook.example.com/events\" },\n * { host: \"another.com\", url: \"https://webhook.another.com/events\" }\n * ]);\n * console.log(\"Total hooks configured:\", response.hooks.length);\n * ```\n */\n async setSipHooks(hooks: SipHook[]): Promise<SipHooksResponse> {\n if (!Array.isArray(hooks)) {\n throw new SaynaValidationError(\"hooks must be an array\");\n }\n\n if (hooks.length === 0) {\n throw new SaynaValidationError(\"hooks array cannot be empty\");\n }\n\n for (const [i, hook] of hooks.entries()) {\n if (!hook.host || typeof hook.host !== \"string\" || hook.host.trim().length === 0) {\n throw new SaynaValidationError(`hooks[${i}].host must be a non-empty string`);\n }\n if (!hook.url || typeof hook.url !== \"string\" || hook.url.trim().length === 0) {\n throw new SaynaValidationError(`hooks[${i}].url must be a non-empty string`);\n }\n }\n\n return this.fetchFromSayna<SipHooksResponse>(\"sip/hooks\", {\n method: \"POST\",\n body: JSON.stringify({ hooks }),\n });\n }\n\n /**\n * Deletes SIP webhook hooks by host name from the runtime cache.\n *\n * If a deleted host exists in the original server configuration,\n * it will revert to its config value after deletion.\n *\n * @param hosts - Array of host names to remove (case-insensitive)\n * @returns Promise that resolves with the updated list of hooks after deletion\n * @throws {SaynaValidationError} If hosts array is empty or contains invalid entries\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error (e.g., 400 for empty hosts, 500 for cache errors)\n *\n * @example\n * ```typescript\n * const response = await client.deleteSipHooks([\"example.com\", \"another.com\"]);\n * console.log(\"Remaining hooks:\", response.hooks.length);\n * ```\n */\n async deleteSipHooks(hosts: string[]): Promise<SipHooksResponse> {\n if (!Array.isArray(hosts)) {\n throw new SaynaValidationError(\"hosts must be an array\");\n }\n\n if (hosts.length === 0) {\n throw new SaynaValidationError(\"hosts array cannot be empty\");\n }\n\n for (const [i, host] of hosts.entries()) {\n if (!host || typeof host !== \"string\" || host.trim().length === 0) {\n throw new SaynaValidationError(`hosts[${i}] must be a non-empty string`);\n }\n }\n\n return this.fetchFromSayna<SipHooksResponse>(\"sip/hooks\", {\n method: \"DELETE\",\n body: JSON.stringify({ hosts }),\n });\n }\n\n /**\n * Whether the client is ready to send/receive data.\n */\n get ready(): boolean {\n return this.isReady;\n }\n\n /**\n * Whether the client is connected to the WebSocket.\n */\n get connected(): boolean {\n return this.isConnected;\n }\n\n /**\n * LiveKit room name acknowledged by the server, if available.\n */\n get livekitRoomName(): string | undefined {\n return this._livekitRoomName;\n }\n\n /**\n * LiveKit WebSocket URL configured on the server, if available.\n */\n get livekitUrl(): string | undefined {\n return this._livekitUrl;\n }\n\n /**\n * Identity assigned to the agent participant when LiveKit is enabled, if available.\n */\n get saynaParticipantIdentity(): string | undefined {\n return this._saynaParticipantIdentity;\n }\n\n /**\n * Display name assigned to the agent participant when LiveKit is enabled, if available.\n */\n get saynaParticipantName(): string | undefined {\n return this._saynaParticipantName;\n }\n}\n",
6
+ "import type {\n STTConfig,\n TTSConfig,\n LiveKitConfig,\n ConfigMessage,\n SpeakMessage,\n ClearMessage,\n SendMessageMessage,\n STTResultMessage,\n ErrorMessage,\n OutgoingMessage,\n SaynaMessage,\n Participant,\n VoicesResponse,\n HealthResponse,\n LiveKitTokenResponse,\n SipHook,\n SipHooksResponse,\n} from \"./types\";\nimport {\n SaynaNotConnectedError,\n SaynaNotReadyError,\n SaynaConnectionError,\n SaynaValidationError,\n SaynaServerError,\n} from \"./errors\";\n\n// Node.js 18+ has native fetch support\ndeclare const fetch: typeof globalThis.fetch;\n\n/**\n * Event handler for speech-to-text results.\n */\nexport type STTResultHandler = (\n result: STTResultMessage\n) => void | Promise<void>;\n\n/**\n * Event handler for text-to-speech audio data.\n */\nexport type TTSAudioHandler = (audio: ArrayBuffer) => void | Promise<void>;\n\n/**\n * Event handler for error messages.\n */\nexport type ErrorHandler = (error: ErrorMessage) => void | Promise<void>;\n\n/**\n * Event handler for participant messages.\n */\nexport type MessageHandler = (message: SaynaMessage) => void | Promise<void>;\n\n/**\n * Event handler for participant disconnections.\n */\nexport type ParticipantDisconnectedHandler = (\n participant: Participant\n) => void | Promise<void>;\n\n/**\n * Event handler for TTS playback completion.\n */\nexport type TTSPlaybackCompleteHandler = (\n timestamp: number\n) => void | Promise<void>;\n\n/**\n * Client for connecting to Sayna WebSocket server for real-time voice interactions.\n *\n * @example\n * ```typescript\n * const client = await saynaConnect(\n * \"https://api.sayna.ai\",\n * sttConfig,\n * ttsConfig\n * );\n *\n * client.registerOnSttResult((result) => {\n * console.log(\"Transcription:\", result.transcript);\n * });\n *\n * await client.speak(\"Hello, world!\");\n * ```\n */\nexport class SaynaClient {\n private url: string;\n private sttConfig?: STTConfig;\n private ttsConfig?: TTSConfig;\n private livekitConfig?: LiveKitConfig;\n private withoutAudio: boolean;\n private apiKey?: string;\n private websocket?: WebSocket;\n private isConnected: boolean = false;\n private isReady: boolean = false;\n private _livekitRoomName?: string;\n private _livekitUrl?: string;\n private _saynaParticipantIdentity?: string;\n private _saynaParticipantName?: string;\n private _streamId?: string;\n private inputStreamId?: string;\n private sttCallback?: STTResultHandler;\n private ttsCallback?: TTSAudioHandler;\n private errorCallback?: ErrorHandler;\n private messageCallback?: MessageHandler;\n private participantDisconnectedCallback?: ParticipantDisconnectedHandler;\n private ttsPlaybackCompleteCallback?: TTSPlaybackCompleteHandler;\n private readyPromiseResolve?: () => void;\n private readyPromiseReject?: (error: Error) => void;\n\n /**\n * Creates a new SaynaClient instance.\n *\n * @param url - The Sayna server URL (e.g., \"https://api.sayna.ai\")\n * @param sttConfig - Speech-to-text configuration (required when withoutAudio=false)\n * @param ttsConfig - Text-to-speech configuration (required when withoutAudio=false)\n * @param livekitConfig - Optional LiveKit room configuration\n * @param withoutAudio - If true, disables audio streaming (default: false)\n * @param apiKey - Optional API key used to authorize HTTP and WebSocket calls (defaults to SAYNA_API_KEY env)\n * @param streamId - Optional session identifier for recording paths; server generates a UUID when omitted\n *\n * @throws {SaynaValidationError} If URL is invalid or if audio configs are missing when audio is enabled\n */\n constructor(\n url: string,\n sttConfig?: STTConfig,\n ttsConfig?: TTSConfig,\n livekitConfig?: LiveKitConfig,\n withoutAudio: boolean = false,\n apiKey?: string,\n streamId?: string\n ) {\n // Validate URL\n if (!url || typeof url !== \"string\") {\n throw new SaynaValidationError(\"URL must be a non-empty string\");\n }\n if (\n !url.startsWith(\"http://\") &&\n !url.startsWith(\"https://\") &&\n !url.startsWith(\"ws://\") &&\n !url.startsWith(\"wss://\")\n ) {\n throw new SaynaValidationError(\n \"URL must start with http://, https://, ws://, or wss://\"\n );\n }\n\n // Validate audio config requirements\n if (!withoutAudio) {\n if (!sttConfig || !ttsConfig) {\n throw new SaynaValidationError(\n \"sttConfig and ttsConfig are required when withoutAudio=false (audio streaming enabled). \" +\n \"Either provide both configs or set withoutAudio=true for non-audio use cases.\"\n );\n }\n }\n\n this.url = url;\n this.sttConfig = sttConfig;\n this.ttsConfig = ttsConfig;\n this.livekitConfig = livekitConfig;\n this.withoutAudio = withoutAudio;\n this.apiKey = apiKey ?? process.env.SAYNA_API_KEY;\n this.inputStreamId = streamId;\n }\n\n /**\n * Establishes connection to the Sayna WebSocket server.\n *\n * @throws {SaynaConnectionError} If connection fails\n * @returns Promise that resolves when the connection is ready\n */\n async connect(): Promise<void> {\n if (this.isConnected) {\n return;\n }\n\n // Convert HTTP(S) URL to WebSocket URL\n const wsUrl =\n this.url.replace(/^https:\\/\\//, \"wss://\").replace(/^http:\\/\\//, \"ws://\") +\n (this.url.endsWith(\"/\") ? \"ws\" : \"/ws\");\n\n return new Promise((resolve, reject) => {\n this.readyPromiseResolve = resolve;\n this.readyPromiseReject = reject;\n\n try {\n const headers = this.apiKey\n ? { Authorization: `Bearer ${this.apiKey}` }\n : undefined;\n\n const WebSocketConstructor = WebSocket as unknown as {\n new (\n url: string,\n protocols?: string | string[],\n options?: { headers?: Record<string, string> }\n ): WebSocket;\n };\n\n this.websocket = headers\n ? new WebSocketConstructor(wsUrl, undefined, { headers })\n : new WebSocket(wsUrl);\n\n this.websocket.onopen = () => {\n this.isConnected = true;\n\n // Send initial configuration\n const configMessage: ConfigMessage = {\n type: \"config\",\n stream_id: this.inputStreamId,\n stt_config: this.sttConfig,\n tts_config: this.ttsConfig,\n livekit: this.livekitConfig,\n audio: !this.withoutAudio,\n };\n\n try {\n if (this.websocket) {\n this.websocket.send(JSON.stringify(configMessage));\n }\n } catch (error) {\n this.cleanup();\n const err = new SaynaConnectionError(\n \"Failed to send configuration\",\n error\n );\n if (this.readyPromiseReject) {\n this.readyPromiseReject(err);\n }\n }\n };\n\n this.websocket.onmessage = async (event) => {\n try {\n if (\n event.data instanceof Blob ||\n event.data instanceof ArrayBuffer\n ) {\n // Binary TTS audio data\n const buffer =\n event.data instanceof Blob\n ? await event.data.arrayBuffer()\n : event.data;\n if (this.ttsCallback) {\n await this.ttsCallback(buffer);\n }\n } else {\n // JSON control messages\n if (typeof event.data !== \"string\") {\n throw new Error(\"Expected string data for JSON messages\");\n }\n const data = JSON.parse(event.data) as OutgoingMessage;\n await this.handleJsonMessage(data);\n }\n } catch (error) {\n // Log parse errors but don't break the connection\n if (this.errorCallback) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n await this.errorCallback({\n type: \"error\",\n message: `Failed to process message: ${errorMessage}`,\n });\n }\n }\n };\n\n this.websocket.onerror = () => {\n const error = new SaynaConnectionError(\"WebSocket connection error\");\n if (this.readyPromiseReject && !this.isReady) {\n this.readyPromiseReject(error);\n }\n };\n\n this.websocket.onclose = (event) => {\n const wasReady = this.isReady;\n this.cleanup();\n\n // If connection closed before ready, reject the promise\n if (!wasReady && this.readyPromiseReject) {\n this.readyPromiseReject(\n new SaynaConnectionError(\n `WebSocket closed before ready (code: ${event.code}, reason: ${event.reason || \"none\"})`\n )\n );\n }\n };\n } catch (error) {\n reject(new SaynaConnectionError(\"Failed to create WebSocket\", error));\n }\n });\n }\n\n /**\n * Handles incoming JSON messages from the WebSocket.\n * @internal\n */\n private async handleJsonMessage(data: OutgoingMessage): Promise<void> {\n const messageType = data.type;\n\n try {\n switch (messageType) {\n case \"ready\": {\n const readyMsg = data;\n this.isReady = true;\n this._livekitRoomName = readyMsg.livekit_room_name;\n this._livekitUrl = readyMsg.livekit_url;\n this._saynaParticipantIdentity = readyMsg.sayna_participant_identity;\n this._saynaParticipantName = readyMsg.sayna_participant_name;\n this._streamId = readyMsg.stream_id;\n if (this.readyPromiseResolve) {\n this.readyPromiseResolve();\n }\n break;\n }\n\n case \"stt_result\": {\n const sttResult = data;\n if (this.sttCallback) {\n await this.sttCallback(sttResult);\n }\n break;\n }\n\n case \"error\": {\n const errorMsg = data;\n if (this.errorCallback) {\n await this.errorCallback(errorMsg);\n }\n if (this.readyPromiseReject && !this.isReady) {\n this.readyPromiseReject(new SaynaServerError(errorMsg.message));\n }\n break;\n }\n\n case \"message\": {\n const messageData = data;\n if (this.messageCallback) {\n await this.messageCallback(messageData.message);\n }\n break;\n }\n\n case \"participant_disconnected\": {\n const participantMsg = data;\n if (this.participantDisconnectedCallback) {\n await this.participantDisconnectedCallback(\n participantMsg.participant\n );\n }\n break;\n }\n\n case \"tts_playback_complete\": {\n const ttsPlaybackCompleteMsg = data;\n if (this.ttsPlaybackCompleteCallback) {\n await this.ttsPlaybackCompleteCallback(\n ttsPlaybackCompleteMsg.timestamp\n );\n }\n break;\n }\n }\n } catch (error) {\n // Notify error callback if handler fails\n if (this.errorCallback) {\n await this.errorCallback({\n type: \"error\",\n message: `Handler error: ${error instanceof Error ? error.message : String(error)}`,\n });\n }\n }\n }\n\n /**\n * Cleans up internal state.\n * @internal\n */\n private cleanup(): void {\n this.isConnected = false;\n this.isReady = false;\n this._livekitRoomName = undefined;\n this._livekitUrl = undefined;\n this._saynaParticipantIdentity = undefined;\n this._saynaParticipantName = undefined;\n this._streamId = undefined;\n }\n\n /**\n * Converts WebSocket URL to HTTP URL for REST API calls.\n * @internal\n */\n private getHttpUrl(): string {\n return this.url\n .replace(/^ws:\\/\\//, \"http://\")\n .replace(/^wss:\\/\\//, \"https://\");\n }\n\n /**\n * Generic fetch helper for making REST API calls to Sayna server.\n * Handles URL construction, headers, error responses, and type conversion.\n * @internal\n *\n * @param endpoint - API endpoint path (e.g., \"/voices\", \"/speak\")\n * @param options - Fetch options including method, body, headers, etc.\n * @param responseType - Expected response type: \"json\" or \"arrayBuffer\"\n * @returns Promise resolving to the parsed response\n * @throws {SaynaConnectionError} If the network request fails\n * @throws {SaynaServerError} If the server returns an error response\n */\n private async fetchFromSayna<T>(\n endpoint: string,\n options: RequestInit = {},\n responseType: \"json\" | \"arrayBuffer\" = \"json\"\n ): Promise<T> {\n const httpUrl = this.getHttpUrl();\n const url = `${httpUrl}${httpUrl.endsWith(\"/\") ? \"\" : \"/\"}${endpoint.startsWith(\"/\") ? endpoint.slice(1) : endpoint}`;\n\n // Merge default headers with user-provided headers\n const headers: Record<string, string> = {\n ...(options.headers as Record<string, string>),\n };\n\n // Add Authorization header when an API key is provided, unless user supplied one\n const hasAuthHeader = Object.keys(headers).some(\n (key) => key.toLowerCase() === \"authorization\"\n );\n if (this.apiKey && !hasAuthHeader) {\n headers[\"Authorization\"] = `Bearer ${this.apiKey}`;\n }\n\n // Add Content-Type for JSON requests if not already set\n if (options.method === \"POST\" && options.body && !headers[\"Content-Type\"]) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n try {\n const response = await fetch(url, {\n ...options,\n headers,\n });\n\n if (!response.ok) {\n // Try to parse error message from JSON response\n let errorMessage: string;\n try {\n const errorData: unknown = await response.json();\n errorMessage =\n errorData &&\n typeof errorData === \"object\" &&\n \"error\" in errorData &&\n typeof errorData.error === \"string\"\n ? errorData.error\n : `Request failed: ${response.status} ${response.statusText}`;\n } catch {\n errorMessage = `Request failed: ${response.status} ${response.statusText}`;\n }\n throw new SaynaServerError(errorMessage);\n }\n\n // Parse response based on expected type\n if (responseType === \"arrayBuffer\") {\n return (await response.arrayBuffer()) as T;\n } else {\n return (await response.json()) as T;\n }\n } catch (error) {\n // Re-throw SaynaServerError as-is\n if (error instanceof SaynaServerError) {\n throw error;\n }\n // Wrap other errors in SaynaConnectionError\n throw new SaynaConnectionError(`Failed to fetch from ${endpoint}`, error);\n }\n }\n\n /**\n * Disconnects from the Sayna WebSocket server and cleans up resources.\n */\n disconnect(): void {\n if (this.websocket) {\n // Remove event handlers to prevent memory leaks\n this.websocket.onopen = null;\n this.websocket.onmessage = null;\n this.websocket.onerror = null;\n this.websocket.onclose = null;\n\n if (this.websocket.readyState === WebSocket.OPEN) {\n this.websocket.close(1000, \"Client disconnect\");\n }\n\n this.websocket = undefined;\n }\n\n this.cleanup();\n }\n\n /**\n * Sends audio data to the server for speech recognition.\n *\n * @param audioData - Raw audio data as ArrayBuffer\n * @throws {SaynaNotConnectedError} If not connected\n * @throws {SaynaNotReadyError} If connection is not ready\n */\n onAudioInput(audioData: ArrayBuffer): void {\n if (!this.isConnected || !this.websocket) {\n throw new SaynaNotConnectedError();\n }\n\n if (!this.isReady) {\n throw new SaynaNotReadyError();\n }\n\n if (!(audioData instanceof ArrayBuffer)) {\n throw new SaynaValidationError(\"audioData must be an ArrayBuffer\");\n }\n\n if (audioData.byteLength === 0) {\n throw new SaynaValidationError(\"audioData cannot be empty\");\n }\n\n try {\n this.websocket.send(audioData);\n } catch (error) {\n throw new SaynaConnectionError(\"Failed to send audio data\", error);\n }\n }\n\n /**\n * Registers a callback for speech-to-text results.\n *\n * @param callback - Function to call when STT results are received\n */\n registerOnSttResult(callback: STTResultHandler): void {\n this.sttCallback = callback;\n }\n\n /**\n * Registers a callback for text-to-speech audio data.\n *\n * @param callback - Function to call when TTS audio is received\n */\n registerOnTtsAudio(callback: TTSAudioHandler): void {\n this.ttsCallback = callback;\n }\n\n /**\n * Registers a callback for error messages.\n *\n * @param callback - Function to call when errors occur\n */\n registerOnError(callback: ErrorHandler): void {\n this.errorCallback = callback;\n }\n\n /**\n * Registers a callback for participant messages.\n *\n * @param callback - Function to call when messages are received\n */\n registerOnMessage(callback: MessageHandler): void {\n this.messageCallback = callback;\n }\n\n /**\n * Registers a callback for participant disconnection events.\n *\n * @param callback - Function to call when a participant disconnects\n */\n registerOnParticipantDisconnected(\n callback: ParticipantDisconnectedHandler\n ): void {\n this.participantDisconnectedCallback = callback;\n }\n\n /**\n * Registers a callback for TTS playback completion.\n *\n * @param callback - Function to call when TTS playback is complete\n */\n registerOnTtsPlaybackComplete(callback: TTSPlaybackCompleteHandler): void {\n this.ttsPlaybackCompleteCallback = callback;\n }\n\n /**\n * Sends text to be synthesized as speech.\n *\n * @param text - Text to synthesize\n * @param flush - Whether to flush the TTS queue before speaking (default: true)\n * @param allowInterruption - Whether this speech can be interrupted (default: true)\n * @throws {SaynaNotConnectedError} If not connected\n * @throws {SaynaNotReadyError} If connection is not ready\n * @throws {SaynaValidationError} If text is not a string\n */\n speak(\n text: string,\n flush: boolean = true,\n allowInterruption: boolean = true\n ): void {\n if (!this.isConnected || !this.websocket) {\n throw new SaynaNotConnectedError();\n }\n\n if (!this.isReady) {\n throw new SaynaNotReadyError();\n }\n\n if (typeof text !== \"string\") {\n throw new SaynaValidationError(\"text must be a string\");\n }\n\n try {\n const speakMessage: SpeakMessage = {\n type: \"speak\",\n text,\n flush,\n allow_interruption: allowInterruption,\n };\n this.websocket.send(JSON.stringify(speakMessage));\n } catch (error) {\n throw new SaynaConnectionError(\"Failed to send speak command\", error);\n }\n }\n\n /**\n * Clears the text-to-speech queue.\n *\n * @throws {SaynaNotConnectedError} If not connected\n * @throws {SaynaNotReadyError} If connection is not ready\n */\n clear(): void {\n if (!this.isConnected || !this.websocket) {\n throw new SaynaNotConnectedError();\n }\n\n if (!this.isReady) {\n throw new SaynaNotReadyError();\n }\n\n try {\n const clearMessage: ClearMessage = {\n type: \"clear\",\n };\n this.websocket.send(JSON.stringify(clearMessage));\n } catch (error) {\n throw new SaynaConnectionError(\"Failed to send clear command\", error);\n }\n }\n\n /**\n * Flushes the TTS queue by sending an empty speak command.\n *\n * @param allowInterruption - Whether the flush can be interrupted (default: true)\n * @throws {SaynaNotConnectedError} If not connected\n * @throws {SaynaNotReadyError} If connection is not ready\n */\n ttsFlush(allowInterruption: boolean = true): void {\n this.speak(\"\", true, allowInterruption);\n }\n\n /**\n * Sends a message to the Sayna session.\n *\n * @param message - Message content\n * @param role - Message role (e.g., \"user\", \"assistant\")\n * @param topic - Optional topic identifier\n * @param debug - Optional debug metadata\n * @throws {SaynaNotConnectedError} If not connected\n * @throws {SaynaNotReadyError} If connection is not ready\n * @throws {SaynaValidationError} If parameters are invalid\n */\n sendMessage(\n message: string,\n role: string,\n topic?: string,\n debug?: Record<string, unknown>\n ): void {\n if (!this.isConnected || !this.websocket) {\n throw new SaynaNotConnectedError();\n }\n\n if (!this.isReady) {\n throw new SaynaNotReadyError();\n }\n\n if (typeof message !== \"string\") {\n throw new SaynaValidationError(\"message must be a string\");\n }\n\n if (typeof role !== \"string\") {\n throw new SaynaValidationError(\"role must be a string\");\n }\n\n try {\n const sendMsg: SendMessageMessage = {\n type: \"send_message\",\n message,\n role,\n topic,\n debug,\n };\n this.websocket.send(JSON.stringify(sendMsg));\n } catch (error) {\n throw new SaynaConnectionError(\"Failed to send message\", error);\n }\n }\n\n /**\n * Performs a health check on the Sayna server.\n *\n * @returns Promise that resolves with the health status\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error\n *\n * @example\n * ```typescript\n * const health = await client.health();\n * console.log(health.status); // \"OK\"\n * ```\n */\n async health(): Promise<HealthResponse> {\n return this.fetchFromSayna<HealthResponse>(\"\");\n }\n\n /**\n * Retrieves the catalogue of text-to-speech voices grouped by provider.\n *\n * @returns Promise that resolves with voices organized by provider\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error\n *\n * @example\n * ```typescript\n * const voices = await client.getVoices();\n * for (const [provider, voiceList] of Object.entries(voices)) {\n * console.log(`${provider}:`, voiceList.map(v => v.name));\n * }\n * ```\n */\n async getVoices(): Promise<VoicesResponse> {\n return this.fetchFromSayna<VoicesResponse>(\"voices\");\n }\n\n /**\n * Synthesizes text into audio using the REST API endpoint.\n * This is a standalone synthesis method that doesn't require an active WebSocket connection.\n *\n * @param text - Text to synthesize\n * @param ttsConfig - Text-to-speech configuration\n * @returns Promise that resolves with the audio data as ArrayBuffer\n * @throws {SaynaValidationError} If text is empty\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error\n *\n * @example\n * ```typescript\n * const audioBuffer = await client.speakRest(\"Hello, world!\", {\n * provider: \"elevenlabs\",\n * voice_id: \"21m00Tcm4TlvDq8ikWAM\",\n * model: \"eleven_turbo_v2\",\n * speaking_rate: 1.0,\n * audio_format: \"mp3\",\n * sample_rate: 24000,\n * connection_timeout: 30,\n * request_timeout: 60,\n * pronunciations: []\n * });\n * ```\n */\n async speakRest(text: string, ttsConfig: TTSConfig): Promise<ArrayBuffer> {\n if (!text || text.trim().length === 0) {\n throw new SaynaValidationError(\"Text cannot be empty\");\n }\n\n return this.fetchFromSayna<ArrayBuffer>(\n \"speak\",\n {\n method: \"POST\",\n body: JSON.stringify({\n text,\n tts_config: ttsConfig,\n }),\n },\n \"arrayBuffer\"\n );\n }\n\n /**\n * Issues a LiveKit access token for a participant.\n *\n * @param roomName - LiveKit room to join or create\n * @param participantName - Display name assigned to the participant\n * @param participantIdentity - Unique identifier for the participant\n * @returns Promise that resolves with the LiveKit token and connection details\n * @throws {SaynaValidationError} If any parameter is empty\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error\n *\n * @example\n * ```typescript\n * const tokenInfo = await client.getLiveKitToken(\n * \"my-room\",\n * \"John Doe\",\n * \"user-123\"\n * );\n * console.log(\"Token:\", tokenInfo.token);\n * console.log(\"LiveKit URL:\", tokenInfo.livekit_url);\n * ```\n */\n async getLiveKitToken(\n roomName: string,\n participantName: string,\n participantIdentity: string\n ): Promise<LiveKitTokenResponse> {\n if (!roomName || roomName.trim().length === 0) {\n throw new SaynaValidationError(\"room_name cannot be empty\");\n }\n\n if (!participantName || participantName.trim().length === 0) {\n throw new SaynaValidationError(\"participant_name cannot be empty\");\n }\n\n if (!participantIdentity || participantIdentity.trim().length === 0) {\n throw new SaynaValidationError(\"participant_identity cannot be empty\");\n }\n\n return this.fetchFromSayna<LiveKitTokenResponse>(\"livekit/token\", {\n method: \"POST\",\n body: JSON.stringify({\n room_name: roomName,\n participant_name: participantName,\n participant_identity: participantIdentity,\n }),\n });\n }\n\n /**\n * Downloads the recorded audio file for a completed session.\n *\n * @param streamId - The session identifier (obtained from the `streamId` getter after connection)\n * @returns Promise that resolves with the audio data as ArrayBuffer (OGG format)\n * @throws {SaynaValidationError} If streamId is empty\n * @throws {SaynaConnectionError} If the network request fails\n * @throws {SaynaServerError} If the recording is not found or server returns an error\n *\n * @example\n * ```typescript\n * // After a session completes, download the recording\n * const audioBuffer = await client.getRecording(client.streamId!);\n *\n * // Save to file (Node.js)\n * import { writeFile } from \"fs/promises\";\n * await writeFile(\"recording.ogg\", Buffer.from(audioBuffer));\n * ```\n */\n async getRecording(streamId: string): Promise<ArrayBuffer> {\n if (!streamId || streamId.trim().length === 0) {\n throw new SaynaValidationError(\"streamId cannot be empty\");\n }\n\n return this.fetchFromSayna<ArrayBuffer>(\n `recording/${encodeURIComponent(streamId)}`,\n { method: \"GET\" },\n \"arrayBuffer\"\n );\n }\n\n /**\n * Retrieves all configured SIP webhook hooks from the runtime cache.\n *\n * @returns Promise that resolves with the list of configured SIP hooks\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error (e.g., 500 if reading cache fails)\n *\n * @example\n * ```typescript\n * const response = await client.getSipHooks();\n * for (const hook of response.hooks) {\n * console.log(`Host: ${hook.host}, URL: ${hook.url}`);\n * }\n * ```\n */\n async getSipHooks(): Promise<SipHooksResponse> {\n return this.fetchFromSayna<SipHooksResponse>(\"sip/hooks\");\n }\n\n /**\n * Sets or updates SIP webhook hooks in the runtime cache.\n *\n * Hooks with matching hosts will be replaced; new hosts will be added.\n * The response contains the merged list of all hooks (existing + new).\n *\n * @param hooks - Array of SIP hook configurations to add or replace\n * @returns Promise that resolves with the merged list of all configured hooks\n * @throws {SaynaValidationError} If hooks array is empty or contains invalid entries\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error (e.g., 400 for duplicate hosts, 500 for cache errors)\n *\n * @example\n * ```typescript\n * const response = await client.setSipHooks([\n * { host: \"example.com\", url: \"https://webhook.example.com/events\" },\n * { host: \"another.com\", url: \"https://webhook.another.com/events\" }\n * ]);\n * console.log(\"Total hooks configured:\", response.hooks.length);\n * ```\n */\n async setSipHooks(hooks: SipHook[]): Promise<SipHooksResponse> {\n if (!Array.isArray(hooks)) {\n throw new SaynaValidationError(\"hooks must be an array\");\n }\n\n if (hooks.length === 0) {\n throw new SaynaValidationError(\"hooks array cannot be empty\");\n }\n\n for (const [i, hook] of hooks.entries()) {\n if (\n !hook.host ||\n typeof hook.host !== \"string\" ||\n hook.host.trim().length === 0\n ) {\n throw new SaynaValidationError(\n `hooks[${i}].host must be a non-empty string`\n );\n }\n if (\n !hook.url ||\n typeof hook.url !== \"string\" ||\n hook.url.trim().length === 0\n ) {\n throw new SaynaValidationError(\n `hooks[${i}].url must be a non-empty string`\n );\n }\n }\n\n return this.fetchFromSayna<SipHooksResponse>(\"sip/hooks\", {\n method: \"POST\",\n body: JSON.stringify({ hooks }),\n });\n }\n\n /**\n * Deletes SIP webhook hooks by host name from the runtime cache.\n *\n * If a deleted host exists in the original server configuration,\n * it will revert to its config value after deletion.\n *\n * @param hosts - Array of host names to remove (case-insensitive)\n * @returns Promise that resolves with the updated list of hooks after deletion\n * @throws {SaynaValidationError} If hosts array is empty or contains invalid entries\n * @throws {SaynaConnectionError} If the request fails\n * @throws {SaynaServerError} If server returns an error (e.g., 400 for empty hosts, 500 for cache errors)\n *\n * @example\n * ```typescript\n * const response = await client.deleteSipHooks([\"example.com\", \"another.com\"]);\n * console.log(\"Remaining hooks:\", response.hooks.length);\n * ```\n */\n async deleteSipHooks(hosts: string[]): Promise<SipHooksResponse> {\n if (!Array.isArray(hosts)) {\n throw new SaynaValidationError(\"hosts must be an array\");\n }\n\n if (hosts.length === 0) {\n throw new SaynaValidationError(\"hosts array cannot be empty\");\n }\n\n for (const [i, host] of hosts.entries()) {\n if (!host || typeof host !== \"string\" || host.trim().length === 0) {\n throw new SaynaValidationError(\n `hosts[${i}] must be a non-empty string`\n );\n }\n }\n\n return this.fetchFromSayna<SipHooksResponse>(\"sip/hooks\", {\n method: \"DELETE\",\n body: JSON.stringify({ hosts }),\n });\n }\n\n /**\n * Whether the client is ready to send/receive data.\n */\n get ready(): boolean {\n return this.isReady;\n }\n\n /**\n * Whether the client is connected to the WebSocket.\n */\n get connected(): boolean {\n return this.isConnected;\n }\n\n /**\n * LiveKit room name acknowledged by the server, if available.\n */\n get livekitRoomName(): string | undefined {\n return this._livekitRoomName;\n }\n\n /**\n * LiveKit WebSocket URL configured on the server, if available.\n */\n get livekitUrl(): string | undefined {\n return this._livekitUrl;\n }\n\n /**\n * Identity assigned to the agent participant when LiveKit is enabled, if available.\n */\n get saynaParticipantIdentity(): string | undefined {\n return this._saynaParticipantIdentity;\n }\n\n /**\n * Display name assigned to the agent participant when LiveKit is enabled, if available.\n */\n get saynaParticipantName(): string | undefined {\n return this._saynaParticipantName;\n }\n\n /**\n * Session identifier returned by the server.\n * This can be used to download recordings or correlate session data.\n * The value is available after the connection is ready.\n */\n get streamId(): string | undefined {\n return this._streamId;\n }\n}\n",
7
7
  "import { createHmac, timingSafeEqual } from \"crypto\";\nimport { SaynaValidationError } from \"./errors\";\nimport type { WebhookSIPOutput } from \"./types\";\n\n/**\n * Minimum required secret length in characters for security.\n * @internal\n */\nconst MIN_SECRET_LENGTH = 16;\n\n/**\n * Maximum allowed time difference in seconds for replay protection.\n * Webhooks with timestamps outside this window will be rejected.\n * @internal\n */\nconst TIMESTAMP_TOLERANCE_SECONDS = 300; // 5 minutes\n\n/**\n * Receives and verifies cryptographically signed webhooks from Sayna SIP service.\n *\n * This class handles the secure verification of webhook signatures using HMAC-SHA256,\n * validates timestamp freshness to prevent replay attacks, and parses the webhook\n * payload into a strongly-typed WebhookSIPOutput object.\n *\n * ## Security Features\n *\n * - **HMAC-SHA256 Signature Verification**: Ensures webhook authenticity\n * - **Constant-Time Comparison**: Prevents timing attack vulnerabilities\n * - **Replay Protection**: 5-minute timestamp window prevents replay attacks\n * - **Strict Validation**: Comprehensive checks on all required fields\n *\n * ## Usage\n *\n * ### Basic Example\n *\n * ```typescript\n * import { WebhookReceiver } from \"@sayna-ai/node-sdk\";\n *\n * // Initialize with secret (or uses SAYNA_WEBHOOK_SECRET env variable)\n * const receiver = new WebhookReceiver(\"your-secret-key-min-16-chars\");\n *\n * // In your Express route handler\n * app.post('/webhook', express.json({ verify: (req, res, buf) => {\n * req.rawBody = buf.toString('utf8');\n * }}), (req, res) => {\n * try {\n * const webhook = receiver.receive(req.headers, req.rawBody);\n *\n * console.log('Valid webhook received:');\n * console.log(' From:', webhook.from_phone_number);\n * console.log(' To:', webhook.to_phone_number);\n * console.log(' Room:', webhook.room.name);\n * console.log(' SIP Host:', webhook.sip_host);\n * console.log(' Participant:', webhook.participant.identity);\n *\n * res.status(200).json({ received: true });\n * } catch (error) {\n * console.error('Webhook verification failed:', error.message);\n * res.status(401).json({ error: 'Invalid signature' });\n * }\n * });\n * ```\n *\n * ### With Environment Variable\n *\n * ```typescript\n * // Set environment variable\n * process.env.SAYNA_WEBHOOK_SECRET = \"your-secret-key\";\n *\n * // Receiver automatically uses env variable\n * const receiver = new WebhookReceiver();\n * ```\n *\n * ### Fastify Example\n *\n * ```typescript\n * import Fastify from 'fastify';\n * import { WebhookReceiver } from \"@sayna-ai/node-sdk\";\n *\n * const fastify = Fastify();\n * const receiver = new WebhookReceiver();\n *\n * fastify.post('/webhook', {\n * config: {\n * rawBody: true\n * }\n * }, async (request, reply) => {\n * try {\n * const webhook = receiver.receive(\n * request.headers,\n * request.rawBody\n * );\n *\n * // Process webhook...\n *\n * return { received: true };\n * } catch (error) {\n * reply.code(401);\n * return { error: error.message };\n * }\n * });\n * ```\n *\n * ## Important Notes\n *\n * - **Raw Body Required**: You MUST pass the raw request body string, not the parsed JSON object.\n * The signature is computed over the exact bytes received, so any formatting changes will\n * cause verification to fail.\n *\n * - **Case-Insensitive Headers**: Header names are case-insensitive in HTTP. This class handles\n * both `X-Sayna-Signature` and `x-sayna-signature` correctly.\n *\n * - **Secret Security**: Never commit secrets to version control. Use environment variables\n * or a secret management system.\n *\n * @see WebhookSIPOutput\n */\nexport class WebhookReceiver {\n private readonly secret: string;\n\n /**\n * Creates a new webhook receiver with the specified signing secret.\n *\n * @param secret - HMAC signing secret (min 16 chars, 32+ recommended).\n * If not provided, uses SAYNA_WEBHOOK_SECRET environment variable.\n *\n * @throws {SaynaValidationError} If secret is missing or too short\n *\n * @example\n * ```typescript\n * // Explicit secret\n * const receiver = new WebhookReceiver(\"my-secret-key-at-least-16-chars\");\n *\n * // From environment variable\n * const receiver = new WebhookReceiver();\n * ```\n */\n constructor(secret?: string) {\n const effectiveSecret = secret ?? process.env.SAYNA_WEBHOOK_SECRET;\n\n if (!effectiveSecret) {\n throw new SaynaValidationError(\n \"Webhook secret is required. Provide it as a constructor parameter or set SAYNA_WEBHOOK_SECRET environment variable.\"\n );\n }\n\n const trimmedSecret = effectiveSecret.trim();\n\n if (trimmedSecret.length < MIN_SECRET_LENGTH) {\n throw new SaynaValidationError(\n `Webhook secret must be at least ${MIN_SECRET_LENGTH} characters long. ` +\n `Received ${trimmedSecret.length} characters. ` +\n `Generate a secure secret with: openssl rand -hex 32`\n );\n }\n\n this.secret = trimmedSecret;\n }\n\n /**\n * Verifies and parses an incoming SIP webhook from Sayna.\n *\n * This method performs the following security checks:\n * 1. Validates presence of required headers\n * 2. Verifies timestamp is within acceptable window (prevents replay attacks)\n * 3. Computes HMAC-SHA256 signature over canonical string\n * 4. Performs constant-time comparison to prevent timing attacks\n * 5. Parses and validates the webhook payload structure\n *\n * @param headers - HTTP request headers (case-insensitive)\n * @param body - Raw request body as string (not parsed JSON)\n *\n * @returns Parsed and validated webhook payload\n *\n * @throws {SaynaValidationError} If signature verification fails or payload is invalid\n *\n * @example\n * ```typescript\n * const receiver = new WebhookReceiver(\"your-secret\");\n *\n * // Express example\n * app.post('/webhook', express.json({ verify: (req, res, buf) => {\n * req.rawBody = buf.toString();\n * }}), (req, res) => {\n * const webhook = receiver.receive(req.headers, req.rawBody);\n * // webhook is now a validated WebhookSIPOutput object\n * });\n * ```\n */\n receive(\n headers: Record<string, string | string[] | undefined>,\n body: string\n ): WebhookSIPOutput {\n // Normalize headers to lowercase for case-insensitive lookup\n const normalizedHeaders = this.normalizeHeaders(headers);\n\n // Extract required headers\n const signature = this.getRequiredHeader(\n normalizedHeaders,\n \"x-sayna-signature\"\n );\n const timestamp = this.getRequiredHeader(\n normalizedHeaders,\n \"x-sayna-timestamp\"\n );\n const eventId = this.getRequiredHeader(\n normalizedHeaders,\n \"x-sayna-event-id\"\n );\n\n // Parse and validate signature format\n if (!signature.startsWith(\"v1=\")) {\n throw new SaynaValidationError(\n \"Invalid signature format. Expected 'v1=<hex>' but got: \" +\n signature.substring(0, 10) +\n \"...\"\n );\n }\n const signatureHex = signature.substring(3);\n\n // Validate signature is valid hex (64 chars for SHA256)\n if (!/^[0-9a-f]{64}$/i.test(signatureHex)) {\n throw new SaynaValidationError(\n \"Invalid signature: must be 64 hex characters (HMAC-SHA256)\"\n );\n }\n\n // Validate and check timestamp\n this.validateTimestamp(timestamp);\n\n // Build canonical string for signature verification\n const canonical = `v1:${timestamp}:${eventId}:${body}`;\n\n // Compute expected signature\n const hmac = createHmac(\"sha256\", this.secret);\n hmac.update(canonical, \"utf8\");\n const expectedSignature = hmac.digest(\"hex\");\n\n // Constant-time comparison to prevent timing attacks\n if (!this.constantTimeEqual(signatureHex, expectedSignature)) {\n throw new SaynaValidationError(\n \"Signature verification failed. The webhook may have been tampered with or the secret is incorrect.\"\n );\n }\n\n // Parse and validate the webhook payload\n return this.parseAndValidatePayload(body);\n }\n\n /**\n * Normalizes HTTP headers to lowercase for case-insensitive access.\n * Handles both single string values and arrays of strings.\n *\n * @internal\n */\n private normalizeHeaders(\n headers: Record<string, string | string[] | undefined>\n ): Record<string, string> {\n const normalized: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(headers)) {\n if (value !== undefined) {\n // Handle array values (take first element)\n const stringValue = Array.isArray(value) ? value[0] : value;\n if (stringValue) {\n normalized[key.toLowerCase()] = stringValue;\n }\n }\n }\n\n return normalized;\n }\n\n /**\n * Retrieves a required header value or throws a validation error.\n *\n * @internal\n */\n private getRequiredHeader(\n headers: Record<string, string>,\n name: string\n ): string {\n const value = headers[name.toLowerCase()];\n\n if (!value) {\n throw new SaynaValidationError(`Missing required header: ${name}`);\n }\n\n return value;\n }\n\n /**\n * Validates the timestamp is within the acceptable window.\n *\n * @internal\n */\n private validateTimestamp(timestampStr: string): void {\n // Parse timestamp\n const timestamp = Number(timestampStr);\n\n if (isNaN(timestamp)) {\n throw new SaynaValidationError(\n `Invalid timestamp format: expected Unix seconds but got '${timestampStr}'`\n );\n }\n\n // Check if timestamp is within acceptable range\n const now = Math.floor(Date.now() / 1000);\n const diff = Math.abs(now - timestamp);\n\n if (diff > TIMESTAMP_TOLERANCE_SECONDS) {\n throw new SaynaValidationError(\n `Timestamp outside replay protection window. ` +\n `Difference: ${diff} seconds (max allowed: ${TIMESTAMP_TOLERANCE_SECONDS}). ` +\n `This webhook may be a replay attack or there may be significant clock skew.`\n );\n }\n }\n\n /**\n * Performs constant-time string comparison to prevent timing attacks.\n *\n * @internal\n */\n private constantTimeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) {\n return false;\n }\n\n const bufA = Buffer.from(a, \"utf8\");\n const bufB = Buffer.from(b, \"utf8\");\n\n return timingSafeEqual(bufA, bufB);\n }\n\n /**\n * Parses and validates the webhook payload structure.\n *\n * @internal\n */\n private parseAndValidatePayload(body: string): WebhookSIPOutput {\n let payload: unknown;\n\n // Parse JSON\n try {\n payload = JSON.parse(body);\n } catch (error) {\n throw new SaynaValidationError(\n `Invalid JSON payload: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n\n // Validate payload is an object\n if (!payload || typeof payload !== \"object\" || Array.isArray(payload)) {\n throw new SaynaValidationError(\"Webhook payload must be a JSON object\");\n }\n\n const data = payload as Record<string, unknown>;\n\n // Validate required fields\n this.validateParticipant(data.participant);\n this.validateRoom(data.room);\n this.validateStringField(data, \"from_phone_number\", \"from_phone_number\");\n this.validateStringField(data, \"to_phone_number\", \"to_phone_number\");\n this.validateStringField(data, \"room_prefix\", \"room_prefix\");\n this.validateStringField(data, \"sip_host\", \"sip_host\");\n\n // TypeScript type assertion is safe here because we've validated all fields\n // We use double assertion through unknown to satisfy strict type checking\n return data as unknown as WebhookSIPOutput;\n }\n\n /**\n * Validates the participant object structure.\n *\n * @internal\n */\n private validateParticipant(participant: unknown): void {\n if (\n !participant ||\n typeof participant !== \"object\" ||\n Array.isArray(participant)\n ) {\n throw new SaynaValidationError(\n \"Webhook payload missing required field 'participant' (must be an object)\"\n );\n }\n\n const p = participant as Record<string, unknown>;\n\n this.validateStringField(p, \"identity\", \"participant.identity\");\n this.validateStringField(p, \"sid\", \"participant.sid\");\n\n // name is optional, but if present must be a string\n if (p.name !== undefined && typeof p.name !== \"string\") {\n throw new SaynaValidationError(\n \"Field 'participant.name' must be a string if present\"\n );\n }\n }\n\n /**\n * Validates the room object structure.\n *\n * @internal\n */\n private validateRoom(room: unknown): void {\n if (!room || typeof room !== \"object\" || Array.isArray(room)) {\n throw new SaynaValidationError(\n \"Webhook payload missing required field 'room' (must be an object)\"\n );\n }\n\n const r = room as Record<string, unknown>;\n\n this.validateStringField(r, \"name\", \"room.name\");\n this.validateStringField(r, \"sid\", \"room.sid\");\n }\n\n /**\n * Validates a required string field.\n *\n * @internal\n */\n private validateStringField(\n obj: Record<string, unknown>,\n field: string,\n displayName: string\n ): void {\n const value = obj[field];\n\n if (typeof value !== \"string\" || value.length === 0) {\n throw new SaynaValidationError(\n `Webhook payload missing required field '${displayName}' (must be a non-empty string)`\n );\n }\n }\n}\n",
8
8
  "import { SaynaClient } from \"./sayna-client\";\nimport type { STTConfig, TTSConfig, LiveKitConfig } from \"./types\";\n\nexport * from \"./sayna-client\";\nexport * from \"./types\";\nexport * from \"./errors\";\nexport * from \"./webhook-receiver\";\n\n/**\n * Creates and connects a new SaynaClient instance.\n *\n * This is the recommended way to create a Sayna client. It handles both\n * instantiation and connection, returning a ready-to-use client.\n *\n * @param url - The Sayna server URL (e.g., \"https://api.sayna.ai\")\n * @param sttConfig - Speech-to-text configuration\n * @param ttsConfig - Text-to-speech configuration\n * @param livekitConfig - Optional LiveKit room configuration\n * @param withoutAudio - If true, disables audio streaming (default: false)\n * @param apiKey - Optional API key used to authorize HTTP and WebSocket calls (defaults to SAYNA_API_KEY env)\n *\n * @returns Promise that resolves to a connected SaynaClient\n *\n * @throws {SaynaValidationError} If parameters are invalid\n * @throws {SaynaConnectionError} If connection fails\n * @throws {SaynaServerError} If server returns an error during setup\n *\n * @example\n * ```typescript\n * import { saynaConnect } from \"@sayna/node-sdk\";\n *\n * const client = await saynaConnect(\n * \"https://api.sayna.ai\",\n * {\n * provider: \"deepgram\",\n * language: \"en-US\",\n * sample_rate: 16000,\n * channels: 1,\n * punctuation: true,\n * encoding: \"linear16\",\n * model: \"nova-2\"\n * },\n * {\n * provider: \"elevenlabs\",\n * voice_id: \"21m00Tcm4TlvDq8ikWAM\",\n * speaking_rate: 1.0,\n * audio_format: \"pcm\",\n * sample_rate: 16000,\n * connection_timeout: 5000,\n * request_timeout: 10000,\n * model: \"eleven_turbo_v2\",\n * pronunciations: []\n * }\n * );\n *\n * // Register event handlers\n * client.registerOnSttResult((result) => {\n * console.log(\"Transcription:\", result.transcript);\n * });\n *\n * // Send text to be spoken\n * await client.speak(\"Hello, world!\");\n *\n * // Clean up\n * await client.disconnect();\n * ```\n */\nexport async function saynaConnect(\n url: string,\n sttConfig: STTConfig,\n ttsConfig: TTSConfig,\n livekitConfig?: LiveKitConfig,\n withoutAudio: boolean = false,\n apiKey?: string\n): Promise<SaynaClient> {\n const client = new SaynaClient(\n url,\n sttConfig,\n ttsConfig,\n livekitConfig,\n withoutAudio,\n apiKey\n );\n await client.connect();\n return client;\n}\n"
9
9
  ],
10
- "mappings": "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",
11
- "debugId": "DAB85213A282150564756E2164756E21",
10
+ "mappings": "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",
11
+ "debugId": "439FAA862268544264756E2164756E21",
12
12
  "names": []
13
13
  }
@@ -55,6 +55,8 @@ export declare class SaynaClient {
55
55
  private _livekitUrl?;
56
56
  private _saynaParticipantIdentity?;
57
57
  private _saynaParticipantName?;
58
+ private _streamId?;
59
+ private inputStreamId?;
58
60
  private sttCallback?;
59
61
  private ttsCallback?;
60
62
  private errorCallback?;
@@ -72,10 +74,11 @@ export declare class SaynaClient {
72
74
  * @param livekitConfig - Optional LiveKit room configuration
73
75
  * @param withoutAudio - If true, disables audio streaming (default: false)
74
76
  * @param apiKey - Optional API key used to authorize HTTP and WebSocket calls (defaults to SAYNA_API_KEY env)
77
+ * @param streamId - Optional session identifier for recording paths; server generates a UUID when omitted
75
78
  *
76
79
  * @throws {SaynaValidationError} If URL is invalid or if audio configs are missing when audio is enabled
77
80
  */
78
- constructor(url: string, sttConfig?: STTConfig, ttsConfig?: TTSConfig, livekitConfig?: LiveKitConfig, withoutAudio?: boolean, apiKey?: string);
81
+ constructor(url: string, sttConfig?: STTConfig, ttsConfig?: TTSConfig, livekitConfig?: LiveKitConfig, withoutAudio?: boolean, apiKey?: string, streamId?: string);
79
82
  /**
80
83
  * Establishes connection to the Sayna WebSocket server.
81
84
  *
@@ -277,6 +280,26 @@ export declare class SaynaClient {
277
280
  * ```
278
281
  */
279
282
  getLiveKitToken(roomName: string, participantName: string, participantIdentity: string): Promise<LiveKitTokenResponse>;
283
+ /**
284
+ * Downloads the recorded audio file for a completed session.
285
+ *
286
+ * @param streamId - The session identifier (obtained from the `streamId` getter after connection)
287
+ * @returns Promise that resolves with the audio data as ArrayBuffer (OGG format)
288
+ * @throws {SaynaValidationError} If streamId is empty
289
+ * @throws {SaynaConnectionError} If the network request fails
290
+ * @throws {SaynaServerError} If the recording is not found or server returns an error
291
+ *
292
+ * @example
293
+ * ```typescript
294
+ * // After a session completes, download the recording
295
+ * const audioBuffer = await client.getRecording(client.streamId!);
296
+ *
297
+ * // Save to file (Node.js)
298
+ * import { writeFile } from "fs/promises";
299
+ * await writeFile("recording.ogg", Buffer.from(audioBuffer));
300
+ * ```
301
+ */
302
+ getRecording(streamId: string): Promise<ArrayBuffer>;
280
303
  /**
281
304
  * Retrieves all configured SIP webhook hooks from the runtime cache.
282
305
  *
@@ -358,5 +381,11 @@ export declare class SaynaClient {
358
381
  * Display name assigned to the agent participant when LiveKit is enabled, if available.
359
382
  */
360
383
  get saynaParticipantName(): string | undefined;
384
+ /**
385
+ * Session identifier returned by the server.
386
+ * This can be used to download recordings or correlate session data.
387
+ * The value is available after the connection is ready.
388
+ */
389
+ get streamId(): string | undefined;
361
390
  }
362
391
  //# sourceMappingURL=sayna-client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sayna-client.d.ts","sourceRoot":"","sources":["../../src/sayna-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,SAAS,EACT,aAAa,EAKb,gBAAgB,EAChB,YAAY,EAEZ,YAAY,EACZ,WAAW,EACX,cAAc,EACd,cAAc,EACd,oBAAoB,EACpB,OAAO,EACP,gBAAgB,EACjB,MAAM,SAAS,CAAC;AAYjB;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAC7B,MAAM,EAAE,gBAAgB,KACrB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1B;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE3E;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEzE;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE7E;;GAEG;AACH,MAAM,MAAM,8BAA8B,GAAG,CAC3C,WAAW,EAAE,WAAW,KACrB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1B;;GAEG;AACH,MAAM,MAAM,0BAA0B,GAAG,CACvC,SAAS,EAAE,MAAM,KACd,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1B;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,SAAS,CAAC,CAAY;IAC9B,OAAO,CAAC,SAAS,CAAC,CAAY;IAC9B,OAAO,CAAC,aAAa,CAAC,CAAgB;IACtC,OAAO,CAAC,YAAY,CAAU;IAC9B,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,SAAS,CAAC,CAAY;IAC9B,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,gBAAgB,CAAC,CAAS;IAClC,OAAO,CAAC,WAAW,CAAC,CAAS;IAC7B,OAAO,CAAC,yBAAyB,CAAC,CAAS;IAC3C,OAAO,CAAC,qBAAqB,CAAC,CAAS;IACvC,OAAO,CAAC,WAAW,CAAC,CAAmB;IACvC,OAAO,CAAC,WAAW,CAAC,CAAkB;IACtC,OAAO,CAAC,aAAa,CAAC,CAAe;IACrC,OAAO,CAAC,eAAe,CAAC,CAAiB;IACzC,OAAO,CAAC,+BAA+B,CAAC,CAAiC;IACzE,OAAO,CAAC,2BAA2B,CAAC,CAA6B;IACjE,OAAO,CAAC,mBAAmB,CAAC,CAAa;IACzC,OAAO,CAAC,kBAAkB,CAAC,CAAyB;IAEpD;;;;;;;;;;;OAWG;gBAED,GAAG,EAAE,MAAM,EACX,SAAS,CAAC,EAAE,SAAS,EACrB,SAAS,CAAC,EAAE,SAAS,EACrB,aAAa,CAAC,EAAE,aAAa,EAC7B,YAAY,GAAE,OAAe,EAC7B,MAAM,CAAC,EAAE,MAAM;IAmCjB;;;;;OAKG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAwH9B;;;OAGG;YACW,iBAAiB;IA4E/B;;;OAGG;IACH,OAAO,CAAC,OAAO;IASf;;;OAGG;IACH,OAAO,CAAC,UAAU;IAMlB;;;;;;;;;;;OAWG;YACW,cAAc;IAkE5B;;OAEG;IACH,UAAU,IAAI,IAAI;IAkBlB;;;;;;OAMG;IACH,YAAY,CAAC,SAAS,EAAE,WAAW,GAAG,IAAI;IAwB1C;;;;OAIG;IACH,mBAAmB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,IAAI;IAIrD;;;;OAIG;IACH,kBAAkB,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI;IAInD;;;;OAIG;IACH,eAAe,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI;IAI7C;;;;OAIG;IACH,iBAAiB,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI;IAIjD;;;;OAIG;IACH,iCAAiC,CAC/B,QAAQ,EAAE,8BAA8B,GACvC,IAAI;IAIP;;;;OAIG;IACH,6BAA6B,CAAC,QAAQ,EAAE,0BAA0B,GAAG,IAAI;IAIzE;;;;;;;;;OASG;IACH,KAAK,CACH,IAAI,EAAE,MAAM,EACZ,KAAK,GAAE,OAAc,EACrB,iBAAiB,GAAE,OAAc,GAChC,IAAI;IA0BP;;;;;OAKG;IACH,KAAK,IAAI,IAAI;IAmBb;;;;;;OAMG;IACH,QAAQ,CAAC,iBAAiB,GAAE,OAAc,GAAG,IAAI;IAIjD;;;;;;;;;;OAUG;IACH,WAAW,CACT,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,IAAI;IA+BP;;;;;;;;;;;;OAYG;IACG,MAAM,IAAI,OAAO,CAAC,cAAc,CAAC;IAIvC;;;;;;;;;;;;;;OAcG;IACG,SAAS,IAAI,OAAO,CAAC,cAAc,CAAC;IAI1C;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACG,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC;IAkBzE;;;;;;;;;;;;;;;;;;;;;OAqBG;IACG,eAAe,CACnB,QAAQ,EAAE,MAAM,EAChB,eAAe,EAAE,MAAM,EACvB,mBAAmB,EAAE,MAAM,GAC1B,OAAO,CAAC,oBAAoB,CAAC;IAuBhC;;;;;;;;;;;;;;OAcG;IACG,WAAW,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAI9C;;;;;;;;;;;;;;;;;;;;OAoBG;IACG,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAwB9D;;;;;;;;;;;;;;;;;OAiBG;IACG,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAqBhE;;OAEG;IACH,IAAI,KAAK,IAAI,OAAO,CAEnB;IAED;;OAEG;IACH,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED;;OAEG;IACH,IAAI,eAAe,IAAI,MAAM,GAAG,SAAS,CAExC;IAED;;OAEG;IACH,IAAI,UAAU,IAAI,MAAM,GAAG,SAAS,CAEnC;IAED;;OAEG;IACH,IAAI,wBAAwB,IAAI,MAAM,GAAG,SAAS,CAEjD;IAED;;OAEG;IACH,IAAI,oBAAoB,IAAI,MAAM,GAAG,SAAS,CAE7C;CACF"}
1
+ {"version":3,"file":"sayna-client.d.ts","sourceRoot":"","sources":["../../src/sayna-client.ts"],"names":[],"mappings":"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"}
@@ -57,8 +57,6 @@ export interface LiveKitConfig {
57
57
  room_name: string;
58
58
  /** Whether to enable session recording */
59
59
  enable_recording?: boolean;
60
- /** Storage key for the recording file (required when enable_recording is true) */
61
- recording_file_key?: string;
62
60
  /** Identity assigned to the agent participant (defaults to "sayna-ai") */
63
61
  sayna_participant_identity?: string;
64
62
  /** Display name for the agent participant (defaults to "Sayna AI") */
@@ -72,6 +70,8 @@ export interface LiveKitConfig {
72
70
  */
73
71
  export interface ConfigMessage {
74
72
  type: "config";
73
+ /** Session identifier used for recording paths; server generates a UUID when omitted */
74
+ stream_id?: string;
75
75
  /** Whether audio streaming is enabled */
76
76
  audio?: boolean;
77
77
  /** Speech-to-text configuration (required when audio=true) */
@@ -121,6 +121,8 @@ export interface SendMessageMessage {
121
121
  */
122
122
  export interface ReadyMessage {
123
123
  type: "ready";
124
+ /** Session identifier; may be auto-generated by the server if not provided in config */
125
+ stream_id?: string;
124
126
  /** LiveKit room name acknowledged by the server (present only when LiveKit is enabled) */
125
127
  livekit_room_name?: string;
126
128
  /** LiveKit WebSocket URL configured on the server */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sayna-ai/node-sdk",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "description": "Node.js SDK for Sayna.ai server-side WebSocket connections",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",