@mentra/sdk 2.1.27 โ†’ 2.1.28

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.
Files changed (90) hide show
  1. package/dist/app/session/api-client.d.ts.map +1 -1
  2. package/dist/app/session/dashboard.d.ts +5 -8
  3. package/dist/app/session/dashboard.d.ts.map +1 -1
  4. package/dist/app/session/events.d.ts +2 -1
  5. package/dist/app/session/events.d.ts.map +1 -1
  6. package/dist/app/session/index.d.ts +62 -3
  7. package/dist/app/session/index.d.ts.map +1 -1
  8. package/dist/app/session/modules/audio.d.ts +33 -4
  9. package/dist/app/session/modules/audio.d.ts.map +1 -1
  10. package/dist/app/session/modules/camera-managed-extension.d.ts +2 -3
  11. package/dist/app/session/modules/camera-managed-extension.d.ts.map +1 -1
  12. package/dist/app/session/modules/camera.d.ts +5 -5
  13. package/dist/app/session/modules/camera.d.ts.map +1 -1
  14. package/dist/app/session/modules/led.d.ts +141 -0
  15. package/dist/app/session/modules/led.d.ts.map +1 -0
  16. package/dist/app/session/modules/location.d.ts +1 -2
  17. package/dist/app/session/modules/location.d.ts.map +1 -1
  18. package/dist/app/session/modules/simple-storage.d.ts.map +1 -1
  19. package/dist/index.d.ts +7 -7
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +5347 -112
  22. package/dist/index.js.map +45 -0
  23. package/dist/logging/logger.d.ts +1 -1
  24. package/dist/logging/logger.d.ts.map +1 -1
  25. package/dist/types/capabilities.d.ts +3 -0
  26. package/dist/types/capabilities.d.ts.map +1 -1
  27. package/dist/types/index.d.ts +4 -14
  28. package/dist/types/index.d.ts.map +1 -1
  29. package/dist/types/message-types.d.ts +8 -1
  30. package/dist/types/message-types.d.ts.map +1 -1
  31. package/dist/types/messages/app-to-cloud.d.ts +48 -2
  32. package/dist/types/messages/app-to-cloud.d.ts.map +1 -1
  33. package/dist/types/messages/cloud-to-app.d.ts +16 -6
  34. package/dist/types/messages/cloud-to-app.d.ts.map +1 -1
  35. package/dist/types/messages/cloud-to-glasses.d.ts +29 -1
  36. package/dist/types/messages/cloud-to-glasses.d.ts.map +1 -1
  37. package/dist/types/messages/glasses-to-cloud.d.ts +24 -1
  38. package/dist/types/messages/glasses-to-cloud.d.ts.map +1 -1
  39. package/dist/types/rtmp-stream.d.ts +1 -1
  40. package/dist/types/rtmp-stream.d.ts.map +1 -1
  41. package/dist/types/streams.d.ts +28 -1
  42. package/dist/types/streams.d.ts.map +1 -1
  43. package/package.json +9 -3
  44. package/dist/app/index.js +0 -24
  45. package/dist/app/server/index.js +0 -658
  46. package/dist/app/session/api-client.js +0 -101
  47. package/dist/app/session/dashboard.js +0 -149
  48. package/dist/app/session/events.js +0 -315
  49. package/dist/app/session/index.js +0 -1573
  50. package/dist/app/session/layouts.js +0 -372
  51. package/dist/app/session/modules/audio.js +0 -321
  52. package/dist/app/session/modules/camera-managed-extension.js +0 -310
  53. package/dist/app/session/modules/camera.js +0 -607
  54. package/dist/app/session/modules/index.js +0 -19
  55. package/dist/app/session/modules/location.js +0 -61
  56. package/dist/app/session/modules/simple-storage.js +0 -232
  57. package/dist/app/session/settings.js +0 -358
  58. package/dist/app/token/index.js +0 -22
  59. package/dist/app/token/utils.js +0 -144
  60. package/dist/app/webview/index.js +0 -382
  61. package/dist/constants/index.js +0 -16
  62. package/dist/constants/log-messages/color.js +0 -14
  63. package/dist/constants/log-messages/logos.js +0 -48
  64. package/dist/constants/log-messages/updates.js +0 -55
  65. package/dist/constants/log-messages/warning.js +0 -89
  66. package/dist/examples/managed-rtmp-streaming-example.js +0 -158
  67. package/dist/examples/managed-rtmp-streaming-with-restream-example.js +0 -124
  68. package/dist/examples/rtmp-streaming-example.js +0 -102
  69. package/dist/logging/logger.js +0 -79
  70. package/dist/types/capabilities.js +0 -9
  71. package/dist/types/dashboard/index.js +0 -12
  72. package/dist/types/enums.js +0 -75
  73. package/dist/types/index.js +0 -101
  74. package/dist/types/layouts.js +0 -3
  75. package/dist/types/message-types.js +0 -212
  76. package/dist/types/messages/app-to-cloud.js +0 -95
  77. package/dist/types/messages/base.js +0 -3
  78. package/dist/types/messages/cloud-to-app.js +0 -78
  79. package/dist/types/messages/cloud-to-glasses.js +0 -68
  80. package/dist/types/messages/glasses-to-cloud.js +0 -140
  81. package/dist/types/models.js +0 -101
  82. package/dist/types/photo-data.js +0 -2
  83. package/dist/types/rtmp-stream.js +0 -3
  84. package/dist/types/streams.js +0 -306
  85. package/dist/types/token.js +0 -7
  86. package/dist/types/webhooks.js +0 -28
  87. package/dist/utils/animation-utils.js +0 -340
  88. package/dist/utils/bitmap-utils.js +0 -475
  89. package/dist/utils/permissions-utils.js +0 -263
  90. package/dist/utils/resource-tracker.js +0 -153
@@ -1,372 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LayoutManager = void 0;
4
- /**
5
- * ๐ŸŽจ Layout Manager Module
6
- *
7
- * Manages AR display layouts for Apps. This class provides an easy-to-use interface
8
- * for showing different types of content in the user's AR view.
9
- *
10
- * @example
11
- * ```typescript
12
- * const layouts = new LayoutManager('org.example.myapp', sendMessage);
13
- *
14
- * // Show a simple message
15
- * layouts.showTextWall('Hello AR World!');
16
- *
17
- * // Show a card with title
18
- * layouts.showReferenceCard('Weather', 'Sunny and 75ยฐF');
19
- * ```
20
- */
21
- const bitmap_utils_1 = require("../../utils/bitmap-utils");
22
- const types_1 = require("../../types");
23
- class LayoutManager {
24
- /**
25
- * ๐ŸŽฏ Creates a new LayoutManager instance
26
- *
27
- * @param packageName - App package identifier
28
- * @param sendMessage - Function to send display requests to MentraOS
29
- */
30
- constructor(packageName, sendMessage) {
31
- this.packageName = packageName;
32
- this.sendMessage = sendMessage;
33
- }
34
- /**
35
- * ๐Ÿ“ฆ Creates a display event request with validation
36
- *
37
- * @param layout - Layout configuration to display
38
- * @param view - View type (main or dashboard)
39
- * @param durationMs - How long to show the layout (optional)
40
- * @returns Formatted display request
41
- * @throws Error if layout is invalid
42
- */
43
- createDisplayEvent(layout, view = types_1.ViewType.MAIN, durationMs) {
44
- try {
45
- // Validate layout data before sending
46
- if (!layout) {
47
- throw new Error("Layout cannot be null or undefined");
48
- }
49
- if (!layout.layoutType) {
50
- throw new Error("Layout must have a layoutType property");
51
- }
52
- // Layout-specific validations
53
- switch (layout.layoutType) {
54
- case types_1.LayoutType.TEXT_WALL:
55
- if (typeof layout.text !== "string") {
56
- throw new Error("TextWall layout must have a text property");
57
- }
58
- // Ensure text is not too long (prevent performance issues)
59
- if (layout.text.length > 1000) {
60
- console.warn("TextWall text is very long, this may cause performance issues");
61
- }
62
- break;
63
- case types_1.LayoutType.DOUBLE_TEXT_WALL:
64
- const doubleText = layout;
65
- if (typeof doubleText.topText !== "string") {
66
- throw new Error("DoubleTextWall layout must have a topText property");
67
- }
68
- if (typeof doubleText.bottomText !== "string") {
69
- throw new Error("DoubleTextWall layout must have a bottomText property");
70
- }
71
- break;
72
- case types_1.LayoutType.REFERENCE_CARD:
73
- const refCard = layout;
74
- if (typeof refCard.title !== "string") {
75
- throw new Error("ReferenceCard layout must have a title property");
76
- }
77
- if (typeof refCard.text !== "string") {
78
- throw new Error("ReferenceCard layout must have a text property");
79
- }
80
- break;
81
- case types_1.LayoutType.DASHBOARD_CARD:
82
- const dashCard = layout;
83
- if (typeof dashCard.leftText !== "string") {
84
- throw new Error("DashboardCard layout must have a leftText property");
85
- }
86
- if (typeof dashCard.rightText !== "string") {
87
- throw new Error("DashboardCard layout must have a rightText property");
88
- }
89
- break;
90
- case types_1.LayoutType.BITMAP_VIEW:
91
- const bitmapView = layout;
92
- if (typeof bitmapView.data !== "string") {
93
- throw new Error("BitmapView layout must have a data property");
94
- }
95
- // Check if data is too large (prevent OOM errors)
96
- if (bitmapView.data.length > 1000000) {
97
- // 1MB limit
98
- throw new Error("Bitmap data is too large (>1MB), please reduce size");
99
- }
100
- break;
101
- case types_1.LayoutType.CLEAR_VIEW:
102
- // ClearView has no additional validation needed
103
- break;
104
- }
105
- // Validate view type
106
- if (view !== types_1.ViewType.MAIN && view !== types_1.ViewType.DASHBOARD) {
107
- console.warn(`Invalid view type: ${view}, defaulting to MAIN`);
108
- view = types_1.ViewType.MAIN;
109
- }
110
- // Validate duration if provided
111
- if (durationMs !== undefined) {
112
- if (typeof durationMs !== "number" || durationMs < 0) {
113
- console.warn(`Invalid duration: ${durationMs}, ignoring`);
114
- durationMs = undefined;
115
- }
116
- }
117
- // Create the display request with validated data
118
- return {
119
- timestamp: new Date(),
120
- sessionId: "", // Will be filled by session
121
- type: types_1.AppToCloudMessageType.DISPLAY_REQUEST,
122
- packageName: this.packageName,
123
- view,
124
- layout,
125
- durationMs,
126
- };
127
- }
128
- catch (error) {
129
- console.error("Error creating display event:", error);
130
- throw error; // Re-throw to notify caller
131
- }
132
- }
133
- /**
134
- * ๐Ÿ“ Shows a single block of text
135
- *
136
- * Best for:
137
- * - Simple messages
138
- * - Status updates
139
- * - Notifications
140
- *
141
- * @param text - Text content to display
142
- * @param options - Optional parameters (view, duration, priority)
143
- * - priority: If true, this display will not be overridden by other requests (default: false)
144
- *
145
- * @example
146
- * ```typescript
147
- * layouts.showTextWall('Connected to server');
148
- * layouts.showTextWall('Onboarding!', { priority: true });
149
- * ```
150
- */
151
- showTextWall(text, options) {
152
- try {
153
- // Validate input before processing
154
- if (text === undefined || text === null) {
155
- text = ""; // Default to empty string instead of crashing
156
- console.warn("showTextWall called with null/undefined text");
157
- }
158
- // Ensure text is a string
159
- if (typeof text !== "string") {
160
- text = String(text); // Convert to string
161
- console.warn("showTextWall: Non-string input converted to string");
162
- }
163
- // Create layout with validated text
164
- const layout = {
165
- layoutType: types_1.LayoutType.TEXT_WALL,
166
- text,
167
- };
168
- // Create and send display event with error handling
169
- try {
170
- const displayEvent = this.createDisplayEvent(layout, options?.view, options?.durationMs);
171
- this.sendMessage(displayEvent);
172
- }
173
- catch (error) {
174
- console.error("Failed to display text wall:", error);
175
- // Don't re-throw - prevent app crashes
176
- }
177
- }
178
- catch (error) {
179
- console.error("Error in showTextWall:", error);
180
- // Don't crash the App - fail gracefully
181
- }
182
- }
183
- /**
184
- * โ†•๏ธ Shows two sections of text, one above the other
185
- *
186
- * Best for:
187
- * - Before/After content
188
- * - Question/Answer displays
189
- * - Two-part messages
190
- * - Comparisons
191
- *
192
- * @param topText - Text to show in top section
193
- * @param bottomText - Text to show in bottom section
194
- * @param options - Optional parameters (view, duration)
195
- *
196
- * @example
197
- * ```typescript
198
- * layouts.showDoubleTextWall(
199
- * 'Original: Hello',
200
- * 'Translated: Bonjour'
201
- * );
202
- * ```
203
- */
204
- showDoubleTextWall(topText, bottomText, options) {
205
- const layout = {
206
- layoutType: types_1.LayoutType.DOUBLE_TEXT_WALL,
207
- topText,
208
- bottomText,
209
- };
210
- this.sendMessage(this.createDisplayEvent(layout, options?.view, options?.durationMs));
211
- }
212
- /**
213
- * ๐Ÿ“‡ Shows a card with a title and content
214
- *
215
- * Best for:
216
- * - Titled content
217
- * - Important information
218
- * - Structured data
219
- * - Notifications with context
220
- *
221
- * @param title - Card title
222
- * @param text - Main content text
223
- * @param options - Optional parameters (view, duration)
224
- *
225
- * @example
226
- * ```typescript
227
- * layouts.showReferenceCard(
228
- * 'Meeting Reminder',
229
- * 'Team standup in 5 minutes'
230
- * );
231
- * ```
232
- */
233
- showReferenceCard(title, text, options) {
234
- const layout = {
235
- layoutType: types_1.LayoutType.REFERENCE_CARD,
236
- title,
237
- text,
238
- };
239
- this.sendMessage(this.createDisplayEvent(layout, options?.view, options?.durationMs));
240
- }
241
- /**
242
- * ๐Ÿ“‡ Shows a bitmap
243
- *
244
- * Uses the proven animation system internally for proper L/R eye synchronization.
245
- * This ensures single bitmap displays work consistently with the same
246
- * hardware-optimized timing as animations.
247
- *
248
- * @param data - hex or base64 encoded bitmap data
249
- * @param options - Optional parameters (view, duration)
250
- *
251
- * @example
252
- * ```typescript
253
- * layouts.showBitmapView(
254
- * yourHexOrBase64EncodedBitmapDataString
255
- * );
256
- * ```
257
- */
258
- async showBitmapView(base64Bitmap, options) {
259
- const padding = options?.padding ?? { left: 50, top: 35 };
260
- const bitmapFrame = await bitmap_utils_1.BitmapUtils.padBase64Bitmap(base64Bitmap, padding);
261
- const validation = bitmap_utils_1.BitmapUtils.validateBase64Bitmap(bitmapFrame);
262
- if (!validation.isValid) {
263
- throw new Error(`โŒ Frame validation failed: ${validation.errors.join(", ")}`);
264
- }
265
- const layout = {
266
- layoutType: types_1.LayoutType.BITMAP_VIEW,
267
- data: bitmapFrame,
268
- };
269
- this.sendMessage(this.createDisplayEvent(layout, options?.view));
270
- }
271
- /**
272
- * ๐Ÿ“Š Shows a dashboard card with left and right text
273
- *
274
- * Best for:
275
- * - Key-value pairs
276
- * - Dashboard displays
277
- * - Metrics
278
- *
279
- * @param leftText - Left side text (typically label/key)
280
- * @param rightText - Right side text (typically value)
281
- * @param options - Optional parameters (view, duration)
282
- *
283
- * @example
284
- * ```typescript
285
- * layouts.showDashboardCard('Weather', '72ยฐF');
286
- * ```
287
- */
288
- showDashboardCard(leftText, rightText, options) {
289
- const layout = {
290
- layoutType: types_1.LayoutType.DASHBOARD_CARD,
291
- leftText,
292
- rightText,
293
- };
294
- this.sendMessage(this.createDisplayEvent(layout, options?.view || types_1.ViewType.DASHBOARD, options?.durationMs));
295
- }
296
- /**
297
- * ๐Ÿงน Clears the display
298
- *
299
- * Best for:
300
- * - Clearing previous content
301
- * - Resetting display state
302
- * - Starting fresh
303
- *
304
- * @param options - Optional parameters (view)
305
- *
306
- * @example
307
- * ```typescript
308
- * layouts.clearView();
309
- * layouts.clearView({ view: ViewType.DASHBOARD });
310
- * ```
311
- */
312
- clearView(options) {
313
- const layout = {
314
- layoutType: types_1.LayoutType.CLEAR_VIEW,
315
- };
316
- this.sendMessage(this.createDisplayEvent(layout, options?.view));
317
- }
318
- /**
319
- * ๐ŸŽฌ Shows an animated sequence of bitmap images (iOS-controlled timing)
320
- *
321
- * Sends complete animation package to iOS for device-controlled timing.
322
- * This provides superior performance and synchronization by letting
323
- * the device control the display timing directly.
324
- *
325
- * Best for:
326
- * - Smooth high-performance animations
327
- * - Precise timing control
328
- * - Synchronized left/right display
329
- * - EvenDemo-quality performance
330
- *
331
- * @param bitmapDataArray - Array of bitmap data strings (hex or base64 encoded)
332
- * @param intervalMs - Time between frames in milliseconds (default: 1650ms)
333
- * @param repeat - Whether to loop the animation continuously (default: false)
334
- * @param options - Optional parameters (view)
335
- *
336
- * @example
337
- * ```typescript
338
- * // Device-controlled animation (recommended)
339
- * const frames = ['hexdata1', 'hexdata2', 'hexdata3'];
340
- * layouts.showBitmapAnimation(frames, 1650, true);
341
- *
342
- * // Single-cycle animation
343
- * layouts.showBitmapAnimation(loadingFrames, 1650, false);
344
- * ```
345
- *
346
- * @returns Animation controller object with stop() method
347
- */
348
- showBitmapAnimation(bitmapDataArray, intervalMs = 1650, repeat = false, options) {
349
- // Validation
350
- if (!Array.isArray(bitmapDataArray) || bitmapDataArray.length === 0) {
351
- throw new Error("showBitmapAnimation requires a non-empty array of bitmap data");
352
- }
353
- // Send complete animation package to iOS for device-controlled timing
354
- const layout = {
355
- layoutType: types_1.LayoutType.BITMAP_ANIMATION,
356
- frames: bitmapDataArray,
357
- interval: intervalMs,
358
- repeat: repeat,
359
- };
360
- this.sendMessage(this.createDisplayEvent(layout, options?.view));
361
- console.log(`๐ŸŽฌ Sent batched animation to iOS: ${bitmapDataArray.length} frames at ${intervalMs}ms${repeat ? " (repeating)" : ""}`);
362
- // Return controller for compatibility
363
- return {
364
- stop: () => {
365
- // Send stop command to iOS
366
- this.clearView();
367
- console.log("๐Ÿ›‘ Animation stop requested");
368
- },
369
- };
370
- }
371
- }
372
- exports.LayoutManager = LayoutManager;
@@ -1,321 +0,0 @@
1
- "use strict";
2
- /**
3
- * ๐Ÿ”Š Audio Module
4
- *
5
- * Audio functionality for App Sessions.
6
- * Handles audio playback on connected glasses.
7
- */
8
- Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.AudioManager = void 0;
10
- const types_1 = require("../../../types");
11
- /**
12
- * ๐Ÿ”Š Audio Module Implementation
13
- *
14
- * Audio management for App Sessions.
15
- * Provides methods for:
16
- * - ๐ŸŽต Playing audio on glasses
17
- * - โน๏ธ Stopping audio playback
18
- * - ๐Ÿ” Monitoring audio request status
19
- * - ๐Ÿงน Cleanup and cancellation
20
- *
21
- * @example
22
- * ```typescript
23
- * // Play audio
24
- * const result = await session.audio.playAudio({
25
- * audioUrl: 'https://example.com/sound.mp3',
26
- * volume: 0.8
27
- * });
28
- *
29
- * // Stop all audio
30
- * session.audio.stopAudio();
31
- * ```
32
- */
33
- class AudioManager {
34
- /**
35
- * Create a new AudioManager
36
- *
37
- * @param packageName - The App package name
38
- * @param sessionId - The current session ID
39
- * @param send - Function to send messages to the cloud
40
- * @param session - Reference to the parent AppSession (optional)
41
- * @param logger - Logger instance for debugging
42
- */
43
- constructor(packageName, sessionId, send, session, logger) {
44
- /** Map to store pending audio play request promises */
45
- this.pendingAudioRequests = new Map();
46
- this.packageName = packageName;
47
- this.sessionId = sessionId;
48
- this.send = send;
49
- this.session = session;
50
- this.logger = logger || console;
51
- }
52
- // =====================================
53
- // ๐ŸŽต Audio Playback Functionality
54
- // =====================================
55
- /**
56
- * ๐Ÿ”Š Play audio on the connected glasses
57
- * @param options - Audio playback configuration
58
- * @returns Promise that resolves with playback result
59
- *
60
- * @example
61
- * ```typescript
62
- * // Play audio from URL
63
- * const result = await session.audio.playAudio({
64
- * audioUrl: 'https://example.com/sound.mp3',
65
- * volume: 0.8
66
- * });
67
- * ```
68
- */
69
- async playAudio(options) {
70
- return new Promise((resolve, reject) => {
71
- try {
72
- // Validate input
73
- if (!options.audioUrl) {
74
- reject("audioUrl must be provided");
75
- return;
76
- }
77
- // Generate unique request ID
78
- const requestId = `audio_req_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
79
- // Store promise resolvers for when we get the response
80
- this.pendingAudioRequests.set(requestId, { resolve, reject });
81
- // Create audio play request message
82
- const message = {
83
- type: types_1.AppToCloudMessageType.AUDIO_PLAY_REQUEST,
84
- packageName: this.packageName,
85
- sessionId: this.sessionId,
86
- requestId,
87
- timestamp: new Date(),
88
- audioUrl: options.audioUrl,
89
- volume: options.volume ?? 1.0,
90
- stopOtherAudio: options.stopOtherAudio ?? true,
91
- };
92
- // Send request to cloud
93
- this.send(message);
94
- // Set timeout to avoid hanging promises
95
- const timeoutMs = 60000; // 60 seconds
96
- if (this.session && this.session.resources) {
97
- // Use session's resource tracker for automatic cleeanup
98
- this.session.resources.setTimeout(() => {
99
- if (this.pendingAudioRequests.has(requestId)) {
100
- this.pendingAudioRequests
101
- .get(requestId)
102
- .reject("Audio play request timed out");
103
- this.pendingAudioRequests.delete(requestId);
104
- this.logger.warn({ requestId }, `๐Ÿ”Š Audio play request timed out`);
105
- }
106
- }, timeoutMs);
107
- }
108
- else {
109
- // Fallback to regular setTimeout if session not available
110
- setTimeout(() => {
111
- if (this.pendingAudioRequests.has(requestId)) {
112
- this.pendingAudioRequests
113
- .get(requestId)
114
- .reject("Audio play request timed out");
115
- this.pendingAudioRequests.delete(requestId);
116
- this.logger.warn({ requestId }, `๐Ÿ”Š Audio play request timed out`);
117
- }
118
- }, timeoutMs);
119
- }
120
- }
121
- catch (error) {
122
- const errorMessage = error instanceof Error ? error.message : String(error);
123
- reject(`Failed to play audio: ${errorMessage}`);
124
- }
125
- });
126
- }
127
- /**
128
- * ๐Ÿ”‡ Stop audio playback on the connected glasses
129
- *
130
- * @example
131
- * ```typescript
132
- * // Stop all currently playing audio
133
- * session.audio.stopAudio();
134
- * ```
135
- */
136
- stopAudio() {
137
- try {
138
- // Create audio stop request message
139
- const message = {
140
- type: types_1.AppToCloudMessageType.AUDIO_STOP_REQUEST,
141
- packageName: this.packageName,
142
- sessionId: this.sessionId,
143
- timestamp: new Date(),
144
- };
145
- // Send request to cloud (one-way, no response expected)
146
- this.send(message);
147
- this.logger.info(`๐Ÿ”‡ Audio stop request sent`);
148
- }
149
- catch (error) {
150
- const errorMessage = error instanceof Error ? error.message : String(error);
151
- this.logger.error(`Failed to stop audio: ${errorMessage}`);
152
- }
153
- }
154
- /**
155
- * ๐Ÿ—ฃ๏ธ Convert text to speech and play it on the connected glasses
156
- * @param text - Text to convert to speech (required)
157
- * @param options - Text-to-speech configuration (optional)
158
- * @returns Promise that resolves with playback result
159
- *
160
- * @example
161
- * ```typescript
162
- * // Basic text-to-speech
163
- * const result = await session.audio.speak('Hello, world!');
164
- *
165
- * // With custom voice settings
166
- * const result = await session.audio.speak('Hello, world!', {
167
- * voice_id: 'your_voice_id',
168
- * voice_settings: {
169
- * stability: 0.5,
170
- * speed: 1.2
171
- * },
172
- * volume: 0.8
173
- * });
174
- * ```
175
- */
176
- async speak(text, options = {}) {
177
- // Validate input
178
- if (!text) {
179
- throw new Error("text must be provided");
180
- }
181
- // Get the HTTPS server URL from the session
182
- const baseUrl = this.session?.getHttpsServerUrl?.();
183
- if (!baseUrl) {
184
- throw new Error("Cannot determine server URL for TTS endpoint");
185
- }
186
- // Build query parameters for the TTS endpoint
187
- const queryParams = new URLSearchParams();
188
- queryParams.append("text", text);
189
- if (options.voice_id) {
190
- queryParams.append("voice_id", options.voice_id);
191
- }
192
- if (options.model_id) {
193
- queryParams.append("model_id", options.model_id);
194
- }
195
- if (options.voice_settings) {
196
- queryParams.append("voice_settings", JSON.stringify(options.voice_settings));
197
- }
198
- // Construct the TTS URL
199
- const ttsUrl = `${baseUrl}/api/tts?${queryParams.toString()}`;
200
- this.logger.info({ text, ttsUrl }, `๐Ÿ—ฃ๏ธ Generating speech from text`);
201
- // Use the existing playAudio method to play the TTS audio
202
- return this.playAudio({
203
- audioUrl: ttsUrl,
204
- volume: options.volume,
205
- });
206
- }
207
- // =====================================
208
- // ๐Ÿ“ฅ Response Handling
209
- // =====================================
210
- /**
211
- * ๐Ÿ“ฅ Handle audio play response from cloud
212
- *
213
- * This method is called internally when an audio play response is received.
214
- * It resolves the corresponding pending promise with the response data.
215
- *
216
- * @param response - The audio play response received
217
- * @internal This method is used internally by AppSession
218
- */
219
- handleAudioPlayResponse(response) {
220
- const pendingRequest = this.pendingAudioRequests.get(response.requestId);
221
- if (pendingRequest) {
222
- // Resolve the promise with the response data
223
- pendingRequest.resolve({
224
- success: response.success,
225
- error: response.error,
226
- duration: response.duration,
227
- });
228
- // Clean up
229
- this.pendingAudioRequests.delete(response.requestId);
230
- this.logger.info({
231
- requestId: response.requestId,
232
- success: response.success,
233
- duration: response.duration,
234
- }, `๐Ÿ”Š Audio play response received`);
235
- }
236
- else {
237
- this.logger.warn({ requestId: response.requestId }, `๐Ÿ”Š Received audio play response for unknown request ID`);
238
- }
239
- }
240
- // =====================================
241
- // ๐Ÿ” Status and Management
242
- // =====================================
243
- /**
244
- * ๐Ÿ” Check if there are pending audio requests
245
- * @param requestId - Optional specific request ID to check
246
- * @returns True if there are pending requests (or specific request exists)
247
- */
248
- hasPendingRequest(requestId) {
249
- if (requestId) {
250
- return this.pendingAudioRequests.has(requestId);
251
- }
252
- return this.pendingAudioRequests.size > 0;
253
- }
254
- /**
255
- * ๐Ÿ“Š Get the number of pending audio requests
256
- * @returns Number of pending requests
257
- */
258
- getPendingRequestCount() {
259
- return this.pendingAudioRequests.size;
260
- }
261
- /**
262
- * ๐Ÿ“‹ Get all pending request IDs
263
- * @returns Array of pending request IDs
264
- */
265
- getPendingRequestIds() {
266
- return Array.from(this.pendingAudioRequests.keys());
267
- }
268
- /**
269
- * โŒ Cancel a specific audio request
270
- * @param requestId - The request ID to cancel
271
- * @returns True if the request was found and cancelled
272
- */
273
- cancelAudioRequest(requestId) {
274
- const pendingRequest = this.pendingAudioRequests.get(requestId);
275
- if (pendingRequest) {
276
- pendingRequest.reject("Audio request cancelled");
277
- this.pendingAudioRequests.delete(requestId);
278
- this.logger.info({ requestId }, `๐Ÿ”Š Audio request cancelled`);
279
- return true;
280
- }
281
- return false;
282
- }
283
- /**
284
- * ๐Ÿงน Cancel all pending audio requests
285
- * @returns Number of requests that were cancelled
286
- */
287
- cancelAllAudioRequests() {
288
- const count = this.pendingAudioRequests.size;
289
- this.pendingAudioRequests.forEach((request, requestId) => {
290
- request.reject("Audio request cancelled due to cleanup");
291
- this.logger.debug({ requestId }, `๐Ÿ”Š Audio request cancelled during cleanup`);
292
- });
293
- this.pendingAudioRequests.clear();
294
- if (count > 0) {
295
- this.logger.info({ cancelledCount: count }, `๐Ÿงน Cancelled all pending audio requests`);
296
- }
297
- return count;
298
- }
299
- // =====================================
300
- // ๐Ÿ”ง Internal Management
301
- // =====================================
302
- /**
303
- * ๐Ÿ”„ Update the session ID when reconnecting
304
- * @param newSessionId - The new session ID
305
- * @internal Used by AppSession during reconnection
306
- */
307
- updateSessionId(newSessionId) {
308
- this.sessionId = newSessionId;
309
- this.logger.debug({ newSessionId }, `๐Ÿ”„ Audio module session ID updated`);
310
- }
311
- /**
312
- * ๐Ÿงน Cancel all pending requests (cleanup)
313
- * @returns Object with count of cancelled requests
314
- * @internal Used by AppSession during cleanup
315
- */
316
- cancelAllRequests() {
317
- const audioRequests = this.cancelAllAudioRequests();
318
- return { audioRequests };
319
- }
320
- }
321
- exports.AudioManager = AudioManager;