@mentra/sdk 2.1.11 โ 2.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app/server/index.d.ts +15 -12
- package/dist/app/server/index.d.ts.map +1 -1
- package/dist/app/server/index.js +104 -79
- package/dist/app/session/layouts.js +1 -1
- package/dist/app/webview/index.d.ts +5 -3
- package/dist/app/webview/index.d.ts.map +1 -1
- package/dist/app/webview/index.js +73 -37
- package/dist/types/index.d.ts +29 -27
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/messages/cloud-to-app.d.ts +14 -12
- package/dist/types/messages/cloud-to-app.d.ts.map +1 -1
- package/dist/types/messages/cloud-to-app.js +2 -1
- package/package.json +1 -1
@@ -4,10 +4,11 @@
|
|
4
4
|
* Creates and manages a server for Apps in the MentraOS ecosystem.
|
5
5
|
* Handles webhook endpoints, session management, and cleanup.
|
6
6
|
*/
|
7
|
-
import { type Express } from
|
8
|
-
import { AppSession } from
|
9
|
-
import { ToolCall } from
|
10
|
-
import { Logger } from
|
7
|
+
import { type Express } from "express";
|
8
|
+
import { AppSession } from "../session/index";
|
9
|
+
import { ToolCall } from "../../types";
|
10
|
+
import { Logger } from "pino";
|
11
|
+
export declare const GIVE_APP_CONTROL_OF_TOOL_RESPONSE: string;
|
11
12
|
/**
|
12
13
|
* ๐ง Configuration options for App Server
|
13
14
|
*
|
@@ -81,6 +82,8 @@ export declare class AppServer {
|
|
81
82
|
private app;
|
82
83
|
/** Map of active user sessions by sessionId */
|
83
84
|
private activeSessions;
|
85
|
+
/** Map of active user sessions by userId */
|
86
|
+
private activeSessionsByUserId;
|
84
87
|
/** Array of cleanup handlers to run on shutdown */
|
85
88
|
private cleanupHandlers;
|
86
89
|
/** App instructions string shown to the user */
|
@@ -130,14 +133,14 @@ export declare class AppServer {
|
|
130
133
|
*/
|
131
134
|
stop(): void;
|
132
135
|
/**
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
136
|
+
* ๐ Generate a App token for a user
|
137
|
+
* This should be called when handling a session webhook request.
|
138
|
+
*
|
139
|
+
* @param userId - User identifier
|
140
|
+
* @param sessionId - Session identifier
|
141
|
+
* @param secretKey - Secret key for signing the token
|
142
|
+
* @returns JWT token string
|
143
|
+
*/
|
141
144
|
protected generateToken(userId: string, sessionId: string, secretKey: string): string;
|
142
145
|
/**
|
143
146
|
* ๐งน Add Cleanup Handler
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/app/server/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAgB,EAAE,KAAK,OAAO,EAAE,MAAM,SAAS,CAAC;AAEhD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,OAAO,EAQL,QAAQ,EACT,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAI9B;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,eAAe;IAC9B,oIAAoI;IACpI,WAAW,EAAE,MAAM,CAAC;IACpB,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,+FAA+F;IAC/F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAE3B,iEAAiE;IACjE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gDAAgD;IAChD,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAAa,SAAS;
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/app/server/index.ts"],"names":[],"mappings":"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"}
|
package/dist/app/server/index.js
CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
4
|
};
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
-
exports.TpaServer = exports.AppServer = void 0;
|
6
|
+
exports.TpaServer = exports.AppServer = exports.GIVE_APP_CONTROL_OF_TOOL_RESPONSE = void 0;
|
7
7
|
/**
|
8
8
|
* ๐ App Server Module
|
9
9
|
*
|
@@ -16,6 +16,7 @@ const index_1 = require("../session/index");
|
|
16
16
|
const webview_1 = require("../webview");
|
17
17
|
const types_1 = require("../../types");
|
18
18
|
const logger_1 = require("../../logging/logger");
|
19
|
+
exports.GIVE_APP_CONTROL_OF_TOOL_RESPONSE = "GIVE_APP_CONTROL_OF_TOOL_RESPONSE";
|
19
20
|
/**
|
20
21
|
* ๐ฏ App Server Implementation
|
21
22
|
*
|
@@ -51,6 +52,8 @@ class AppServer {
|
|
51
52
|
this.config = config;
|
52
53
|
/** Map of active user sessions by sessionId */
|
53
54
|
this.activeSessions = new Map();
|
55
|
+
/** Map of active user sessions by userId */
|
56
|
+
this.activeSessionsByUserId = new Map();
|
54
57
|
/** Array of cleanup handlers to run on shutdown */
|
55
58
|
this.cleanupHandlers = [];
|
56
59
|
/** App instructions string shown to the user */
|
@@ -58,22 +61,31 @@ class AppServer {
|
|
58
61
|
// Set defaults and merge with provided config
|
59
62
|
this.config = {
|
60
63
|
port: 7010,
|
61
|
-
webhookPath:
|
64
|
+
webhookPath: "/webhook",
|
62
65
|
publicDir: false,
|
63
66
|
healthCheck: true,
|
64
|
-
...config
|
67
|
+
...config,
|
65
68
|
};
|
66
|
-
this.logger = logger_1.logger.child({
|
69
|
+
this.logger = logger_1.logger.child({
|
70
|
+
app: this.config.packageName,
|
71
|
+
packageName: this.config.packageName,
|
72
|
+
service: "app-server",
|
73
|
+
});
|
67
74
|
// Initialize Express app
|
68
75
|
this.app = (0, express_1.default)();
|
69
76
|
this.app.use(express_1.default.json());
|
70
|
-
const cookieParser = require(
|
71
|
-
this.app.use(cookieParser(this.config.cookieSecret ||
|
77
|
+
const cookieParser = require("cookie-parser");
|
78
|
+
this.app.use(cookieParser(this.config.cookieSecret ||
|
79
|
+
`AOS_${this.config.packageName}_${this.config.apiKey.substring(0, 8)}`));
|
72
80
|
// Apply authentication middleware
|
73
81
|
this.app.use((0, webview_1.createAuthMiddleware)({
|
74
82
|
apiKey: this.config.apiKey,
|
75
83
|
packageName: this.config.packageName,
|
76
|
-
|
84
|
+
getAppSessionForUser: (userId) => {
|
85
|
+
return this.activeSessionsByUserId.get(userId) || null;
|
86
|
+
},
|
87
|
+
cookieSecret: this.config.cookieSecret ||
|
88
|
+
`AOS_${this.config.packageName}_${this.config.apiKey.substring(0, 8)}`,
|
77
89
|
}));
|
78
90
|
this.appInstructions = config.appInstructions || null;
|
79
91
|
// Setup server features
|
@@ -120,6 +132,7 @@ class AppServer {
|
|
120
132
|
if (session) {
|
121
133
|
session.disconnect();
|
122
134
|
this.activeSessions.delete(sessionId);
|
135
|
+
this.activeSessionsByUserId.delete(userId);
|
123
136
|
}
|
124
137
|
}
|
125
138
|
/**
|
@@ -157,25 +170,25 @@ class AppServer {
|
|
157
170
|
* Gracefully shuts down the server and cleans up all sessions.
|
158
171
|
*/
|
159
172
|
stop() {
|
160
|
-
this.logger.info(
|
173
|
+
this.logger.info("\n๐ Shutting down...");
|
161
174
|
this.cleanup();
|
162
175
|
process.exit(0);
|
163
176
|
}
|
164
177
|
/**
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
178
|
+
* ๐ Generate a App token for a user
|
179
|
+
* This should be called when handling a session webhook request.
|
180
|
+
*
|
181
|
+
* @param userId - User identifier
|
182
|
+
* @param sessionId - Session identifier
|
183
|
+
* @param secretKey - Secret key for signing the token
|
184
|
+
* @returns JWT token string
|
185
|
+
*/
|
173
186
|
generateToken(userId, sessionId, secretKey) {
|
174
|
-
const { createToken } = require(
|
187
|
+
const { createToken } = require("../token/utils");
|
175
188
|
return createToken({
|
176
189
|
userId,
|
177
190
|
packageName: this.config.packageName,
|
178
|
-
sessionId
|
191
|
+
sessionId,
|
179
192
|
}, { secretKey });
|
180
193
|
}
|
181
194
|
/**
|
@@ -193,8 +206,8 @@ class AppServer {
|
|
193
206
|
*/
|
194
207
|
setupWebhook() {
|
195
208
|
if (!this.config.webhookPath) {
|
196
|
-
this.logger.error(
|
197
|
-
throw new Error(
|
209
|
+
this.logger.error("โ Webhook path not set");
|
210
|
+
throw new Error("Webhook path not set");
|
198
211
|
}
|
199
212
|
this.app.post(this.config.webhookPath, async (req, res) => {
|
200
213
|
try {
|
@@ -209,18 +222,18 @@ class AppServer {
|
|
209
222
|
}
|
210
223
|
// Unknown webhook type
|
211
224
|
else {
|
212
|
-
this.logger.error(
|
225
|
+
this.logger.error("โ Unknown webhook request type");
|
213
226
|
res.status(400).json({
|
214
|
-
status:
|
215
|
-
message:
|
227
|
+
status: "error",
|
228
|
+
message: "Unknown webhook request type",
|
216
229
|
});
|
217
230
|
}
|
218
231
|
}
|
219
232
|
catch (error) {
|
220
|
-
this.logger.error(error,
|
233
|
+
this.logger.error(error, "โ Error handling webhook: " + error.message);
|
221
234
|
res.status(500).json({
|
222
|
-
status:
|
223
|
-
message:
|
235
|
+
status: "error",
|
236
|
+
message: "Error handling webhook: " + error.message,
|
224
237
|
});
|
225
238
|
}
|
226
239
|
});
|
@@ -230,30 +243,39 @@ class AppServer {
|
|
230
243
|
* Creates a /tool endpoint for handling tool calls from MentraOS Cloud.
|
231
244
|
*/
|
232
245
|
setupToolCallEndpoint() {
|
233
|
-
this.app.post(
|
246
|
+
this.app.post("/tool", async (req, res) => {
|
234
247
|
try {
|
235
248
|
const toolCall = req.body;
|
249
|
+
if (this.activeSessionsByUserId.has(toolCall.userId)) {
|
250
|
+
toolCall.activeSession =
|
251
|
+
this.activeSessionsByUserId.get(toolCall.userId) || null;
|
252
|
+
}
|
253
|
+
else {
|
254
|
+
toolCall.activeSession = null;
|
255
|
+
}
|
236
256
|
this.logger.info({ body: req.body }, `๐ง Received tool call: ${toolCall.toolId}`);
|
237
257
|
// Call the onToolCall handler and get the response
|
238
258
|
const response = await this.onToolCall(toolCall);
|
239
259
|
// Send back the response if one was provided
|
240
260
|
if (response !== undefined) {
|
241
|
-
res.json({ status:
|
261
|
+
res.json({ status: "success", reply: response });
|
242
262
|
}
|
243
263
|
else {
|
244
|
-
res.json({ status:
|
264
|
+
res.json({ status: "success", reply: null });
|
245
265
|
}
|
246
266
|
}
|
247
267
|
catch (error) {
|
248
|
-
this.logger.error(error,
|
268
|
+
this.logger.error(error, "โ Error handling tool call:");
|
249
269
|
res.status(500).json({
|
250
|
-
status:
|
251
|
-
message: error instanceof Error
|
270
|
+
status: "error",
|
271
|
+
message: error instanceof Error
|
272
|
+
? error.message
|
273
|
+
: "Unknown error occurred calling tool",
|
252
274
|
});
|
253
275
|
}
|
254
276
|
});
|
255
|
-
this.app.get(
|
256
|
-
res.json({ status:
|
277
|
+
this.app.get("/tool", async (req, res) => {
|
278
|
+
res.json({ status: "success", reply: "Hello, world!" });
|
257
279
|
});
|
258
280
|
}
|
259
281
|
/**
|
@@ -273,7 +295,7 @@ class AppServer {
|
|
273
295
|
// Setup session event handlers
|
274
296
|
const cleanupDisconnect = session.events.onDisconnected((info) => {
|
275
297
|
// Handle different disconnect info formats (string or object)
|
276
|
-
if (typeof info ===
|
298
|
+
if (typeof info === "string") {
|
277
299
|
this.logger.info(`๐ Session ${sessionId} disconnected: ${info}`);
|
278
300
|
}
|
279
301
|
else {
|
@@ -285,13 +307,14 @@ class AppServer {
|
|
285
307
|
// Keep track of the original session before removal
|
286
308
|
const session = this.activeSessions.get(sessionId);
|
287
309
|
// Call onStop with a reconnection failure reason
|
288
|
-
this.onStop(sessionId, userId, `Connection permanently lost: ${info.reason}`).catch(error => {
|
310
|
+
this.onStop(sessionId, userId, `Connection permanently lost: ${info.reason}`).catch((error) => {
|
289
311
|
this.logger.error(error, `โ Error in onStop handler for permanent disconnection:`);
|
290
312
|
});
|
291
313
|
}
|
292
314
|
}
|
293
315
|
// Remove the session from active sessions in all cases
|
294
316
|
this.activeSessions.delete(sessionId);
|
317
|
+
this.activeSessionsByUserId.delete(userId);
|
295
318
|
});
|
296
319
|
const cleanupError = session.events.onError((error) => {
|
297
320
|
this.logger.error(error, `โ [Session ${sessionId}] Error:`);
|
@@ -300,16 +323,17 @@ class AppServer {
|
|
300
323
|
try {
|
301
324
|
await session.connect(sessionId);
|
302
325
|
this.activeSessions.set(sessionId, session);
|
326
|
+
this.activeSessionsByUserId.set(userId, session);
|
303
327
|
await this.onSession(session, sessionId, userId);
|
304
|
-
res.status(200).json({ status:
|
328
|
+
res.status(200).json({ status: "success" });
|
305
329
|
}
|
306
330
|
catch (error) {
|
307
|
-
this.logger.error(error,
|
331
|
+
this.logger.error(error, "โ Failed to connect:");
|
308
332
|
cleanupDisconnect();
|
309
333
|
cleanupError();
|
310
334
|
res.status(500).json({
|
311
|
-
status:
|
312
|
-
message:
|
335
|
+
status: "error",
|
336
|
+
message: "Failed to connect",
|
313
337
|
});
|
314
338
|
}
|
315
339
|
}
|
@@ -321,13 +345,13 @@ class AppServer {
|
|
321
345
|
this.logger.info(`\n\n๐ Received stop request for user ${userId}, session ${sessionId}, reason: ${reason}\n\n`);
|
322
346
|
try {
|
323
347
|
await this.onStop(sessionId, userId, reason);
|
324
|
-
res.status(200).json({ status:
|
348
|
+
res.status(200).json({ status: "success" });
|
325
349
|
}
|
326
350
|
catch (error) {
|
327
|
-
this.logger.error(error,
|
351
|
+
this.logger.error(error, "โ Error handling stop request:");
|
328
352
|
res.status(500).json({
|
329
|
-
status:
|
330
|
-
message:
|
353
|
+
status: "error",
|
354
|
+
message: "Failed to process stop request",
|
331
355
|
});
|
332
356
|
}
|
333
357
|
}
|
@@ -337,11 +361,11 @@ class AppServer {
|
|
337
361
|
*/
|
338
362
|
setupHealthCheck() {
|
339
363
|
if (this.config.healthCheck) {
|
340
|
-
this.app.get(
|
364
|
+
this.app.get("/health", (req, res) => {
|
341
365
|
res.json({
|
342
|
-
status:
|
366
|
+
status: "healthy",
|
343
367
|
app: this.config.packageName,
|
344
|
-
activeSessions: this.activeSessions.size
|
368
|
+
activeSessions: this.activeSessions.size,
|
345
369
|
});
|
346
370
|
});
|
347
371
|
}
|
@@ -351,13 +375,13 @@ class AppServer {
|
|
351
375
|
* Creates a /settings endpoint that the MentraOS Cloud can use to update settings.
|
352
376
|
*/
|
353
377
|
setupSettingsEndpoint() {
|
354
|
-
this.app.post(
|
378
|
+
this.app.post("/settings", async (req, res) => {
|
355
379
|
try {
|
356
380
|
const { userIdForSettings, settings } = req.body;
|
357
381
|
if (!userIdForSettings || !Array.isArray(settings)) {
|
358
382
|
return res.status(400).json({
|
359
|
-
status:
|
360
|
-
message:
|
383
|
+
status: "error",
|
384
|
+
message: "Missing userId or settings array in request body",
|
361
385
|
});
|
362
386
|
}
|
363
387
|
this.logger.info(`โ๏ธ Received settings update for user ${userIdForSettings}`);
|
@@ -367,7 +391,7 @@ class AppServer {
|
|
367
391
|
this.activeSessions.forEach((session, sessionId) => {
|
368
392
|
// Check if the session has this userId (not directly accessible)
|
369
393
|
// We're relying on the webhook handler to have already verified this
|
370
|
-
if (
|
394
|
+
if (session.userId === userIdForSettings) {
|
371
395
|
userSessions.push(session);
|
372
396
|
}
|
373
397
|
});
|
@@ -382,20 +406,20 @@ class AppServer {
|
|
382
406
|
session.updateSettingsForTesting(settings);
|
383
407
|
}
|
384
408
|
// Allow subclasses to handle settings updates if they implement the method
|
385
|
-
if (typeof this.onSettingsUpdate ===
|
409
|
+
if (typeof this.onSettingsUpdate === "function") {
|
386
410
|
await this.onSettingsUpdate(userIdForSettings, settings);
|
387
411
|
}
|
388
412
|
res.json({
|
389
|
-
status:
|
390
|
-
message:
|
391
|
-
sessionsUpdated: userSessions.length
|
413
|
+
status: "success",
|
414
|
+
message: "Settings updated successfully",
|
415
|
+
sessionsUpdated: userSessions.length,
|
392
416
|
});
|
393
417
|
}
|
394
418
|
catch (error) {
|
395
|
-
this.logger.error(error,
|
419
|
+
this.logger.error(error, "โ Error handling settings update:");
|
396
420
|
res.status(500).json({
|
397
|
-
status:
|
398
|
-
message:
|
421
|
+
status: "error",
|
422
|
+
message: "Internal server error processing settings update",
|
399
423
|
});
|
400
424
|
}
|
401
425
|
});
|
@@ -416,8 +440,8 @@ class AppServer {
|
|
416
440
|
* Registers process signal handlers for graceful shutdown.
|
417
441
|
*/
|
418
442
|
setupShutdown() {
|
419
|
-
process.on(
|
420
|
-
process.on(
|
443
|
+
process.on("SIGTERM", () => this.stop());
|
444
|
+
process.on("SIGINT", () => this.stop());
|
421
445
|
}
|
422
446
|
/**
|
423
447
|
* ๐งน Cleanup
|
@@ -430,15 +454,16 @@ class AppServer {
|
|
430
454
|
session.disconnect();
|
431
455
|
}
|
432
456
|
this.activeSessions.clear();
|
457
|
+
this.activeSessionsByUserId.clear();
|
433
458
|
// Run cleanup handlers
|
434
|
-
this.cleanupHandlers.forEach(handler => handler());
|
459
|
+
this.cleanupHandlers.forEach((handler) => handler());
|
435
460
|
}
|
436
461
|
/**
|
437
462
|
* ๐ฏ Setup Photo Upload Endpoint
|
438
463
|
* Creates a /photo-upload endpoint for receiving photos directly from ASG glasses
|
439
464
|
*/
|
440
465
|
setupPhotoUploadEndpoint() {
|
441
|
-
const multer = require(
|
466
|
+
const multer = require("multer");
|
442
467
|
// Configure multer for handling multipart form data
|
443
468
|
const upload = multer({
|
444
469
|
storage: multer.memoryStorage(),
|
@@ -447,50 +472,50 @@ class AppServer {
|
|
447
472
|
},
|
448
473
|
fileFilter: (req, file, cb) => {
|
449
474
|
// Accept image files only
|
450
|
-
if (file.mimetype && file.mimetype.startsWith(
|
475
|
+
if (file.mimetype && file.mimetype.startsWith("image/")) {
|
451
476
|
cb(null, true);
|
452
477
|
}
|
453
478
|
else {
|
454
|
-
cb(new Error(
|
479
|
+
cb(new Error("Only image files are allowed"), false);
|
455
480
|
}
|
456
|
-
}
|
481
|
+
},
|
457
482
|
});
|
458
|
-
this.app.post(
|
483
|
+
this.app.post("/photo-upload", upload.single("photo"), async (req, res) => {
|
459
484
|
try {
|
460
485
|
const { requestId, type } = req.body;
|
461
486
|
const photoFile = req.file;
|
462
487
|
this.logger.info({ requestId, type }, `๐ธ Received photo upload: ${requestId}`);
|
463
488
|
if (!photoFile) {
|
464
|
-
this.logger.error({ requestId },
|
489
|
+
this.logger.error({ requestId }, "No photo file in upload");
|
465
490
|
return res.status(400).json({
|
466
491
|
success: false,
|
467
|
-
error:
|
492
|
+
error: "No photo file provided",
|
468
493
|
});
|
469
494
|
}
|
470
495
|
if (!requestId) {
|
471
|
-
this.logger.error(
|
496
|
+
this.logger.error("No requestId in photo upload");
|
472
497
|
return res.status(400).json({
|
473
498
|
success: false,
|
474
|
-
error:
|
499
|
+
error: "No requestId provided",
|
475
500
|
});
|
476
501
|
}
|
477
502
|
// Find the corresponding session that made this photo request
|
478
503
|
const session = this.findSessionByPhotoRequestId(requestId);
|
479
504
|
if (!session) {
|
480
|
-
this.logger.warn({ requestId },
|
505
|
+
this.logger.warn({ requestId }, "No active session found for photo request");
|
481
506
|
return res.status(404).json({
|
482
507
|
success: false,
|
483
|
-
error:
|
508
|
+
error: "No active session found for this photo request",
|
484
509
|
});
|
485
510
|
}
|
486
511
|
// Create photo data object
|
487
512
|
const photoData = {
|
488
513
|
buffer: photoFile.buffer,
|
489
514
|
mimeType: photoFile.mimetype,
|
490
|
-
filename: photoFile.originalname ||
|
515
|
+
filename: photoFile.originalname || "photo.jpg",
|
491
516
|
requestId,
|
492
517
|
size: photoFile.size,
|
493
|
-
timestamp: new Date()
|
518
|
+
timestamp: new Date(),
|
494
519
|
};
|
495
520
|
// Deliver photo to the session
|
496
521
|
session.camera.handlePhotoReceived(photoData);
|
@@ -498,14 +523,14 @@ class AppServer {
|
|
498
523
|
res.json({
|
499
524
|
success: true,
|
500
525
|
requestId,
|
501
|
-
message:
|
526
|
+
message: "Photo received successfully",
|
502
527
|
});
|
503
528
|
}
|
504
529
|
catch (error) {
|
505
|
-
this.logger.error(error,
|
530
|
+
this.logger.error(error, "โ Error handling photo upload");
|
506
531
|
res.status(500).json({
|
507
532
|
success: false,
|
508
|
-
error:
|
533
|
+
error: "Internal server error processing photo upload",
|
509
534
|
});
|
510
535
|
}
|
511
536
|
});
|
@@ -540,8 +565,8 @@ class TpaServer extends AppServer {
|
|
540
565
|
constructor(config) {
|
541
566
|
super(config);
|
542
567
|
// Emit a deprecation warning to help developers migrate
|
543
|
-
console.warn(
|
544
|
-
|
568
|
+
console.warn("โ ๏ธ DEPRECATION WARNING: TpaServer is deprecated and will be removed in a future version. " +
|
569
|
+
"Please use AppServer instead. " +
|
545
570
|
'Simply replace "TpaServer" with "AppServer" in your code.');
|
546
571
|
}
|
547
572
|
}
|
@@ -18,7 +18,7 @@ exports.LayoutManager = void 0;
|
|
18
18
|
* layouts.showReferenceCard('Weather', 'Sunny and 75ยฐF');
|
19
19
|
* ```
|
20
20
|
*/
|
21
|
-
const bitmap_utils_1 = require("
|
21
|
+
const bitmap_utils_1 = require("../../utils/bitmap-utils");
|
22
22
|
const types_1 = require("../../types");
|
23
23
|
class LayoutManager {
|
24
24
|
/**
|
@@ -1,5 +1,6 @@
|
|
1
|
-
import { Response, NextFunction } from
|
2
|
-
import { AuthenticatedRequest } from
|
1
|
+
import { Response, NextFunction } from "express";
|
2
|
+
import { AuthenticatedRequest } from "../../types";
|
3
|
+
import { AppSession } from "../session";
|
3
4
|
/**
|
4
5
|
* Extracts the temporary token from a URL string.
|
5
6
|
* @param url The URL string, typically window.location.href.
|
@@ -36,11 +37,12 @@ export declare function createAuthMiddleware(options: {
|
|
36
37
|
packageName: string;
|
37
38
|
cookieName?: string;
|
38
39
|
cookieSecret: string;
|
40
|
+
getAppSessionForUser?: (userId: string) => AppSession | null;
|
39
41
|
cookieOptions?: {
|
40
42
|
httpOnly?: boolean;
|
41
43
|
secure?: boolean;
|
42
44
|
maxAge?: number;
|
43
|
-
sameSite?: boolean |
|
45
|
+
sameSite?: boolean | "lax" | "strict" | "none";
|
44
46
|
path?: string;
|
45
47
|
};
|
46
48
|
}): (req: AuthenticatedRequest, res: Response, next: NextFunction) => Promise<void>;
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/app/webview/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/app/webview/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAKnD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAMxC;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQ3D;AAED;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CACjC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAyC7B;AA6JD;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,UAAU,GAAG,IAAI,CAAC;IAC7D,aAAa,CAAC,EAAE;QACd,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;QAC/C,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;CACH,IA+BG,KAAK,oBAAoB,EACzB,KAAK,QAAQ,EACb,MAAM,YAAY,mBA0JrB"}
|
@@ -45,7 +45,8 @@ const axios_1 = __importDefault(require("axios"));
|
|
45
45
|
// Example: app.use(require('cookie-parser')());
|
46
46
|
const crypto = __importStar(require("crypto"));
|
47
47
|
const jsrsasign_1 = require("jsrsasign");
|
48
|
-
const userTokenPublicKey = process.env.MENTRAOS_CLOUD_USER_TOKEN_PUBLIC_KEY ||
|
48
|
+
const userTokenPublicKey = process.env.MENTRAOS_CLOUD_USER_TOKEN_PUBLIC_KEY ||
|
49
|
+
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Yt2RtNOdeKQxWMY0c84\nADpY1Jy58YWZhaEgP2A5tBwFUKgy/TH9gQLWZjQ3dQ/6XXO8qq0kluoYFqM7ZDRF\nzJ0E4Yi0WQncioLRcCx4q8pDmqY9vPKgv6PruJdFWca0l0s3gZ3BqSeWum/C23xK\nFPHPwi8gvRdc6ALrkcHeciM+7NykU8c0EY8PSitNL+Tchti95kGu+j6APr5vNewi\nzRpQGOdqaLWe+ahHmtj6KtUZjm8o6lan4f/o08C6litizguZXuw2Nn/Kd9fFI1xF\nIVNJYMy9jgGaOi71+LpGw+vIpwAawp/7IvULDppvY3DdX5nt05P1+jvVJXPxMKzD\nTQIDAQAB\n-----END PUBLIC KEY-----";
|
49
50
|
/**
|
50
51
|
* Extracts the temporary token from a URL string.
|
51
52
|
* @param url The URL string, typically window.location.href.
|
@@ -54,7 +55,7 @@ const userTokenPublicKey = process.env.MENTRAOS_CLOUD_USER_TOKEN_PUBLIC_KEY || "
|
|
54
55
|
function extractTempToken(url) {
|
55
56
|
try {
|
56
57
|
const parsedUrl = new URL(url);
|
57
|
-
return parsedUrl.searchParams.get(
|
58
|
+
return parsedUrl.searchParams.get("aos_temp_token");
|
58
59
|
}
|
59
60
|
catch (e) {
|
60
61
|
console.error("Error parsing URL for temp token:", e);
|
@@ -76,12 +77,14 @@ async function exchangeToken(cloudApiUrl, tempToken, apiKey, packageName) {
|
|
76
77
|
try {
|
77
78
|
const response = await axios_1.default.post(endpoint, { aos_temp_token: tempToken, packageName: packageName }, {
|
78
79
|
headers: {
|
79
|
-
|
80
|
-
|
80
|
+
"Content-Type": "application/json",
|
81
|
+
Authorization: `Bearer ${apiKey}`,
|
81
82
|
},
|
82
83
|
timeout: 10000, // 10 second timeout
|
83
84
|
});
|
84
|
-
if (response.status === 200 &&
|
85
|
+
if (response.status === 200 &&
|
86
|
+
response.data.success &&
|
87
|
+
response.data.userId) {
|
85
88
|
return { userId: response.data.userId };
|
86
89
|
}
|
87
90
|
else {
|
@@ -94,13 +97,13 @@ async function exchangeToken(cloudApiUrl, tempToken, apiKey, packageName) {
|
|
94
97
|
if (axios_1.default.isAxiosError(error)) {
|
95
98
|
const status = error.response?.status;
|
96
99
|
const data = error.response?.data;
|
97
|
-
const message = data?.error || error.message ||
|
100
|
+
const message = data?.error || error.message || "Unknown error during token exchange";
|
98
101
|
console.error(`Token exchange failed with status ${status}: ${message}`);
|
99
102
|
throw new Error(`Token exchange failed: ${message}`);
|
100
103
|
}
|
101
104
|
else {
|
102
|
-
console.error(
|
103
|
-
throw new Error(
|
105
|
+
console.error("Unexpected error during token exchange:", error);
|
106
|
+
throw new Error("An unexpected error occurred during token exchange.");
|
104
107
|
}
|
105
108
|
}
|
106
109
|
}
|
@@ -115,9 +118,9 @@ function signSession(userId, secret) {
|
|
115
118
|
const timestamp = Date.now();
|
116
119
|
const data = `${userId}|${timestamp}`;
|
117
120
|
const signature = crypto
|
118
|
-
.createHmac(
|
121
|
+
.createHmac("sha256", secret)
|
119
122
|
.update(data)
|
120
|
-
.digest(
|
123
|
+
.digest("hex");
|
121
124
|
return `${data}|${signature}`;
|
122
125
|
}
|
123
126
|
/**
|
@@ -129,7 +132,7 @@ function signSession(userId, secret) {
|
|
129
132
|
*/
|
130
133
|
function verifySession(token, secret, maxAge) {
|
131
134
|
try {
|
132
|
-
const parts = token.split(
|
135
|
+
const parts = token.split("|");
|
133
136
|
if (parts.length !== 3)
|
134
137
|
return null;
|
135
138
|
const [userId, timestampStr, signature] = parts;
|
@@ -142,9 +145,9 @@ function verifySession(token, secret, maxAge) {
|
|
142
145
|
// Verify signature
|
143
146
|
const data = `${userId}|${timestamp}`;
|
144
147
|
const expectedSignature = crypto
|
145
|
-
.createHmac(
|
148
|
+
.createHmac("sha256", secret)
|
146
149
|
.update(data)
|
147
|
-
.digest(
|
150
|
+
.digest("hex");
|
148
151
|
if (signature !== expectedSignature) {
|
149
152
|
console.log(`Session token signature mismatch: ${signature} !== ${expectedSignature}`);
|
150
153
|
return null;
|
@@ -193,18 +196,22 @@ async function verifySignedUserToken(signedUserToken) {
|
|
193
196
|
function verifyFrontendToken(frontendToken, apiKey) {
|
194
197
|
try {
|
195
198
|
// Check if the token contains a user ID and hash separated by a colon
|
196
|
-
const tokenParts = frontendToken.split(
|
199
|
+
const tokenParts = frontendToken.split(":");
|
197
200
|
if (tokenParts.length === 2) {
|
198
201
|
// Format: userId:hash
|
199
202
|
const [tokenUserId, tokenHash] = tokenParts;
|
200
203
|
// Align the hashing algorithm with server-side `hashWithApiKey`
|
201
204
|
// 1. Hash the API key first (server only stores the hashed version)
|
202
|
-
const hashedApiKey = crypto
|
205
|
+
const hashedApiKey = crypto
|
206
|
+
.createHash("sha256")
|
207
|
+
.update(apiKey)
|
208
|
+
.digest("hex");
|
203
209
|
// 2. Create the expected hash using userId + hashedApiKey (same order & update calls)
|
204
|
-
const expectedHash = crypto
|
210
|
+
const expectedHash = crypto
|
211
|
+
.createHash("sha256")
|
205
212
|
.update(tokenUserId)
|
206
213
|
.update(hashedApiKey)
|
207
|
-
.digest(
|
214
|
+
.digest("hex");
|
208
215
|
if (tokenHash === expectedHash) {
|
209
216
|
return tokenUserId;
|
210
217
|
}
|
@@ -220,10 +227,12 @@ function verifyFrontendToken(frontendToken, apiKey) {
|
|
220
227
|
}
|
221
228
|
}
|
222
229
|
function validateCloudApiUrlChecksum(checksum, cloudApiUrl, apiKey) {
|
223
|
-
const hashedApiKey = crypto.createHash(
|
224
|
-
const expectedChecksum = crypto
|
230
|
+
const hashedApiKey = crypto.createHash("sha256").update(apiKey).digest("hex");
|
231
|
+
const expectedChecksum = crypto
|
232
|
+
.createHash("sha256")
|
233
|
+
.update(cloudApiUrl)
|
225
234
|
.update(hashedApiKey)
|
226
|
-
.digest(
|
235
|
+
.digest("hex");
|
227
236
|
return expectedChecksum === checksum;
|
228
237
|
}
|
229
238
|
/**
|
@@ -240,47 +249,56 @@ function validateCloudApiUrlChecksum(checksum, cloudApiUrl, apiKey) {
|
|
240
249
|
* @param options.cookieOptions Options for the session cookie (default: { httpOnly: true, secure: process.env.NODE_ENV === 'production' }).
|
241
250
|
*/
|
242
251
|
function createAuthMiddleware(options) {
|
243
|
-
const { apiKey, packageName, cookieName =
|
252
|
+
const { apiKey, packageName, cookieName = "aos_session", cookieSecret, getAppSessionForUser, cookieOptions = {
|
244
253
|
httpOnly: true,
|
245
|
-
secure: process.env.NODE_ENV ===
|
254
|
+
secure: process.env.NODE_ENV === "production",
|
246
255
|
maxAge: 30 * 24 * 60 * 60 * 1000, // 30 days by default
|
247
|
-
sameSite: process.env.NODE_ENV ===
|
248
|
-
path:
|
249
|
-
} } = options;
|
256
|
+
sameSite: process.env.NODE_ENV === "production" ? "none" : "lax",
|
257
|
+
path: "/",
|
258
|
+
}, } = options;
|
250
259
|
if (!apiKey) {
|
251
260
|
throw new Error("API Key are required for the auth middleware.");
|
252
261
|
}
|
253
|
-
if (!cookieSecret ||
|
262
|
+
if (!cookieSecret ||
|
263
|
+
typeof cookieSecret !== "string" ||
|
264
|
+
cookieSecret.length < 8) {
|
254
265
|
throw new Error("A strong cookieSecret (at least 8 characters) is required for secure session management.");
|
255
266
|
}
|
256
267
|
return async (req, res, next) => {
|
257
268
|
// First check for temporary token in the query string
|
258
|
-
const tempToken = req.query[
|
259
|
-
const frontendToken = req.headers.authorization?.replace(
|
260
|
-
|
269
|
+
const tempToken = req.query["aos_temp_token"];
|
270
|
+
const frontendToken = req.headers.authorization?.replace("Bearer ", "") ||
|
271
|
+
req.query["aos_frontend_token"];
|
272
|
+
const signedUserToken = req.query["aos_signed_user_token"];
|
261
273
|
// first check for signed user token
|
262
274
|
if (signedUserToken) {
|
263
275
|
const userId = await verifySignedUserToken(signedUserToken);
|
264
276
|
if (userId) {
|
265
277
|
// Set the user ID on the request
|
266
278
|
req.authUserId = userId;
|
279
|
+
if (getAppSessionForUser) {
|
280
|
+
const appSession = getAppSessionForUser(userId);
|
281
|
+
if (appSession) {
|
282
|
+
req.activeSession = appSession;
|
283
|
+
}
|
284
|
+
}
|
267
285
|
// Create a signed session token and store it in a cookie
|
268
286
|
const signedSession = signSession(userId, cookieSecret);
|
269
287
|
res.cookie(cookieName, signedSession, cookieOptions);
|
270
|
-
console.log(
|
288
|
+
console.log("[auth.middleware] User ID verified from signed user token: ", userId);
|
271
289
|
return next();
|
272
290
|
}
|
273
291
|
else {
|
274
|
-
console.log(
|
292
|
+
console.log("[auth.middleware] Signed user token invalid");
|
275
293
|
}
|
276
294
|
}
|
277
295
|
// If temporary token exists, authenticate with it
|
278
296
|
if (tempToken) {
|
279
297
|
try {
|
280
|
-
let cloudApiUrl = `https://
|
281
|
-
const cloudApiUrlFromQuery = req.query[
|
298
|
+
let cloudApiUrl = `https://api.mentra.glass`;
|
299
|
+
const cloudApiUrlFromQuery = req.query["cloudApiUrl"];
|
282
300
|
if (cloudApiUrlFromQuery) {
|
283
|
-
const cloudApiUrlChecksum = req.query[
|
301
|
+
const cloudApiUrlChecksum = req.query["cloudApiUrlChecksum"];
|
284
302
|
if (validateCloudApiUrlChecksum(cloudApiUrlChecksum, cloudApiUrlFromQuery, apiKey)) {
|
285
303
|
console.log(`Cloud API is being routed to alternate url at request of the server: ${cloudApiUrlFromQuery}`);
|
286
304
|
cloudApiUrl = cloudApiUrlFromQuery;
|
@@ -292,10 +310,16 @@ function createAuthMiddleware(options) {
|
|
292
310
|
const { userId } = await exchangeToken(cloudApiUrl, tempToken, apiKey, packageName);
|
293
311
|
// Set the user ID on the request
|
294
312
|
req.authUserId = userId;
|
313
|
+
if (getAppSessionForUser) {
|
314
|
+
const appSession = getAppSessionForUser(userId);
|
315
|
+
if (appSession) {
|
316
|
+
req.activeSession = appSession;
|
317
|
+
}
|
318
|
+
}
|
295
319
|
// Create a signed session token and store it in a cookie
|
296
320
|
const signedSession = signSession(userId, cookieSecret);
|
297
321
|
res.cookie(cookieName, signedSession, cookieOptions);
|
298
|
-
console.log(
|
322
|
+
console.log("[auth.middleware] User ID verified from temporary token: ", userId);
|
299
323
|
return next();
|
300
324
|
}
|
301
325
|
catch (error) {
|
@@ -308,14 +332,20 @@ function createAuthMiddleware(options) {
|
|
308
332
|
const userId = verifyFrontendToken(frontendToken, apiKey);
|
309
333
|
if (userId) {
|
310
334
|
req.authUserId = userId;
|
335
|
+
if (getAppSessionForUser) {
|
336
|
+
const appSession = getAppSessionForUser(userId);
|
337
|
+
if (appSession) {
|
338
|
+
req.activeSession = appSession;
|
339
|
+
}
|
340
|
+
}
|
311
341
|
// Create a signed session token and store it in a cookie
|
312
342
|
const signedSession = signSession(userId, cookieSecret);
|
313
343
|
res.cookie(cookieName, signedSession, cookieOptions);
|
314
|
-
console.log(
|
344
|
+
console.log("[auth.middleware] User ID verified from frontend user token: ", userId);
|
315
345
|
return next();
|
316
346
|
}
|
317
347
|
else {
|
318
|
-
console.log(
|
348
|
+
console.log("[auth.middleware] Frontend token invalid");
|
319
349
|
}
|
320
350
|
}
|
321
351
|
// No valid temporary token, check for existing session cookie
|
@@ -326,6 +356,12 @@ function createAuthMiddleware(options) {
|
|
326
356
|
const userId = verifySession(sessionCookie, cookieSecret, cookieOptions.maxAge);
|
327
357
|
if (userId) {
|
328
358
|
req.authUserId = userId;
|
359
|
+
if (getAppSessionForUser) {
|
360
|
+
const appSession = getAppSessionForUser(userId);
|
361
|
+
if (appSession) {
|
362
|
+
req.activeSession = appSession;
|
363
|
+
}
|
364
|
+
}
|
329
365
|
return next();
|
330
366
|
}
|
331
367
|
// Invalid or expired session, clear the cookie
|
package/dist/types/index.d.ts
CHANGED
@@ -1,30 +1,30 @@
|
|
1
|
-
export * from
|
2
|
-
export * from
|
3
|
-
export * from
|
4
|
-
export * from
|
5
|
-
export * from
|
6
|
-
export * from
|
1
|
+
export * from "./token";
|
2
|
+
export * from "./message-types";
|
3
|
+
export * from "./messages/base";
|
4
|
+
export * from "./messages/glasses-to-cloud";
|
5
|
+
export * from "./messages/cloud-to-glasses";
|
6
|
+
export * from "./messages/app-to-cloud";
|
7
7
|
export { AppConnectionAck, AppConnectionError, AppStopped, SettingsUpdate as AppSettingsUpdate, // Alias to avoid conflict with cloud-to-glasses SettingsUpdate
|
8
|
-
DataStream, CloudToAppMessage, AudioPlayResponse, TranslationData, ToolCall, StandardConnectionError, CustomMessage, ManagedStreamStatus, OutputStatus, MentraosSettingsUpdate, TranscriptionData, AudioChunk, PermissionError, PermissionErrorDetail, isAppConnectionAck, isAppConnectionError, isAppStopped, isSettingsUpdate, isDataStream, isAudioChunk, isAudioPlayResponse, isDashboardModeChanged, isDashboardAlwaysOnChanged, isManagedStreamStatus, isPhotoResponse as isPhotoResponseFromCloud, isRtmpStreamStatus as isRtmpStreamStatusFromCloud } from
|
9
|
-
export * from
|
10
|
-
export * from
|
11
|
-
export * from
|
12
|
-
export * from
|
13
|
-
export * from
|
14
|
-
export * from
|
15
|
-
export * from
|
16
|
-
export * from
|
17
|
-
export * from
|
18
|
-
export * from
|
19
|
-
export { ButtonPress, HeadPosition, GlassesBatteryUpdate, PhoneBatteryUpdate, GlassesConnectionState, LocationUpdate, CalendarEvent, Vad, PhoneNotification, PhoneNotificationDismissed, StartApp, StopApp, ConnectionInit, DashboardState, OpenDashboard, GlassesToCloudMessage, PhotoResponse, RtmpStreamStatus, KeepAliveAck } from
|
20
|
-
export { ConnectionAck, ConnectionError, AuthError, DisplayEvent, AppStateChange, MicrophoneStateChange, CloudToGlassesMessage, PhotoRequestToGlasses, AudioPlayRequestToGlasses, AudioStopRequestToGlasses, SettingsUpdate, StartRtmpStream, StopRtmpStream, KeepRtmpStreamAlive } from
|
21
|
-
export { AppConnectionInit, AppSubscriptionUpdate, AudioPlayRequest, AudioStopRequest, RtmpStreamRequest, RtmpStreamStopRequest, AppToCloudMessage, PhotoRequest } from
|
22
|
-
export { TextWall, DoubleTextWall, DashboardCard, ReferenceCard, Layout, DisplayRequest } from
|
23
|
-
export { isButtonPress, isHeadPosition, isConnectionInit, isStartApp, isStopApp, isPhotoResponse as isPhotoResponseFromGlasses, isRtmpStreamStatus as isRtmpStreamStatusFromGlasses, isKeepAliveAck } from
|
24
|
-
export { isConnectionAck, isDisplayEvent, isAppStateChange, isPhotoRequest, isAudioPlayRequestToGlasses, isAudioStopRequestToGlasses, isSettingsUpdate as isSettingsUpdateToGlasses, isStartRtmpStream, isStopRtmpStream, isKeepRtmpStreamAlive } from
|
25
|
-
export { isAppConnectionInit, isAppSubscriptionUpdate, isDisplayRequest, isAudioPlayRequest, isAudioStopRequest, isRtmpStreamRequest, isRtmpStreamStopRequest, isPhotoRequest as isPhotoRequestFromApp } from
|
26
|
-
export { BaseAppSetting, AppSetting, AppSettings, AppConfig, validateAppConfig, ToolSchema, ToolParameterSchema } from
|
27
|
-
export { VideoConfig, AudioConfig, StreamConfig, StreamStatusHandler } from
|
8
|
+
DataStream, CloudToAppMessage, AudioPlayResponse, TranslationData, ToolCall, StandardConnectionError, CustomMessage, ManagedStreamStatus, OutputStatus, MentraosSettingsUpdate, TranscriptionData, AudioChunk, PermissionError, PermissionErrorDetail, isAppConnectionAck, isAppConnectionError, isAppStopped, isSettingsUpdate, isDataStream, isAudioChunk, isAudioPlayResponse, isDashboardModeChanged, isDashboardAlwaysOnChanged, isManagedStreamStatus, isPhotoResponse as isPhotoResponseFromCloud, isRtmpStreamStatus as isRtmpStreamStatusFromCloud, } from "./messages/cloud-to-app";
|
9
|
+
export * from "./streams";
|
10
|
+
export * from "./layouts";
|
11
|
+
export * from "./dashboard";
|
12
|
+
export * from "./rtmp-stream";
|
13
|
+
export * from "./enums";
|
14
|
+
export * from "./models";
|
15
|
+
export * from "./user-session";
|
16
|
+
export * from "./webhooks";
|
17
|
+
export * from "./capabilities";
|
18
|
+
export * from "./photo-data";
|
19
|
+
export { ButtonPress, HeadPosition, GlassesBatteryUpdate, PhoneBatteryUpdate, GlassesConnectionState, LocationUpdate, CalendarEvent, Vad, PhoneNotification, PhoneNotificationDismissed, StartApp, StopApp, ConnectionInit, DashboardState, OpenDashboard, GlassesToCloudMessage, PhotoResponse, RtmpStreamStatus, KeepAliveAck, } from "./messages/glasses-to-cloud";
|
20
|
+
export { ConnectionAck, ConnectionError, AuthError, DisplayEvent, AppStateChange, MicrophoneStateChange, CloudToGlassesMessage, PhotoRequestToGlasses, AudioPlayRequestToGlasses, AudioStopRequestToGlasses, SettingsUpdate, StartRtmpStream, StopRtmpStream, KeepRtmpStreamAlive, } from "./messages/cloud-to-glasses";
|
21
|
+
export { AppConnectionInit, AppSubscriptionUpdate, AudioPlayRequest, AudioStopRequest, RtmpStreamRequest, RtmpStreamStopRequest, AppToCloudMessage, PhotoRequest, } from "./messages/app-to-cloud";
|
22
|
+
export { TextWall, DoubleTextWall, DashboardCard, ReferenceCard, Layout, DisplayRequest, } from "./layouts";
|
23
|
+
export { isButtonPress, isHeadPosition, isConnectionInit, isStartApp, isStopApp, isPhotoResponse as isPhotoResponseFromGlasses, isRtmpStreamStatus as isRtmpStreamStatusFromGlasses, isKeepAliveAck, } from "./messages/glasses-to-cloud";
|
24
|
+
export { isConnectionAck, isDisplayEvent, isAppStateChange, isPhotoRequest, isAudioPlayRequestToGlasses, isAudioStopRequestToGlasses, isSettingsUpdate as isSettingsUpdateToGlasses, isStartRtmpStream, isStopRtmpStream, isKeepRtmpStreamAlive, } from "./messages/cloud-to-glasses";
|
25
|
+
export { isAppConnectionInit, isAppSubscriptionUpdate, isDisplayRequest, isAudioPlayRequest, isAudioStopRequest, isRtmpStreamRequest, isRtmpStreamStopRequest, isPhotoRequest as isPhotoRequestFromApp, } from "./messages/app-to-cloud";
|
26
|
+
export { BaseAppSetting, AppSetting, AppSettings, AppConfig, validateAppConfig, ToolSchema, ToolParameterSchema, } from "./models";
|
27
|
+
export { VideoConfig, AudioConfig, StreamConfig, StreamStatusHandler, } from "./rtmp-stream";
|
28
28
|
/**
|
29
29
|
* WebSocket error information
|
30
30
|
*/
|
@@ -33,8 +33,10 @@ export interface WebSocketError {
|
|
33
33
|
message: string;
|
34
34
|
details?: unknown;
|
35
35
|
}
|
36
|
-
import { Request } from
|
36
|
+
import { Request } from "express";
|
37
|
+
import { AppSession } from "src/app/session";
|
37
38
|
export interface AuthenticatedRequest extends Request {
|
38
39
|
authUserId?: string;
|
40
|
+
activeSession: AppSession;
|
39
41
|
}
|
40
42
|
//# sourceMappingURL=index.d.ts.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAEA,cAAc,SAAS,CAAC;AAGxB,cAAc,iBAAiB,CAAC;AAGhC,cAAc,iBAAiB,CAAC;AAGhC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AAGxC,OAAO,EAEL,gBAAgB,EAChB,kBAAkB,EAClB,UAAU,EACV,cAAc,IAAI,iBAAiB,
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
|
@@ -1,10 +1,11 @@
|
|
1
|
-
import { BaseMessage } from
|
2
|
-
import { CloudToAppMessageType } from
|
3
|
-
import { StreamType } from
|
4
|
-
import { AppSettings, AppConfig } from
|
5
|
-
import { LocationUpdate, CalendarEvent, RtmpStreamStatus, PhotoResponse } from
|
6
|
-
import { DashboardMode } from
|
7
|
-
import { Capabilities } from
|
1
|
+
import { BaseMessage } from "./base";
|
2
|
+
import { CloudToAppMessageType } from "../message-types";
|
3
|
+
import { StreamType } from "../streams";
|
4
|
+
import { AppSettings, AppConfig } from "../models";
|
5
|
+
import { LocationUpdate, CalendarEvent, RtmpStreamStatus, PhotoResponse } from "./glasses-to-cloud";
|
6
|
+
import { DashboardMode } from "../dashboard";
|
7
|
+
import { Capabilities } from "../capabilities";
|
8
|
+
import { AppSession } from "src/app/session";
|
8
9
|
/**
|
9
10
|
* Connection acknowledgment to App
|
10
11
|
*/
|
@@ -65,7 +66,7 @@ export interface SettingsUpdate extends BaseMessage {
|
|
65
66
|
* MentraOS settings update to App
|
66
67
|
*/
|
67
68
|
export interface MentraosSettingsUpdate extends BaseMessage {
|
68
|
-
type:
|
69
|
+
type: "augmentos_settings_update";
|
69
70
|
sessionId: string;
|
70
71
|
settings: Record<string, any>;
|
71
72
|
timestamp: Date;
|
@@ -120,6 +121,7 @@ export interface ToolCall {
|
|
120
121
|
toolParameters: Record<string, string | number | boolean>;
|
121
122
|
timestamp: Date;
|
122
123
|
userId: string;
|
124
|
+
activeSession: AppSession | null;
|
123
125
|
}
|
124
126
|
/**
|
125
127
|
* Stream data to App
|
@@ -147,7 +149,7 @@ export interface DashboardAlwaysOnChanged extends BaseMessage {
|
|
147
149
|
* Standard connection error (for server compatibility)
|
148
150
|
*/
|
149
151
|
export interface StandardConnectionError extends BaseMessage {
|
150
|
-
type:
|
152
|
+
type: "connection_error";
|
151
153
|
message: string;
|
152
154
|
}
|
153
155
|
/**
|
@@ -167,7 +169,7 @@ export interface OutputStatus {
|
|
167
169
|
/** Friendly name if provided */
|
168
170
|
name?: string;
|
169
171
|
/** Status of this output */
|
170
|
-
status:
|
172
|
+
status: "active" | "error" | "stopped";
|
171
173
|
/** Error message if status is error */
|
172
174
|
error?: string;
|
173
175
|
}
|
@@ -177,7 +179,7 @@ export interface OutputStatus {
|
|
177
179
|
*/
|
178
180
|
export interface ManagedStreamStatus extends BaseMessage {
|
179
181
|
type: CloudToAppMessageType.MANAGED_STREAM_STATUS;
|
180
|
-
status:
|
182
|
+
status: "initializing" | "preparing" | "active" | "stopping" | "stopped" | "error";
|
181
183
|
hlsUrl?: string;
|
182
184
|
dashUrl?: string;
|
183
185
|
webrtcUrl?: string;
|
@@ -253,7 +255,7 @@ export interface AppUserLeft extends BaseMessage {
|
|
253
255
|
export interface AppRoomUpdated extends BaseMessage {
|
254
256
|
type: CloudToAppMessageType.APP_ROOM_UPDATED;
|
255
257
|
roomId: string;
|
256
|
-
updateType:
|
258
|
+
updateType: "user_joined" | "user_left" | "config_changed" | "room_closed";
|
257
259
|
roomData: {
|
258
260
|
memberCount: number;
|
259
261
|
maxUsers?: number;
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"cloud-to-app.d.ts","sourceRoot":"","sources":["../../../src/types/messages/cloud-to-app.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,
|
1
|
+
{"version":3,"file":"cloud-to-app.d.ts","sourceRoot":"","sources":["../../../src/types/messages/cloud-to-app.ts"],"names":[],"mappings":"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"}
|
@@ -25,7 +25,8 @@ function isAppConnectionAck(message) {
|
|
25
25
|
return message.type === message_types_1.CloudToAppMessageType.CONNECTION_ACK;
|
26
26
|
}
|
27
27
|
function isAppConnectionError(message) {
|
28
|
-
return message.type === message_types_1.CloudToAppMessageType.CONNECTION_ERROR ||
|
28
|
+
return (message.type === message_types_1.CloudToAppMessageType.CONNECTION_ERROR ||
|
29
|
+
message.type === "connection_error");
|
29
30
|
}
|
30
31
|
function isAppStopped(message) {
|
31
32
|
return message.type === message_types_1.CloudToAppMessageType.APP_STOPPED;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@mentra/sdk",
|
3
|
-
"version": "2.1.
|
3
|
+
"version": "2.1.13",
|
4
4
|
"description": "Build apps for MentraOS smartglasses. This SDK provides everything you need to create real-time smartglasses applications.",
|
5
5
|
"source": "src/index.ts",
|
6
6
|
"main": "dist/index.js",
|