@mentra/sdk 2.1.31-beta.5 → 3.0.0-alpha.1

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 (169) hide show
  1. package/README.md +54 -9
  2. package/dist/MiniAppServer.d.ts +58 -0
  3. package/dist/MiniAppServer.d.ts.map +1 -0
  4. package/dist/app/server/index.d.ts +208 -70
  5. package/dist/app/server/index.d.ts.map +1 -1
  6. package/dist/app/session/events.d.ts +20 -3
  7. package/dist/app/session/events.d.ts.map +1 -1
  8. package/dist/app/session/index.d.ts +56 -3
  9. package/dist/app/session/index.d.ts.map +1 -1
  10. package/dist/app/session/modules/audio-output-stream.d.ts +108 -0
  11. package/dist/app/session/modules/audio-output-stream.d.ts.map +1 -0
  12. package/dist/app/session/modules/audio.d.ts +36 -1
  13. package/dist/app/session/modules/audio.d.ts.map +1 -1
  14. package/dist/app/session/modules/camera-managed-extension.d.ts +37 -33
  15. package/dist/app/session/modules/camera-managed-extension.d.ts.map +1 -1
  16. package/dist/app/session/modules/camera.d.ts +113 -93
  17. package/dist/app/session/modules/camera.d.ts.map +1 -1
  18. package/dist/app/session/modules/index.d.ts +4 -3
  19. package/dist/app/session/modules/index.d.ts.map +1 -1
  20. package/dist/app/session/modules/led.d.ts.map +1 -1
  21. package/dist/app/session/modules/location.d.ts.map +1 -1
  22. package/dist/app/session/settings.d.ts +5 -1
  23. package/dist/app/session/settings.d.ts.map +1 -1
  24. package/dist/app/webview/index.d.ts +67 -9
  25. package/dist/app/webview/index.d.ts.map +1 -1
  26. package/dist/constants/log-messages/updates.d.ts +32 -9
  27. package/dist/constants/log-messages/updates.d.ts.map +1 -1
  28. package/dist/constants/log-messages/warning.d.ts +12 -0
  29. package/dist/constants/log-messages/warning.d.ts.map +1 -1
  30. package/dist/display-utils.d.ts +3 -1
  31. package/dist/display-utils.d.ts.map +1 -1
  32. package/dist/display-utils.js +443 -26
  33. package/dist/display-utils.js.map +10 -6
  34. package/dist/index.d.ts +22 -14
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +8957 -4428
  37. package/dist/index.js.map +63 -31
  38. package/dist/internal/_SessionManager.d.ts +76 -0
  39. package/dist/internal/_SessionManager.d.ts.map +1 -0
  40. package/dist/logging/clean-transport.d.ts +50 -0
  41. package/dist/logging/clean-transport.d.ts.map +1 -0
  42. package/dist/logging/errors.d.ts +90 -0
  43. package/dist/logging/errors.d.ts.map +1 -0
  44. package/dist/logging/logger.d.ts +72 -1
  45. package/dist/logging/logger.d.ts.map +1 -1
  46. package/dist/logging/telemetry-transport.d.ts +51 -0
  47. package/dist/logging/telemetry-transport.d.ts.map +1 -0
  48. package/dist/session/DataStreamRouter.d.ts +219 -0
  49. package/dist/session/DataStreamRouter.d.ts.map +1 -0
  50. package/dist/session/MentraSession.d.ts +102 -0
  51. package/dist/session/MentraSession.d.ts.map +1 -0
  52. package/dist/session/index.d.ts +2 -0
  53. package/dist/session/index.d.ts.map +1 -0
  54. package/dist/session/internal/_ConnectionManager.d.ts +43 -0
  55. package/dist/session/internal/_ConnectionManager.d.ts.map +1 -0
  56. package/dist/session/internal/_MessageRouter.d.ts +11 -0
  57. package/dist/session/internal/_MessageRouter.d.ts.map +1 -0
  58. package/dist/session/internal/_SubscriptionManager.d.ts +32 -0
  59. package/dist/session/internal/_SubscriptionManager.d.ts.map +1 -0
  60. package/dist/session/internal/_V2AudioStreamShim.d.ts +12 -0
  61. package/dist/session/internal/_V2AudioStreamShim.d.ts.map +1 -0
  62. package/dist/session/internal/_V2CameraShim.d.ts +52 -0
  63. package/dist/session/internal/_V2CameraShim.d.ts.map +1 -0
  64. package/dist/session/internal/_V2EventManagerShim.d.ts +52 -0
  65. package/dist/session/internal/_V2EventManagerShim.d.ts.map +1 -0
  66. package/dist/session/internal/_V2SessionShim.d.ts +169 -0
  67. package/dist/session/internal/_V2SessionShim.d.ts.map +1 -0
  68. package/dist/session/internal/_V2SettingsShim.d.ts +17 -0
  69. package/dist/session/internal/_V2SettingsShim.d.ts.map +1 -0
  70. package/dist/session/managers/CameraManager.d.ts +198 -0
  71. package/dist/session/managers/CameraManager.d.ts.map +1 -0
  72. package/dist/session/managers/DashboardManager.d.ts +131 -0
  73. package/dist/session/managers/DashboardManager.d.ts.map +1 -0
  74. package/dist/session/managers/DeviceManager.d.ts +348 -0
  75. package/dist/session/managers/DeviceManager.d.ts.map +1 -0
  76. package/dist/session/managers/DisplayManager.d.ts +171 -0
  77. package/dist/session/managers/DisplayManager.d.ts.map +1 -0
  78. package/dist/session/managers/LedManager.d.ts +116 -0
  79. package/dist/session/managers/LedManager.d.ts.map +1 -0
  80. package/dist/session/managers/LocationManager.d.ts +224 -0
  81. package/dist/session/managers/LocationManager.d.ts.map +1 -0
  82. package/dist/session/managers/MicManager.d.ts +252 -0
  83. package/dist/session/managers/MicManager.d.ts.map +1 -0
  84. package/dist/session/managers/PermissionsManager.d.ts +139 -0
  85. package/dist/session/managers/PermissionsManager.d.ts.map +1 -0
  86. package/dist/session/managers/PhoneManager.d.ts +351 -0
  87. package/dist/session/managers/PhoneManager.d.ts.map +1 -0
  88. package/dist/session/managers/SpeakerManager.d.ts +285 -0
  89. package/dist/session/managers/SpeakerManager.d.ts.map +1 -0
  90. package/dist/session/managers/StorageManager.d.ts +289 -0
  91. package/dist/session/managers/StorageManager.d.ts.map +1 -0
  92. package/dist/session/managers/TimeUtils.d.ts +175 -0
  93. package/dist/session/managers/TimeUtils.d.ts.map +1 -0
  94. package/dist/session/managers/TranscriptionManager.d.ts +195 -0
  95. package/dist/session/managers/TranscriptionManager.d.ts.map +1 -0
  96. package/dist/session/managers/TranslationManager.d.ts +189 -0
  97. package/dist/session/managers/TranslationManager.d.ts.map +1 -0
  98. package/dist/session.d.ts +41 -0
  99. package/dist/session.d.ts.map +1 -0
  100. package/dist/session.js +4110 -0
  101. package/dist/session.js.map +44 -0
  102. package/dist/transport/Transport.d.ts +119 -0
  103. package/dist/transport/Transport.d.ts.map +1 -0
  104. package/dist/transport/WebSocketTransport.d.ts +73 -0
  105. package/dist/transport/WebSocketTransport.d.ts.map +1 -0
  106. package/dist/types/index.d.ts +31 -5
  107. package/dist/types/index.d.ts.map +1 -1
  108. package/dist/types/message-types.d.ts +25 -9
  109. package/dist/types/message-types.d.ts.map +1 -1
  110. package/dist/types/messages/app-to-cloud.d.ts +113 -16
  111. package/dist/types/messages/app-to-cloud.d.ts.map +1 -1
  112. package/dist/types/messages/cloud-to-app.d.ts +50 -4
  113. package/dist/types/messages/cloud-to-app.d.ts.map +1 -1
  114. package/dist/types/messages/cloud-to-glasses.d.ts +43 -14
  115. package/dist/types/messages/cloud-to-glasses.d.ts.map +1 -1
  116. package/dist/types/messages/glasses-to-cloud.d.ts +5 -5
  117. package/dist/types/messages/glasses-to-cloud.d.ts.map +1 -1
  118. package/dist/types/models.d.ts +17 -0
  119. package/dist/types/models.d.ts.map +1 -1
  120. package/dist/types/rtmp-stream.d.ts +4 -4
  121. package/dist/types/rtmp-stream.d.ts.map +1 -1
  122. package/dist/types/streams.d.ts +6 -1
  123. package/dist/types/streams.d.ts.map +1 -1
  124. package/dist/types/webhooks.d.ts +11 -0
  125. package/dist/types/webhooks.d.ts.map +1 -1
  126. package/dist/utils/error-utils.d.ts +139 -0
  127. package/dist/utils/error-utils.d.ts.map +1 -0
  128. package/dist/utils/permissions-utils.d.ts +30 -7
  129. package/dist/utils/permissions-utils.d.ts.map +1 -1
  130. package/node_modules/@mentra/types/dist/applet.d.ts +5 -1
  131. package/node_modules/@mentra/types/dist/applet.d.ts.map +1 -1
  132. package/node_modules/@mentra/types/dist/capabilities/even-realities-g2.d.ts +12 -0
  133. package/node_modules/@mentra/types/dist/capabilities/even-realities-g2.d.ts.map +1 -0
  134. package/node_modules/@mentra/types/dist/capabilities/even-realities-g2.js +61 -0
  135. package/node_modules/@mentra/types/dist/capabilities/mentra-display.d.ts +12 -0
  136. package/node_modules/@mentra/types/dist/capabilities/mentra-display.d.ts.map +1 -0
  137. package/node_modules/@mentra/types/dist/capabilities/mentra-display.js +54 -0
  138. package/node_modules/@mentra/types/dist/capabilities/none.d.ts +13 -0
  139. package/node_modules/@mentra/types/dist/capabilities/none.d.ts.map +1 -0
  140. package/node_modules/@mentra/types/dist/capabilities/none.js +67 -0
  141. package/node_modules/@mentra/types/dist/enums.d.ts +8 -2
  142. package/node_modules/@mentra/types/dist/enums.d.ts.map +1 -1
  143. package/node_modules/@mentra/types/dist/enums.js +9 -2
  144. package/node_modules/@mentra/types/dist/hardware.d.ts +3 -1
  145. package/node_modules/@mentra/types/dist/hardware.d.ts.map +1 -1
  146. package/node_modules/@mentra/types/dist/hardware.js +12 -2
  147. package/node_modules/@mentra/types/dist/index.d.ts +1 -1
  148. package/node_modules/@mentra/types/dist/index.d.ts.map +1 -1
  149. package/node_modules/@mentra/types/dist/index.js +2 -1
  150. package/node_modules/@mentra/types/package.json +2 -2
  151. package/package.json +24 -12
  152. package/dist/examples/managed-rtmp-streaming-example.d.ts +0 -2
  153. package/dist/examples/managed-rtmp-streaming-example.d.ts.map +0 -1
  154. package/dist/examples/managed-rtmp-streaming-with-restream-example.d.ts +0 -11
  155. package/dist/examples/managed-rtmp-streaming-with-restream-example.d.ts.map +0 -1
  156. package/dist/examples/rtmp-streaming-example.d.ts +0 -2
  157. package/dist/examples/rtmp-streaming-example.d.ts.map +0 -1
  158. package/node_modules/@mentra/types/src/applet.ts +0 -51
  159. package/node_modules/@mentra/types/src/capabilities/even-realities-g1.ts +0 -63
  160. package/node_modules/@mentra/types/src/capabilities/mentra-live.ts +0 -103
  161. package/node_modules/@mentra/types/src/capabilities/simulated-glasses.ts +0 -76
  162. package/node_modules/@mentra/types/src/capabilities/vuzix-z100.ts +0 -60
  163. package/node_modules/@mentra/types/src/cli.ts +0 -169
  164. package/node_modules/@mentra/types/src/device.ts +0 -43
  165. package/node_modules/@mentra/types/src/enums.ts +0 -36
  166. package/node_modules/@mentra/types/src/hardware.ts +0 -172
  167. package/node_modules/@mentra/types/src/index.ts +0 -64
  168. package/node_modules/@mentra/types/tsconfig.json +0 -22
  169. package/node_modules/@mentra/types/tsconfig.tsbuildinfo +0 -1
@@ -0,0 +1,4110 @@
1
+ import { createRequire } from "node:module";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ function __accessProp(key) {
7
+ return this[key];
8
+ }
9
+ var __toCommonJS = (from) => {
10
+ var entry = (__moduleCache ??= new WeakMap).get(from), desc;
11
+ if (entry)
12
+ return entry;
13
+ entry = __defProp({}, "__esModule", { value: true });
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (var key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(entry, key))
17
+ __defProp(entry, key, {
18
+ get: __accessProp.bind(from, key),
19
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
20
+ });
21
+ }
22
+ __moduleCache.set(from, entry);
23
+ return entry;
24
+ };
25
+ var __moduleCache;
26
+ var __returnValue = (v) => v;
27
+ function __exportSetter(name, newValue) {
28
+ this[name] = __returnValue.bind(null, newValue);
29
+ }
30
+ var __export = (target, all) => {
31
+ for (var name in all)
32
+ __defProp(target, name, {
33
+ get: all[name],
34
+ enumerable: true,
35
+ configurable: true,
36
+ set: __exportSetter.bind(all, name)
37
+ });
38
+ };
39
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
40
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
41
+
42
+ // src/logging/clean-transport.ts
43
+ import { Writable } from "stream";
44
+ import chalk from "chalk";
45
+ function createCleanStream() {
46
+ return new Writable({
47
+ write(chunk, _encoding, callback) {
48
+ try {
49
+ const line = chunk.toString().trim();
50
+ if (!line) {
51
+ callback();
52
+ return;
53
+ }
54
+ const obj = JSON.parse(line);
55
+ const level = obj.level ?? 30;
56
+ const msg = obj.msg ?? "";
57
+ if (!msg) {
58
+ callback();
59
+ return;
60
+ }
61
+ if (obj._sdk === true && level < WARN_LEVEL && !VERBOSE) {
62
+ callback();
63
+ return;
64
+ }
65
+ const config = LEVEL_CONFIG[level] ?? DEFAULT_LEVEL_CONFIG;
66
+ const symbol = config.color(config.symbol);
67
+ const formatted = `${PREFIX} ${symbol} ${msg}
68
+ `;
69
+ process.stderr.write(formatted);
70
+ } catch {
71
+ process.stderr.write(chunk);
72
+ }
73
+ callback();
74
+ }
75
+ });
76
+ }
77
+ var LEVEL_CONFIG, VERBOSE, WARN_LEVEL = 40, DEFAULT_LEVEL_CONFIG, PREFIX;
78
+ var init_clean_transport = __esm(() => {
79
+ LEVEL_CONFIG = {
80
+ 10: { symbol: "·", color: chalk.dim },
81
+ 20: { symbol: "·", color: chalk.dim },
82
+ 30: { symbol: "✓", color: chalk.green },
83
+ 40: { symbol: "⚠", color: chalk.yellow },
84
+ 50: { symbol: "✗", color: chalk.red },
85
+ 60: { symbol: "✗", color: chalk.red }
86
+ };
87
+ VERBOSE = process.env.MENTRA_VERBOSE === "true" || process.env.MENTRA_VERBOSE === "1";
88
+ DEFAULT_LEVEL_CONFIG = { symbol: "·", color: chalk.dim };
89
+ PREFIX = chalk.dim("MentraOS");
90
+ });
91
+
92
+ // src/logging/logger.ts
93
+ var exports_logger = {};
94
+ __export(exports_logger, {
95
+ logger: () => logger,
96
+ default: () => logger_default,
97
+ createLogger: () => createLogger
98
+ });
99
+ import pino from "pino";
100
+ import { Writable as Writable2 } from "stream";
101
+ function toPinoLevel(level) {
102
+ return level === "none" ? "silent" : level;
103
+ }
104
+ function resolveConfig(config) {
105
+ const envVerbose = process.env.MENTRA_VERBOSE === "true" || process.env.MENTRA_VERBOSE === "1";
106
+ const envLevel = process.env.MENTRA_LOG_LEVEL;
107
+ if (envVerbose) {
108
+ return { pinoLevel: "debug", verbose: true };
109
+ }
110
+ if (envLevel && VALID_LEVELS.includes(envLevel)) {
111
+ const pinoLevel = toPinoLevel(envLevel);
112
+ return { pinoLevel, verbose: envLevel === "debug" };
113
+ }
114
+ if (config?.verbose) {
115
+ return { pinoLevel: "debug", verbose: true };
116
+ }
117
+ if (config?.logLevel && VALID_LEVELS.includes(config.logLevel)) {
118
+ const pinoLevel = toPinoLevel(config.logLevel);
119
+ return { pinoLevel, verbose: config.logLevel === "debug" };
120
+ }
121
+ return { pinoLevel: "info", verbose: false };
122
+ }
123
+ function createNoopStream() {
124
+ return new Writable2({
125
+ write(_chunk, _encoding, callback) {
126
+ callback();
127
+ }
128
+ });
129
+ }
130
+ function tryCreatePrettyTransport() {
131
+ try {
132
+ const prettyTransport = pino.transport({
133
+ target: "pino-pretty",
134
+ options: {
135
+ colorize: true,
136
+ translateTime: "SYS:standard",
137
+ ignore: "pid,hostname,env,module,server",
138
+ messageFormat: "{msg}",
139
+ errorProps: "*"
140
+ }
141
+ });
142
+ return { stream: prettyTransport, level: "debug" };
143
+ } catch {
144
+ return null;
145
+ }
146
+ }
147
+ function tryCreateBetterStackTransport() {
148
+ const token = process.env.BETTERSTACK_SOURCE_TOKEN;
149
+ if (!token)
150
+ return null;
151
+ const endpoint = process.env.BETTERSTACK_ENDPOINT || "https://s1311181.eu-nbg-2.betterstackdata.com";
152
+ try {
153
+ const transport = pino.transport({
154
+ target: "@logtail/pino",
155
+ options: {
156
+ sourceToken: token,
157
+ options: { endpoint }
158
+ }
159
+ });
160
+ return { stream: transport, level: "debug" };
161
+ } catch {
162
+ return null;
163
+ }
164
+ }
165
+ function createLogger(config) {
166
+ const { pinoLevel, verbose } = resolveConfig(config);
167
+ const NODE_ENV = "development";
168
+ const PORTER_APP_NAME = process.env.PORTER_APP_NAME || "cloud-local";
169
+ const streams = [];
170
+ if (pinoLevel !== "silent") {
171
+ if (verbose) {
172
+ const pretty = tryCreatePrettyTransport();
173
+ if (pretty) {
174
+ pretty.level = pinoLevel;
175
+ streams.push(pretty);
176
+ } else {
177
+ process.stderr.write("[MentraOS] pino-pretty not installed — verbose output will be JSON. " + `Install it with: bun add -d pino-pretty
178
+ `);
179
+ streams.push({ stream: process.stdout, level: pinoLevel });
180
+ }
181
+ } else {
182
+ streams.push({ stream: createCleanStream(), level: pinoLevel });
183
+ }
184
+ }
185
+ const betterStack = tryCreateBetterStackTransport();
186
+ if (betterStack) {
187
+ streams.push(betterStack);
188
+ }
189
+ if (streams.length === 0) {
190
+ streams.push({ stream: createNoopStream(), level: "silent" });
191
+ }
192
+ const multistream = pino.multistream(streams);
193
+ return pino({
194
+ level: "debug",
195
+ base: {
196
+ env: NODE_ENV,
197
+ server: PORTER_APP_NAME
198
+ },
199
+ timestamp: pino.stdTimeFunctions.isoTime
200
+ }, multistream);
201
+ }
202
+ var VALID_LEVELS, logger, logger_default;
203
+ var init_logger = __esm(() => {
204
+ init_clean_transport();
205
+ VALID_LEVELS = ["none", "error", "warn", "info", "debug"];
206
+ logger = createLogger({
207
+ logLevel: "debug",
208
+ verbose: true
209
+ });
210
+ logger_default = logger;
211
+ });
212
+
213
+ // src/types/streams.ts
214
+ function createLanguageStream(type) {
215
+ return type;
216
+ }
217
+ function isValidLanguageCode(code) {
218
+ return /^[a-z]{2,3}-[A-Z]{2}$/.test(code);
219
+ }
220
+ function parseLanguageStream(subscription) {
221
+ if (typeof subscription !== "string") {
222
+ return null;
223
+ }
224
+ if (subscription.startsWith(`${"transcription" /* TRANSCRIPTION */}:`)) {
225
+ const [baseType, rest] = subscription.split(":");
226
+ const [languageCode, queryString] = rest?.split("?") ?? [];
227
+ if (languageCode && (languageCode === "auto" || isValidLanguageCode(languageCode))) {
228
+ const options = {};
229
+ if (queryString) {
230
+ const params = new URLSearchParams(queryString);
231
+ for (const [key, value] of params.entries()) {
232
+ if (value === "true") {
233
+ options[key] = true;
234
+ } else if (value === "false") {
235
+ options[key] = false;
236
+ } else {
237
+ options[key] = value;
238
+ }
239
+ }
240
+ }
241
+ return {
242
+ type: "transcription" /* TRANSCRIPTION */,
243
+ baseType,
244
+ transcribeLanguage: languageCode,
245
+ options: Object.keys(options).length > 0 ? options : undefined,
246
+ original: subscription
247
+ };
248
+ }
249
+ }
250
+ if (subscription.startsWith(`${"translation" /* TRANSLATION */}:`)) {
251
+ const [baseType, rest] = subscription.split(":");
252
+ const [languagePair, queryString] = rest?.split("?") ?? [];
253
+ const [sourceLanguage, targetLanguage] = languagePair?.split("-to-") ?? [];
254
+ const isAllToFormat = sourceLanguage === "all" && targetLanguage && isValidLanguageCode(targetLanguage);
255
+ const isSpecificPairFormat = sourceLanguage && targetLanguage && isValidLanguageCode(sourceLanguage) && isValidLanguageCode(targetLanguage);
256
+ if (isAllToFormat || isSpecificPairFormat) {
257
+ const options = {};
258
+ if (queryString) {
259
+ const params = new URLSearchParams(queryString);
260
+ for (const [key, value] of params.entries()) {
261
+ if (value === "true") {
262
+ options[key] = true;
263
+ } else if (value === "false") {
264
+ options[key] = false;
265
+ } else {
266
+ options[key] = value;
267
+ }
268
+ }
269
+ }
270
+ return {
271
+ type: "translation" /* TRANSLATION */,
272
+ baseType,
273
+ transcribeLanguage: sourceLanguage,
274
+ translateLanguage: targetLanguage,
275
+ options: Object.keys(options).length > 0 ? options : undefined,
276
+ original: subscription
277
+ };
278
+ }
279
+ }
280
+ return null;
281
+ }
282
+ function createTranscriptionStream(language, options) {
283
+ console.log(`\uD83C\uDFA4 Creating transcription stream for language: ${language}`);
284
+ console.log(`\uD83C\uDFA4 Options: ${JSON.stringify(options)}`);
285
+ const languageCode = language.split("?")[0];
286
+ if (languageCode !== "auto" && !isValidLanguageCode(languageCode)) {
287
+ throw new Error(`Invalid language code: ${languageCode}`);
288
+ }
289
+ const base = `${"transcription" /* TRANSCRIPTION */}:${languageCode}`;
290
+ const params = new URLSearchParams;
291
+ if (options?.disableLanguageIdentification) {
292
+ params.set("no-language-identification", "true");
293
+ }
294
+ if (options?.hints && options.hints.length > 0) {
295
+ params.set("hints", options.hints.join(","));
296
+ }
297
+ const queryString = params.toString();
298
+ return queryString ? `${base}?${queryString}` : base;
299
+ }
300
+ function createTranslationStream(sourceLanguage, targetLanguage, options) {
301
+ const cleanSourceLanguage = sourceLanguage.split("?")[0];
302
+ const cleanTargetLanguage = targetLanguage.split("?")[0];
303
+ const isAllToFormat = cleanSourceLanguage === "all";
304
+ if (!isAllToFormat && !isValidLanguageCode(cleanSourceLanguage) || !isValidLanguageCode(cleanTargetLanguage)) {
305
+ throw new Error(`Invalid language code(s): ${cleanSourceLanguage}, ${cleanTargetLanguage}`);
306
+ }
307
+ const base = `${"translation" /* TRANSLATION */}:${cleanSourceLanguage}-to-${cleanTargetLanguage}`;
308
+ if (options?.disableLanguageIdentification) {
309
+ return `${base}?no-language-identification=true`;
310
+ }
311
+ return createLanguageStream(base);
312
+ }
313
+ function parseTouchEventStream(subscription) {
314
+ if (typeof subscription !== "string") {
315
+ return null;
316
+ }
317
+ if (subscription.startsWith(`${"touch_event" /* TOUCH_EVENT */}:`)) {
318
+ const [, gestureName] = subscription.split(":");
319
+ const validGestures = [
320
+ "single_tap",
321
+ "double_tap",
322
+ "triple_tap",
323
+ "long_press",
324
+ "forward_swipe",
325
+ "backward_swipe",
326
+ "up_swipe",
327
+ "down_swipe"
328
+ ];
329
+ if (gestureName && validGestures.includes(gestureName)) {
330
+ return gestureName;
331
+ }
332
+ }
333
+ return null;
334
+ }
335
+ function createTouchEventStream(gesture) {
336
+ const validGestures = [
337
+ "single_tap",
338
+ "double_tap",
339
+ "triple_tap",
340
+ "long_press",
341
+ "forward_swipe",
342
+ "backward_swipe",
343
+ "up_swipe",
344
+ "down_swipe"
345
+ ];
346
+ if (!validGestures.includes(gesture)) {
347
+ throw new Error(`Invalid gesture: ${gesture}`);
348
+ }
349
+ return `${"touch_event" /* TOUCH_EVENT */}:${gesture}`;
350
+ }
351
+ function createUniversalTranslationStream(targetLanguage, options) {
352
+ const cleanTargetLanguage = targetLanguage.split("?")[0];
353
+ if (!isValidLanguageCode(cleanTargetLanguage)) {
354
+ throw new Error(`Invalid target language code: ${cleanTargetLanguage}`);
355
+ }
356
+ const base = `${"translation" /* TRANSLATION */}:all-to-${cleanTargetLanguage}`;
357
+ if (options?.disableLanguageIdentification) {
358
+ return `${base}?no-language-identification=true`;
359
+ }
360
+ return createLanguageStream(base);
361
+ }
362
+ function isValidStreamType(subscription) {
363
+ if (Object.values(StreamType).includes(subscription)) {
364
+ return true;
365
+ }
366
+ const languageStream = parseLanguageStream(subscription);
367
+ if (languageStream !== null) {
368
+ return true;
369
+ }
370
+ const gestureStream = parseTouchEventStream(subscription);
371
+ if (gestureStream !== null) {
372
+ return true;
373
+ }
374
+ return false;
375
+ }
376
+ function isStreamCategory(streamType, category) {
377
+ const baseType = getBaseStreamType(streamType);
378
+ return baseType ? STREAM_CATEGORIES[baseType] === category : false;
379
+ }
380
+ function getStreamTypesByCategory(category) {
381
+ return Object.entries(STREAM_CATEGORIES).filter(([_, cat]) => cat === category).map(([type]) => type);
382
+ }
383
+ function getBaseStreamType(subscription) {
384
+ if (Object.values(StreamType).includes(subscription)) {
385
+ return subscription;
386
+ }
387
+ const languageStream = parseLanguageStream(subscription);
388
+ if (languageStream) {
389
+ return languageStream.type;
390
+ }
391
+ const gestureStream = parseTouchEventStream(subscription);
392
+ if (gestureStream) {
393
+ return "touch_event" /* TOUCH_EVENT */;
394
+ }
395
+ return null;
396
+ }
397
+ function isLanguageStream(subscription) {
398
+ return parseLanguageStream(subscription) !== null;
399
+ }
400
+ function getLanguageInfo(subscription) {
401
+ return parseLanguageStream(subscription);
402
+ }
403
+ var StreamType, StreamCategory, STREAM_CATEGORIES;
404
+ var init_streams = __esm(() => {
405
+ ((StreamType2) => {
406
+ StreamType2["BUTTON_PRESS"] = "button_press";
407
+ StreamType2["HEAD_POSITION"] = "head_position";
408
+ StreamType2["TOUCH_EVENT"] = "touch_event";
409
+ StreamType2["GLASSES_BATTERY_UPDATE"] = "glasses_battery_update";
410
+ StreamType2["PHONE_BATTERY_UPDATE"] = "phone_battery_update";
411
+ StreamType2["GLASSES_CONNECTION_STATE"] = "glasses_connection_state";
412
+ StreamType2["LOCATION_UPDATE"] = "location_update";
413
+ StreamType2["LOCATION_STREAM"] = "location_stream";
414
+ StreamType2["VPS_COORDINATES"] = "vps_coordinates";
415
+ StreamType2["TRANSCRIPTION"] = "transcription";
416
+ StreamType2["TRANSLATION"] = "translation";
417
+ StreamType2["VAD"] = "VAD";
418
+ StreamType2["AUDIO_CHUNK"] = "audio_chunk";
419
+ StreamType2["PHONE_NOTIFICATION"] = "phone_notification";
420
+ StreamType2["PHONE_NOTIFICATION_DISMISSED"] = "phone_notification_dismissed";
421
+ StreamType2["CALENDAR_EVENT"] = "calendar_event";
422
+ StreamType2["START_APP"] = "start_app";
423
+ StreamType2["STOP_APP"] = "stop_app";
424
+ StreamType2["OPEN_DASHBOARD"] = "open_dashboard";
425
+ StreamType2["CORE_STATUS_UPDATE"] = "core_status_update";
426
+ StreamType2["VIDEO"] = "video";
427
+ StreamType2["PHOTO_REQUEST"] = "photo_request";
428
+ StreamType2["PHOTO_RESPONSE"] = "photo_response";
429
+ StreamType2["STREAM_STATUS"] = "stream_status";
430
+ StreamType2["MANAGED_STREAM_STATUS"] = "managed_stream_status";
431
+ StreamType2["ALL"] = "all";
432
+ StreamType2["WILDCARD"] = "*";
433
+ StreamType2["MENTRAOS_SETTINGS_UPDATE_REQUEST"] = "settings_update_request";
434
+ StreamType2["CUSTOM_MESSAGE"] = "custom_message";
435
+ StreamType2["PHOTO_TAKEN"] = "photo_taken";
436
+ })(StreamType ||= {});
437
+ ((StreamCategory2) => {
438
+ StreamCategory2["HARDWARE"] = "hardware";
439
+ StreamCategory2["AUDIO"] = "audio";
440
+ StreamCategory2["PHONE"] = "phone";
441
+ StreamCategory2["SYSTEM"] = "system";
442
+ })(StreamCategory ||= {});
443
+ STREAM_CATEGORIES = {
444
+ ["button_press" /* BUTTON_PRESS */]: "hardware" /* HARDWARE */,
445
+ ["head_position" /* HEAD_POSITION */]: "hardware" /* HARDWARE */,
446
+ ["touch_event" /* TOUCH_EVENT */]: "hardware" /* HARDWARE */,
447
+ ["glasses_battery_update" /* GLASSES_BATTERY_UPDATE */]: "hardware" /* HARDWARE */,
448
+ ["phone_battery_update" /* PHONE_BATTERY_UPDATE */]: "hardware" /* HARDWARE */,
449
+ ["glasses_connection_state" /* GLASSES_CONNECTION_STATE */]: "hardware" /* HARDWARE */,
450
+ ["location_update" /* LOCATION_UPDATE */]: "hardware" /* HARDWARE */,
451
+ ["location_stream" /* LOCATION_STREAM */]: "hardware" /* HARDWARE */,
452
+ ["vps_coordinates" /* VPS_COORDINATES */]: "hardware" /* HARDWARE */,
453
+ ["transcription" /* TRANSCRIPTION */]: "audio" /* AUDIO */,
454
+ ["translation" /* TRANSLATION */]: "audio" /* AUDIO */,
455
+ ["VAD" /* VAD */]: "audio" /* AUDIO */,
456
+ ["audio_chunk" /* AUDIO_CHUNK */]: "audio" /* AUDIO */,
457
+ ["phone_notification" /* PHONE_NOTIFICATION */]: "phone" /* PHONE */,
458
+ ["phone_notification_dismissed" /* PHONE_NOTIFICATION_DISMISSED */]: "phone" /* PHONE */,
459
+ ["calendar_event" /* CALENDAR_EVENT */]: "phone" /* PHONE */,
460
+ ["start_app" /* START_APP */]: "system" /* SYSTEM */,
461
+ ["stop_app" /* STOP_APP */]: "system" /* SYSTEM */,
462
+ ["open_dashboard" /* OPEN_DASHBOARD */]: "system" /* SYSTEM */,
463
+ ["core_status_update" /* CORE_STATUS_UPDATE */]: "system" /* SYSTEM */,
464
+ ["video" /* VIDEO */]: "hardware" /* HARDWARE */,
465
+ ["photo_request" /* PHOTO_REQUEST */]: "hardware" /* HARDWARE */,
466
+ ["photo_response" /* PHOTO_RESPONSE */]: "hardware" /* HARDWARE */,
467
+ ["stream_status" /* STREAM_STATUS */]: "hardware" /* HARDWARE */,
468
+ ["managed_stream_status" /* MANAGED_STREAM_STATUS */]: "hardware" /* HARDWARE */,
469
+ ["all" /* ALL */]: "system" /* SYSTEM */,
470
+ ["*" /* WILDCARD */]: "system" /* SYSTEM */,
471
+ ["settings_update_request" /* MENTRAOS_SETTINGS_UPDATE_REQUEST */]: "system" /* SYSTEM */,
472
+ ["custom_message" /* CUSTOM_MESSAGE */]: "system" /* SYSTEM */,
473
+ ["photo_taken" /* PHOTO_TAKEN */]: "hardware" /* HARDWARE */
474
+ };
475
+ });
476
+
477
+ // src/types/message-types.ts
478
+ var GlassesToCloudMessageType, CloudToGlassesMessageType, AppToCloudMessageType, CloudToAppMessageType, ControlActionTypes, EventTypes, ResponseTypes, UpdateTypes, DashboardMessageTypes;
479
+ var init_message_types = __esm(() => {
480
+ init_streams();
481
+ ((GlassesToCloudMessageType2) => {
482
+ GlassesToCloudMessageType2["CONNECTION_INIT"] = "connection_init";
483
+ GlassesToCloudMessageType2["REQUEST_SETTINGS"] = "request_settings";
484
+ GlassesToCloudMessageType2[GlassesToCloudMessageType2["START_APP"] = "start_app" /* START_APP */] = "START_APP";
485
+ GlassesToCloudMessageType2[GlassesToCloudMessageType2["STOP_APP"] = "stop_app" /* STOP_APP */] = "STOP_APP";
486
+ GlassesToCloudMessageType2["DASHBOARD_STATE"] = "dashboard_state";
487
+ GlassesToCloudMessageType2[GlassesToCloudMessageType2["OPEN_DASHBOARD"] = "open_dashboard" /* OPEN_DASHBOARD */] = "OPEN_DASHBOARD";
488
+ GlassesToCloudMessageType2[GlassesToCloudMessageType2["PHOTO_RESPONSE"] = "photo_response" /* PHOTO_RESPONSE */] = "PHOTO_RESPONSE";
489
+ GlassesToCloudMessageType2["LOCAL_TRANSCRIPTION"] = "local_transcription";
490
+ GlassesToCloudMessageType2[GlassesToCloudMessageType2["STREAM_STATUS"] = "stream_status" /* STREAM_STATUS */] = "STREAM_STATUS";
491
+ GlassesToCloudMessageType2["KEEP_ALIVE_ACK"] = "keep_alive_ack";
492
+ GlassesToCloudMessageType2[GlassesToCloudMessageType2["BUTTON_PRESS"] = "button_press" /* BUTTON_PRESS */] = "BUTTON_PRESS";
493
+ GlassesToCloudMessageType2[GlassesToCloudMessageType2["HEAD_POSITION"] = "head_position" /* HEAD_POSITION */] = "HEAD_POSITION";
494
+ GlassesToCloudMessageType2[GlassesToCloudMessageType2["TOUCH_EVENT"] = "touch_event" /* TOUCH_EVENT */] = "TOUCH_EVENT";
495
+ GlassesToCloudMessageType2[GlassesToCloudMessageType2["GLASSES_BATTERY_UPDATE"] = "glasses_battery_update" /* GLASSES_BATTERY_UPDATE */] = "GLASSES_BATTERY_UPDATE";
496
+ GlassesToCloudMessageType2[GlassesToCloudMessageType2["PHONE_BATTERY_UPDATE"] = "phone_battery_update" /* PHONE_BATTERY_UPDATE */] = "PHONE_BATTERY_UPDATE";
497
+ GlassesToCloudMessageType2[GlassesToCloudMessageType2["GLASSES_CONNECTION_STATE"] = "glasses_connection_state" /* GLASSES_CONNECTION_STATE */] = "GLASSES_CONNECTION_STATE";
498
+ GlassesToCloudMessageType2[GlassesToCloudMessageType2["LOCATION_UPDATE"] = "location_update" /* LOCATION_UPDATE */] = "LOCATION_UPDATE";
499
+ GlassesToCloudMessageType2[GlassesToCloudMessageType2["VPS_COORDINATES"] = "vps_coordinates" /* VPS_COORDINATES */] = "VPS_COORDINATES";
500
+ GlassesToCloudMessageType2[GlassesToCloudMessageType2["VAD"] = "VAD" /* VAD */] = "VAD";
501
+ GlassesToCloudMessageType2[GlassesToCloudMessageType2["PHONE_NOTIFICATION"] = "phone_notification" /* PHONE_NOTIFICATION */] = "PHONE_NOTIFICATION";
502
+ GlassesToCloudMessageType2[GlassesToCloudMessageType2["PHONE_NOTIFICATION_DISMISSED"] = "phone_notification_dismissed" /* PHONE_NOTIFICATION_DISMISSED */] = "PHONE_NOTIFICATION_DISMISSED";
503
+ GlassesToCloudMessageType2[GlassesToCloudMessageType2["CALENDAR_EVENT"] = "calendar_event" /* CALENDAR_EVENT */] = "CALENDAR_EVENT";
504
+ GlassesToCloudMessageType2[GlassesToCloudMessageType2["MENTRAOS_SETTINGS_UPDATE_REQUEST"] = "settings_update_request" /* MENTRAOS_SETTINGS_UPDATE_REQUEST */] = "MENTRAOS_SETTINGS_UPDATE_REQUEST";
505
+ GlassesToCloudMessageType2[GlassesToCloudMessageType2["CORE_STATUS_UPDATE"] = "core_status_update" /* CORE_STATUS_UPDATE */] = "CORE_STATUS_UPDATE";
506
+ GlassesToCloudMessageType2[GlassesToCloudMessageType2["PHOTO_TAKEN"] = "photo_taken" /* PHOTO_TAKEN */] = "PHOTO_TAKEN";
507
+ GlassesToCloudMessageType2["AUDIO_PLAY_RESPONSE"] = "audio_play_response";
508
+ GlassesToCloudMessageType2["RGB_LED_CONTROL_RESPONSE"] = "rgb_led_control_response";
509
+ GlassesToCloudMessageType2["LIVEKIT_INIT"] = "livekit_init";
510
+ GlassesToCloudMessageType2["UDP_REGISTER"] = "udp_register";
511
+ GlassesToCloudMessageType2["UDP_UNREGISTER"] = "udp_unregister";
512
+ })(GlassesToCloudMessageType ||= {});
513
+ ((CloudToGlassesMessageType2) => {
514
+ CloudToGlassesMessageType2["CONNECTION_ACK"] = "connection_ack";
515
+ CloudToGlassesMessageType2["CONNECTION_ERROR"] = "connection_error";
516
+ CloudToGlassesMessageType2["AUTH_ERROR"] = "auth_error";
517
+ CloudToGlassesMessageType2["DISPLAY_EVENT"] = "display_event";
518
+ CloudToGlassesMessageType2["APP_STATE_CHANGE"] = "app_state_change";
519
+ CloudToGlassesMessageType2["MICROPHONE_STATE_CHANGE"] = "microphone_state_change";
520
+ CloudToGlassesMessageType2["SETTINGS_UPDATE"] = "settings_update";
521
+ CloudToGlassesMessageType2["PHOTO_REQUEST"] = "photo_request";
522
+ CloudToGlassesMessageType2["AUDIO_PLAY_REQUEST"] = "audio_play_request";
523
+ CloudToGlassesMessageType2["AUDIO_STOP_REQUEST"] = "audio_stop_request";
524
+ CloudToGlassesMessageType2["RGB_LED_CONTROL"] = "rgb_led_control";
525
+ CloudToGlassesMessageType2["CAMERA_FOV_SET"] = "camera_fov_set";
526
+ CloudToGlassesMessageType2["SHOW_WIFI_SETUP"] = "show_wifi_setup";
527
+ CloudToGlassesMessageType2["START_STREAM"] = "start_stream";
528
+ CloudToGlassesMessageType2["STOP_STREAM"] = "stop_stream";
529
+ CloudToGlassesMessageType2["KEEP_STREAM_ALIVE"] = "keep_stream_alive";
530
+ CloudToGlassesMessageType2["DASHBOARD_MODE_CHANGE"] = "dashboard_mode_change";
531
+ CloudToGlassesMessageType2["DASHBOARD_ALWAYS_ON_CHANGE"] = "dashboard_always_on_change";
532
+ CloudToGlassesMessageType2["SET_LOCATION_TIER"] = "set_location_tier";
533
+ CloudToGlassesMessageType2["REQUEST_SINGLE_LOCATION"] = "request_single_location";
534
+ CloudToGlassesMessageType2["WEBSOCKET_ERROR"] = "websocket_error";
535
+ CloudToGlassesMessageType2["LIVEKIT_INFO"] = "livekit_info";
536
+ CloudToGlassesMessageType2["UDP_PING_ACK"] = "udp_ping_ack";
537
+ })(CloudToGlassesMessageType ||= {});
538
+ ((AppToCloudMessageType2) => {
539
+ AppToCloudMessageType2["CONNECTION_INIT"] = "tpa_connection_init";
540
+ AppToCloudMessageType2["RECONNECT"] = "reconnect";
541
+ AppToCloudMessageType2["SUBSCRIPTION_UPDATE"] = "subscription_update";
542
+ AppToCloudMessageType2["LOCATION_POLL_REQUEST"] = "location_poll_request";
543
+ AppToCloudMessageType2["DISPLAY_REQUEST"] = "display_event";
544
+ AppToCloudMessageType2["PHOTO_REQUEST"] = "photo_request";
545
+ AppToCloudMessageType2["AUDIO_PLAY_REQUEST"] = "audio_play_request";
546
+ AppToCloudMessageType2["AUDIO_STOP_REQUEST"] = "audio_stop_request";
547
+ AppToCloudMessageType2["AUDIO_STREAM_START"] = "audio_stream_start";
548
+ AppToCloudMessageType2["AUDIO_STREAM_END"] = "audio_stream_end";
549
+ AppToCloudMessageType2["RGB_LED_CONTROL"] = "rgb_led_control";
550
+ AppToCloudMessageType2["CAMERA_FOV_SET"] = "camera_fov_set";
551
+ AppToCloudMessageType2["REQUEST_WIFI_SETUP"] = "request_wifi_setup";
552
+ AppToCloudMessageType2["STREAM_REQUEST"] = "stream_request";
553
+ AppToCloudMessageType2["STREAM_STOP"] = "stream_stop";
554
+ AppToCloudMessageType2["MANAGED_STREAM_REQUEST"] = "managed_stream_request";
555
+ AppToCloudMessageType2["MANAGED_STREAM_STOP"] = "managed_stream_stop";
556
+ AppToCloudMessageType2["STREAM_STATUS_CHECK"] = "stream_status_check";
557
+ AppToCloudMessageType2["DASHBOARD_CONTENT_UPDATE"] = "dashboard_content_update";
558
+ AppToCloudMessageType2["DASHBOARD_MODE_CHANGE"] = "dashboard_mode_change";
559
+ AppToCloudMessageType2["DASHBOARD_SYSTEM_UPDATE"] = "dashboard_system_update";
560
+ AppToCloudMessageType2["APP_BROADCAST_MESSAGE"] = "app_broadcast_message";
561
+ AppToCloudMessageType2["APP_DIRECT_MESSAGE"] = "app_direct_message";
562
+ AppToCloudMessageType2["APP_USER_DISCOVERY"] = "app_user_discovery";
563
+ AppToCloudMessageType2["APP_ROOM_JOIN"] = "app_room_join";
564
+ AppToCloudMessageType2["APP_ROOM_LEAVE"] = "app_room_leave";
565
+ AppToCloudMessageType2["OWNERSHIP_RELEASE"] = "ownership_release";
566
+ AppToCloudMessageType2["TELEMETRY_RESPONSE"] = "telemetry_response";
567
+ })(AppToCloudMessageType ||= {});
568
+ ((CloudToAppMessageType2) => {
569
+ CloudToAppMessageType2["CONNECTION_ACK"] = "tpa_connection_ack";
570
+ CloudToAppMessageType2["CONNECTION_ERROR"] = "tpa_connection_error";
571
+ CloudToAppMessageType2["RECONNECT_ACK"] = "reconnect_ack";
572
+ CloudToAppMessageType2["RECONNECT_REJECTED"] = "reconnect_rejected";
573
+ CloudToAppMessageType2["RECONNECT_DEFERRED"] = "reconnect_deferred";
574
+ CloudToAppMessageType2["APP_STOPPED"] = "app_stopped";
575
+ CloudToAppMessageType2["SETTINGS_UPDATE"] = "settings_update";
576
+ CloudToAppMessageType2["CAPABILITIES_UPDATE"] = "capabilities_update";
577
+ CloudToAppMessageType2["DEVICE_STATE_UPDATE"] = "device_state_update";
578
+ CloudToAppMessageType2["DASHBOARD_MODE_CHANGED"] = "dashboard_mode_changed";
579
+ CloudToAppMessageType2["DASHBOARD_ALWAYS_ON_CHANGED"] = "dashboard_always_on_changed";
580
+ CloudToAppMessageType2["DATA_STREAM"] = "data_stream";
581
+ CloudToAppMessageType2["PHOTO_RESPONSE"] = "photo_response";
582
+ CloudToAppMessageType2["AUDIO_PLAY_RESPONSE"] = "audio_play_response";
583
+ CloudToAppMessageType2["AUDIO_STREAM_READY"] = "audio_stream_ready";
584
+ CloudToAppMessageType2["RGB_LED_CONTROL_RESPONSE"] = "rgb_led_control_response";
585
+ CloudToAppMessageType2["STREAM_STATUS"] = "stream_status";
586
+ CloudToAppMessageType2["MANAGED_STREAM_STATUS"] = "managed_stream_status";
587
+ CloudToAppMessageType2["STREAM_STATUS_CHECK_RESPONSE"] = "stream_status_check_response";
588
+ CloudToAppMessageType2["WEBSOCKET_ERROR"] = "websocket_error";
589
+ CloudToAppMessageType2["PERMISSION_ERROR"] = "permission_error";
590
+ CloudToAppMessageType2["REQUEST_TELEMETRY"] = "request_telemetry";
591
+ CloudToAppMessageType2["CUSTOM_MESSAGE"] = "custom_message";
592
+ CloudToAppMessageType2["APP_MESSAGE_RECEIVED"] = "app_message_received";
593
+ CloudToAppMessageType2["APP_USER_JOINED"] = "app_user_joined";
594
+ CloudToAppMessageType2["APP_USER_LEFT"] = "app_user_left";
595
+ CloudToAppMessageType2["APP_ROOM_UPDATED"] = "app_room_updated";
596
+ CloudToAppMessageType2["APP_DIRECT_MESSAGE_RESPONSE"] = "app_direct_message_response";
597
+ })(CloudToAppMessageType ||= {});
598
+ ControlActionTypes = [
599
+ "connection_init" /* CONNECTION_INIT */,
600
+ GlassesToCloudMessageType.START_APP,
601
+ GlassesToCloudMessageType.STOP_APP,
602
+ "dashboard_state" /* DASHBOARD_STATE */,
603
+ GlassesToCloudMessageType.OPEN_DASHBOARD
604
+ ];
605
+ EventTypes = [
606
+ GlassesToCloudMessageType.BUTTON_PRESS,
607
+ GlassesToCloudMessageType.HEAD_POSITION,
608
+ GlassesToCloudMessageType.GLASSES_BATTERY_UPDATE,
609
+ GlassesToCloudMessageType.PHONE_BATTERY_UPDATE,
610
+ GlassesToCloudMessageType.GLASSES_CONNECTION_STATE,
611
+ GlassesToCloudMessageType.LOCATION_UPDATE,
612
+ GlassesToCloudMessageType.VPS_COORDINATES,
613
+ GlassesToCloudMessageType.VAD,
614
+ GlassesToCloudMessageType.PHONE_NOTIFICATION,
615
+ GlassesToCloudMessageType.PHONE_NOTIFICATION_DISMISSED,
616
+ GlassesToCloudMessageType.CALENDAR_EVENT,
617
+ GlassesToCloudMessageType.MENTRAOS_SETTINGS_UPDATE_REQUEST,
618
+ GlassesToCloudMessageType.CORE_STATUS_UPDATE,
619
+ "local_transcription" /* LOCAL_TRANSCRIPTION */
620
+ ];
621
+ ResponseTypes = [
622
+ "connection_ack" /* CONNECTION_ACK */,
623
+ "connection_error" /* CONNECTION_ERROR */,
624
+ "auth_error" /* AUTH_ERROR */
625
+ ];
626
+ UpdateTypes = [
627
+ "display_event" /* DISPLAY_EVENT */,
628
+ "app_state_change" /* APP_STATE_CHANGE */,
629
+ "microphone_state_change" /* MICROPHONE_STATE_CHANGE */,
630
+ "photo_request" /* PHOTO_REQUEST */,
631
+ "audio_play_request" /* AUDIO_PLAY_REQUEST */,
632
+ "audio_stop_request" /* AUDIO_STOP_REQUEST */,
633
+ "rgb_led_control" /* RGB_LED_CONTROL */,
634
+ "settings_update" /* SETTINGS_UPDATE */,
635
+ "dashboard_mode_change" /* DASHBOARD_MODE_CHANGE */,
636
+ "dashboard_always_on_change" /* DASHBOARD_ALWAYS_ON_CHANGE */,
637
+ "start_stream" /* START_STREAM */,
638
+ "stop_stream" /* STOP_STREAM */,
639
+ "keep_stream_alive" /* KEEP_STREAM_ALIVE */,
640
+ "livekit_info" /* LIVEKIT_INFO */
641
+ ];
642
+ DashboardMessageTypes = [
643
+ "dashboard_content_update" /* DASHBOARD_CONTENT_UPDATE */,
644
+ "dashboard_mode_change" /* DASHBOARD_MODE_CHANGE */,
645
+ "dashboard_system_update" /* DASHBOARD_SYSTEM_UPDATE */,
646
+ "dashboard_mode_changed" /* DASHBOARD_MODE_CHANGED */,
647
+ "dashboard_always_on_changed" /* DASHBOARD_ALWAYS_ON_CHANGED */
648
+ ];
649
+ });
650
+ // src/types/messages/glasses-to-cloud.ts
651
+ function isControlAction(message) {
652
+ return ControlActionTypes.includes(message.type);
653
+ }
654
+ function isEvent(message) {
655
+ return EventTypes.includes(message.type);
656
+ }
657
+ function isConnectionInit(message) {
658
+ return message.type === "connection_init" /* CONNECTION_INIT */;
659
+ }
660
+ function isRequestSettings(message) {
661
+ return message.type === "request_settings" /* REQUEST_SETTINGS */;
662
+ }
663
+ function isStartApp(message) {
664
+ return message.type === GlassesToCloudMessageType.START_APP;
665
+ }
666
+ function isStopApp(message) {
667
+ return message.type === GlassesToCloudMessageType.STOP_APP;
668
+ }
669
+ function isButtonPress(message) {
670
+ return message.type === GlassesToCloudMessageType.BUTTON_PRESS;
671
+ }
672
+ function isHeadPosition(message) {
673
+ return message.type === GlassesToCloudMessageType.HEAD_POSITION;
674
+ }
675
+ function isGlassesBatteryUpdate(message) {
676
+ return message.type === GlassesToCloudMessageType.GLASSES_BATTERY_UPDATE;
677
+ }
678
+ function isPhoneBatteryUpdate(message) {
679
+ return message.type === GlassesToCloudMessageType.PHONE_BATTERY_UPDATE;
680
+ }
681
+ function isGlassesConnectionState(message) {
682
+ return message.type === GlassesToCloudMessageType.GLASSES_CONNECTION_STATE;
683
+ }
684
+ function isLocationUpdate(message) {
685
+ return message.type === GlassesToCloudMessageType.LOCATION_UPDATE;
686
+ }
687
+ function isCalendarEvent(message) {
688
+ return message.type === GlassesToCloudMessageType.CALENDAR_EVENT;
689
+ }
690
+ function isVad(message) {
691
+ return message.type === GlassesToCloudMessageType.VAD;
692
+ }
693
+ function isPhoneNotification(message) {
694
+ return message.type === GlassesToCloudMessageType.PHONE_NOTIFICATION;
695
+ }
696
+ function isPhoneNotificationDismissed(message) {
697
+ return message.type === GlassesToCloudMessageType.PHONE_NOTIFICATION_DISMISSED;
698
+ }
699
+ function isStreamStatus(message) {
700
+ return message.type === GlassesToCloudMessageType.STREAM_STATUS;
701
+ }
702
+ function isPhotoResponse(message) {
703
+ return message.type === GlassesToCloudMessageType.PHOTO_RESPONSE;
704
+ }
705
+ function isRgbLedControlResponse(message) {
706
+ return message.type === "rgb_led_control_response" /* RGB_LED_CONTROL_RESPONSE */;
707
+ }
708
+ function isKeepAliveAck(message) {
709
+ return message.type === "keep_alive_ack" /* KEEP_ALIVE_ACK */;
710
+ }
711
+ function isPhotoTaken(message) {
712
+ return message.type === GlassesToCloudMessageType.PHOTO_TAKEN;
713
+ }
714
+ function isAudioPlayResponse(message) {
715
+ return message.type === "audio_play_response" /* AUDIO_PLAY_RESPONSE */;
716
+ }
717
+ function isLocalTranscription(message) {
718
+ return message.type === "local_transcription" /* LOCAL_TRANSCRIPTION */;
719
+ }
720
+ function isUdpRegister(message) {
721
+ return message.type === "udp_register" /* UDP_REGISTER */;
722
+ }
723
+ function isUdpUnregister(message) {
724
+ return message.type === "udp_unregister" /* UDP_UNREGISTER */;
725
+ }
726
+ var PhotoErrorCode, PhotoStage;
727
+ var init_glasses_to_cloud = __esm(() => {
728
+ init_message_types();
729
+ ((PhotoErrorCode2) => {
730
+ PhotoErrorCode2["CAMERA_INIT_FAILED"] = "CAMERA_INIT_FAILED";
731
+ PhotoErrorCode2["CAMERA_CAPTURE_FAILED"] = "CAMERA_CAPTURE_FAILED";
732
+ PhotoErrorCode2["CAMERA_TIMEOUT"] = "CAMERA_TIMEOUT";
733
+ PhotoErrorCode2["CAMERA_BUSY"] = "CAMERA_BUSY";
734
+ PhotoErrorCode2["UPLOAD_FAILED"] = "UPLOAD_FAILED";
735
+ PhotoErrorCode2["UPLOAD_TIMEOUT"] = "UPLOAD_TIMEOUT";
736
+ PhotoErrorCode2["BLE_TRANSFER_FAILED"] = "BLE_TRANSFER_FAILED";
737
+ PhotoErrorCode2["BLE_TRANSFER_BUSY"] = "BLE_TRANSFER_BUSY";
738
+ PhotoErrorCode2["BLE_TRANSFER_FAILED_TO_START"] = "BLE_TRANSFER_FAILED_TO_START";
739
+ PhotoErrorCode2["BLE_TRANSFER_TIMEOUT"] = "BLE_TRANSFER_TIMEOUT";
740
+ PhotoErrorCode2["COMPRESSION_FAILED"] = "COMPRESSION_FAILED";
741
+ PhotoErrorCode2["PERMISSION_DENIED"] = "PERMISSION_DENIED";
742
+ PhotoErrorCode2["STORAGE_FULL"] = "STORAGE_FULL";
743
+ PhotoErrorCode2["NETWORK_ERROR"] = "NETWORK_ERROR";
744
+ PhotoErrorCode2["PHONE_GLASSES_NOT_CONNECTED"] = "PHONE_GLASSES_NOT_CONNECTED";
745
+ PhotoErrorCode2["PHONE_BLE_TRANSFER_FAILED"] = "PHONE_BLE_TRANSFER_FAILED";
746
+ PhotoErrorCode2["PHONE_UPLOAD_FAILED"] = "PHONE_UPLOAD_FAILED";
747
+ PhotoErrorCode2["PHONE_TIMEOUT"] = "PHONE_TIMEOUT";
748
+ PhotoErrorCode2["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
749
+ })(PhotoErrorCode ||= {});
750
+ ((PhotoStage2) => {
751
+ PhotoStage2["REQUEST_RECEIVED"] = "REQUEST_RECEIVED";
752
+ PhotoStage2["CAMERA_INIT"] = "CAMERA_INIT";
753
+ PhotoStage2["PHOTO_CAPTURE"] = "PHOTO_CAPTURE";
754
+ PhotoStage2["COMPRESSION"] = "COMPRESSION";
755
+ PhotoStage2["UPLOAD_START"] = "UPLOAD_START";
756
+ PhotoStage2["UPLOAD_PROGRESS"] = "UPLOAD_PROGRESS";
757
+ PhotoStage2["BLE_TRANSFER"] = "BLE_TRANSFER";
758
+ PhotoStage2["RESPONSE_SENT"] = "RESPONSE_SENT";
759
+ })(PhotoStage ||= {});
760
+ });
761
+
762
+ // src/types/messages/cloud-to-glasses.ts
763
+ function isResponse(message) {
764
+ return ResponseTypes.includes(message.type);
765
+ }
766
+ function isUpdate(message) {
767
+ return UpdateTypes.includes(message.type);
768
+ }
769
+ function isConnectionAck(message) {
770
+ return message.type === "connection_ack" /* CONNECTION_ACK */;
771
+ }
772
+ function isConnectionError(message) {
773
+ return message.type === "connection_error" /* CONNECTION_ERROR */;
774
+ }
775
+ function isAuthError(message) {
776
+ return message.type === "auth_error" /* AUTH_ERROR */;
777
+ }
778
+ function isDisplayEvent(message) {
779
+ return message.type === "display_event" /* DISPLAY_EVENT */;
780
+ }
781
+ function isAppStateChange(message) {
782
+ return message.type === "app_state_change" /* APP_STATE_CHANGE */;
783
+ }
784
+ function isMicrophoneStateChange(message) {
785
+ return message.type === "microphone_state_change" /* MICROPHONE_STATE_CHANGE */;
786
+ }
787
+ function isPhotoRequest(message) {
788
+ return message.type === "photo_request" /* PHOTO_REQUEST */;
789
+ }
790
+ function isRgbLedControl(message) {
791
+ return message.type === "rgb_led_control" /* RGB_LED_CONTROL */;
792
+ }
793
+ function isSettingsUpdate(message) {
794
+ return message.type === "settings_update" /* SETTINGS_UPDATE */;
795
+ }
796
+ function isStartStream(message) {
797
+ return message.type === "start_stream" /* START_STREAM */;
798
+ }
799
+ function isStopStream(message) {
800
+ return message.type === "stop_stream" /* STOP_STREAM */;
801
+ }
802
+ function isKeepStreamAlive(message) {
803
+ return message.type === "keep_stream_alive" /* KEEP_STREAM_ALIVE */;
804
+ }
805
+ function isAudioPlayRequestToGlasses(message) {
806
+ return message.type === "audio_play_request" /* AUDIO_PLAY_REQUEST */;
807
+ }
808
+ function isAudioStopRequestToGlasses(message) {
809
+ return message.type === "audio_stop_request" /* AUDIO_STOP_REQUEST */;
810
+ }
811
+ var init_cloud_to_glasses = __esm(() => {
812
+ init_message_types();
813
+ });
814
+
815
+ // src/types/messages/app-to-cloud.ts
816
+ function isAppConnectionInit(message) {
817
+ return message.type === "tpa_connection_init" /* CONNECTION_INIT */;
818
+ }
819
+ function isAppReconnect(message) {
820
+ return message.type === "reconnect" /* RECONNECT */;
821
+ }
822
+ function isAppSubscriptionUpdate(message) {
823
+ return message.type === "subscription_update" /* SUBSCRIPTION_UPDATE */;
824
+ }
825
+ function isDisplayRequest(message) {
826
+ return message.type === "display_event" /* DISPLAY_REQUEST */;
827
+ }
828
+ function isPhotoRequest2(message) {
829
+ return message.type === "photo_request" /* PHOTO_REQUEST */;
830
+ }
831
+ function isRgbLedControlRequest(message) {
832
+ return message.type === "rgb_led_control" /* RGB_LED_CONTROL */;
833
+ }
834
+ function isCameraFovSetRequest(message) {
835
+ return message.type === "camera_fov_set" /* CAMERA_FOV_SET */;
836
+ }
837
+ function isAudioPlayRequest(message) {
838
+ return message.type === "audio_play_request" /* AUDIO_PLAY_REQUEST */;
839
+ }
840
+ function isAudioStopRequest(message) {
841
+ return message.type === "audio_stop_request" /* AUDIO_STOP_REQUEST */;
842
+ }
843
+ function isDashboardContentUpdate(message) {
844
+ return message.type === "dashboard_content_update" /* DASHBOARD_CONTENT_UPDATE */;
845
+ }
846
+ function isDashboardModeChange(message) {
847
+ return message.type === "dashboard_mode_change" /* DASHBOARD_MODE_CHANGE */;
848
+ }
849
+ function isDashboardSystemUpdate(message) {
850
+ return message.type === "dashboard_system_update" /* DASHBOARD_SYSTEM_UPDATE */;
851
+ }
852
+ function isManagedStreamRequest(message) {
853
+ return message.type === "managed_stream_request" /* MANAGED_STREAM_REQUEST */;
854
+ }
855
+ function isManagedStreamStopRequest(message) {
856
+ return message.type === "managed_stream_stop" /* MANAGED_STREAM_STOP */;
857
+ }
858
+ function isStreamRequest(message) {
859
+ return message.type === "stream_request" /* STREAM_REQUEST */;
860
+ }
861
+ function isStreamStopRequest(message) {
862
+ return message.type === "stream_stop" /* STREAM_STOP */;
863
+ }
864
+ function isOwnershipRelease(message) {
865
+ return message.type === "ownership_release" /* OWNERSHIP_RELEASE */;
866
+ }
867
+ function isTelemetryResponse(message) {
868
+ return message.type === "telemetry_response" /* TELEMETRY_RESPONSE */;
869
+ }
870
+ var init_app_to_cloud = __esm(() => {
871
+ init_message_types();
872
+ });
873
+
874
+ // src/types/messages/cloud-to-app.ts
875
+ function isAppConnectionAck(message) {
876
+ return message.type === "tpa_connection_ack" /* CONNECTION_ACK */;
877
+ }
878
+ function isAppConnectionError(message) {
879
+ return message.type === "tpa_connection_error" /* CONNECTION_ERROR */ || message.type === "connection_error";
880
+ }
881
+ function isAppStopped(message) {
882
+ return message.type === "app_stopped" /* APP_STOPPED */;
883
+ }
884
+ function isSettingsUpdate2(message) {
885
+ return message.type === "settings_update" /* SETTINGS_UPDATE */;
886
+ }
887
+ function isCapabilitiesUpdate(message) {
888
+ return message.type === "capabilities_update" /* CAPABILITIES_UPDATE */;
889
+ }
890
+ function isDeviceStateUpdate(message) {
891
+ return message.type === "device_state_update" /* DEVICE_STATE_UPDATE */;
892
+ }
893
+ function isDataStream(message) {
894
+ return message.type === "data_stream" /* DATA_STREAM */;
895
+ }
896
+ function isAudioChunk(message) {
897
+ return message.type === "audio_chunk" /* AUDIO_CHUNK */;
898
+ }
899
+ function isDashboardModeChanged(message) {
900
+ return message.type === "dashboard_mode_changed" /* DASHBOARD_MODE_CHANGED */;
901
+ }
902
+ function isDashboardAlwaysOnChanged(message) {
903
+ return message.type === "dashboard_always_on_changed" /* DASHBOARD_ALWAYS_ON_CHANGED */;
904
+ }
905
+ function isManagedStreamStatus(message) {
906
+ return message.type === "managed_stream_status" /* MANAGED_STREAM_STATUS */;
907
+ }
908
+ function isStreamStatus2(message) {
909
+ return message.type === GlassesToCloudMessageType.STREAM_STATUS;
910
+ }
911
+ function isPhotoResponse2(message) {
912
+ return message.type === GlassesToCloudMessageType.PHOTO_RESPONSE;
913
+ }
914
+ function isRgbLedControlResponse2(message) {
915
+ return message.type === "rgb_led_control_response" /* RGB_LED_CONTROL_RESPONSE */;
916
+ }
917
+ function isStreamStatusCheckResponse(message) {
918
+ return message.type === "stream_status_check_response" /* STREAM_STATUS_CHECK_RESPONSE */;
919
+ }
920
+ function isAudioPlayResponse2(message) {
921
+ return message.type === "audio_play_response" /* AUDIO_PLAY_RESPONSE */;
922
+ }
923
+ function isRequestTelemetry(message) {
924
+ return message.type === "request_telemetry" /* REQUEST_TELEMETRY */;
925
+ }
926
+ var init_cloud_to_app = __esm(() => {
927
+ init_message_types();
928
+ init_streams();
929
+ });
930
+ // src/types/dashboard/index.ts
931
+ var DashboardMode;
932
+ var init_dashboard = __esm(() => {
933
+ ((DashboardMode2) => {
934
+ DashboardMode2["MAIN"] = "main";
935
+ DashboardMode2["EXPANDED"] = "expanded";
936
+ })(DashboardMode ||= {});
937
+ });
938
+ // src/types/enums.ts
939
+ var AppType, LayoutType, ViewType, AppSettingType, HardwareType, HardwareRequirementLevel;
940
+ var init_enums = __esm(() => {
941
+ ((AppType2) => {
942
+ AppType2["SYSTEM_DASHBOARD"] = "system_dashboard";
943
+ AppType2["BACKGROUND"] = "background";
944
+ AppType2["STANDARD"] = "standard";
945
+ })(AppType ||= {});
946
+ ((LayoutType2) => {
947
+ LayoutType2["TEXT_WALL"] = "text_wall";
948
+ LayoutType2["DOUBLE_TEXT_WALL"] = "double_text_wall";
949
+ LayoutType2["DASHBOARD_CARD"] = "dashboard_card";
950
+ LayoutType2["REFERENCE_CARD"] = "reference_card";
951
+ LayoutType2["BITMAP_VIEW"] = "bitmap_view";
952
+ LayoutType2["BITMAP_ANIMATION"] = "bitmap_animation";
953
+ LayoutType2["CLEAR_VIEW"] = "clear_view";
954
+ })(LayoutType ||= {});
955
+ ((ViewType2) => {
956
+ ViewType2["DASHBOARD"] = "dashboard";
957
+ ViewType2["MAIN"] = "main";
958
+ })(ViewType ||= {});
959
+ ((AppSettingType2) => {
960
+ AppSettingType2["TOGGLE"] = "toggle";
961
+ AppSettingType2["TEXT"] = "text";
962
+ AppSettingType2["SELECT"] = "select";
963
+ AppSettingType2["SLIDER"] = "slider";
964
+ AppSettingType2["GROUP"] = "group";
965
+ AppSettingType2["TEXT_NO_SAVE_BUTTON"] = "text_no_save_button";
966
+ AppSettingType2["SELECT_WITH_SEARCH"] = "select_with_search";
967
+ AppSettingType2["MULTISELECT"] = "multiselect";
968
+ AppSettingType2["TITLE_VALUE"] = "titleValue";
969
+ AppSettingType2["NUMERIC_INPUT"] = "numeric_input";
970
+ AppSettingType2["TIME_PICKER"] = "time_picker";
971
+ })(AppSettingType ||= {});
972
+ ((HardwareType2) => {
973
+ HardwareType2["CAMERA"] = "CAMERA";
974
+ HardwareType2["DISPLAY"] = "DISPLAY";
975
+ HardwareType2["MICROPHONE"] = "MICROPHONE";
976
+ HardwareType2["SPEAKER"] = "SPEAKER";
977
+ HardwareType2["IMU"] = "IMU";
978
+ HardwareType2["BUTTON"] = "BUTTON";
979
+ HardwareType2["LIGHT"] = "LIGHT";
980
+ HardwareType2["WIFI"] = "WIFI";
981
+ })(HardwareType ||= {});
982
+ ((HardwareRequirementLevel2) => {
983
+ HardwareRequirementLevel2["REQUIRED"] = "REQUIRED";
984
+ HardwareRequirementLevel2["OPTIONAL"] = "OPTIONAL";
985
+ })(HardwareRequirementLevel ||= {});
986
+ });
987
+
988
+ // src/types/models.ts
989
+ function validateAppConfig(config) {
990
+ if (!config || typeof config !== "object")
991
+ return false;
992
+ if (typeof config.name !== "string" || typeof config.description !== "string" || typeof config.version !== "string") {
993
+ return false;
994
+ }
995
+ if (!Array.isArray(config.settings))
996
+ return false;
997
+ return config.settings.every((setting) => {
998
+ if (setting.type === "group") {
999
+ return typeof setting.title === "string";
1000
+ }
1001
+ if (setting.type === "titleValue") {
1002
+ return typeof setting.label === "string" && "value" in setting;
1003
+ }
1004
+ if (typeof setting.key !== "string" || typeof setting.label !== "string") {
1005
+ return false;
1006
+ }
1007
+ switch (setting.type) {
1008
+ case "toggle" /* TOGGLE */:
1009
+ return typeof setting.defaultValue === "boolean";
1010
+ case "text" /* TEXT */:
1011
+ case "text_no_save_button" /* TEXT_NO_SAVE_BUTTON */:
1012
+ return setting.defaultValue === undefined || typeof setting.defaultValue === "string";
1013
+ case "select" /* SELECT */:
1014
+ case "select_with_search" /* SELECT_WITH_SEARCH */:
1015
+ return Array.isArray(setting.options) && setting.options.every((opt) => typeof opt.label === "string" && ("value" in opt));
1016
+ case "multiselect" /* MULTISELECT */:
1017
+ return Array.isArray(setting.options) && setting.options.every((opt) => typeof opt.label === "string" && ("value" in opt)) && (setting.defaultValue === undefined || Array.isArray(setting.defaultValue));
1018
+ case "slider" /* SLIDER */:
1019
+ return typeof setting.defaultValue === "number" && typeof setting.min === "number" && typeof setting.max === "number" && setting.min <= setting.max;
1020
+ case "numeric_input" /* NUMERIC_INPUT */:
1021
+ return (setting.defaultValue === undefined || typeof setting.defaultValue === "number") && (setting.min === undefined || typeof setting.min === "number") && (setting.max === undefined || typeof setting.max === "number") && (setting.step === undefined || typeof setting.step === "number") && (setting.placeholder === undefined || typeof setting.placeholder === "string");
1022
+ case "time_picker" /* TIME_PICKER */:
1023
+ return (setting.defaultValue === undefined || typeof setting.defaultValue === "number") && (setting.showSeconds === undefined || typeof setting.showSeconds === "boolean");
1024
+ case "group" /* GROUP */:
1025
+ return typeof setting.title === "string";
1026
+ case "titleValue" /* TITLE_VALUE */:
1027
+ return typeof setting.label === "string" && "value" in setting;
1028
+ default:
1029
+ return false;
1030
+ }
1031
+ });
1032
+ }
1033
+ var PermissionType, LEGACY_PERMISSION_MAP;
1034
+ var init_models = __esm(() => {
1035
+ init_enums();
1036
+ ((PermissionType2) => {
1037
+ PermissionType2["MICROPHONE"] = "MICROPHONE";
1038
+ PermissionType2["LOCATION"] = "LOCATION";
1039
+ PermissionType2["BACKGROUND_LOCATION"] = "BACKGROUND_LOCATION";
1040
+ PermissionType2["CALENDAR"] = "CALENDAR";
1041
+ PermissionType2["CAMERA"] = "CAMERA";
1042
+ PermissionType2["NOTIFICATIONS"] = "NOTIFICATIONS";
1043
+ PermissionType2["READ_NOTIFICATIONS"] = "READ_NOTIFICATIONS";
1044
+ PermissionType2["POST_NOTIFICATIONS"] = "POST_NOTIFICATIONS";
1045
+ PermissionType2["ALL"] = "ALL";
1046
+ })(PermissionType ||= {});
1047
+ LEGACY_PERMISSION_MAP = new Map([
1048
+ ["NOTIFICATIONS" /* NOTIFICATIONS */, ["READ_NOTIFICATIONS" /* READ_NOTIFICATIONS */]]
1049
+ ]);
1050
+ });
1051
+
1052
+ // src/types/webhooks.ts
1053
+ function isSessionWebhookRequest(request) {
1054
+ return request.type === "session_request" /* SESSION_REQUEST */;
1055
+ }
1056
+ function isStopWebhookRequest(request) {
1057
+ return request.type === "stop_request" /* STOP_REQUEST */;
1058
+ }
1059
+ var WebhookRequestType;
1060
+ var init_webhooks = __esm(() => {
1061
+ ((WebhookRequestType2) => {
1062
+ WebhookRequestType2["SESSION_REQUEST"] = "session_request";
1063
+ WebhookRequestType2["STOP_REQUEST"] = "stop_request";
1064
+ })(WebhookRequestType ||= {});
1065
+ });
1066
+ // src/types/index.ts
1067
+ var init_types = __esm(() => {
1068
+ init_app_to_cloud();
1069
+ init_cloud_to_app();
1070
+ init_message_types();
1071
+ init_glasses_to_cloud();
1072
+ init_cloud_to_glasses();
1073
+ init_streams();
1074
+ init_dashboard();
1075
+ init_enums();
1076
+ init_models();
1077
+ init_webhooks();
1078
+ });
1079
+
1080
+ // src/session/MentraSession.ts
1081
+ init_logger();
1082
+ init_message_types();
1083
+ import EventEmitter2 from "events";
1084
+
1085
+ // src/session/managers/CameraManager.ts
1086
+ init_types();
1087
+ import { EventEmitter } from "events";
1088
+ var DEFAULT_TIMEOUT_MS = 30000;
1089
+ var STREAM_CHECK_TIMEOUT_MS = 5000;
1090
+ var MANAGED_STREAM_TIMEOUT_MS = 30000;
1091
+ function generateRequestId(prefix) {
1092
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
1093
+ return `${prefix}_${crypto.randomUUID()}`;
1094
+ }
1095
+ return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
1096
+ }
1097
+
1098
+ class CameraManager {
1099
+ deps;
1100
+ events = new EventEmitter;
1101
+ handlerCleanups = [];
1102
+ pendingRequests = new Map;
1103
+ pendingStreamChecks = new Map;
1104
+ pendingManagedStreamRequest;
1105
+ _hasPermission = true;
1106
+ isStreaming = false;
1107
+ currentStreamUrl;
1108
+ currentStreamState;
1109
+ isManagedStreaming = false;
1110
+ currentManagedStreamId;
1111
+ currentManagedStreamUrls;
1112
+ managedStreamStatus;
1113
+ constructor(deps) {
1114
+ this.deps = deps;
1115
+ this.handlerCleanups.push(this.deps.messageHandlers.register("photo_response" /* PHOTO_RESPONSE */, (msg) => this.handlePhotoResponse(msg)), this.deps.messageHandlers.register("stream_status" /* STREAM_STATUS */, (msg) => this.handleStreamStatus(msg)), this.deps.messageHandlers.register("rtmp_stream_status", (msg) => this.handleStreamStatus(msg)), this.deps.messageHandlers.register("managed_stream_status" /* MANAGED_STREAM_STATUS */, (msg) => this.handleManagedStreamStatus(msg)), this.deps.messageHandlers.register("stream_status_check_response" /* STREAM_STATUS_CHECK_RESPONSE */, (msg) => this.handleStreamCheckResponse(msg)));
1116
+ }
1117
+ takePhoto(opts) {
1118
+ return new Promise((resolve, reject) => {
1119
+ const requestId = generateRequestId("photo_req");
1120
+ const timeoutMs = opts?.timeout ?? DEFAULT_TIMEOUT_MS;
1121
+ const timer = setTimeout(() => {
1122
+ this.pendingRequests.delete(requestId);
1123
+ reject(new Error(`Photo request timed out after ${timeoutMs}ms (requestId: ${requestId})`));
1124
+ }, timeoutMs);
1125
+ this.pendingRequests.set(requestId, { requestId, resolve, reject, timer });
1126
+ const message = {
1127
+ type: "photo_request" /* PHOTO_REQUEST */,
1128
+ packageName: this.deps.getPackageName(),
1129
+ sessionId: this.deps.getSessionId(),
1130
+ requestId,
1131
+ timestamp: new Date,
1132
+ saveToGallery: opts?.saveToGallery ?? false,
1133
+ size: opts?.size ?? "medium",
1134
+ compress: opts?.compression ?? "none",
1135
+ sound: opts?.sound
1136
+ };
1137
+ try {
1138
+ this.deps.sendMessage(message);
1139
+ this.deps.logger.info({ requestId, size: message.size, compress: message.compress, saveToGallery: message.saveToGallery }, "\uD83D\uDCF8 Photo request sent");
1140
+ } catch (err) {
1141
+ clearTimeout(timer);
1142
+ this.pendingRequests.delete(requestId);
1143
+ reject(err instanceof Error ? err : new Error(String(err)));
1144
+ }
1145
+ });
1146
+ }
1147
+ onPhotoTaken(handler) {
1148
+ const streamKey = "photo_taken" /* PHOTO_TAKEN */;
1149
+ this.deps.addSubscription(streamKey);
1150
+ const routerCleanup = this.deps.router.on(streamKey, (_streamType, data) => {
1151
+ try {
1152
+ handler(normalisePhotoData(data));
1153
+ } catch (err) {
1154
+ this.deps.logger.error("[CameraManager] Error in onPhotoTaken handler:", err);
1155
+ }
1156
+ });
1157
+ return () => {
1158
+ routerCleanup();
1159
+ this.deps.removeSubscription(streamKey);
1160
+ };
1161
+ }
1162
+ async startStream(options) {
1163
+ const opts = options ?? {};
1164
+ if (opts.direct) {
1165
+ return this._startDirectStream(opts);
1166
+ }
1167
+ return this._startManagedStream(opts);
1168
+ }
1169
+ async stopStream() {
1170
+ if (this.isStreaming) {
1171
+ this.deps.sendMessage({
1172
+ type: "stream_stop" /* STREAM_STOP */,
1173
+ packageName: this.deps.getPackageName(),
1174
+ sessionId: this.deps.getSessionId(),
1175
+ streamId: this.currentStreamState?.streamId,
1176
+ timestamp: new Date
1177
+ });
1178
+ }
1179
+ if (this.isManagedStreaming) {
1180
+ this.deps.sendMessage({
1181
+ type: "managed_stream_stop" /* MANAGED_STREAM_STOP */,
1182
+ packageName: this.deps.getPackageName(),
1183
+ sessionId: this.deps.getSessionId(),
1184
+ timestamp: new Date
1185
+ });
1186
+ this.isManagedStreaming = false;
1187
+ this.currentManagedStreamId = undefined;
1188
+ this.currentManagedStreamUrls = undefined;
1189
+ }
1190
+ }
1191
+ onStreamStatus(handler) {
1192
+ this.deps.addSubscription("stream_status" /* STREAM_STATUS */);
1193
+ this.deps.addSubscription("managed_stream_status" /* MANAGED_STREAM_STATUS */);
1194
+ this.events.on("stream_status", handler);
1195
+ this.events.on("managed_stream_status", handler);
1196
+ return () => {
1197
+ this.events.off("stream_status", handler);
1198
+ this.events.off("managed_stream_status", handler);
1199
+ this.deps.removeSubscription("stream_status" /* STREAM_STATUS */);
1200
+ this.deps.removeSubscription("managed_stream_status" /* MANAGED_STREAM_STATUS */);
1201
+ };
1202
+ }
1203
+ isCurrentlyStreaming() {
1204
+ return this.isStreaming || this.isManagedStreaming;
1205
+ }
1206
+ getCurrentStreamUrl() {
1207
+ return this.currentStreamUrl;
1208
+ }
1209
+ getStreamStatus() {
1210
+ return this.currentStreamState;
1211
+ }
1212
+ getStreamUrls() {
1213
+ return this.currentManagedStreamUrls;
1214
+ }
1215
+ async _startDirectStream(opts) {
1216
+ const url = opts.direct;
1217
+ if (!url.startsWith("rtmp://") && !url.startsWith("rtmps://") && !url.startsWith("srt://") && !url.startsWith("https://") && !url.startsWith("http://")) {
1218
+ throw new Error("Invalid stream URL: must start with rtmp://, rtmps://, srt://, https://, or http://");
1219
+ }
1220
+ if (this.isStreaming || this.isManagedStreaming) {
1221
+ throw new Error("Already streaming. Stop the current stream before starting a new one.");
1222
+ }
1223
+ this.currentStreamUrl = url;
1224
+ this.deps.sendMessage({
1225
+ type: "stream_request" /* STREAM_REQUEST */,
1226
+ packageName: this.deps.getPackageName(),
1227
+ sessionId: this.deps.getSessionId(),
1228
+ streamUrl: url,
1229
+ video: opts.video,
1230
+ audio: opts.audio,
1231
+ stream: opts.stream,
1232
+ sound: opts.sound,
1233
+ timestamp: new Date
1234
+ });
1235
+ this.isStreaming = true;
1236
+ }
1237
+ async _startManagedStream(opts) {
1238
+ if (this.isStreaming || this.isManagedStreaming) {
1239
+ throw new Error("Already streaming. Stop the current stream before starting a new one.");
1240
+ }
1241
+ const restreamDestinations = opts.destinations?.map((url) => ({ url }));
1242
+ this.deps.sendMessage({
1243
+ type: "managed_stream_request" /* MANAGED_STREAM_REQUEST */,
1244
+ packageName: this.deps.getPackageName(),
1245
+ sessionId: this.deps.getSessionId(),
1246
+ quality: opts.quality,
1247
+ enableWebRTC: opts.enableWebRTC ?? true,
1248
+ video: opts.video,
1249
+ audio: opts.audio,
1250
+ stream: opts.stream,
1251
+ restreamDestinations,
1252
+ sound: opts.sound,
1253
+ timestamp: new Date
1254
+ });
1255
+ this.isManagedStreaming = true;
1256
+ return new Promise((resolve, reject) => {
1257
+ const timeoutId = setTimeout(() => {
1258
+ if (this.pendingManagedStreamRequest?.timeoutId === timeoutId) {
1259
+ this.pendingManagedStreamRequest = undefined;
1260
+ this.isManagedStreaming = false;
1261
+ reject(new Error("Managed stream request timeout"));
1262
+ }
1263
+ }, MANAGED_STREAM_TIMEOUT_MS);
1264
+ this.pendingManagedStreamRequest = { resolve, reject, timeoutId };
1265
+ });
1266
+ }
1267
+ async startDirectStream(options) {
1268
+ return this._startDirectStream({ direct: options.rtmpUrl, video: options.video, audio: options.audio, stream: options.stream, sound: options.sound });
1269
+ }
1270
+ async startManagedStream(options = {}) {
1271
+ return this._startManagedStream({
1272
+ quality: options.quality,
1273
+ enableWebRTC: options.enableWebRTC,
1274
+ video: options.video,
1275
+ audio: options.audio,
1276
+ stream: options.stream,
1277
+ destinations: options.restreamDestinations?.map((d) => d.url),
1278
+ sound: options.sound
1279
+ });
1280
+ }
1281
+ async stopManagedStream() {
1282
+ return this.stopStream();
1283
+ }
1284
+ onManagedStreamStatus(handler) {
1285
+ this.deps.addSubscription("managed_stream_status" /* MANAGED_STREAM_STATUS */);
1286
+ this.events.on("managed_stream_status", handler);
1287
+ return () => {
1288
+ this.events.off("managed_stream_status", handler);
1289
+ this.deps.removeSubscription("managed_stream_status" /* MANAGED_STREAM_STATUS */);
1290
+ };
1291
+ }
1292
+ isManagedStreamActive() {
1293
+ return this.isManagedStreaming;
1294
+ }
1295
+ getManagedStreamUrls() {
1296
+ return this.currentManagedStreamUrls;
1297
+ }
1298
+ getManagedStreamStatus() {
1299
+ return this.managedStreamStatus;
1300
+ }
1301
+ async checkExistingStream() {
1302
+ return new Promise((resolve) => {
1303
+ const requestId = generateRequestId("stream_check");
1304
+ const timeoutId = setTimeout(() => {
1305
+ this.pendingStreamChecks.delete(requestId);
1306
+ resolve({ hasActiveStream: false });
1307
+ }, STREAM_CHECK_TIMEOUT_MS);
1308
+ this.pendingStreamChecks.set(requestId, { resolve, timeoutId });
1309
+ this.deps.sendMessage({
1310
+ type: "stream_status_check" /* STREAM_STATUS_CHECK */,
1311
+ packageName: this.deps.getPackageName(),
1312
+ sessionId: this.deps.getSessionId(),
1313
+ requestId,
1314
+ timestamp: new Date
1315
+ });
1316
+ });
1317
+ }
1318
+ get hasPermission() {
1319
+ return this._hasPermission;
1320
+ }
1321
+ handlePhotoResponse(message) {
1322
+ const requestId = message?.requestId;
1323
+ if (!requestId) {
1324
+ this.deps.logger.warn("[CameraManager] Received PHOTO_RESPONSE without requestId:", message);
1325
+ return;
1326
+ }
1327
+ const pending = this.pendingRequests.get(requestId);
1328
+ if (!pending) {
1329
+ this.deps.logger.debug(`[CameraManager] No pending request for requestId="${requestId}" — ignoring.`);
1330
+ return;
1331
+ }
1332
+ clearTimeout(pending.timer);
1333
+ this.pendingRequests.delete(requestId);
1334
+ if (message.error?.code === "permission_denied") {
1335
+ this._hasPermission = false;
1336
+ }
1337
+ if (message.success === false) {
1338
+ const errorMsg = message.error?.message ?? message.error?.code ?? "Photo capture failed";
1339
+ pending.reject(new Error(errorMsg));
1340
+ return;
1341
+ }
1342
+ pending.resolve({
1343
+ url: message.photoUrl ?? "",
1344
+ width: message.width ?? 0,
1345
+ height: message.height ?? 0,
1346
+ timestamp: message.timestamp ? new Date(message.timestamp).getTime() : Date.now(),
1347
+ savedToGallery: message.savedToGallery ?? false
1348
+ });
1349
+ }
1350
+ handleStreamStatus(message) {
1351
+ this.currentStreamState = {
1352
+ ...message,
1353
+ timestamp: message.timestamp ? new Date(message.timestamp) : new Date
1354
+ };
1355
+ if (this.isStreaming) {
1356
+ if (message.status === "stopped" || message.status === "error" || message.status === "timeout") {
1357
+ this.isStreaming = false;
1358
+ this.currentStreamUrl = undefined;
1359
+ }
1360
+ }
1361
+ this.events.emit("stream_status", this.currentStreamState);
1362
+ }
1363
+ handleManagedStreamStatus(status) {
1364
+ this.managedStreamStatus = status;
1365
+ if (status.status === "initializing" && status.streamId) {
1366
+ this.isManagedStreaming = true;
1367
+ this.currentManagedStreamId = status.streamId;
1368
+ }
1369
+ if (status.status === "active") {
1370
+ this.isManagedStreaming = true;
1371
+ this.currentManagedStreamId = status.streamId;
1372
+ if (status.hlsUrl && status.dashUrl) {
1373
+ const result = {
1374
+ hlsUrl: status.hlsUrl,
1375
+ dashUrl: status.dashUrl,
1376
+ webrtcUrl: status.webrtcUrl,
1377
+ previewUrl: status.previewUrl,
1378
+ thumbnailUrl: status.thumbnailUrl,
1379
+ streamId: status.streamId ?? ""
1380
+ };
1381
+ this.currentManagedStreamUrls = result;
1382
+ if (this.pendingManagedStreamRequest) {
1383
+ clearTimeout(this.pendingManagedStreamRequest.timeoutId);
1384
+ this.pendingManagedStreamRequest.resolve(result);
1385
+ this.pendingManagedStreamRequest = undefined;
1386
+ }
1387
+ }
1388
+ }
1389
+ if (status.status === "error" || status.status === "stopped") {
1390
+ if (this.pendingManagedStreamRequest) {
1391
+ clearTimeout(this.pendingManagedStreamRequest.timeoutId);
1392
+ this.pendingManagedStreamRequest.reject(new Error(status.message || "Managed stream failed"));
1393
+ this.pendingManagedStreamRequest = undefined;
1394
+ }
1395
+ this.isManagedStreaming = false;
1396
+ this.currentManagedStreamId = undefined;
1397
+ this.currentManagedStreamUrls = undefined;
1398
+ }
1399
+ this.events.emit("managed_stream_status", status);
1400
+ }
1401
+ handleStreamCheckResponse(response) {
1402
+ const requestId = response.requestId;
1403
+ const pending = requestId ? this.pendingStreamChecks.get(requestId) : undefined;
1404
+ if (!pending) {
1405
+ const firstEntry = this.pendingStreamChecks.entries().next();
1406
+ if (firstEntry.done || !firstEntry.value) {
1407
+ return;
1408
+ }
1409
+ const [fallbackId, fallbackPending] = firstEntry.value;
1410
+ clearTimeout(fallbackPending.timeoutId);
1411
+ this.pendingStreamChecks.delete(fallbackId);
1412
+ fallbackPending.resolve({
1413
+ hasActiveStream: response.hasActiveStream,
1414
+ streamInfo: response.streamInfo ? {
1415
+ ...response.streamInfo,
1416
+ createdAt: new Date(response.streamInfo.createdAt)
1417
+ } : undefined
1418
+ });
1419
+ return;
1420
+ }
1421
+ clearTimeout(pending.timeoutId);
1422
+ this.pendingStreamChecks.delete(requestId);
1423
+ pending.resolve({
1424
+ hasActiveStream: response.hasActiveStream,
1425
+ streamInfo: response.streamInfo ? {
1426
+ ...response.streamInfo,
1427
+ createdAt: new Date(response.streamInfo.createdAt)
1428
+ } : undefined
1429
+ });
1430
+ }
1431
+ destroy() {
1432
+ for (const [requestId, pending] of this.pendingRequests) {
1433
+ clearTimeout(pending.timer);
1434
+ pending.reject(new Error(`CameraManager destroyed — session disconnected (${requestId})`));
1435
+ }
1436
+ this.pendingRequests.clear();
1437
+ for (const [requestId, pending] of this.pendingStreamChecks) {
1438
+ clearTimeout(pending.timeoutId);
1439
+ pending.resolve({ hasActiveStream: false });
1440
+ this.pendingStreamChecks.delete(requestId);
1441
+ }
1442
+ if (this.pendingManagedStreamRequest) {
1443
+ clearTimeout(this.pendingManagedStreamRequest.timeoutId);
1444
+ this.pendingManagedStreamRequest.reject(new Error("CameraManager destroyed — session disconnected"));
1445
+ this.pendingManagedStreamRequest = undefined;
1446
+ }
1447
+ for (const cleanup of this.handlerCleanups) {
1448
+ cleanup();
1449
+ }
1450
+ this.handlerCleanups.length = 0;
1451
+ this.events.removeAllListeners();
1452
+ this.currentStreamState = undefined;
1453
+ this.currentStreamUrl = undefined;
1454
+ this.isStreaming = false;
1455
+ this.managedStreamStatus = undefined;
1456
+ this.currentManagedStreamId = undefined;
1457
+ this.currentManagedStreamUrls = undefined;
1458
+ this.isManagedStreaming = false;
1459
+ }
1460
+ }
1461
+ function normalisePhotoData(raw) {
1462
+ return {
1463
+ url: raw.photoUrl ?? raw.url ?? "",
1464
+ width: raw.width ?? 0,
1465
+ height: raw.height ?? 0,
1466
+ timestamp: raw.timestamp ? new Date(raw.timestamp).getTime() : Date.now(),
1467
+ savedToGallery: raw.savedToGallery ?? false
1468
+ };
1469
+ }
1470
+
1471
+ // src/session/managers/DashboardManager.ts
1472
+ init_message_types();
1473
+ init_dashboard();
1474
+
1475
+ class DashboardManager {
1476
+ deps;
1477
+ constructor(deps) {
1478
+ this.deps = deps;
1479
+ }
1480
+ showText(text) {
1481
+ const content = Array.isArray(text) ? text.join(`
1482
+ `) : text;
1483
+ const message = {
1484
+ type: "dashboard_content_update" /* DASHBOARD_CONTENT_UPDATE */,
1485
+ packageName: this.deps.getPackageName(),
1486
+ sessionId: `${this.deps.getSessionId()}-${this.deps.getPackageName()}`,
1487
+ content,
1488
+ modes: ["main" /* MAIN */],
1489
+ timestamp: new Date
1490
+ };
1491
+ this.deps.sendMessage(message);
1492
+ this.deps.logger.debug({ contentLength: content.length }, "\uD83D\uDCCA Dashboard content update sent");
1493
+ }
1494
+ clear() {
1495
+ const message = {
1496
+ type: "dashboard_content_update" /* DASHBOARD_CONTENT_UPDATE */,
1497
+ packageName: this.deps.getPackageName(),
1498
+ sessionId: `${this.deps.getSessionId()}-${this.deps.getPackageName()}`,
1499
+ content: "",
1500
+ modes: ["main" /* MAIN */],
1501
+ timestamp: new Date
1502
+ };
1503
+ this.deps.sendMessage(message);
1504
+ this.deps.logger.debug("\uD83D\uDCCA Dashboard content cleared");
1505
+ }
1506
+ destroy() {
1507
+ this.deps.logger.debug("[DashboardManager] Destroyed.");
1508
+ }
1509
+ }
1510
+
1511
+ // src/utils/Observable.ts
1512
+ class Observable {
1513
+ _value;
1514
+ _listeners = new Set;
1515
+ _initialized = false;
1516
+ constructor(initialValue) {
1517
+ this._value = initialValue;
1518
+ }
1519
+ get value() {
1520
+ return this._value;
1521
+ }
1522
+ valueOf() {
1523
+ return this._value;
1524
+ }
1525
+ toString() {
1526
+ return String(this._value);
1527
+ }
1528
+ [Symbol.toPrimitive](hint) {
1529
+ if (hint === "string") {
1530
+ return String(this._value);
1531
+ }
1532
+ return this._value;
1533
+ }
1534
+ onChange(callback) {
1535
+ this._listeners.add(callback);
1536
+ if (this._initialized) {
1537
+ callback(this._value);
1538
+ }
1539
+ return () => this._listeners.delete(callback);
1540
+ }
1541
+ setValue(value) {
1542
+ const isFirstInit = !this._initialized;
1543
+ if (isFirstInit) {
1544
+ this._initialized = true;
1545
+ }
1546
+ if (isFirstInit || this._value !== value) {
1547
+ this._value = value;
1548
+ this._listeners.forEach((cb) => {
1549
+ try {
1550
+ cb(value);
1551
+ } catch (error) {
1552
+ console.error("Error in Observable onChange callback:", error);
1553
+ }
1554
+ });
1555
+ }
1556
+ }
1557
+ get listenerCount() {
1558
+ return this._listeners.size;
1559
+ }
1560
+ }
1561
+
1562
+ // src/session/managers/DeviceManager.ts
1563
+ init_streams();
1564
+ function normaliseTouchEvent(raw) {
1565
+ return {
1566
+ ...raw,
1567
+ gesture: raw.gesture_name ?? raw.gesture ?? "unknown",
1568
+ model: raw.device_model ?? raw.model ?? "unknown",
1569
+ timestamp: raw.timestamp ?? new Date().toISOString()
1570
+ };
1571
+ }
1572
+
1573
+ class DeviceManager {
1574
+ state;
1575
+ capabilities = null;
1576
+ deps;
1577
+ permissions;
1578
+ _connected;
1579
+ _modelName;
1580
+ _batteryLevel;
1581
+ _charging;
1582
+ _caseBatteryLevel;
1583
+ _caseCharging;
1584
+ _caseOpen;
1585
+ _caseRemoved;
1586
+ _wifiConnected;
1587
+ _wifiSsid;
1588
+ _wifiLocalIp;
1589
+ _hotspotEnabled;
1590
+ _hotspotSsid;
1591
+ capabilitiesListeners = new Set;
1592
+ handlerCounts = new Map;
1593
+ cleanups = [];
1594
+ constructor(deps) {
1595
+ this.deps = deps;
1596
+ this.permissions = deps.permissions;
1597
+ this._connected = new Observable(false);
1598
+ this._modelName = new Observable(null);
1599
+ this._batteryLevel = new Observable(null);
1600
+ this._charging = new Observable(null);
1601
+ this._caseBatteryLevel = new Observable(null);
1602
+ this._caseCharging = new Observable(null);
1603
+ this._caseOpen = new Observable(null);
1604
+ this._caseRemoved = new Observable(null);
1605
+ this._wifiConnected = new Observable(false);
1606
+ this._wifiSsid = new Observable(null);
1607
+ this._wifiLocalIp = new Observable(null);
1608
+ this._hotspotEnabled = new Observable(null);
1609
+ this._hotspotSsid = new Observable(null);
1610
+ this.state = {
1611
+ connected: this._connected,
1612
+ modelName: this._modelName,
1613
+ batteryLevel: this._batteryLevel,
1614
+ charging: this._charging,
1615
+ caseBatteryLevel: this._caseBatteryLevel,
1616
+ caseCharging: this._caseCharging,
1617
+ caseOpen: this._caseOpen,
1618
+ caseRemoved: this._caseRemoved,
1619
+ wifiConnected: this._wifiConnected,
1620
+ wifiSsid: this._wifiSsid,
1621
+ wifiLocalIp: this._wifiLocalIp,
1622
+ hotspotEnabled: this._hotspotEnabled,
1623
+ hotspotSsid: this._hotspotSsid
1624
+ };
1625
+ this.cleanups.push(deps.messageHandlers.register("device_state_update", (msg) => {
1626
+ this.handleDeviceStateUpdate(msg);
1627
+ }));
1628
+ this.cleanups.push(deps.messageHandlers.register("capabilities_update", (msg) => {
1629
+ this.handleCapabilitiesUpdate(msg);
1630
+ }));
1631
+ }
1632
+ onButtonPress(handler) {
1633
+ return this.addStreamHandler("button_press" /* BUTTON_PRESS */, (_st, data) => {
1634
+ handler({
1635
+ buttonId: data.buttonId ?? data.button_id ?? "unknown",
1636
+ pressType: data.pressType ?? data.press_type ?? "short"
1637
+ });
1638
+ });
1639
+ }
1640
+ onHeadPosition(handler) {
1641
+ return this.addStreamHandler("head_position" /* HEAD_POSITION */, (_st, data) => {
1642
+ handler({
1643
+ position: data.position ?? "down"
1644
+ });
1645
+ });
1646
+ }
1647
+ onTouchEvent(gestureOrHandler, handler) {
1648
+ if (typeof gestureOrHandler === "function") {
1649
+ return this.addStreamHandler("touch_event" /* TOUCH_EVENT */, (_st, data) => {
1650
+ gestureOrHandler(normaliseTouchEvent(data));
1651
+ });
1652
+ }
1653
+ const gesture = gestureOrHandler;
1654
+ const gestureStream = `${"touch_event" /* TOUCH_EVENT */}:${gesture}`;
1655
+ return this.addStreamHandler(gestureStream, (_st, data) => {
1656
+ handler(normaliseTouchEvent(data));
1657
+ });
1658
+ }
1659
+ subscribeToGestures(gestures) {
1660
+ const cleanupFns = [];
1661
+ for (const gesture of gestures) {
1662
+ const gestureStream = `${"touch_event" /* TOUCH_EVENT */}:${gesture}`;
1663
+ const cleanup = this.addStreamHandler(gestureStream, () => {});
1664
+ cleanupFns.push(cleanup);
1665
+ }
1666
+ return () => {
1667
+ for (const fn of cleanupFns) {
1668
+ fn();
1669
+ }
1670
+ };
1671
+ }
1672
+ onBatteryUpdate(handler) {
1673
+ return this.addStreamHandler("glasses_battery_update" /* GLASSES_BATTERY_UPDATE */, (_st, data) => {
1674
+ if (data.level !== undefined) {
1675
+ this._batteryLevel.setValue(data.level);
1676
+ }
1677
+ if (data.charging !== undefined) {
1678
+ this._charging.setValue(data.charging);
1679
+ }
1680
+ handler({
1681
+ level: data.level ?? 0,
1682
+ charging: data.charging ?? false,
1683
+ timeRemaining: data.timeRemaining ?? data.time_remaining
1684
+ });
1685
+ });
1686
+ }
1687
+ onVpsCoordinates(handler) {
1688
+ return this.addStreamHandler("vps_coordinates" /* VPS_COORDINATES */, (_st, data) => {
1689
+ handler(data);
1690
+ });
1691
+ }
1692
+ requestWifiSetup(reason) {
1693
+ this.deps.logger.info(`DeviceManager: Requesting WiFi setup${reason ? ` — ${reason}` : ""}`);
1694
+ this.deps.sendMessage({
1695
+ type: "request_wifi_setup",
1696
+ packageName: this.deps.getPackageName(),
1697
+ sessionId: this.deps.getSessionId(),
1698
+ timestamp: new Date().toISOString(),
1699
+ ...reason ? { reason } : {}
1700
+ });
1701
+ }
1702
+ onCapabilitiesChange(handler) {
1703
+ this.capabilitiesListeners.add(handler);
1704
+ return () => {
1705
+ this.capabilitiesListeners.delete(handler);
1706
+ };
1707
+ }
1708
+ handleDeviceStateUpdate(message) {
1709
+ const state = message?.state ?? message?.data ?? message;
1710
+ if (!state) {
1711
+ this.deps.logger.debug("DeviceManager: Received empty device_state_update");
1712
+ return;
1713
+ }
1714
+ this.deps.logger.debug("DeviceManager: Processing device state update");
1715
+ if (state.connected !== undefined)
1716
+ this._connected.setValue(state.connected);
1717
+ if (state.modelName !== undefined)
1718
+ this._modelName.setValue(state.modelName);
1719
+ if (state.wifiConnected !== undefined)
1720
+ this._wifiConnected.setValue(state.wifiConnected);
1721
+ if (state.wifiSsid !== undefined)
1722
+ this._wifiSsid.setValue(state.wifiSsid ?? null);
1723
+ if (state.wifiLocalIp !== undefined)
1724
+ this._wifiLocalIp.setValue(state.wifiLocalIp ?? null);
1725
+ if (state.batteryLevel !== undefined)
1726
+ this._batteryLevel.setValue(state.batteryLevel ?? null);
1727
+ if (state.charging !== undefined)
1728
+ this._charging.setValue(state.charging ?? null);
1729
+ if (state.caseBatteryLevel !== undefined)
1730
+ this._caseBatteryLevel.setValue(state.caseBatteryLevel ?? null);
1731
+ if (state.caseCharging !== undefined)
1732
+ this._caseCharging.setValue(state.caseCharging ?? null);
1733
+ if (state.caseOpen !== undefined)
1734
+ this._caseOpen.setValue(state.caseOpen ?? null);
1735
+ if (state.caseRemoved !== undefined)
1736
+ this._caseRemoved.setValue(state.caseRemoved ?? null);
1737
+ if (state.hotspotEnabled !== undefined)
1738
+ this._hotspotEnabled.setValue(state.hotspotEnabled ?? null);
1739
+ if (state.hotspotSsid !== undefined)
1740
+ this._hotspotSsid.setValue(state.hotspotSsid ?? null);
1741
+ }
1742
+ handleCapabilitiesUpdate(message) {
1743
+ const caps = message?.capabilities ?? message?.data?.capabilities ?? null;
1744
+ const modelName = message?.modelName ?? message?.data?.modelName ?? null;
1745
+ if (modelName) {
1746
+ this._modelName.setValue(modelName);
1747
+ }
1748
+ this.setCapabilities(caps);
1749
+ }
1750
+ setCapabilities(caps) {
1751
+ this.capabilities = caps;
1752
+ this.deps.logger.info(`DeviceManager: Capabilities ${caps ? "updated" : "cleared"}`);
1753
+ for (const listener of this.capabilitiesListeners) {
1754
+ try {
1755
+ listener(caps);
1756
+ } catch (err) {
1757
+ this.deps.logger.error(`DeviceManager: Error in capabilities listener: ${err instanceof Error ? err.message : String(err)}`);
1758
+ }
1759
+ }
1760
+ }
1761
+ destroy() {
1762
+ for (const cleanup of this.cleanups) {
1763
+ cleanup();
1764
+ }
1765
+ this.cleanups.length = 0;
1766
+ this.handlerCounts.clear();
1767
+ this.capabilitiesListeners.clear();
1768
+ }
1769
+ addStreamHandler(streamKey, handler) {
1770
+ const currentCount = this.handlerCounts.get(streamKey) ?? 0;
1771
+ if (currentCount === 0) {
1772
+ this.deps.addSubscription(streamKey);
1773
+ }
1774
+ this.handlerCounts.set(streamKey, currentCount + 1);
1775
+ const routerCleanup = this.deps.router.on(streamKey, handler);
1776
+ let cleaned = false;
1777
+ const cleanup = () => {
1778
+ if (cleaned)
1779
+ return;
1780
+ cleaned = true;
1781
+ routerCleanup();
1782
+ const count = this.handlerCounts.get(streamKey) ?? 0;
1783
+ const newCount = count - 1;
1784
+ if (newCount <= 0) {
1785
+ this.handlerCounts.delete(streamKey);
1786
+ this.deps.removeSubscription(streamKey);
1787
+ } else {
1788
+ this.handlerCounts.set(streamKey, newCount);
1789
+ }
1790
+ };
1791
+ this.cleanups.push(cleanup);
1792
+ return cleanup;
1793
+ }
1794
+ }
1795
+
1796
+ // src/session/managers/DisplayManager.ts
1797
+ init_enums();
1798
+ init_message_types();
1799
+
1800
+ class DisplayManager {
1801
+ deps;
1802
+ constructor(deps) {
1803
+ this.deps = deps;
1804
+ }
1805
+ showText(text) {
1806
+ const resolved = Array.isArray(text) ? text.join(`
1807
+ `) : text;
1808
+ this.showTextWall(resolved);
1809
+ }
1810
+ showTextWall(text) {
1811
+ if (text === undefined || text === null) {
1812
+ text = "";
1813
+ this.deps.logger.warn("showTextWall called with null/undefined text");
1814
+ }
1815
+ if (typeof text !== "string") {
1816
+ text = String(text);
1817
+ this.deps.logger.warn("showTextWall: non-string input converted to string");
1818
+ }
1819
+ const layout = {
1820
+ layoutType: "text_wall" /* TEXT_WALL */,
1821
+ text
1822
+ };
1823
+ try {
1824
+ this.sendDisplayEvent(layout);
1825
+ } catch (err) {
1826
+ this.deps.logger.error("Failed to display text wall:", err);
1827
+ }
1828
+ }
1829
+ showDoubleTextWall(leftText, rightText) {
1830
+ const layout = {
1831
+ layoutType: "double_text_wall" /* DOUBLE_TEXT_WALL */,
1832
+ topText: leftText,
1833
+ bottomText: rightText
1834
+ };
1835
+ this.sendDisplayEvent(layout);
1836
+ }
1837
+ showReferenceCard(title, body) {
1838
+ const layout = {
1839
+ layoutType: "reference_card" /* REFERENCE_CARD */,
1840
+ title,
1841
+ text: body
1842
+ };
1843
+ this.sendDisplayEvent(layout);
1844
+ }
1845
+ showDashboardCard(leftText, rightText) {
1846
+ const layout = {
1847
+ layoutType: "dashboard_card" /* DASHBOARD_CARD */,
1848
+ leftText,
1849
+ rightText
1850
+ };
1851
+ this.sendDisplayEvent(layout, "dashboard" /* DASHBOARD */);
1852
+ }
1853
+ showBitmap(data) {
1854
+ if (typeof data !== "string") {
1855
+ this.deps.logger.error("showBitmap: data must be a string");
1856
+ return;
1857
+ }
1858
+ if (data.length > 1e6) {
1859
+ this.deps.logger.error("showBitmap: data exceeds 1 MB limit");
1860
+ return;
1861
+ }
1862
+ const layout = {
1863
+ layoutType: "bitmap_view" /* BITMAP_VIEW */,
1864
+ data
1865
+ };
1866
+ this.sendDisplayEvent(layout);
1867
+ }
1868
+ clear() {
1869
+ const layout = {
1870
+ layoutType: "clear_view" /* CLEAR_VIEW */
1871
+ };
1872
+ this.sendDisplayEvent(layout);
1873
+ }
1874
+ sendDisplayEvent(layout, view = "main" /* MAIN */, durationMs) {
1875
+ if (!layout || !layout.layoutType) {
1876
+ this.deps.logger.error("sendDisplayEvent: layout must have a layoutType property");
1877
+ return;
1878
+ }
1879
+ if (view !== "main" /* MAIN */ && view !== "dashboard" /* DASHBOARD */) {
1880
+ this.deps.logger.warn(`Invalid view type: ${view}, defaulting to MAIN`);
1881
+ view = "main" /* MAIN */;
1882
+ }
1883
+ if (durationMs !== undefined) {
1884
+ if (typeof durationMs !== "number" || durationMs < 0) {
1885
+ this.deps.logger.warn(`Invalid duration: ${durationMs}, ignoring`);
1886
+ durationMs = undefined;
1887
+ }
1888
+ }
1889
+ const message = {
1890
+ timestamp: new Date,
1891
+ sessionId: this.deps.getSessionId(),
1892
+ type: "display_event" /* DISPLAY_REQUEST */,
1893
+ packageName: this.deps.getPackageName(),
1894
+ view,
1895
+ layout,
1896
+ durationMs
1897
+ };
1898
+ this.deps.sendMessage(message);
1899
+ }
1900
+ }
1901
+
1902
+ // src/session/managers/LedManager.ts
1903
+ init_types();
1904
+ function generateRequestId2() {
1905
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
1906
+ return `led_req_${crypto.randomUUID()}`;
1907
+ }
1908
+ return `led_req_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
1909
+ }
1910
+
1911
+ class LedManager {
1912
+ deps;
1913
+ constructor(deps) {
1914
+ this.deps = deps;
1915
+ }
1916
+ setColor(color, onTimeMs) {
1917
+ const requestId = generateRequestId2();
1918
+ const message = {
1919
+ type: "rgb_led_control" /* RGB_LED_CONTROL */,
1920
+ packageName: this.deps.getPackageName(),
1921
+ sessionId: this.deps.getSessionId(),
1922
+ requestId,
1923
+ timestamp: new Date,
1924
+ action: "on",
1925
+ color,
1926
+ ontime: onTimeMs ?? 1000,
1927
+ offtime: 0,
1928
+ count: 1
1929
+ };
1930
+ this.deps.sendMessage(message);
1931
+ this.deps.logger.debug({ requestId, color, ontime: message.ontime }, "\uD83D\uDCA1 LED setColor request sent");
1932
+ }
1933
+ off() {
1934
+ const requestId = generateRequestId2();
1935
+ const message = {
1936
+ type: "rgb_led_control" /* RGB_LED_CONTROL */,
1937
+ packageName: this.deps.getPackageName(),
1938
+ sessionId: this.deps.getSessionId(),
1939
+ requestId,
1940
+ timestamp: new Date,
1941
+ action: "off"
1942
+ };
1943
+ this.deps.sendMessage(message);
1944
+ this.deps.logger.debug({ requestId }, "\uD83D\uDCA1 LED off request sent");
1945
+ }
1946
+ destroy() {
1947
+ this.deps.logger.debug("[LedManager] Destroyed.");
1948
+ }
1949
+ }
1950
+
1951
+ // src/session/managers/LocationManager.ts
1952
+ init_types();
1953
+ var LOCATION_STREAM = "location_stream" /* LOCATION_STREAM */;
1954
+ var LOCATION_UPDATE = "location_update" /* LOCATION_UPDATE */;
1955
+ function generateCorrelationId() {
1956
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
1957
+ return `poll_${crypto.randomUUID()}`;
1958
+ }
1959
+ return `poll_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
1960
+ }
1961
+ function normalise(raw) {
1962
+ return {
1963
+ lat: typeof raw.lat === "number" ? raw.lat : 0,
1964
+ lng: typeof raw.lng === "number" ? raw.lng : 0,
1965
+ accuracy: typeof raw.accuracy === "number" ? raw.accuracy : undefined,
1966
+ timestamp: raw.timestamp ? new Date(raw.timestamp).getTime() : Date.now(),
1967
+ correlationId: raw.correlationId
1968
+ };
1969
+ }
1970
+
1971
+ class LocationManager {
1972
+ deps;
1973
+ registrations = new Set;
1974
+ streamRefCount = 0;
1975
+ _lat = null;
1976
+ _lng = null;
1977
+ _accuracy = null;
1978
+ _timestamp = null;
1979
+ _hasPermission = true;
1980
+ locationUpdateCleanup = null;
1981
+ constructor(deps) {
1982
+ this.deps = deps;
1983
+ this.locationUpdateCleanup = this.deps.router.on(LOCATION_UPDATE, (_streamType, data, _message) => {
1984
+ this.cacheLocation(data);
1985
+ });
1986
+ }
1987
+ onUpdate(handler, accuracy) {
1988
+ const streamKey = LOCATION_STREAM;
1989
+ const routerCleanup = this.deps.router.on(streamKey, (_streamType, data, _message) => {
1990
+ try {
1991
+ const location = normalise(data);
1992
+ this.cacheLocation(data);
1993
+ handler(location);
1994
+ } catch (err) {
1995
+ this.deps.logger.error("[LocationManager] Error in onUpdate handler:", err);
1996
+ }
1997
+ });
1998
+ const updateCleanup = this.deps.router.on(LOCATION_UPDATE, (_streamType, data, _message) => {
1999
+ try {
2000
+ const location = normalise(data);
2001
+ this.cacheLocation(data);
2002
+ handler(location);
2003
+ } catch (err) {
2004
+ this.deps.logger.error("[LocationManager] Error in onUpdate handler (location_update):", err);
2005
+ }
2006
+ });
2007
+ const reg = { routerCleanup, updateCleanup, streamKey };
2008
+ this.registrations.add(reg);
2009
+ this.streamRefCount++;
2010
+ if (this.streamRefCount === 1) {
2011
+ this.deps.addSubscription(streamKey);
2012
+ this.deps.logger.debug({ accuracy: accuracy ?? "standard" }, `[LocationManager] Subscribed to "${streamKey}".`);
2013
+ }
2014
+ return () => {
2015
+ if (!this.registrations.has(reg))
2016
+ return;
2017
+ routerCleanup();
2018
+ updateCleanup();
2019
+ this.registrations.delete(reg);
2020
+ this.streamRefCount--;
2021
+ if (this.streamRefCount <= 0) {
2022
+ this.streamRefCount = 0;
2023
+ this.deps.removeSubscription(streamKey);
2024
+ this.deps.logger.debug(`[LocationManager] Unsubscribed from "${streamKey}".`);
2025
+ }
2026
+ };
2027
+ }
2028
+ requestUpdate(accuracy) {
2029
+ const correlationId = generateCorrelationId();
2030
+ const message = {
2031
+ type: "location_poll_request" /* LOCATION_POLL_REQUEST */,
2032
+ correlationId,
2033
+ packageName: this.deps.getPackageName(),
2034
+ sessionId: this.deps.getSessionId(),
2035
+ accuracy: accuracy ?? "standard"
2036
+ };
2037
+ this.deps.sendMessage(message);
2038
+ this.deps.logger.debug({ correlationId, accuracy: message.accuracy }, "\uD83D\uDCCD Location poll request sent");
2039
+ }
2040
+ stop() {
2041
+ const snapshot = Array.from(this.registrations);
2042
+ for (const reg of snapshot) {
2043
+ reg.routerCleanup();
2044
+ reg.updateCleanup();
2045
+ this.registrations.delete(reg);
2046
+ }
2047
+ if (this.streamRefCount > 0) {
2048
+ this.deps.removeSubscription(LOCATION_STREAM);
2049
+ }
2050
+ this.streamRefCount = 0;
2051
+ this.deps.logger.debug("[LocationManager] All subscriptions stopped.");
2052
+ }
2053
+ get lat() {
2054
+ return this._lat;
2055
+ }
2056
+ get lng() {
2057
+ return this._lng;
2058
+ }
2059
+ get accuracy() {
2060
+ return this._accuracy;
2061
+ }
2062
+ get timestamp() {
2063
+ return this._timestamp;
2064
+ }
2065
+ get hasPermission() {
2066
+ return this._hasPermission;
2067
+ }
2068
+ cacheLocation(raw) {
2069
+ if (typeof raw.lat === "number") {
2070
+ this._lat = raw.lat;
2071
+ }
2072
+ if (typeof raw.lng === "number") {
2073
+ this._lng = raw.lng;
2074
+ }
2075
+ if (typeof raw.accuracy === "number") {
2076
+ this._accuracy = raw.accuracy;
2077
+ }
2078
+ this._timestamp = raw.timestamp ? new Date(raw.timestamp).getTime() : Date.now();
2079
+ }
2080
+ setPermission(granted) {
2081
+ this._hasPermission = granted;
2082
+ }
2083
+ destroy() {
2084
+ this.stop();
2085
+ if (this.locationUpdateCleanup) {
2086
+ this.locationUpdateCleanup();
2087
+ this.locationUpdateCleanup = null;
2088
+ }
2089
+ this._lat = null;
2090
+ this._lng = null;
2091
+ this._accuracy = null;
2092
+ this._timestamp = null;
2093
+ this.deps.logger.debug("[LocationManager] Destroyed.");
2094
+ }
2095
+ }
2096
+
2097
+ // src/session/managers/MicManager.ts
2098
+ var AUDIO_CHUNK_STREAM = "audio_chunk";
2099
+ var VAD_STREAM = "VAD";
2100
+
2101
+ class MicManager {
2102
+ deps;
2103
+ chunkHandlers = new Set;
2104
+ vadHandlers = new Set;
2105
+ vadRouterCleanup = null;
2106
+ _isSpeaking = false;
2107
+ _hasPermission = true;
2108
+ constructor(deps) {
2109
+ this.deps = deps;
2110
+ }
2111
+ get isSpeaking() {
2112
+ return this._isSpeaking;
2113
+ }
2114
+ get isActive() {
2115
+ return this.chunkHandlers.size > 0;
2116
+ }
2117
+ get hasPermission() {
2118
+ return this._hasPermission;
2119
+ }
2120
+ onChunk(handler) {
2121
+ const isFirst = this.chunkHandlers.size === 0;
2122
+ this.chunkHandlers.add(handler);
2123
+ if (isFirst) {
2124
+ this.deps.addSubscription(AUDIO_CHUNK_STREAM);
2125
+ this.deps.logger.debug("Subscribed to audio_chunk stream");
2126
+ }
2127
+ return () => {
2128
+ this.chunkHandlers.delete(handler);
2129
+ if (this.chunkHandlers.size === 0) {
2130
+ this.deps.removeSubscription(AUDIO_CHUNK_STREAM);
2131
+ this.deps.logger.debug("Unsubscribed from audio_chunk stream");
2132
+ }
2133
+ };
2134
+ }
2135
+ onVoiceActivity(handler) {
2136
+ const isFirst = this.vadHandlers.size === 0;
2137
+ this.vadHandlers.add(handler);
2138
+ if (isFirst) {
2139
+ this.deps.addSubscription(VAD_STREAM);
2140
+ this.vadRouterCleanup = this.deps.router.on(VAD_STREAM, (_streamType, data, _message) => {
2141
+ this.handleVadMessage(data);
2142
+ });
2143
+ this.deps.logger.debug("Subscribed to VAD stream");
2144
+ }
2145
+ return () => {
2146
+ this.vadHandlers.delete(handler);
2147
+ if (this.vadHandlers.size === 0) {
2148
+ this.deps.removeSubscription(VAD_STREAM);
2149
+ if (this.vadRouterCleanup) {
2150
+ this.vadRouterCleanup();
2151
+ this.vadRouterCleanup = null;
2152
+ }
2153
+ this.deps.logger.debug("Unsubscribed from VAD stream");
2154
+ }
2155
+ };
2156
+ }
2157
+ stop() {
2158
+ if (this.chunkHandlers.size > 0) {
2159
+ this.chunkHandlers.clear();
2160
+ this.deps.removeSubscription(AUDIO_CHUNK_STREAM);
2161
+ this.deps.logger.debug("Stopped audio_chunk subscriptions");
2162
+ }
2163
+ if (this.vadHandlers.size > 0) {
2164
+ this.vadHandlers.clear();
2165
+ this.deps.removeSubscription(VAD_STREAM);
2166
+ if (this.vadRouterCleanup) {
2167
+ this.vadRouterCleanup();
2168
+ this.vadRouterCleanup = null;
2169
+ }
2170
+ this.deps.logger.debug("Stopped VAD subscriptions");
2171
+ }
2172
+ this._isSpeaking = false;
2173
+ }
2174
+ handleBinaryAudio(data) {
2175
+ if (this.chunkHandlers.size === 0) {
2176
+ return;
2177
+ }
2178
+ const chunk = {
2179
+ data,
2180
+ sampleRate: 16000,
2181
+ channels: 1,
2182
+ timestamp: Date.now()
2183
+ };
2184
+ for (const handler of this.chunkHandlers) {
2185
+ try {
2186
+ handler(chunk);
2187
+ } catch (err) {
2188
+ this.deps.logger.error("Audio chunk handler error:", err);
2189
+ }
2190
+ }
2191
+ }
2192
+ handleVadMessage(data) {
2193
+ if (!data)
2194
+ return;
2195
+ const rawStatus = data.status;
2196
+ let isSpeaking;
2197
+ if (typeof rawStatus === "boolean") {
2198
+ isSpeaking = rawStatus;
2199
+ } else if (typeof rawStatus === "string") {
2200
+ isSpeaking = rawStatus.toLowerCase() === "true";
2201
+ } else {
2202
+ this.deps.logger.warn("Unexpected VAD status type:", typeof rawStatus, rawStatus);
2203
+ return;
2204
+ }
2205
+ this._isSpeaking = isSpeaking;
2206
+ const event = {
2207
+ isSpeaking,
2208
+ timestamp: Date.now()
2209
+ };
2210
+ for (const handler of this.vadHandlers) {
2211
+ try {
2212
+ handler(event);
2213
+ } catch (err) {
2214
+ this.deps.logger.error("VAD handler error:", err);
2215
+ }
2216
+ }
2217
+ }
2218
+ }
2219
+
2220
+ // src/session/managers/PermissionsManager.ts
2221
+ var ALL_PERMISSIONS = [
2222
+ "location",
2223
+ "microphone",
2224
+ "camera",
2225
+ "notifications",
2226
+ "calendar"
2227
+ ];
2228
+ function createDefaultPermissions() {
2229
+ return {
2230
+ location: false,
2231
+ microphone: false,
2232
+ camera: false,
2233
+ notifications: false,
2234
+ calendar: false
2235
+ };
2236
+ }
2237
+
2238
+ class PermissionsManager {
2239
+ permissions;
2240
+ listeners = new Set;
2241
+ logger;
2242
+ constructor(deps) {
2243
+ this.logger = deps.logger;
2244
+ this.permissions = createDefaultPermissions();
2245
+ }
2246
+ has(permission) {
2247
+ return this.permissions[permission] ?? false;
2248
+ }
2249
+ getAll() {
2250
+ return { ...this.permissions };
2251
+ }
2252
+ onUpdate(handler) {
2253
+ this.listeners.add(handler);
2254
+ return () => {
2255
+ this.listeners.delete(handler);
2256
+ };
2257
+ }
2258
+ updateFromSettings(settings) {
2259
+ if (!settings) {
2260
+ this.logger.debug("PermissionsManager: No settings provided, skipping update");
2261
+ return;
2262
+ }
2263
+ const previous = { ...this.permissions };
2264
+ let updated = false;
2265
+ const permissionsSource = settings.permissions ?? settings.appPermissions ?? settings;
2266
+ for (const perm of ALL_PERMISSIONS) {
2267
+ if (perm in permissionsSource) {
2268
+ const value = Boolean(permissionsSource[perm]);
2269
+ if (this.permissions[perm] !== value) {
2270
+ this.permissions[perm] = value;
2271
+ updated = true;
2272
+ }
2273
+ }
2274
+ }
2275
+ if (updated) {
2276
+ this.logger.info("PermissionsManager: Permissions updated — " + ALL_PERMISSIONS.map((p) => `${p}=${this.permissions[p]}`).join(", "));
2277
+ for (const perm of ALL_PERMISSIONS) {
2278
+ if (previous[perm] !== this.permissions[perm]) {
2279
+ this.logger.debug(`PermissionsManager: ${perm}: ${previous[perm]} → ${this.permissions[perm]}`);
2280
+ }
2281
+ }
2282
+ const snapshot = this.getAll();
2283
+ for (const listener of this.listeners) {
2284
+ try {
2285
+ listener(snapshot);
2286
+ } catch (err) {
2287
+ this.logger.error(`PermissionsManager: Error in onUpdate listener: ${err instanceof Error ? err.message : String(err)}`);
2288
+ }
2289
+ }
2290
+ } else {
2291
+ this.logger.debug("PermissionsManager: No permission changes detected");
2292
+ }
2293
+ }
2294
+ }
2295
+
2296
+ // src/session/managers/PhoneManager.ts
2297
+ init_streams();
2298
+ function normaliseCalendarEvent(raw) {
2299
+ return {
2300
+ ...raw,
2301
+ eventId: raw.eventId ?? raw.event_id ?? "unknown",
2302
+ title: raw.title ?? "",
2303
+ start: raw.dtStart ?? raw.start ?? "",
2304
+ end: raw.dtEnd ?? raw.end ?? "",
2305
+ timezone: raw.timezone ?? "",
2306
+ timestamp: raw.timeStamp ?? raw.timestamp ?? new Date().toISOString()
2307
+ };
2308
+ }
2309
+
2310
+ class StreamHandlerTracker {
2311
+ router;
2312
+ addSubscription;
2313
+ removeSubscription;
2314
+ handlerCounts = new Map;
2315
+ cleanups = [];
2316
+ constructor(router, addSubscription, removeSubscription) {
2317
+ this.router = router;
2318
+ this.addSubscription = addSubscription;
2319
+ this.removeSubscription = removeSubscription;
2320
+ }
2321
+ add(streamKey, handler) {
2322
+ const currentCount = this.handlerCounts.get(streamKey) ?? 0;
2323
+ if (currentCount === 0) {
2324
+ this.addSubscription(streamKey);
2325
+ }
2326
+ this.handlerCounts.set(streamKey, currentCount + 1);
2327
+ const routerCleanup = this.router.on(streamKey, handler);
2328
+ let cleaned = false;
2329
+ const cleanup = () => {
2330
+ if (cleaned)
2331
+ return;
2332
+ cleaned = true;
2333
+ routerCleanup();
2334
+ const count = this.handlerCounts.get(streamKey) ?? 0;
2335
+ const newCount = count - 1;
2336
+ if (newCount <= 0) {
2337
+ this.handlerCounts.delete(streamKey);
2338
+ this.removeSubscription(streamKey);
2339
+ } else {
2340
+ this.handlerCounts.set(streamKey, newCount);
2341
+ }
2342
+ };
2343
+ this.cleanups.push(cleanup);
2344
+ return cleanup;
2345
+ }
2346
+ destroyAll() {
2347
+ for (const fn of this.cleanups) {
2348
+ fn();
2349
+ }
2350
+ this.cleanups.length = 0;
2351
+ this.handlerCounts.clear();
2352
+ }
2353
+ }
2354
+
2355
+ class NotificationSubManager {
2356
+ permissions;
2357
+ tracker;
2358
+ logger;
2359
+ constructor(permissions, tracker, logger2) {
2360
+ this.permissions = permissions;
2361
+ this.tracker = tracker;
2362
+ this.logger = logger2;
2363
+ }
2364
+ get hasPermission() {
2365
+ return this.permissions.has("notifications");
2366
+ }
2367
+ on(handler) {
2368
+ return this.tracker.add("phone_notification" /* PHONE_NOTIFICATION */, (_streamType, data) => {
2369
+ try {
2370
+ handler({
2371
+ notificationId: data.notificationId ?? data.notification_id ?? "unknown",
2372
+ app: data.app ?? "unknown",
2373
+ title: data.title ?? "",
2374
+ content: data.content ?? "",
2375
+ priority: data.priority ?? "normal"
2376
+ });
2377
+ } catch (err) {
2378
+ this.logger.error(`NotificationSubManager: Error in notification handler: ${err instanceof Error ? err.message : String(err)}`);
2379
+ }
2380
+ });
2381
+ }
2382
+ onDismissed(handler) {
2383
+ return this.tracker.add("phone_notification_dismissed" /* PHONE_NOTIFICATION_DISMISSED */, (_streamType, data) => {
2384
+ try {
2385
+ handler({
2386
+ notificationId: data.notificationId ?? data.notification_id ?? "unknown",
2387
+ app: data.app ?? "unknown",
2388
+ title: data.title ?? "",
2389
+ content: data.content ?? "",
2390
+ notificationKey: data.notificationKey ?? data.notification_key ?? ""
2391
+ });
2392
+ } catch (err) {
2393
+ this.logger.error(`NotificationSubManager: Error in dismissal handler: ${err instanceof Error ? err.message : String(err)}`);
2394
+ }
2395
+ });
2396
+ }
2397
+ }
2398
+
2399
+ class CalendarSubManager {
2400
+ permissions;
2401
+ tracker;
2402
+ logger;
2403
+ constructor(permissions, tracker, logger2) {
2404
+ this.permissions = permissions;
2405
+ this.tracker = tracker;
2406
+ this.logger = logger2;
2407
+ }
2408
+ get hasPermission() {
2409
+ return this.permissions.has("calendar");
2410
+ }
2411
+ on(handler) {
2412
+ return this.tracker.add("calendar_event" /* CALENDAR_EVENT */, (_streamType, data) => {
2413
+ try {
2414
+ handler(normaliseCalendarEvent(data));
2415
+ } catch (err) {
2416
+ this.logger.error(`CalendarSubManager: Error in calendar handler: ${err instanceof Error ? err.message : String(err)}`);
2417
+ }
2418
+ });
2419
+ }
2420
+ }
2421
+
2422
+ class PhoneManager {
2423
+ notifications;
2424
+ calendar;
2425
+ deps;
2426
+ permissions;
2427
+ tracker;
2428
+ _battery = null;
2429
+ constructor(deps) {
2430
+ this.deps = deps;
2431
+ this.permissions = deps.permissions;
2432
+ this.tracker = new StreamHandlerTracker(deps.router, deps.addSubscription, deps.removeSubscription);
2433
+ this.notifications = new NotificationSubManager(this.permissions, this.tracker, deps.logger);
2434
+ this.calendar = new CalendarSubManager(this.permissions, this.tracker, deps.logger);
2435
+ }
2436
+ get battery() {
2437
+ return this._battery;
2438
+ }
2439
+ onBatteryUpdate(handler) {
2440
+ return this.tracker.add("phone_battery_update" /* PHONE_BATTERY_UPDATE */, (_streamType, data) => {
2441
+ const level = data.level ?? data.batteryLevel ?? data.battery_level;
2442
+ if (level !== undefined) {
2443
+ this._battery = level;
2444
+ }
2445
+ try {
2446
+ handler({
2447
+ level: level ?? 0,
2448
+ charging: data.charging ?? false,
2449
+ timeRemaining: data.timeRemaining ?? data.time_remaining
2450
+ });
2451
+ } catch (err) {
2452
+ this.deps.logger.error(`PhoneManager: Error in battery handler: ${err instanceof Error ? err.message : String(err)}`);
2453
+ }
2454
+ });
2455
+ }
2456
+ destroy() {
2457
+ this.tracker.destroyAll();
2458
+ this._battery = null;
2459
+ this.deps.logger.debug("PhoneManager: Destroyed.");
2460
+ }
2461
+ }
2462
+
2463
+ // src/session/managers/SpeakerManager.ts
2464
+ init_message_types();
2465
+ var STREAM_ID_LENGTH = 36;
2466
+ var STREAM_READY_TIMEOUT_MS = 1e4;
2467
+ var PLAY_RESPONSE_TIMEOUT_MS = 60000;
2468
+
2469
+ class AudioOutputStreamImpl {
2470
+ id;
2471
+ _state = "created";
2472
+ deps;
2473
+ streamIdBytes;
2474
+ options;
2475
+ stateChangeHandlers = [];
2476
+ streamUrl = null;
2477
+ constructor(streamId, deps, opts = {}) {
2478
+ this.id = streamId;
2479
+ this.deps = deps;
2480
+ this.options = {
2481
+ format: opts.format ?? "mp3",
2482
+ sampleRate: opts.sampleRate ?? 24000,
2483
+ channels: opts.channels ?? 1,
2484
+ bitrate: opts.bitrate ?? 128,
2485
+ volume: opts.volume ?? 1,
2486
+ trackId: opts.trackId ?? 1,
2487
+ stopOtherAudio: opts.stopOtherAudio ?? true
2488
+ };
2489
+ this.streamIdBytes = new TextEncoder().encode(this.id);
2490
+ }
2491
+ get state() {
2492
+ return this._state;
2493
+ }
2494
+ async open() {
2495
+ if (this._state !== "created") {
2496
+ throw new Error(`Cannot open stream in state "${this._state}"`);
2497
+ }
2498
+ const startMessage = {
2499
+ type: "audio_stream_start" /* AUDIO_STREAM_START */,
2500
+ packageName: this.deps.getPackageName(),
2501
+ sessionId: this.deps.getSessionId(),
2502
+ streamId: this.id,
2503
+ contentType: "audio/mpeg",
2504
+ timestamp: new Date
2505
+ };
2506
+ this.deps.sendMessage(startMessage);
2507
+ this.streamUrl = await this.waitForReady();
2508
+ this.setState("streaming");
2509
+ const playMessage = {
2510
+ type: "audio_play_request" /* AUDIO_PLAY_REQUEST */,
2511
+ packageName: this.deps.getPackageName(),
2512
+ sessionId: this.deps.getSessionId(),
2513
+ requestId: `stream_${this.id}`,
2514
+ audioUrl: this.streamUrl,
2515
+ volume: this.options.volume,
2516
+ stopOtherAudio: this.options.stopOtherAudio,
2517
+ trackId: this.options.trackId,
2518
+ timestamp: new Date
2519
+ };
2520
+ this.deps.sendMessage(playMessage);
2521
+ this.deps.logger.debug("Audio output stream opened", this.id);
2522
+ }
2523
+ write(chunk) {
2524
+ if (this._state !== "streaming") {
2525
+ this.deps.logger.debug(`Write called on non-streaming output (state=${this._state}), ignoring`);
2526
+ return;
2527
+ }
2528
+ if (chunk.length === 0)
2529
+ return;
2530
+ this.sendBinaryFrame(chunk);
2531
+ }
2532
+ async end() {
2533
+ if (this._state !== "streaming")
2534
+ return;
2535
+ this.setState("ending");
2536
+ const endMessage = {
2537
+ type: "audio_stream_end" /* AUDIO_STREAM_END */,
2538
+ packageName: this.deps.getPackageName(),
2539
+ sessionId: this.deps.getSessionId(),
2540
+ streamId: this.id,
2541
+ timestamp: new Date
2542
+ };
2543
+ this.deps.sendMessage(endMessage);
2544
+ this.setState("ended");
2545
+ this.deps.logger.debug("Audio output stream ended");
2546
+ }
2547
+ flush() {
2548
+ if (this._state !== "streaming")
2549
+ return;
2550
+ this.setState("ending");
2551
+ const endMessage = {
2552
+ type: "audio_stream_end" /* AUDIO_STREAM_END */,
2553
+ packageName: this.deps.getPackageName(),
2554
+ sessionId: this.deps.getSessionId(),
2555
+ streamId: this.id,
2556
+ timestamp: new Date
2557
+ };
2558
+ this.deps.sendMessage(endMessage);
2559
+ const stopMessage = {
2560
+ type: "audio_stop_request" /* AUDIO_STOP_REQUEST */,
2561
+ packageName: this.deps.getPackageName(),
2562
+ sessionId: this.deps.getSessionId(),
2563
+ trackId: this.options.trackId,
2564
+ timestamp: new Date
2565
+ };
2566
+ this.deps.sendMessage(stopMessage);
2567
+ this.setState("ended");
2568
+ this.deps.logger.debug("Audio output stream flushed (interrupted)");
2569
+ }
2570
+ onStateChange(handler) {
2571
+ this.stateChangeHandlers.push(handler);
2572
+ }
2573
+ setState(state) {
2574
+ this._state = state;
2575
+ for (const handler of this.stateChangeHandlers) {
2576
+ try {
2577
+ handler(state);
2578
+ } catch (err) {
2579
+ this.deps.logger.error("AudioOutputStream state change handler error:", err);
2580
+ }
2581
+ }
2582
+ }
2583
+ sendBinaryFrame(audioData) {
2584
+ const frame = new Uint8Array(STREAM_ID_LENGTH + audioData.length);
2585
+ frame.set(this.streamIdBytes, 0);
2586
+ frame.set(audioData, STREAM_ID_LENGTH);
2587
+ try {
2588
+ this.deps.sendBinary(frame);
2589
+ } catch (err) {
2590
+ this.deps.logger.error("Failed to send binary audio frame:", err);
2591
+ this.setState("error");
2592
+ }
2593
+ }
2594
+ waitForReady() {
2595
+ return new Promise((resolve, reject) => {
2596
+ let settled = false;
2597
+ const timeout = setTimeout(() => {
2598
+ if (settled)
2599
+ return;
2600
+ settled = true;
2601
+ unregister();
2602
+ reject(new Error(`Audio stream relay not ready after ${STREAM_READY_TIMEOUT_MS}ms`));
2603
+ }, STREAM_READY_TIMEOUT_MS);
2604
+ const unregister = this.deps.messageHandlers.register("audio_stream_ready" /* AUDIO_STREAM_READY */, (message) => {
2605
+ if (message?.streamId === this.id) {
2606
+ if (settled)
2607
+ return;
2608
+ settled = true;
2609
+ clearTimeout(timeout);
2610
+ unregister();
2611
+ resolve(message.streamUrl);
2612
+ }
2613
+ });
2614
+ });
2615
+ }
2616
+ }
2617
+
2618
+ class SpeakerManager {
2619
+ deps;
2620
+ pendingRequests = new Map;
2621
+ activeStream = null;
2622
+ _hasPermission = true;
2623
+ responseHandlerCleanup = null;
2624
+ constructor(deps) {
2625
+ this.deps = deps;
2626
+ this.responseHandlerCleanup = this.deps.messageHandlers.register("audio_play_response" /* AUDIO_PLAY_RESPONSE */, (message) => {
2627
+ this.handleAudioPlayResponse(message);
2628
+ });
2629
+ }
2630
+ get hasPermission() {
2631
+ return this._hasPermission;
2632
+ }
2633
+ async play(opts) {
2634
+ if (!opts.url) {
2635
+ throw new Error("PlayOptions.url must be provided");
2636
+ }
2637
+ const requestId = `audio_req_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
2638
+ const volume = opts.volume ?? 1;
2639
+ const trackId = opts.trackId ?? 0;
2640
+ const stopOtherAudio = opts.stopOtherAudio ?? false;
2641
+ const message = {
2642
+ type: "audio_play_request" /* AUDIO_PLAY_REQUEST */,
2643
+ packageName: this.deps.getPackageName(),
2644
+ sessionId: this.deps.getSessionId(),
2645
+ requestId,
2646
+ timestamp: new Date,
2647
+ audioUrl: opts.url,
2648
+ volume,
2649
+ stopOtherAudio,
2650
+ trackId
2651
+ };
2652
+ if (!stopOtherAudio) {
2653
+ this.deps.sendMessage(message);
2654
+ this.deps.logger.debug("Audio playback started in non-blocking mode", requestId);
2655
+ return { duration: 0 };
2656
+ }
2657
+ return new Promise((resolve, reject) => {
2658
+ const timer = setTimeout(() => {
2659
+ this.pendingRequests.delete(requestId);
2660
+ reject(new Error("Audio play request timed out"));
2661
+ this.deps.logger.warn("Audio play request timed out", requestId);
2662
+ }, PLAY_RESPONSE_TIMEOUT_MS);
2663
+ this.pendingRequests.set(requestId, { resolve, reject, timer });
2664
+ this.deps.sendMessage(message);
2665
+ });
2666
+ }
2667
+ async stop(trackId) {
2668
+ const message = {
2669
+ type: "audio_stop_request" /* AUDIO_STOP_REQUEST */,
2670
+ packageName: this.deps.getPackageName(),
2671
+ sessionId: this.deps.getSessionId(),
2672
+ trackId,
2673
+ timestamp: new Date
2674
+ };
2675
+ this.deps.sendMessage(message);
2676
+ const trackInfo = trackId !== undefined ? ` (track ${trackId})` : " (all tracks)";
2677
+ this.deps.logger.info(`Audio stop request sent${trackInfo}`);
2678
+ }
2679
+ async speak(text, opts = {}) {
2680
+ if (!text) {
2681
+ throw new Error("text must be provided");
2682
+ }
2683
+ const queryParams = new URLSearchParams;
2684
+ queryParams.append("text", text);
2685
+ if (opts.voiceId) {
2686
+ queryParams.append("voice_id", opts.voiceId);
2687
+ }
2688
+ if (opts.modelId) {
2689
+ queryParams.append("model_id", opts.modelId);
2690
+ }
2691
+ if (opts.voiceSettings) {
2692
+ const settings = {};
2693
+ if (opts.voiceSettings.stability !== undefined)
2694
+ settings.stability = opts.voiceSettings.stability;
2695
+ if (opts.voiceSettings.similarityBoost !== undefined)
2696
+ settings.similarity_boost = opts.voiceSettings.similarityBoost;
2697
+ if (opts.voiceSettings.style !== undefined)
2698
+ settings.style = opts.voiceSettings.style;
2699
+ if (opts.voiceSettings.speed !== undefined)
2700
+ settings.speed = opts.voiceSettings.speed;
2701
+ queryParams.append("voice_settings", JSON.stringify(settings));
2702
+ }
2703
+ const ttsUrl = `/api/tts?${queryParams.toString()}`;
2704
+ this.deps.logger.debug("Generating speech from text", text);
2705
+ return this.play({
2706
+ url: ttsUrl,
2707
+ volume: opts.volume,
2708
+ stopOtherAudio: opts.stopOtherAudio ?? false,
2709
+ trackId: opts.trackId ?? 2
2710
+ });
2711
+ }
2712
+ async createStream(opts = {}) {
2713
+ if (this.activeStream && this.activeStream.state === "streaming") {
2714
+ const err = new Error(`AUDIO_STREAM_ALREADY_ACTIVE: Stream ${this.activeStream.id} is still active. ` + `Call end() or flush() before creating a new output stream.`);
2715
+ err.code = "AUDIO_STREAM_ALREADY_ACTIVE";
2716
+ this.deps.logger.warn("Refusing to create a second output stream while one is active");
2717
+ throw err;
2718
+ }
2719
+ const streamId = crypto.randomUUID();
2720
+ const stream = new AudioOutputStreamImpl(streamId, this.deps, opts);
2721
+ await stream.open();
2722
+ this.activeStream = stream;
2723
+ stream.onStateChange((state) => {
2724
+ if ((state === "ended" || state === "error") && this.activeStream === stream) {
2725
+ this.activeStream = null;
2726
+ }
2727
+ });
2728
+ return stream;
2729
+ }
2730
+ destroy() {
2731
+ if (this.responseHandlerCleanup) {
2732
+ this.responseHandlerCleanup();
2733
+ this.responseHandlerCleanup = null;
2734
+ }
2735
+ for (const [requestId, pending] of this.pendingRequests) {
2736
+ clearTimeout(pending.timer);
2737
+ pending.reject(new Error("SpeakerManager destroyed"));
2738
+ this.deps.logger.debug("Audio request cancelled during cleanup", requestId);
2739
+ }
2740
+ this.pendingRequests.clear();
2741
+ if (this.activeStream && this.activeStream.state === "streaming") {
2742
+ this.activeStream.end().catch(() => {});
2743
+ this.activeStream = null;
2744
+ }
2745
+ }
2746
+ handleAudioPlayResponse(response) {
2747
+ const requestId = response?.requestId;
2748
+ if (!requestId)
2749
+ return;
2750
+ const pending = this.pendingRequests.get(requestId);
2751
+ if (!pending) {
2752
+ this.deps.logger.debug("Received audio play response for unknown request", requestId);
2753
+ return;
2754
+ }
2755
+ clearTimeout(pending.timer);
2756
+ this.pendingRequests.delete(requestId);
2757
+ if (response.success) {
2758
+ pending.resolve({
2759
+ duration: response.duration ?? 0
2760
+ });
2761
+ this.deps.logger.info("Audio play response received", requestId, "duration:", response.duration);
2762
+ } else {
2763
+ pending.reject(new Error(response.error || "Audio playback failed"));
2764
+ this.deps.logger.warn("Audio play failed", requestId, response.error);
2765
+ }
2766
+ }
2767
+ }
2768
+
2769
+ // src/session/managers/StorageManager.ts
2770
+ var MAX_VALUE_SIZE = 1e5;
2771
+ var DEBOUNCE_MS = 3000;
2772
+ var MAX_WAIT_MS = 1e4;
2773
+
2774
+ class StorageManager {
2775
+ deps;
2776
+ userId;
2777
+ apiKey;
2778
+ cache = null;
2779
+ baseUrl;
2780
+ pendingWrites = new Map;
2781
+ debounceTimer;
2782
+ maxWaitTimer;
2783
+ firstWriteTime;
2784
+ constructor(deps, config) {
2785
+ this.deps = deps;
2786
+ this.userId = config.userId;
2787
+ this.apiKey = config.apiKey ?? "unknown-api-key";
2788
+ this.baseUrl = this.resolveBaseUrl();
2789
+ }
2790
+ async get(key) {
2791
+ try {
2792
+ await this.ensureCacheLoaded();
2793
+ return this.cache?.[key];
2794
+ } catch (error) {
2795
+ this.deps.logger.error("[StorageManager] Error getting item:", error);
2796
+ return;
2797
+ }
2798
+ }
2799
+ async set(key, value) {
2800
+ const serialised = typeof value === "string" ? value : JSON.stringify(value);
2801
+ if (serialised.length > MAX_VALUE_SIZE) {
2802
+ throw new Error(`StorageManager value exceeds 100KB limit (${serialised.length} chars). ` + `For large files, use your own S3 bucket storage.`);
2803
+ }
2804
+ await this.ensureCacheLoaded();
2805
+ if (this.cache) {
2806
+ this.cache[key] = serialised;
2807
+ }
2808
+ this.pendingWrites.set(key, serialised);
2809
+ this.scheduleFlush();
2810
+ }
2811
+ async delete(key) {
2812
+ try {
2813
+ await this.ensureCacheLoaded();
2814
+ if (this.cache) {
2815
+ delete this.cache[key];
2816
+ }
2817
+ this.pendingWrites.delete(key);
2818
+ const response = await fetch(`${this.baseUrl}/api/sdk/simple-storage/${encodeURIComponent(this.userId)}/${encodeURIComponent(key)}`, {
2819
+ method: "DELETE",
2820
+ headers: this.getAuthHeaders()
2821
+ });
2822
+ if (!response.ok) {
2823
+ const errorText = await response.text();
2824
+ this.deps.logger.error("[StorageManager] Failed to delete key from server:", errorText);
2825
+ }
2826
+ } catch (error) {
2827
+ this.deps.logger.error("[StorageManager] Error deleting item:", error);
2828
+ }
2829
+ }
2830
+ async clear() {
2831
+ try {
2832
+ this.cache = {};
2833
+ this.pendingWrites.clear();
2834
+ this.clearTimers();
2835
+ const response = await fetch(`${this.baseUrl}/api/sdk/simple-storage/${encodeURIComponent(this.userId)}`, {
2836
+ method: "DELETE",
2837
+ headers: this.getAuthHeaders()
2838
+ });
2839
+ if (!response.ok) {
2840
+ const errorText = await response.text();
2841
+ this.deps.logger.error("[StorageManager] Failed to clear storage on server:", errorText);
2842
+ }
2843
+ } catch (error) {
2844
+ this.deps.logger.error("[StorageManager] Error clearing storage:", error);
2845
+ }
2846
+ }
2847
+ async keys() {
2848
+ try {
2849
+ await this.ensureCacheLoaded();
2850
+ return Object.keys(this.cache || {});
2851
+ } catch (error) {
2852
+ this.deps.logger.error("[StorageManager] Error getting keys:", error);
2853
+ return [];
2854
+ }
2855
+ }
2856
+ async has(key) {
2857
+ try {
2858
+ await this.ensureCacheLoaded();
2859
+ return key in (this.cache || {});
2860
+ } catch (error) {
2861
+ this.deps.logger.error("[StorageManager] Error checking key:", error);
2862
+ return false;
2863
+ }
2864
+ }
2865
+ async getAll() {
2866
+ try {
2867
+ await this.ensureCacheLoaded();
2868
+ return { ...this.cache || {} };
2869
+ } catch (error) {
2870
+ this.deps.logger.error("[StorageManager] Error getting all data:", error);
2871
+ return {};
2872
+ }
2873
+ }
2874
+ async setMultiple(data) {
2875
+ for (const [key, value] of Object.entries(data)) {
2876
+ const serialised = typeof value === "string" ? value : JSON.stringify(value);
2877
+ if (serialised.length > MAX_VALUE_SIZE) {
2878
+ throw new Error(`StorageManager value for key "${key}" exceeds 100KB limit (${serialised.length} chars).`);
2879
+ }
2880
+ }
2881
+ await this.ensureCacheLoaded();
2882
+ for (const [key, value] of Object.entries(data)) {
2883
+ const serialised = typeof value === "string" ? value : JSON.stringify(value);
2884
+ if (this.cache) {
2885
+ this.cache[key] = serialised;
2886
+ }
2887
+ this.pendingWrites.set(key, serialised);
2888
+ }
2889
+ this.scheduleFlush();
2890
+ }
2891
+ async flush() {
2892
+ if (this.pendingWrites.size === 0)
2893
+ return;
2894
+ this.clearTimers();
2895
+ const batch = Object.fromEntries(this.pendingWrites);
2896
+ this.pendingWrites.clear();
2897
+ try {
2898
+ const response = await fetch(`${this.baseUrl}/api/sdk/simple-storage/${encodeURIComponent(this.userId)}`, {
2899
+ method: "PUT",
2900
+ headers: this.getAuthHeaders(),
2901
+ body: JSON.stringify({ data: batch })
2902
+ });
2903
+ if (!response.ok) {
2904
+ const errorText = await response.text();
2905
+ this.deps.logger.error("[StorageManager] Failed to persist writes:", errorText);
2906
+ if (response.status === 413) {
2907
+ throw new Error("StorageManager total size exceeds 1MB limit. Delete unused keys.");
2908
+ }
2909
+ if (response.status === 429) {
2910
+ throw new Error("StorageManager rate limit exceeded.");
2911
+ }
2912
+ throw new Error(`StorageManager flush failed: ${errorText}`);
2913
+ }
2914
+ } catch (error) {
2915
+ this.deps.logger.error("[StorageManager] Error flushing writes:", error);
2916
+ throw error;
2917
+ }
2918
+ }
2919
+ async destroy() {
2920
+ try {
2921
+ await this.flush();
2922
+ } catch (error) {
2923
+ this.deps.logger.error("[StorageManager] Error flushing on destroy:", error);
2924
+ }
2925
+ this.clearTimers();
2926
+ this.pendingWrites.clear();
2927
+ this.cache = null;
2928
+ this.deps.logger.debug("[StorageManager] Destroyed.");
2929
+ }
2930
+ async ensureCacheLoaded() {
2931
+ if (this.cache !== null)
2932
+ return;
2933
+ await this.fetchFromServer();
2934
+ }
2935
+ async fetchFromServer() {
2936
+ try {
2937
+ const response = await fetch(`${this.baseUrl}/api/sdk/simple-storage/${encodeURIComponent(this.userId)}`, {
2938
+ headers: this.getAuthHeaders()
2939
+ });
2940
+ if (response.ok) {
2941
+ const result = await response.json();
2942
+ if (result.success && result.data) {
2943
+ this.cache = result.data;
2944
+ } else {
2945
+ this.cache = {};
2946
+ }
2947
+ } else {
2948
+ this.deps.logger.error("[StorageManager] Failed to fetch storage from server:", await response.text());
2949
+ this.cache = {};
2950
+ }
2951
+ } catch (error) {
2952
+ this.deps.logger.error("[StorageManager] Error fetching storage from server:", error);
2953
+ this.cache = {};
2954
+ }
2955
+ }
2956
+ scheduleFlush() {
2957
+ if (!this.firstWriteTime) {
2958
+ this.firstWriteTime = Date.now();
2959
+ }
2960
+ if (this.debounceTimer !== undefined) {
2961
+ clearTimeout(this.debounceTimer);
2962
+ this.debounceTimer = undefined;
2963
+ }
2964
+ const elapsedMs = Date.now() - this.firstWriteTime;
2965
+ const remainingMaxWaitMs = MAX_WAIT_MS - elapsedMs;
2966
+ if (remainingMaxWaitMs <= 0) {
2967
+ this.flush().catch((err) => {
2968
+ this.deps.logger.error("[StorageManager] Error in scheduled flush:", err);
2969
+ });
2970
+ return;
2971
+ }
2972
+ this.debounceTimer = setTimeout(() => {
2973
+ this.flush().catch((err) => {
2974
+ this.deps.logger.error("[StorageManager] Error in debounced flush:", err);
2975
+ });
2976
+ }, Math.min(DEBOUNCE_MS, remainingMaxWaitMs));
2977
+ if (this.maxWaitTimer === undefined && remainingMaxWaitMs > 0) {
2978
+ this.maxWaitTimer = setTimeout(() => {
2979
+ this.flush().catch((err) => {
2980
+ this.deps.logger.error("[StorageManager] Error in max-wait flush:", err);
2981
+ });
2982
+ }, remainingMaxWaitMs);
2983
+ }
2984
+ }
2985
+ clearTimers() {
2986
+ if (this.debounceTimer !== undefined) {
2987
+ clearTimeout(this.debounceTimer);
2988
+ this.debounceTimer = undefined;
2989
+ }
2990
+ if (this.maxWaitTimer !== undefined) {
2991
+ clearTimeout(this.maxWaitTimer);
2992
+ this.maxWaitTimer = undefined;
2993
+ }
2994
+ this.firstWriteTime = undefined;
2995
+ }
2996
+ resolveBaseUrl() {
2997
+ const serverUrl = this.deps.getServerUrl?.() ?? null;
2998
+ if (!serverUrl)
2999
+ return "http://localhost:8002";
3000
+ return serverUrl.replace(/\/app-ws$/, "").replace(/^ws/, "http");
3001
+ }
3002
+ getAuthHeaders() {
3003
+ return {
3004
+ Authorization: `Bearer ${this.deps.getPackageName()}:${this.apiKey}`,
3005
+ "Content-Type": "application/json"
3006
+ };
3007
+ }
3008
+ }
3009
+
3010
+ // src/session/managers/TimeUtils.ts
3011
+ class TimeUtils {
3012
+ _zone;
3013
+ constructor(timezone) {
3014
+ TimeUtils.validateTimezone(timezone);
3015
+ this._zone = timezone;
3016
+ }
3017
+ get zone() {
3018
+ return this._zone;
3019
+ }
3020
+ now() {
3021
+ return new Date;
3022
+ }
3023
+ toLocal(date = new Date) {
3024
+ const parts = new Intl.DateTimeFormat("en-US", {
3025
+ timeZone: this._zone,
3026
+ year: "numeric",
3027
+ month: "2-digit",
3028
+ day: "2-digit",
3029
+ hour: "2-digit",
3030
+ minute: "2-digit",
3031
+ second: "2-digit",
3032
+ hour12: false
3033
+ }).formatToParts(date);
3034
+ const get = (type) => {
3035
+ const part = parts.find((p) => p.type === type);
3036
+ return part?.value ?? "0";
3037
+ };
3038
+ const year = parseInt(get("year"), 10);
3039
+ const month = parseInt(get("month"), 10) - 1;
3040
+ const day = parseInt(get("day"), 10);
3041
+ let hour = parseInt(get("hour"), 10);
3042
+ const minute = parseInt(get("minute"), 10);
3043
+ const second = parseInt(get("second"), 10);
3044
+ if (hour === 24)
3045
+ hour = 0;
3046
+ return new Date(Date.UTC(year, month, day, hour, minute, second));
3047
+ }
3048
+ format(date, opts) {
3049
+ const mergedOpts = {
3050
+ ...opts,
3051
+ timeZone: this._zone
3052
+ };
3053
+ return new Intl.DateTimeFormat(undefined, mergedOpts).format(date);
3054
+ }
3055
+ setTimezone(tz) {
3056
+ TimeUtils.validateTimezone(tz);
3057
+ this._zone = tz;
3058
+ }
3059
+ static validateTimezone(tz) {
3060
+ try {
3061
+ Intl.DateTimeFormat(undefined, { timeZone: tz });
3062
+ } catch {
3063
+ throw new RangeError(`Invalid timezone: "${tz}". ` + `Must be a valid IANA timezone identifier (e.g., "America/New_York", "UTC", "Asia/Tokyo").`);
3064
+ }
3065
+ }
3066
+ }
3067
+
3068
+ // src/session/managers/TranscriptionManager.ts
3069
+ init_types();
3070
+ var STREAM_PREFIX = "transcription" /* TRANSCRIPTION */;
3071
+ function subscriptionKey(lang) {
3072
+ return `${STREAM_PREFIX}:${lang}`;
3073
+ }
3074
+ function normalise2(streamType, raw) {
3075
+ const language = raw.detectedLanguage ?? raw.transcribeLanguage ?? streamType.replace(`${STREAM_PREFIX}:`, "") ?? "";
3076
+ return {
3077
+ text: raw.text ?? "",
3078
+ isFinal: !!raw.isFinal,
3079
+ language,
3080
+ speakerId: raw.speakerId,
3081
+ utteranceId: raw.utteranceId,
3082
+ confidence: raw.confidence,
3083
+ startTime: raw.startTime ?? 0,
3084
+ endTime: raw.endTime ?? 0,
3085
+ duration: raw.duration,
3086
+ metadata: raw.metadata
3087
+ };
3088
+ }
3089
+
3090
+ class TranscriptionManager {
3091
+ deps;
3092
+ registrations = new Set;
3093
+ refCounts = new Map;
3094
+ currentConfig = null;
3095
+ constructor(deps) {
3096
+ this.deps = deps;
3097
+ }
3098
+ on(handler) {
3099
+ const stream = subscriptionKey("auto");
3100
+ const routerCleanup = this.deps.router.on(STREAM_PREFIX, (_streamType, data, _message) => {
3101
+ try {
3102
+ handler(normalise2(_streamType, data));
3103
+ } catch (err) {
3104
+ this.deps.logger.error(`[TranscriptionManager] Error in on() handler:`, err);
3105
+ }
3106
+ });
3107
+ const reg = {
3108
+ streams: [stream],
3109
+ routerCleanups: [routerCleanup]
3110
+ };
3111
+ this.addRegistration(reg);
3112
+ return () => this.removeRegistration(reg);
3113
+ }
3114
+ forLanguage(lang, handler) {
3115
+ const langs = Array.isArray(lang) ? lang : [lang];
3116
+ if (langs.length === 0) {
3117
+ this.deps.logger.warn("[TranscriptionManager] forLanguage() called with empty language array — no-op.");
3118
+ return () => {};
3119
+ }
3120
+ const streams2 = [];
3121
+ const routerCleanups = [];
3122
+ for (const l of langs) {
3123
+ const stream = subscriptionKey(l);
3124
+ const cleanup = this.deps.router.on(stream, (_streamType, data, _message) => {
3125
+ try {
3126
+ handler(normalise2(_streamType, data));
3127
+ } catch (err) {
3128
+ this.deps.logger.error(`[TranscriptionManager] Error in forLanguage("${l}") handler:`, err);
3129
+ }
3130
+ });
3131
+ streams2.push(stream);
3132
+ routerCleanups.push(cleanup);
3133
+ }
3134
+ const reg = { streams: streams2, routerCleanups };
3135
+ this.addRegistration(reg);
3136
+ return () => this.removeRegistration(reg);
3137
+ }
3138
+ configure(config) {
3139
+ this.currentConfig = { ...config };
3140
+ this.deps.sendMessage({
3141
+ type: "transcription_config",
3142
+ languageHints: config.languageHints,
3143
+ vocabulary: config.vocabulary,
3144
+ diarization: config.diarization ?? true
3145
+ });
3146
+ this.deps.logger.debug("[TranscriptionManager] Configuration sent:", config);
3147
+ }
3148
+ stop() {
3149
+ const snapshot = Array.from(this.registrations);
3150
+ for (const reg of snapshot) {
3151
+ this.removeRegistration(reg);
3152
+ }
3153
+ this.currentConfig = null;
3154
+ this.deps.logger.debug("[TranscriptionManager] All subscriptions stopped.");
3155
+ }
3156
+ get active() {
3157
+ return this.registrations.size > 0;
3158
+ }
3159
+ get config() {
3160
+ return this.currentConfig ? { ...this.currentConfig } : null;
3161
+ }
3162
+ addRegistration(reg) {
3163
+ this.registrations.add(reg);
3164
+ for (const stream of reg.streams) {
3165
+ const prev = this.refCounts.get(stream) ?? 0;
3166
+ this.refCounts.set(stream, prev + 1);
3167
+ if (prev === 0) {
3168
+ this.deps.addSubscription(stream);
3169
+ this.deps.logger.debug(`[TranscriptionManager] Subscribed to "${stream}".`);
3170
+ }
3171
+ }
3172
+ }
3173
+ removeRegistration(reg) {
3174
+ if (!this.registrations.has(reg))
3175
+ return;
3176
+ for (const cleanup of reg.routerCleanups) {
3177
+ try {
3178
+ cleanup();
3179
+ } catch {}
3180
+ }
3181
+ for (const stream of reg.streams) {
3182
+ const count = this.refCounts.get(stream) ?? 0;
3183
+ const next = count - 1;
3184
+ if (next <= 0) {
3185
+ this.refCounts.delete(stream);
3186
+ this.deps.removeSubscription(stream);
3187
+ this.deps.logger.debug(`[TranscriptionManager] Unsubscribed from "${stream}".`);
3188
+ } else {
3189
+ this.refCounts.set(stream, next);
3190
+ }
3191
+ }
3192
+ this.registrations.delete(reg);
3193
+ }
3194
+ }
3195
+
3196
+ // src/session/managers/TranslationManager.ts
3197
+ init_types();
3198
+ var STREAM_PREFIX2 = "translation" /* TRANSLATION */;
3199
+ function subscriptionKey2(source, target) {
3200
+ return `${STREAM_PREFIX2}:${source}-${target}`;
3201
+ }
3202
+ function parseStreamType(streamType) {
3203
+ const prefixLen = STREAM_PREFIX2.length + 1;
3204
+ if (!streamType.startsWith(`${STREAM_PREFIX2}:`))
3205
+ return null;
3206
+ const pair = streamType.slice(prefixLen);
3207
+ const dashIdx = pair.indexOf("-");
3208
+ if (dashIdx === -1)
3209
+ return null;
3210
+ return {
3211
+ source: pair.slice(0, dashIdx),
3212
+ target: pair.slice(dashIdx + 1)
3213
+ };
3214
+ }
3215
+ function normalise3(streamType, raw) {
3216
+ const parsed = parseStreamType(streamType);
3217
+ const sourceLanguage = raw.transcribeLanguage ?? parsed?.source ?? "";
3218
+ const targetLanguage = raw.translateLanguage ?? parsed?.target ?? "";
3219
+ return {
3220
+ text: raw.text ?? "",
3221
+ isFinal: !!raw.isFinal,
3222
+ sourceLanguage,
3223
+ targetLanguage,
3224
+ originalText: raw.originalText,
3225
+ utteranceId: raw.utteranceId,
3226
+ confidence: raw.confidence,
3227
+ startTime: raw.startTime ?? 0,
3228
+ endTime: raw.endTime ?? 0
3229
+ };
3230
+ }
3231
+
3232
+ class TranslationManager {
3233
+ deps;
3234
+ registrations = new Set;
3235
+ refCounts = new Map;
3236
+ constructor(deps) {
3237
+ this.deps = deps;
3238
+ }
3239
+ on(handler) {
3240
+ const routerCleanup = this.deps.router.on(STREAM_PREFIX2, (_streamType, data, _message) => {
3241
+ try {
3242
+ handler(normalise3(_streamType, data));
3243
+ } catch (err) {
3244
+ this.deps.logger.error("[TranslationManager] Error in on() handler:", err);
3245
+ }
3246
+ });
3247
+ const reg = {
3248
+ streams: [],
3249
+ routerCleanups: [routerCleanup]
3250
+ };
3251
+ this.registrations.add(reg);
3252
+ return () => this.removeRegistration(reg);
3253
+ }
3254
+ to(target, handler) {
3255
+ const targets = Array.isArray(target) ? target : [target];
3256
+ if (targets.length === 0) {
3257
+ this.deps.logger.warn("[TranslationManager] to() called with empty target array — no-op.");
3258
+ return () => {};
3259
+ }
3260
+ const streams2 = [];
3261
+ const routerCleanups = [];
3262
+ for (const t of targets) {
3263
+ const stream = subscriptionKey2("auto", t);
3264
+ const cleanup = this.deps.router.on(stream, (_streamType, data, _message) => {
3265
+ try {
3266
+ handler(normalise3(_streamType, data));
3267
+ } catch (err) {
3268
+ this.deps.logger.error(`[TranslationManager] Error in to("${t}") handler:`, err);
3269
+ }
3270
+ });
3271
+ streams2.push(stream);
3272
+ routerCleanups.push(cleanup);
3273
+ }
3274
+ const reg = { streams: streams2, routerCleanups };
3275
+ this.addRegistration(reg);
3276
+ return () => this.removeRegistration(reg);
3277
+ }
3278
+ fromTo(source, target, handler) {
3279
+ const targets = Array.isArray(target) ? target : [target];
3280
+ if (targets.length === 0) {
3281
+ this.deps.logger.warn("[TranslationManager] fromTo() called with empty target array — no-op.");
3282
+ return () => {};
3283
+ }
3284
+ const streams2 = [];
3285
+ const routerCleanups = [];
3286
+ for (const t of targets) {
3287
+ const stream = subscriptionKey2(source, t);
3288
+ const cleanup = this.deps.router.on(stream, (_streamType, data, _message) => {
3289
+ try {
3290
+ handler(normalise3(_streamType, data));
3291
+ } catch (err) {
3292
+ this.deps.logger.error(`[TranslationManager] Error in fromTo("${source}", "${t}") handler:`, err);
3293
+ }
3294
+ });
3295
+ streams2.push(stream);
3296
+ routerCleanups.push(cleanup);
3297
+ }
3298
+ const reg = { streams: streams2, routerCleanups };
3299
+ this.addRegistration(reg);
3300
+ return () => this.removeRegistration(reg);
3301
+ }
3302
+ stop() {
3303
+ const snapshot = Array.from(this.registrations);
3304
+ for (const reg of snapshot) {
3305
+ this.removeRegistration(reg);
3306
+ }
3307
+ this.deps.logger.debug("[TranslationManager] All subscriptions stopped.");
3308
+ }
3309
+ get active() {
3310
+ return this.registrations.size > 0;
3311
+ }
3312
+ addRegistration(reg) {
3313
+ this.registrations.add(reg);
3314
+ for (const stream of reg.streams) {
3315
+ const prev = this.refCounts.get(stream) ?? 0;
3316
+ this.refCounts.set(stream, prev + 1);
3317
+ if (prev === 0) {
3318
+ this.deps.addSubscription(stream);
3319
+ this.deps.logger.debug(`[TranslationManager] Subscribed to "${stream}".`);
3320
+ }
3321
+ }
3322
+ }
3323
+ removeRegistration(reg) {
3324
+ if (!this.registrations.has(reg))
3325
+ return;
3326
+ for (const cleanup of reg.routerCleanups) {
3327
+ try {
3328
+ cleanup();
3329
+ } catch {}
3330
+ }
3331
+ for (const stream of reg.streams) {
3332
+ const count = this.refCounts.get(stream) ?? 0;
3333
+ const next = count - 1;
3334
+ if (next <= 0) {
3335
+ this.refCounts.delete(stream);
3336
+ this.deps.removeSubscription(stream);
3337
+ this.deps.logger.debug(`[TranslationManager] Unsubscribed from "${stream}".`);
3338
+ } else {
3339
+ this.refCounts.set(stream, next);
3340
+ }
3341
+ }
3342
+ this.registrations.delete(reg);
3343
+ }
3344
+ }
3345
+
3346
+ // src/session/DataStreamRouter.ts
3347
+ class MessageHandlerRegistry {
3348
+ handlers = new Map;
3349
+ register(type, handler) {
3350
+ let list = this.handlers.get(type);
3351
+ if (!list) {
3352
+ list = [];
3353
+ this.handlers.set(type, list);
3354
+ }
3355
+ list.push(handler);
3356
+ return () => {
3357
+ const arr = this.handlers.get(type);
3358
+ if (arr) {
3359
+ const idx = arr.indexOf(handler);
3360
+ if (idx !== -1) {
3361
+ arr.splice(idx, 1);
3362
+ }
3363
+ if (arr.length === 0) {
3364
+ this.handlers.delete(type);
3365
+ }
3366
+ }
3367
+ };
3368
+ }
3369
+ dispatch(message) {
3370
+ const list = this.handlers.get(message.type);
3371
+ if (!list || list.length === 0) {
3372
+ return false;
3373
+ }
3374
+ for (const handler of list) {
3375
+ try {
3376
+ handler(message);
3377
+ } catch (err) {
3378
+ console.error(`[MessageHandlerRegistry] Handler error for type="${message.type}":`, err);
3379
+ }
3380
+ }
3381
+ return true;
3382
+ }
3383
+ has(type) {
3384
+ const list = this.handlers.get(type);
3385
+ return !!list && list.length > 0;
3386
+ }
3387
+ clear() {
3388
+ this.handlers.clear();
3389
+ }
3390
+ }
3391
+
3392
+ class DataStreamRouter {
3393
+ handlers = new Map;
3394
+ prefixKeysCache = null;
3395
+ on(key, handler) {
3396
+ let list = this.handlers.get(key);
3397
+ if (!list) {
3398
+ list = [];
3399
+ this.handlers.set(key, list);
3400
+ }
3401
+ list.push(handler);
3402
+ this.prefixKeysCache = null;
3403
+ return () => {
3404
+ const arr = this.handlers.get(key);
3405
+ if (arr) {
3406
+ const idx = arr.indexOf(handler);
3407
+ if (idx !== -1) {
3408
+ arr.splice(idx, 1);
3409
+ }
3410
+ if (arr.length === 0) {
3411
+ this.handlers.delete(key);
3412
+ this.prefixKeysCache = null;
3413
+ }
3414
+ }
3415
+ };
3416
+ }
3417
+ handle(message) {
3418
+ const streamType = message?.streamType;
3419
+ if (!streamType)
3420
+ return false;
3421
+ const data = message?.data ?? message;
3422
+ let matched = false;
3423
+ const exactHandlers = this.handlers.get(streamType);
3424
+ if (exactHandlers && exactHandlers.length > 0) {
3425
+ for (const handler of exactHandlers) {
3426
+ try {
3427
+ handler(streamType, data, message);
3428
+ matched = true;
3429
+ } catch (err) {
3430
+ console.error(`[DataStreamRouter] Handler error for streamType="${streamType}":`, err);
3431
+ }
3432
+ }
3433
+ }
3434
+ const prefixKeys = this.getPrefixKeys();
3435
+ for (const key of prefixKeys) {
3436
+ if (key === streamType)
3437
+ continue;
3438
+ if (streamType.startsWith(key)) {
3439
+ const nextChar = streamType[key.length];
3440
+ if (nextChar === undefined || nextChar === ":") {
3441
+ const handlers = this.handlers.get(key);
3442
+ if (handlers) {
3443
+ for (const handler of handlers) {
3444
+ try {
3445
+ handler(streamType, data, message);
3446
+ matched = true;
3447
+ } catch (err) {
3448
+ console.error(`[DataStreamRouter] Prefix handler error for key="${key}" streamType="${streamType}":`, err);
3449
+ }
3450
+ }
3451
+ }
3452
+ }
3453
+ }
3454
+ }
3455
+ return matched;
3456
+ }
3457
+ has(key) {
3458
+ const list = this.handlers.get(key);
3459
+ return !!list && list.length > 0;
3460
+ }
3461
+ getRegisteredKeys() {
3462
+ return Array.from(this.handlers.keys()).filter((key) => {
3463
+ const list = this.handlers.get(key);
3464
+ return list && list.length > 0;
3465
+ });
3466
+ }
3467
+ clear() {
3468
+ this.handlers.clear();
3469
+ this.prefixKeysCache = null;
3470
+ }
3471
+ getPrefixKeys() {
3472
+ if (this.prefixKeysCache === null) {
3473
+ this.prefixKeysCache = Array.from(this.handlers.keys()).sort((a, b) => b.length - a.length);
3474
+ }
3475
+ return this.prefixKeysCache;
3476
+ }
3477
+ }
3478
+
3479
+ // src/session/internal/_MessageRouter.ts
3480
+ class _MessageRouter {
3481
+ messageHandlers = new MessageHandlerRegistry;
3482
+ dataStreamRouter = new DataStreamRouter;
3483
+ logger;
3484
+ constructor(logger2) {
3485
+ this.logger = logger2;
3486
+ }
3487
+ handleRawText(raw) {
3488
+ let message;
3489
+ try {
3490
+ message = JSON.parse(raw);
3491
+ } catch (error) {
3492
+ this.logger.warn({ raw }, "MentraSession received invalid JSON");
3493
+ throw error instanceof Error ? error : new Error(String(error));
3494
+ }
3495
+ if (!message?.type) {
3496
+ this.logger.debug({ message }, "MentraSession ignored message without type");
3497
+ return false;
3498
+ }
3499
+ const handled = this.messageHandlers.dispatch(message);
3500
+ if (!handled && message.type !== "pong") {
3501
+ this.logger.debug({ type: message.type }, "MentraSession received unhandled message type");
3502
+ }
3503
+ return handled;
3504
+ }
3505
+ destroy() {
3506
+ this.messageHandlers.clear();
3507
+ }
3508
+ }
3509
+
3510
+ // src/transport/Transport.ts
3511
+ var TransportState = {
3512
+ CONNECTING: 0,
3513
+ OPEN: 1,
3514
+ CLOSING: 2,
3515
+ CLOSED: 3
3516
+ };
3517
+ function isTransportOpen(transport) {
3518
+ return transport.readyState === TransportState.OPEN;
3519
+ }
3520
+ function isTransportClosed(transport) {
3521
+ return transport.readyState === TransportState.CLOSING || transport.readyState === TransportState.CLOSED;
3522
+ }
3523
+
3524
+ // src/session/internal/_ConnectionManager.ts
3525
+ var PING_INTERVAL_MS = 15000;
3526
+
3527
+ class _ConnectionManager {
3528
+ deps;
3529
+ connected = false;
3530
+ parked = false;
3531
+ explicitDisconnect = false;
3532
+ reconnectAttempts = 0;
3533
+ reconnectTimer = null;
3534
+ pingInterval = null;
3535
+ parkedTimer = null;
3536
+ constructor(deps) {
3537
+ this.deps = deps;
3538
+ this.attachTransportHandlers();
3539
+ }
3540
+ get isConnected() {
3541
+ return this.connected && this.deps.transport.readyState === TransportState.OPEN;
3542
+ }
3543
+ get isParked() {
3544
+ return this.parked;
3545
+ }
3546
+ async connect() {
3547
+ this.explicitDisconnect = false;
3548
+ this.parked = false;
3549
+ this.stopParkedTimer();
3550
+ const transport = this.deps.transport;
3551
+ if (typeof transport.connect === "function") {
3552
+ await transport.connect();
3553
+ } else if (this.deps.transport.readyState !== TransportState.OPEN) {
3554
+ throw new Error("Transport is not open and does not expose connect()");
3555
+ }
3556
+ this.deps.onTransportReady();
3557
+ }
3558
+ disconnect() {
3559
+ this.explicitDisconnect = true;
3560
+ this.parked = false;
3561
+ this.connected = false;
3562
+ this.stopReconnectTimer();
3563
+ this.stopPingInterval();
3564
+ this.stopParkedTimer();
3565
+ this.deps.transport.close(1000, "Client disconnect");
3566
+ }
3567
+ markConnected() {
3568
+ this.connected = true;
3569
+ this.parked = false;
3570
+ this.reconnectAttempts = 0;
3571
+ this.stopParkedTimer();
3572
+ this.startPingInterval();
3573
+ }
3574
+ park(timeoutMs, onTimeout) {
3575
+ this.connected = false;
3576
+ this.parked = true;
3577
+ this.stopReconnectTimer();
3578
+ this.stopPingInterval();
3579
+ this.stopParkedTimer();
3580
+ this.parkedTimer = setTimeout(() => {
3581
+ this.parkedTimer = null;
3582
+ this.parked = false;
3583
+ onTimeout();
3584
+ }, timeoutMs);
3585
+ }
3586
+ destroy() {
3587
+ this.connected = false;
3588
+ this.parked = false;
3589
+ this.stopReconnectTimer();
3590
+ this.stopPingInterval();
3591
+ this.stopParkedTimer();
3592
+ }
3593
+ attachTransportHandlers() {
3594
+ this.deps.transport.onMessage((raw) => {
3595
+ this.deps.onTextMessage(raw);
3596
+ });
3597
+ this.deps.transport.onBinary((data) => {
3598
+ this.deps.onBinaryMessage(data);
3599
+ });
3600
+ this.deps.transport.onClose((code, reason) => {
3601
+ const permanent = this.explicitDisconnect || !this.deps.autoReconnect;
3602
+ this.connected = false;
3603
+ this.stopPingInterval();
3604
+ this.deps.onClose({ code, reason, permanent });
3605
+ if (!permanent && !this.parked) {
3606
+ this.scheduleReconnect();
3607
+ }
3608
+ });
3609
+ this.deps.transport.onError((error) => {
3610
+ this.deps.onError(error);
3611
+ });
3612
+ }
3613
+ scheduleReconnect() {
3614
+ if (this.reconnectAttempts >= this.deps.maxReconnectAttempts) {
3615
+ this.deps.onClose({
3616
+ code: 4000,
3617
+ reason: "Maximum reconnection attempts exceeded",
3618
+ permanent: true
3619
+ });
3620
+ return;
3621
+ }
3622
+ const delay = this.deps.reconnectDelay * Math.pow(2, this.reconnectAttempts);
3623
+ this.reconnectAttempts += 1;
3624
+ this.stopReconnectTimer();
3625
+ this.deps.logger.warn({ attempt: this.reconnectAttempts, delay }, "MentraSession transport closed; scheduling reconnect");
3626
+ this.reconnectTimer = setTimeout(() => {
3627
+ this.reconnectTimer = null;
3628
+ this.connect().catch((error) => {
3629
+ this.deps.onError(error instanceof Error ? error : new Error(String(error)));
3630
+ this.scheduleReconnect();
3631
+ });
3632
+ }, delay);
3633
+ }
3634
+ startPingInterval() {
3635
+ this.stopPingInterval();
3636
+ this.pingInterval = setInterval(() => {
3637
+ if (this.deps.transport.readyState !== TransportState.OPEN) {
3638
+ return;
3639
+ }
3640
+ this.deps.transport.send(JSON.stringify({ type: "ping" }));
3641
+ }, PING_INTERVAL_MS);
3642
+ }
3643
+ stopPingInterval() {
3644
+ if (this.pingInterval) {
3645
+ clearInterval(this.pingInterval);
3646
+ this.pingInterval = null;
3647
+ }
3648
+ }
3649
+ stopReconnectTimer() {
3650
+ if (this.reconnectTimer) {
3651
+ clearTimeout(this.reconnectTimer);
3652
+ this.reconnectTimer = null;
3653
+ }
3654
+ }
3655
+ stopParkedTimer() {
3656
+ if (this.parkedTimer) {
3657
+ clearTimeout(this.parkedTimer);
3658
+ this.parkedTimer = null;
3659
+ }
3660
+ }
3661
+ }
3662
+
3663
+ // src/session/internal/_SubscriptionManager.ts
3664
+ init_message_types();
3665
+
3666
+ class _SubscriptionManager {
3667
+ deps;
3668
+ subscriptions = new Set;
3669
+ syncScheduled = false;
3670
+ constructor(deps) {
3671
+ this.deps = deps;
3672
+ }
3673
+ add(stream) {
3674
+ if (this.subscriptions.has(stream))
3675
+ return;
3676
+ this.subscriptions.add(stream);
3677
+ this.scheduleSync();
3678
+ }
3679
+ remove(stream) {
3680
+ if (!this.subscriptions.has(stream))
3681
+ return;
3682
+ this.subscriptions.delete(stream);
3683
+ this.scheduleSync();
3684
+ }
3685
+ sync() {
3686
+ this.syncScheduled = false;
3687
+ this.deps.sendMessage({
3688
+ type: "subscription_update" /* SUBSCRIPTION_UPDATE */,
3689
+ packageName: this.deps.getPackageName(),
3690
+ sessionId: this.deps.getSessionId(),
3691
+ subscriptions: this.snapshot(),
3692
+ timestamp: new Date
3693
+ });
3694
+ }
3695
+ snapshot() {
3696
+ return Array.from(this.subscriptions);
3697
+ }
3698
+ clear() {
3699
+ this.subscriptions.clear();
3700
+ this.syncScheduled = false;
3701
+ }
3702
+ scheduleSync() {
3703
+ if (!this.deps.isConnected() || this.syncScheduled)
3704
+ return;
3705
+ this.syncScheduled = true;
3706
+ queueMicrotask(() => {
3707
+ if (!this.syncScheduled)
3708
+ return;
3709
+ this.syncScheduled = false;
3710
+ if (this.deps.isConnected()) {
3711
+ this.sync();
3712
+ }
3713
+ });
3714
+ }
3715
+ }
3716
+
3717
+ // src/session/MentraSession.ts
3718
+ var DEFAULT_RECONNECT_ATTEMPTS = 3;
3719
+ var DEFAULT_RECONNECT_DELAY_MS = 1000;
3720
+ var DEFAULT_PARKED_TIMEOUT_MS = 30000;
3721
+ var SDK_VERSION = "3.0.0-hono.8";
3722
+
3723
+ class MentraSession {
3724
+ transport;
3725
+ logger;
3726
+ permissions;
3727
+ transcription;
3728
+ translation;
3729
+ display;
3730
+ speaker;
3731
+ mic;
3732
+ device;
3733
+ phone;
3734
+ camera;
3735
+ led;
3736
+ location;
3737
+ dashboard;
3738
+ storage;
3739
+ time;
3740
+ settingsData = [];
3741
+ mentraosSettings = {};
3742
+ appConfig = null;
3743
+ capabilities = null;
3744
+ runtimeSessionId;
3745
+ hasCompletedInitialConnect = false;
3746
+ config;
3747
+ lifecycle = new EventEmitter2;
3748
+ cleanupTasks = [];
3749
+ _router;
3750
+ _subscriptions;
3751
+ _lifecycleManager;
3752
+ constructor(config) {
3753
+ this.transport = config.transport;
3754
+ this.runtimeSessionId = config.sessionId;
3755
+ this.config = {
3756
+ packageName: config.packageName,
3757
+ apiKey: config.apiKey,
3758
+ sessionId: config.sessionId,
3759
+ userId: config.userId,
3760
+ serverUrl: config.serverUrl,
3761
+ autoReconnect: config.autoReconnect ?? true,
3762
+ maxReconnectAttempts: config.maxReconnectAttempts ?? DEFAULT_RECONNECT_ATTEMPTS,
3763
+ reconnectDelay: config.reconnectDelay ?? DEFAULT_RECONNECT_DELAY_MS
3764
+ };
3765
+ this.logger = config.logger ?? createLogger({
3766
+ logLevel: config.logLevel,
3767
+ verbose: config.verbose
3768
+ }).child({
3769
+ packageName: this.config.packageName,
3770
+ sessionId: this.config.sessionId,
3771
+ service: "mentra-session"
3772
+ });
3773
+ const sdkLogger = this.logger.child({ _sdk: true });
3774
+ this._router = new _MessageRouter(sdkLogger);
3775
+ this._subscriptions = new _SubscriptionManager({
3776
+ logger: sdkLogger,
3777
+ isConnected: () => this.isConnected,
3778
+ sendMessage: this.sendMessage.bind(this),
3779
+ getPackageName: () => this.config.packageName,
3780
+ getSessionId: () => this.runtimeSessionId
3781
+ });
3782
+ this._lifecycleManager = new _ConnectionManager({
3783
+ transport: this.transport,
3784
+ logger: sdkLogger,
3785
+ autoReconnect: this.config.autoReconnect,
3786
+ maxReconnectAttempts: this.config.maxReconnectAttempts,
3787
+ reconnectDelay: this.config.reconnectDelay,
3788
+ onTransportReady: () => this.sendHandshake(),
3789
+ onTextMessage: (raw) => this.handleTextMessage(raw),
3790
+ onBinaryMessage: (data) => this.mic.handleBinaryAudio(data),
3791
+ onClose: (info) => this.emit("disconnected", info),
3792
+ onError: (error) => {
3793
+ this.emit("error", error);
3794
+ this.logger.error(error, "MentraSession transport error");
3795
+ }
3796
+ });
3797
+ this.permissions = new PermissionsManager({ logger: sdkLogger });
3798
+ const deps = {
3799
+ router: this._router.dataStreamRouter,
3800
+ messageHandlers: this._router.messageHandlers,
3801
+ addSubscription: (stream) => this._subscriptions.add(stream),
3802
+ removeSubscription: (stream) => this._subscriptions.remove(stream),
3803
+ sendMessage: this.sendMessage.bind(this),
3804
+ sendBinary: this.sendBinary.bind(this),
3805
+ logger: sdkLogger,
3806
+ getPackageName: () => this.config.packageName,
3807
+ getSessionId: () => this.runtimeSessionId,
3808
+ getServerUrl: () => this.getServerUrl(),
3809
+ permissions: this.permissions
3810
+ };
3811
+ this.transcription = new TranscriptionManager(deps);
3812
+ this.translation = new TranslationManager(deps);
3813
+ this.display = new DisplayManager(deps);
3814
+ this.speaker = new SpeakerManager(deps);
3815
+ this.mic = new MicManager(deps);
3816
+ this.device = new DeviceManager(deps);
3817
+ this.phone = new PhoneManager(deps);
3818
+ this.camera = new CameraManager(deps);
3819
+ this.led = new LedManager(deps);
3820
+ this.location = new LocationManager(deps);
3821
+ this.dashboard = new DashboardManager(deps);
3822
+ this.storage = new StorageManager(deps, {
3823
+ userId: this.config.userId ?? "unknown-user",
3824
+ apiKey: this.config.apiKey
3825
+ });
3826
+ this.time = new TimeUtils("UTC");
3827
+ this.registerCoreHandlers();
3828
+ }
3829
+ get packageName() {
3830
+ return this.config.packageName;
3831
+ }
3832
+ get sessionId() {
3833
+ return this.runtimeSessionId;
3834
+ }
3835
+ get userId() {
3836
+ return this.config.userId;
3837
+ }
3838
+ get isConnected() {
3839
+ return this._lifecycleManager.isConnected;
3840
+ }
3841
+ get isParked() {
3842
+ return this._lifecycleManager.isParked;
3843
+ }
3844
+ async connect() {
3845
+ await this._lifecycleManager.connect();
3846
+ }
3847
+ async disconnect() {
3848
+ this._lifecycleManager.disconnect();
3849
+ await this.destroyManagers();
3850
+ this._router.destroy();
3851
+ this._subscriptions.clear();
3852
+ }
3853
+ async releaseOwnership(reason) {
3854
+ this.sendMessage({
3855
+ type: "ownership_release" /* OWNERSHIP_RELEASE */,
3856
+ packageName: this.config.packageName,
3857
+ sessionId: this.runtimeSessionId,
3858
+ reason,
3859
+ timestamp: new Date
3860
+ });
3861
+ await new Promise((resolve) => setTimeout(resolve, 100));
3862
+ }
3863
+ updateSettingsForTesting(newSettings) {
3864
+ this.settingsData = newSettings;
3865
+ this.permissions.updateFromSettings(newSettings);
3866
+ this.emit("settings", this.settingsData);
3867
+ }
3868
+ onConnected(handler) {
3869
+ this.lifecycle.on("connected", handler);
3870
+ return () => this.lifecycle.off("connected", handler);
3871
+ }
3872
+ onDisconnected(handler) {
3873
+ this.lifecycle.on("disconnected", handler);
3874
+ return () => this.lifecycle.off("disconnected", handler);
3875
+ }
3876
+ onError(handler) {
3877
+ this.lifecycle.on("error", handler);
3878
+ return () => this.lifecycle.off("error", handler);
3879
+ }
3880
+ onStopped(handler) {
3881
+ this.lifecycle.on("stopped", handler);
3882
+ return () => this.lifecycle.off("stopped", handler);
3883
+ }
3884
+ onSettings(handler) {
3885
+ this.lifecycle.on("settings", handler);
3886
+ return () => this.lifecycle.off("settings", handler);
3887
+ }
3888
+ onReconnected(handler) {
3889
+ this.lifecycle.on("reconnected", handler);
3890
+ return () => this.lifecycle.off("reconnected", handler);
3891
+ }
3892
+ sendMessage(message) {
3893
+ this.transport.send(JSON.stringify(message));
3894
+ }
3895
+ sendBinary(data) {
3896
+ this.transport.sendBinary(data);
3897
+ }
3898
+ getServerUrl() {
3899
+ return this.config.serverUrl ?? null;
3900
+ }
3901
+ registerCoreHandlers() {
3902
+ this.cleanupTasks.push(this._router.messageHandlers.register("data_stream" /* DATA_STREAM */, (message) => {
3903
+ this._router.dataStreamRouter.handle(message);
3904
+ }));
3905
+ this.cleanupTasks.push(this._router.messageHandlers.register("tpa_connection_ack" /* CONNECTION_ACK */, (message) => {
3906
+ this.handleConnectionAck(message, false);
3907
+ }));
3908
+ this.cleanupTasks.push(this._router.messageHandlers.register("reconnect_ack" /* RECONNECT_ACK */, (message) => {
3909
+ this.handleConnectionAck(message, true);
3910
+ }));
3911
+ this.cleanupTasks.push(this._router.messageHandlers.register("settings_update" /* SETTINGS_UPDATE */, (message) => {
3912
+ this.handleSettingsUpdate(message);
3913
+ }));
3914
+ this.cleanupTasks.push(this._router.messageHandlers.register("capabilities_update" /* CAPABILITIES_UPDATE */, (message) => {
3915
+ this.capabilities = message.capabilities ?? null;
3916
+ this.device.handleCapabilitiesUpdate(message);
3917
+ }));
3918
+ this.cleanupTasks.push(this._router.messageHandlers.register("device_state_update" /* DEVICE_STATE_UPDATE */, (message) => {
3919
+ this.device.handleDeviceStateUpdate(message);
3920
+ }));
3921
+ this.cleanupTasks.push(this._router.messageHandlers.register("app_stopped" /* APP_STOPPED */, (message) => {
3922
+ const reason = message.reason ?? "unknown";
3923
+ this.logger.info({ reason }, "MentraSession received app_stopped");
3924
+ this._lifecycleManager.disconnect();
3925
+ this.emit("stopped", reason);
3926
+ }));
3927
+ this.cleanupTasks.push(this._router.messageHandlers.register("tpa_connection_error" /* CONNECTION_ERROR */, (message) => {
3928
+ this.emit("error", new Error(message.message ?? "MentraSession connection error"));
3929
+ }));
3930
+ this.cleanupTasks.push(this._router.messageHandlers.register("reconnect_rejected" /* RECONNECT_REJECTED */, (message) => {
3931
+ if (message.code === "NOT_RUNNING" || message.code === "BOOT_TIMEOUT") {
3932
+ this.transport.close(1000, message.message ?? "Reconnect rejected");
3933
+ this.emit("disconnected", {
3934
+ code: 4002,
3935
+ reason: message.message ?? "Reconnect rejected",
3936
+ permanent: true
3937
+ });
3938
+ return;
3939
+ }
3940
+ this.sendConnectionInit();
3941
+ }));
3942
+ this.cleanupTasks.push(this._router.messageHandlers.register("reconnect_deferred" /* RECONNECT_DEFERRED */, (message) => {
3943
+ const timeoutMs = typeof message.timeoutMs === "number" ? message.timeoutMs : DEFAULT_PARKED_TIMEOUT_MS;
3944
+ this._lifecycleManager.park(timeoutMs, () => {
3945
+ this.transport.close(1000, "Parked timeout");
3946
+ this.emit("disconnected", {
3947
+ code: 4001,
3948
+ reason: "Parked reconnect timeout exceeded",
3949
+ permanent: true
3950
+ });
3951
+ });
3952
+ }));
3953
+ this.cleanupTasks.push(this._router.messageHandlers.register("augmentos_settings_update", (message) => {
3954
+ this.applyMentraosSettings(message.settings ?? {});
3955
+ }));
3956
+ }
3957
+ handleTextMessage(raw) {
3958
+ try {
3959
+ this._router.handleRawText(raw);
3960
+ } catch (error) {
3961
+ this.emit("error", error instanceof Error ? error : new Error(String(error)));
3962
+ }
3963
+ }
3964
+ handleConnectionAck(message, isReconnect) {
3965
+ this.settingsData = message.settings ?? [];
3966
+ this.appConfig = message.config ?? null;
3967
+ this.capabilities = message.capabilities ?? null;
3968
+ this.runtimeSessionId = message.sessionId ?? this.runtimeSessionId;
3969
+ this.permissions.updateFromSettings(message.mentraosSettings ?? message.settings ?? {});
3970
+ this.applyMentraosSettings(message.mentraosSettings ?? {});
3971
+ if (message.capabilities) {
3972
+ this.device.handleCapabilitiesUpdate({
3973
+ type: "capabilities_update" /* CAPABILITIES_UPDATE */,
3974
+ capabilities: message.capabilities,
3975
+ modelName: message.capabilities.modelName ?? null
3976
+ });
3977
+ }
3978
+ this._lifecycleManager.markConnected();
3979
+ this._subscriptions.sync();
3980
+ const wasReconnect = isReconnect || this.hasCompletedInitialConnect;
3981
+ this.hasCompletedInitialConnect = true;
3982
+ const transcriptionConfig = this.transcription.config;
3983
+ if (transcriptionConfig) {
3984
+ this.transcription.configure(transcriptionConfig);
3985
+ }
3986
+ this.emit("connected", this.settingsData);
3987
+ this.emit("settings", this.settingsData);
3988
+ if (wasReconnect) {
3989
+ this.emit("reconnected");
3990
+ }
3991
+ }
3992
+ handleSettingsUpdate(message) {
3993
+ this.settingsData = message.settings ?? [];
3994
+ this.permissions.updateFromSettings(message.settings ?? {});
3995
+ this.emit("settings", this.settingsData);
3996
+ }
3997
+ applyMentraosSettings(settings) {
3998
+ this.mentraosSettings = settings;
3999
+ const timezone = settings?.timezone;
4000
+ if (typeof timezone === "string" && timezone.length > 0) {
4001
+ try {
4002
+ this.time.setTimezone(timezone);
4003
+ } catch (error) {
4004
+ this.logger.warn({ timezone, error }, "MentraSession received invalid timezone");
4005
+ }
4006
+ }
4007
+ }
4008
+ sendHandshake() {
4009
+ if (this.hasCompletedInitialConnect) {
4010
+ this.sendMessage({
4011
+ type: "reconnect" /* RECONNECT */,
4012
+ sessionId: this.runtimeSessionId,
4013
+ sdkVersion: SDK_VERSION,
4014
+ timestamp: new Date
4015
+ });
4016
+ return;
4017
+ }
4018
+ this.sendConnectionInit();
4019
+ }
4020
+ sendConnectionInit() {
4021
+ this.sendMessage({
4022
+ type: "tpa_connection_init" /* CONNECTION_INIT */,
4023
+ packageName: this.config.packageName,
4024
+ apiKey: this.config.apiKey,
4025
+ sdkVersion: SDK_VERSION,
4026
+ timestamp: new Date
4027
+ });
4028
+ }
4029
+ emit(event, ...args) {
4030
+ this.lifecycle.emit(event, ...args);
4031
+ }
4032
+ async destroyManagers() {
4033
+ this._lifecycleManager.destroy();
4034
+ await this.storage.destroy();
4035
+ this.camera.destroy();
4036
+ this.dashboard.destroy();
4037
+ this.device.destroy();
4038
+ this.led.destroy();
4039
+ this.location.destroy();
4040
+ this.mic.stop();
4041
+ this.phone.destroy();
4042
+ this.speaker.destroy();
4043
+ this.transcription.stop();
4044
+ this.translation.stop();
4045
+ for (const cleanup of this.cleanupTasks.splice(0)) {
4046
+ await cleanup();
4047
+ }
4048
+ }
4049
+ }
4050
+ // src/utils/error-utils.ts
4051
+ function toErrorMessage(error) {
4052
+ if (error instanceof Error) {
4053
+ return error.message;
4054
+ }
4055
+ if (typeof error === "string") {
4056
+ return error;
4057
+ }
4058
+ if (error === null) {
4059
+ return "null";
4060
+ }
4061
+ if (error === undefined) {
4062
+ return "undefined";
4063
+ }
4064
+ if (typeof error === "object") {
4065
+ const obj = error;
4066
+ if (typeof obj.message === "string") {
4067
+ return obj.message;
4068
+ }
4069
+ try {
4070
+ return JSON.stringify(error);
4071
+ } catch {
4072
+ return String(error);
4073
+ }
4074
+ }
4075
+ return String(error);
4076
+ }
4077
+ function toError(error, fallbackMessage) {
4078
+ if (error instanceof Error) {
4079
+ return error;
4080
+ }
4081
+ const message = toErrorMessage(error);
4082
+ return new Error(message || fallbackMessage || "Unknown error");
4083
+ }
4084
+ var _deprecationWarnings = new Set;
4085
+ function warnOnce(key, message, logger2) {
4086
+ if (_deprecationWarnings.has(key))
4087
+ return;
4088
+ _deprecationWarnings.add(key);
4089
+ const formatted = `⚠️ DEPRECATION: ${message}`;
4090
+ if (logger2) {
4091
+ logger2.warn(formatted);
4092
+ } else {
4093
+ console.warn(formatted);
4094
+ }
4095
+ }
4096
+ export {
4097
+ warnOnce,
4098
+ toErrorMessage,
4099
+ toError,
4100
+ isTransportOpen,
4101
+ isTransportClosed,
4102
+ TransportState,
4103
+ TimeUtils,
4104
+ Observable,
4105
+ MessageHandlerRegistry,
4106
+ MentraSession,
4107
+ DataStreamRouter
4108
+ };
4109
+
4110
+ //# debugId=8AE847F1727E65C164756E2164756E21