@mentra/sdk 2.1.29-beta.2 → 2.1.31-beta.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.
- package/dist/app/server/index.d.ts +2 -1
- package/dist/app/server/index.d.ts.map +1 -1
- package/dist/app/session/device-state.d.ts +83 -0
- package/dist/app/session/device-state.d.ts.map +1 -0
- package/dist/app/session/events.d.ts +9 -0
- package/dist/app/session/events.d.ts.map +1 -1
- package/dist/app/session/index.d.ts +23 -3
- package/dist/app/session/index.d.ts.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +251 -38
- package/dist/index.js.map +13 -11
- package/dist/types/capabilities.d.ts +3 -90
- package/dist/types/capabilities.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/message-types.d.ts +3 -1
- package/dist/types/message-types.d.ts.map +1 -1
- package/dist/types/messages/app-to-cloud.d.ts +15 -1
- package/dist/types/messages/app-to-cloud.d.ts.map +1 -1
- package/dist/types/messages/cloud-to-app.d.ts +14 -1
- package/dist/types/messages/cloud-to-app.d.ts.map +1 -1
- package/dist/utils/Observable.d.ts +92 -0
- package/dist/utils/Observable.d.ts.map +1 -0
- package/node_modules/@mentra/types/README.md +134 -0
- package/node_modules/@mentra/types/dist/applet.d.ts +39 -0
- package/node_modules/@mentra/types/dist/applet.d.ts.map +1 -0
- package/node_modules/@mentra/types/dist/applet.js +5 -0
- package/node_modules/@mentra/types/dist/capabilities/even-realities-g1.d.ts +12 -0
- package/node_modules/@mentra/types/dist/capabilities/even-realities-g1.d.ts.map +1 -0
- package/node_modules/@mentra/types/dist/capabilities/even-realities-g1.js +54 -0
- package/node_modules/@mentra/types/dist/capabilities/mentra-live.d.ts +12 -0
- package/node_modules/@mentra/types/dist/capabilities/mentra-live.d.ts.map +1 -0
- package/node_modules/@mentra/types/dist/capabilities/mentra-live.js +94 -0
- package/node_modules/@mentra/types/dist/capabilities/simulated-glasses.d.ts +13 -0
- package/node_modules/@mentra/types/dist/capabilities/simulated-glasses.d.ts.map +1 -0
- package/node_modules/@mentra/types/dist/capabilities/simulated-glasses.js +67 -0
- package/node_modules/@mentra/types/dist/capabilities/vuzix-z100.d.ts +12 -0
- package/node_modules/@mentra/types/dist/capabilities/vuzix-z100.d.ts.map +1 -0
- package/node_modules/@mentra/types/dist/capabilities/vuzix-z100.js +51 -0
- package/node_modules/@mentra/types/dist/cli.d.ts +130 -0
- package/node_modules/@mentra/types/dist/cli.d.ts.map +1 -0
- package/node_modules/@mentra/types/dist/cli.js +7 -0
- package/node_modules/@mentra/types/dist/device.d.ts +32 -0
- package/node_modules/@mentra/types/dist/device.d.ts.map +1 -0
- package/node_modules/@mentra/types/dist/device.js +6 -0
- package/node_modules/@mentra/types/dist/enums.d.ts +34 -0
- package/node_modules/@mentra/types/dist/enums.d.ts.map +1 -0
- package/node_modules/@mentra/types/dist/enums.js +39 -0
- package/node_modules/@mentra/types/dist/hardware.d.ts +141 -0
- package/node_modules/@mentra/types/dist/hardware.d.ts.map +1 -0
- package/node_modules/@mentra/types/dist/hardware.js +33 -0
- package/node_modules/@mentra/types/dist/index.d.ts +18 -0
- package/node_modules/@mentra/types/dist/index.d.ts.map +1 -0
- package/node_modules/@mentra/types/dist/index.js +25 -0
- package/node_modules/@mentra/types/package.json +31 -0
- package/package.json +6 -6
- package/dist/display-utils/test/ScrollView.test.d.ts +0 -2
- package/dist/display-utils/test/ScrollView.test.d.ts.map +0 -1
- package/dist/display-utils/test/TextMeasurer.test.d.ts +0 -2
- package/dist/display-utils/test/TextMeasurer.test.d.ts.map +0 -1
- package/dist/display-utils/test/TextWrapper.test.d.ts +0 -2
- package/dist/display-utils/test/TextWrapper.test.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -373,6 +373,7 @@ var init_message_types = __esm(() => {
|
|
|
373
373
|
AppToCloudMessageType2["APP_USER_DISCOVERY"] = "app_user_discovery";
|
|
374
374
|
AppToCloudMessageType2["APP_ROOM_JOIN"] = "app_room_join";
|
|
375
375
|
AppToCloudMessageType2["APP_ROOM_LEAVE"] = "app_room_leave";
|
|
376
|
+
AppToCloudMessageType2["OWNERSHIP_RELEASE"] = "ownership_release";
|
|
376
377
|
})(AppToCloudMessageType ||= {});
|
|
377
378
|
((CloudToAppMessageType2) => {
|
|
378
379
|
CloudToAppMessageType2["CONNECTION_ACK"] = "tpa_connection_ack";
|
|
@@ -380,6 +381,7 @@ var init_message_types = __esm(() => {
|
|
|
380
381
|
CloudToAppMessageType2["APP_STOPPED"] = "app_stopped";
|
|
381
382
|
CloudToAppMessageType2["SETTINGS_UPDATE"] = "settings_update";
|
|
382
383
|
CloudToAppMessageType2["CAPABILITIES_UPDATE"] = "capabilities_update";
|
|
384
|
+
CloudToAppMessageType2["DEVICE_STATE_UPDATE"] = "device_state_update";
|
|
383
385
|
CloudToAppMessageType2["DASHBOARD_MODE_CHANGED"] = "dashboard_mode_changed";
|
|
384
386
|
CloudToAppMessageType2["DASHBOARD_ALWAYS_ON_CHANGED"] = "dashboard_always_on_changed";
|
|
385
387
|
CloudToAppMessageType2["DATA_STREAM"] = "data_stream";
|
|
@@ -829,6 +831,9 @@ function isRtmpStreamRequest(message) {
|
|
|
829
831
|
function isRtmpStreamStopRequest(message) {
|
|
830
832
|
return message.type === "rtmp_stream_stop" /* RTMP_STREAM_STOP */;
|
|
831
833
|
}
|
|
834
|
+
function isOwnershipRelease(message) {
|
|
835
|
+
return message.type === "ownership_release" /* OWNERSHIP_RELEASE */;
|
|
836
|
+
}
|
|
832
837
|
// src/utils/bitmap-utils.ts
|
|
833
838
|
import * as fs from "fs/promises";
|
|
834
839
|
import * as path from "path";
|
|
@@ -1339,6 +1344,9 @@ function isSettingsUpdate2(message) {
|
|
|
1339
1344
|
function isCapabilitiesUpdate(message) {
|
|
1340
1345
|
return message.type === "capabilities_update" /* CAPABILITIES_UPDATE */;
|
|
1341
1346
|
}
|
|
1347
|
+
function isDeviceStateUpdate(message) {
|
|
1348
|
+
return message.type === "device_state_update" /* DEVICE_STATE_UPDATE */;
|
|
1349
|
+
}
|
|
1342
1350
|
function isDataStream(message) {
|
|
1343
1351
|
return message.type === "data_stream" /* DATA_STREAM */;
|
|
1344
1352
|
}
|
|
@@ -1906,6 +1914,9 @@ class EventManager {
|
|
|
1906
1914
|
this.unsubscribe(type);
|
|
1907
1915
|
}
|
|
1908
1916
|
}
|
|
1917
|
+
getRegisteredStreams() {
|
|
1918
|
+
return Array.from(this.handlers.keys());
|
|
1919
|
+
}
|
|
1909
1920
|
emit(event, data) {
|
|
1910
1921
|
try {
|
|
1911
1922
|
this.emitter.emit(event, data);
|
|
@@ -2195,7 +2206,7 @@ class ApiClient {
|
|
|
2195
2206
|
import pino from "pino";
|
|
2196
2207
|
var BETTERSTACK_SOURCE_TOKEN = process.env.BETTERSTACK_SOURCE_TOKEN;
|
|
2197
2208
|
var BETTERSTACK_ENDPOINT = process.env.BETTERSTACK_ENDPOINT || "https://s1311181.eu-nbg-2.betterstackdata.com";
|
|
2198
|
-
var NODE_ENV = "
|
|
2209
|
+
var NODE_ENV = "development";
|
|
2199
2210
|
var PORTER_APP_NAME = process.env.PORTER_APP_NAME || "cloud-local";
|
|
2200
2211
|
var LOG_LEVEL = NODE_ENV === "production" ? "info" : "debug";
|
|
2201
2212
|
var streams2 = [];
|
|
@@ -3527,7 +3538,151 @@ class SimpleStorage {
|
|
|
3527
3538
|
}
|
|
3528
3539
|
}
|
|
3529
3540
|
|
|
3541
|
+
// src/utils/Observable.ts
|
|
3542
|
+
class Observable {
|
|
3543
|
+
_value;
|
|
3544
|
+
_listeners = new Set;
|
|
3545
|
+
_initialized = false;
|
|
3546
|
+
constructor(initialValue) {
|
|
3547
|
+
this._value = initialValue;
|
|
3548
|
+
}
|
|
3549
|
+
get value() {
|
|
3550
|
+
return this._value;
|
|
3551
|
+
}
|
|
3552
|
+
valueOf() {
|
|
3553
|
+
return this._value;
|
|
3554
|
+
}
|
|
3555
|
+
toString() {
|
|
3556
|
+
return String(this._value);
|
|
3557
|
+
}
|
|
3558
|
+
[Symbol.toPrimitive](hint) {
|
|
3559
|
+
if (hint === "string") {
|
|
3560
|
+
return String(this._value);
|
|
3561
|
+
}
|
|
3562
|
+
return this._value;
|
|
3563
|
+
}
|
|
3564
|
+
onChange(callback) {
|
|
3565
|
+
this._listeners.add(callback);
|
|
3566
|
+
if (this._initialized) {
|
|
3567
|
+
callback(this._value);
|
|
3568
|
+
}
|
|
3569
|
+
return () => this._listeners.delete(callback);
|
|
3570
|
+
}
|
|
3571
|
+
setValue(value) {
|
|
3572
|
+
const isFirstInit = !this._initialized;
|
|
3573
|
+
if (isFirstInit) {
|
|
3574
|
+
this._initialized = true;
|
|
3575
|
+
}
|
|
3576
|
+
if (isFirstInit || this._value !== value) {
|
|
3577
|
+
this._value = value;
|
|
3578
|
+
this._listeners.forEach((cb) => {
|
|
3579
|
+
try {
|
|
3580
|
+
cb(value);
|
|
3581
|
+
} catch (error) {
|
|
3582
|
+
console.error("Error in Observable onChange callback:", error);
|
|
3583
|
+
}
|
|
3584
|
+
});
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
get listenerCount() {
|
|
3588
|
+
return this._listeners.size;
|
|
3589
|
+
}
|
|
3590
|
+
}
|
|
3591
|
+
|
|
3592
|
+
// src/app/session/device-state.ts
|
|
3593
|
+
class DeviceState {
|
|
3594
|
+
wifiConnected;
|
|
3595
|
+
wifiSsid;
|
|
3596
|
+
wifiLocalIp;
|
|
3597
|
+
batteryLevel;
|
|
3598
|
+
charging;
|
|
3599
|
+
caseBatteryLevel;
|
|
3600
|
+
caseCharging;
|
|
3601
|
+
caseOpen;
|
|
3602
|
+
caseRemoved;
|
|
3603
|
+
hotspotEnabled;
|
|
3604
|
+
hotspotSsid;
|
|
3605
|
+
connected;
|
|
3606
|
+
modelName;
|
|
3607
|
+
appSession;
|
|
3608
|
+
constructor(appSession) {
|
|
3609
|
+
this.appSession = appSession;
|
|
3610
|
+
this.wifiConnected = new Observable(false);
|
|
3611
|
+
this.wifiSsid = new Observable(null);
|
|
3612
|
+
this.wifiLocalIp = new Observable(null);
|
|
3613
|
+
this.batteryLevel = new Observable(null);
|
|
3614
|
+
this.charging = new Observable(null);
|
|
3615
|
+
this.caseBatteryLevel = new Observable(null);
|
|
3616
|
+
this.caseCharging = new Observable(null);
|
|
3617
|
+
this.caseOpen = new Observable(null);
|
|
3618
|
+
this.caseRemoved = new Observable(null);
|
|
3619
|
+
this.hotspotEnabled = new Observable(null);
|
|
3620
|
+
this.hotspotSsid = new Observable(null);
|
|
3621
|
+
this.connected = new Observable(false);
|
|
3622
|
+
this.modelName = new Observable(null);
|
|
3623
|
+
}
|
|
3624
|
+
updateFromMessage(state) {
|
|
3625
|
+
if (state.connected !== undefined) {
|
|
3626
|
+
this.connected.setValue(state.connected);
|
|
3627
|
+
}
|
|
3628
|
+
if (state.modelName !== undefined) {
|
|
3629
|
+
this.modelName.setValue(state.modelName);
|
|
3630
|
+
}
|
|
3631
|
+
if (state.wifiConnected !== undefined) {
|
|
3632
|
+
this.wifiConnected.setValue(state.wifiConnected);
|
|
3633
|
+
}
|
|
3634
|
+
if (state.wifiSsid !== undefined) {
|
|
3635
|
+
this.wifiSsid.setValue(state.wifiSsid ?? null);
|
|
3636
|
+
}
|
|
3637
|
+
if (state.wifiLocalIp !== undefined) {
|
|
3638
|
+
this.wifiLocalIp.setValue(state.wifiLocalIp ?? null);
|
|
3639
|
+
}
|
|
3640
|
+
if (state.batteryLevel !== undefined) {
|
|
3641
|
+
this.batteryLevel.setValue(state.batteryLevel ?? null);
|
|
3642
|
+
}
|
|
3643
|
+
if (state.charging !== undefined) {
|
|
3644
|
+
this.charging.setValue(state.charging ?? null);
|
|
3645
|
+
}
|
|
3646
|
+
if (state.caseBatteryLevel !== undefined) {
|
|
3647
|
+
this.caseBatteryLevel.setValue(state.caseBatteryLevel ?? null);
|
|
3648
|
+
}
|
|
3649
|
+
if (state.caseCharging !== undefined) {
|
|
3650
|
+
this.caseCharging.setValue(state.caseCharging ?? null);
|
|
3651
|
+
}
|
|
3652
|
+
if (state.caseOpen !== undefined) {
|
|
3653
|
+
this.caseOpen.setValue(state.caseOpen ?? null);
|
|
3654
|
+
}
|
|
3655
|
+
if (state.caseRemoved !== undefined) {
|
|
3656
|
+
this.caseRemoved.setValue(state.caseRemoved ?? null);
|
|
3657
|
+
}
|
|
3658
|
+
if (state.hotspotEnabled !== undefined) {
|
|
3659
|
+
this.hotspotEnabled.setValue(state.hotspotEnabled ?? null);
|
|
3660
|
+
}
|
|
3661
|
+
if (state.hotspotSsid !== undefined) {
|
|
3662
|
+
this.hotspotSsid.setValue(state.hotspotSsid ?? null);
|
|
3663
|
+
}
|
|
3664
|
+
}
|
|
3665
|
+
getSnapshot() {
|
|
3666
|
+
return {
|
|
3667
|
+
connected: this.connected.value,
|
|
3668
|
+
modelName: this.modelName.value ?? undefined,
|
|
3669
|
+
wifiConnected: this.wifiConnected.value,
|
|
3670
|
+
wifiSsid: this.wifiSsid.value ?? undefined,
|
|
3671
|
+
wifiLocalIp: this.wifiLocalIp.value ?? undefined,
|
|
3672
|
+
batteryLevel: this.batteryLevel.value ?? undefined,
|
|
3673
|
+
charging: this.charging.value ?? undefined,
|
|
3674
|
+
caseBatteryLevel: this.caseBatteryLevel.value ?? undefined,
|
|
3675
|
+
caseCharging: this.caseCharging.value ?? undefined,
|
|
3676
|
+
caseOpen: this.caseOpen.value ?? undefined,
|
|
3677
|
+
caseRemoved: this.caseRemoved.value ?? undefined,
|
|
3678
|
+
hotspotEnabled: this.hotspotEnabled.value ?? undefined,
|
|
3679
|
+
hotspotSsid: this.hotspotSsid.value ?? undefined
|
|
3680
|
+
};
|
|
3681
|
+
}
|
|
3682
|
+
}
|
|
3683
|
+
|
|
3530
3684
|
// src/app/session/index.ts
|
|
3685
|
+
var SDK_SUBSCRIPTION_PATCH = "bug007-fix-v2";
|
|
3531
3686
|
var APP_TO_APP_EVENT_TYPES = [
|
|
3532
3687
|
"app_message_received",
|
|
3533
3688
|
"app_user_joined",
|
|
@@ -3541,7 +3696,7 @@ class AppSession {
|
|
|
3541
3696
|
ws = null;
|
|
3542
3697
|
sessionId = null;
|
|
3543
3698
|
reconnectAttempts = 0;
|
|
3544
|
-
|
|
3699
|
+
terminated = false;
|
|
3545
3700
|
streamRates = new Map;
|
|
3546
3701
|
resources = new ResourceTracker;
|
|
3547
3702
|
settingsData = [];
|
|
@@ -3560,6 +3715,7 @@ class AppSession {
|
|
|
3560
3715
|
led;
|
|
3561
3716
|
audio;
|
|
3562
3717
|
simpleStorage;
|
|
3718
|
+
device;
|
|
3563
3719
|
appServer;
|
|
3564
3720
|
logger;
|
|
3565
3721
|
userId;
|
|
@@ -3609,18 +3765,14 @@ class AppSession {
|
|
|
3609
3765
|
this.layouts = new LayoutManager(config.packageName, this.send.bind(this));
|
|
3610
3766
|
this.settings = new SettingsManager(this.settingsData, this.config.packageName, this.config.mentraOSWebsocketUrl, this.sessionId ?? undefined, async (streams3) => {
|
|
3611
3767
|
this.logger.debug({ streams: JSON.stringify(streams3) }, `[AppSession] subscribeFn called for streams`);
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
this.logger.debug(`[AppSession] Already subscribed to stream '${stream}'.`);
|
|
3618
|
-
}
|
|
3619
|
-
});
|
|
3620
|
-
this.logger.debug({ subscriptions: JSON.stringify(Array.from(this.subscriptions)) }, `[AppSession] Current subscriptions after subscribeFn`);
|
|
3768
|
+
const currentHandlerStreams = this.events.getRegisteredStreams();
|
|
3769
|
+
this.logger.debug({
|
|
3770
|
+
requestedStreams: JSON.stringify(streams3),
|
|
3771
|
+
currentHandlerStreams: JSON.stringify(currentHandlerStreams)
|
|
3772
|
+
}, `[AppSession] subscribeFn: requested streams vs current handler streams`);
|
|
3621
3773
|
if (this.ws?.readyState === 1) {
|
|
3622
3774
|
this.updateSubscriptions();
|
|
3623
|
-
this.logger.debug(`[AppSession] Sent updated subscriptions to cloud
|
|
3775
|
+
this.logger.debug(`[AppSession] Sent updated subscriptions to cloud (derived from handlers).`);
|
|
3624
3776
|
} else {
|
|
3625
3777
|
this.logger.debug(`[AppSession] WebSocket not open, will send subscriptions when connected.`);
|
|
3626
3778
|
}
|
|
@@ -3631,6 +3783,7 @@ class AppSession {
|
|
|
3631
3783
|
this.led = new LedModule(this, this.config.packageName, this.sessionId || "unknown-session-id", this.logger.child({ module: "led" }));
|
|
3632
3784
|
this.audio = new AudioManager(this, this.config.packageName, this.sessionId || "unknown-session-id", this.logger.child({ module: "audio" }));
|
|
3633
3785
|
this.simpleStorage = new SimpleStorage(this);
|
|
3786
|
+
this.device = { state: new DeviceState(this) };
|
|
3634
3787
|
this.location = new LocationManager(this);
|
|
3635
3788
|
}
|
|
3636
3789
|
getSessionId() {
|
|
@@ -3697,7 +3850,6 @@ class AppSession {
|
|
|
3697
3850
|
this.logger.warn(`[AppSession] Attempted to subscribe to App-to-App event type '${type}', which is not a valid stream. Use the event handler (e.g., onAppMessage) instead.`);
|
|
3698
3851
|
return;
|
|
3699
3852
|
}
|
|
3700
|
-
this.subscriptions.add(type);
|
|
3701
3853
|
if (rate) {
|
|
3702
3854
|
this.streamRates.set(type, rate);
|
|
3703
3855
|
}
|
|
@@ -3716,7 +3868,6 @@ class AppSession {
|
|
|
3716
3868
|
this.logger.warn(`[AppSession] Attempted to unsubscribe from App-to-App event type '${type}', which is not a valid stream.`);
|
|
3717
3869
|
return;
|
|
3718
3870
|
}
|
|
3719
|
-
this.subscriptions.delete(type);
|
|
3720
3871
|
this.streamRates.delete(type);
|
|
3721
3872
|
if (this.ws?.readyState === 1) {
|
|
3722
3873
|
this.updateSubscriptions();
|
|
@@ -3840,11 +3991,15 @@ class AppSession {
|
|
|
3840
3991
|
const isUserSessionEnded = reason && reason.includes("User session ended");
|
|
3841
3992
|
this.logger.debug(`\uD83D\uDD0C [${this.config.packageName}] WebSocket closed with code ${code}${reasonStr}`);
|
|
3842
3993
|
this.logger.debug(`\uD83D\uDD0C [${this.config.packageName}] isNormalClosure: ${isNormalClosure}, isManualStop: ${isManualStop}, isUserSessionEnded: ${isUserSessionEnded}`);
|
|
3843
|
-
if (
|
|
3994
|
+
if (isUserSessionEnded) {
|
|
3995
|
+
this.terminated = true;
|
|
3996
|
+
this.logger.info(`\uD83D\uDED1 [${this.config.packageName}] User session ended - marking as terminated, no reconnection allowed`);
|
|
3997
|
+
}
|
|
3998
|
+
if (!isNormalClosure && !isManualStop && !this.terminated) {
|
|
3844
3999
|
this.logger.warn(`\uD83D\uDD0C [${this.config.packageName}] Abnormal closure detected, attempting reconnection`);
|
|
3845
4000
|
this.handleReconnection();
|
|
3846
4001
|
} else {
|
|
3847
|
-
this.logger.debug(`\uD83D\uDD0C [${this.config.packageName}] Normal closure detected, not attempting reconnection`);
|
|
4002
|
+
this.logger.debug(`\uD83D\uDD0C [${this.config.packageName}] Normal/terminated closure detected, not attempting reconnection (terminated: ${this.terminated})`);
|
|
3848
4003
|
}
|
|
3849
4004
|
if (isUserSessionEnded) {
|
|
3850
4005
|
this.logger.info(`\uD83D\uDED1 [${this.config.packageName}] User session ended - emitting disconnected event with sessionEnded flag`);
|
|
@@ -3908,7 +4063,26 @@ class AppSession {
|
|
|
3908
4063
|
}
|
|
3909
4064
|
});
|
|
3910
4065
|
}
|
|
3911
|
-
async
|
|
4066
|
+
async releaseOwnership(reason) {
|
|
4067
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
4068
|
+
this.logger.debug(`[${this.config.packageName}] Cannot release ownership - WebSocket not open`);
|
|
4069
|
+
return;
|
|
4070
|
+
}
|
|
4071
|
+
const message = {
|
|
4072
|
+
type: "ownership_release" /* OWNERSHIP_RELEASE */,
|
|
4073
|
+
packageName: this.config.packageName,
|
|
4074
|
+
sessionId: this.sessionId || "",
|
|
4075
|
+
reason,
|
|
4076
|
+
timestamp: new Date
|
|
4077
|
+
};
|
|
4078
|
+
this.logger.info({ reason, sessionId: this.sessionId }, `\uD83D\uDD04 [${this.config.packageName}] Releasing ownership: ${reason}`);
|
|
4079
|
+
this.send(message);
|
|
4080
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
4081
|
+
}
|
|
4082
|
+
async disconnect(options) {
|
|
4083
|
+
if (options?.releaseOwnership && options?.reason) {
|
|
4084
|
+
await this.releaseOwnership(options.reason);
|
|
4085
|
+
}
|
|
3912
4086
|
try {
|
|
3913
4087
|
await this.simpleStorage.flush();
|
|
3914
4088
|
console.log("SimpleStorage flushed on disconnect");
|
|
@@ -3924,7 +4098,6 @@ class AppSession {
|
|
|
3924
4098
|
this.resources.dispose();
|
|
3925
4099
|
this.ws = null;
|
|
3926
4100
|
this.sessionId = null;
|
|
3927
|
-
this.subscriptions.clear();
|
|
3928
4101
|
this.reconnectAttempts = 0;
|
|
3929
4102
|
}
|
|
3930
4103
|
getSettings() {
|
|
@@ -3945,11 +4118,14 @@ class AppSession {
|
|
|
3945
4118
|
if (!this.subscriptionSettingsHandler)
|
|
3946
4119
|
return;
|
|
3947
4120
|
try {
|
|
3948
|
-
const
|
|
3949
|
-
this.
|
|
3950
|
-
|
|
3951
|
-
this.
|
|
3952
|
-
|
|
4121
|
+
const settingsSubscriptions = this.subscriptionSettingsHandler(this.settingsData);
|
|
4122
|
+
const handlerStreams = this.events.getRegisteredStreams();
|
|
4123
|
+
if (settingsSubscriptions.length !== handlerStreams.length) {
|
|
4124
|
+
this.logger.warn({
|
|
4125
|
+
settingsSubscriptions: JSON.stringify(settingsSubscriptions),
|
|
4126
|
+
handlerStreams: JSON.stringify(handlerStreams)
|
|
4127
|
+
}, `[AppSession] Settings-based subscriptions (${settingsSubscriptions.length}) differ from handler-based subscriptions (${handlerStreams.length}). ` + `Subscriptions are now derived from handlers. Ensure handlers are registered for desired streams.`);
|
|
4128
|
+
}
|
|
3953
4129
|
if (this.ws && this.ws.readyState === 1) {
|
|
3954
4130
|
this.updateSubscriptions();
|
|
3955
4131
|
}
|
|
@@ -4079,6 +4255,8 @@ class AppSession {
|
|
|
4079
4255
|
this.logger.debug(`[AppSession] No capabilities provided in CONNECTION_ACK`);
|
|
4080
4256
|
}
|
|
4081
4257
|
this.events.emit("connected", this.settingsData);
|
|
4258
|
+
const handlerCount = this.events.getRegisteredStreams().length;
|
|
4259
|
+
this.logger.info({ patch: SDK_SUBSCRIPTION_PATCH, handlerCount }, `[AppSession] \uD83D\uDD27 SDK Patch Active: ${SDK_SUBSCRIPTION_PATCH} - Subscriptions derived from ${handlerCount} handler(s)`);
|
|
4082
4260
|
this.updateSubscriptions();
|
|
4083
4261
|
if (this.shouldUpdateSubscriptionsOnSettingsChange && this.settingsData.length > 0) {
|
|
4084
4262
|
this.updateSubscriptionsFromSettings();
|
|
@@ -4087,28 +4265,33 @@ class AppSession {
|
|
|
4087
4265
|
const errorMessage = message.message || "Unknown connection error";
|
|
4088
4266
|
this.events.emit("error", new Error(errorMessage));
|
|
4089
4267
|
} else if (message.type === "audio_chunk" /* AUDIO_CHUNK */) {
|
|
4090
|
-
|
|
4268
|
+
const hasAudioHandler = this.events.getRegisteredStreams().includes("audio_chunk" /* AUDIO_CHUNK */);
|
|
4269
|
+
if (hasAudioHandler) {
|
|
4091
4270
|
this.events.emit("audio_chunk" /* AUDIO_CHUNK */, message);
|
|
4092
4271
|
}
|
|
4093
4272
|
} else if (isDataStream(message) && message.streamType === "glasses_connection_state" /* GLASSES_CONNECTION_STATE */) {
|
|
4094
4273
|
this.glassesConnectionState = message.data;
|
|
4095
|
-
|
|
4274
|
+
const hasGlassesStateHandler = this.events.getRegisteredStreams().includes("glasses_connection_state" /* GLASSES_CONNECTION_STATE */);
|
|
4275
|
+
if (hasGlassesStateHandler) {
|
|
4096
4276
|
const sanitizedData = this.sanitizeEventData("glasses_connection_state" /* GLASSES_CONNECTION_STATE */, message.data);
|
|
4097
4277
|
this.events.emit("glasses_connection_state" /* GLASSES_CONNECTION_STATE */, sanitizedData);
|
|
4098
4278
|
}
|
|
4099
4279
|
} else if (isDataStream(message)) {
|
|
4100
4280
|
const messageStreamType = message.streamType;
|
|
4101
|
-
|
|
4281
|
+
const hasHandler = this.events.getRegisteredStreams().includes(messageStreamType);
|
|
4282
|
+
if (messageStreamType && hasHandler) {
|
|
4102
4283
|
const sanitizedData = this.sanitizeEventData(messageStreamType, message.data);
|
|
4103
4284
|
this.events.emit(messageStreamType, sanitizedData);
|
|
4104
4285
|
}
|
|
4105
4286
|
} else if (isRtmpStreamStatus2(message)) {
|
|
4106
|
-
|
|
4287
|
+
const hasRtmpHandler = this.events.getRegisteredStreams().includes("rtmp_stream_status" /* RTMP_STREAM_STATUS */);
|
|
4288
|
+
if (hasRtmpHandler) {
|
|
4107
4289
|
this.events.emit("rtmp_stream_status" /* RTMP_STREAM_STATUS */, message);
|
|
4108
4290
|
}
|
|
4109
4291
|
this.camera.updateStreamState(message);
|
|
4110
4292
|
} else if (isManagedStreamStatus(message)) {
|
|
4111
|
-
|
|
4293
|
+
const hasManagedStreamHandler = this.events.getRegisteredStreams().includes("managed_stream_status" /* MANAGED_STREAM_STATUS */);
|
|
4294
|
+
if (hasManagedStreamHandler) {
|
|
4112
4295
|
this.events.emit("managed_stream_status" /* MANAGED_STREAM_STATUS */, message);
|
|
4113
4296
|
}
|
|
4114
4297
|
this.camera.handleManagedStreamStatus(message);
|
|
@@ -4139,6 +4322,12 @@ class AppSession {
|
|
|
4139
4322
|
modelName: capabilitiesMessage.modelName,
|
|
4140
4323
|
timestamp: capabilitiesMessage.timestamp
|
|
4141
4324
|
});
|
|
4325
|
+
} else if (isDeviceStateUpdate(message)) {
|
|
4326
|
+
this.device.state.updateFromMessage(message.state);
|
|
4327
|
+
this.logger.debug({
|
|
4328
|
+
changedFields: Object.keys(message.state),
|
|
4329
|
+
fullSnapshot: message.fullSnapshot
|
|
4330
|
+
}, `[AppSession] Device state updated via WebSocket`);
|
|
4142
4331
|
} else if (isAppStopped(message)) {
|
|
4143
4332
|
const reason = message.reason || "unknown";
|
|
4144
4333
|
const displayReason = `App stopped: ${reason}`;
|
|
@@ -4245,7 +4434,8 @@ class AppSession {
|
|
|
4245
4434
|
}
|
|
4246
4435
|
handleBinaryMessage(buffer) {
|
|
4247
4436
|
try {
|
|
4248
|
-
|
|
4437
|
+
const hasAudioHandler = this.events.getRegisteredStreams().includes("audio_chunk" /* AUDIO_CHUNK */);
|
|
4438
|
+
if (!hasAudioHandler) {
|
|
4249
4439
|
return;
|
|
4250
4440
|
}
|
|
4251
4441
|
if (!buffer || buffer.byteLength === 0) {
|
|
@@ -4318,8 +4508,9 @@ class AppSession {
|
|
|
4318
4508
|
this.send(message);
|
|
4319
4509
|
}
|
|
4320
4510
|
updateSubscriptions() {
|
|
4321
|
-
|
|
4322
|
-
|
|
4511
|
+
const derivedSubscriptions = this.events.getRegisteredStreams();
|
|
4512
|
+
this.logger.info({ subscriptions: JSON.stringify(derivedSubscriptions) }, `[AppSession] updateSubscriptions: sending ${derivedSubscriptions.length} subscriptions to cloud (derived from handlers)`);
|
|
4513
|
+
const subscriptionPayload = derivedSubscriptions.map((stream) => {
|
|
4323
4514
|
const rate = this.streamRates.get(stream);
|
|
4324
4515
|
if (rate && stream === "location_stream" /* LOCATION_STREAM */) {
|
|
4325
4516
|
return { stream: "location_stream", rate };
|
|
@@ -4336,6 +4527,10 @@ class AppSession {
|
|
|
4336
4527
|
this.send(message);
|
|
4337
4528
|
}
|
|
4338
4529
|
async handleReconnection() {
|
|
4530
|
+
if (this.terminated) {
|
|
4531
|
+
this.logger.info(`\uD83D\uDD04 Reconnection skipped: session was terminated (User session ended). ` + `If cloud restarts app, onSession will be called with fresh handlers.`);
|
|
4532
|
+
return;
|
|
4533
|
+
}
|
|
4339
4534
|
if (!this.config.autoReconnect || !this.sessionId) {
|
|
4340
4535
|
this.logger.debug(`\uD83D\uDD04 Reconnection skipped: autoReconnect=${this.config.autoReconnect}, sessionId=${this.sessionId ? "valid" : "invalid"}`);
|
|
4341
4536
|
return;
|
|
@@ -4415,7 +4610,12 @@ class AppSession {
|
|
|
4415
4610
|
throw new Error(`Failed to send message: ${errorMessage}`);
|
|
4416
4611
|
}
|
|
4417
4612
|
} catch (error) {
|
|
4418
|
-
|
|
4613
|
+
const isDisconnectError = error instanceof Error && (error.message.includes("WebSocket not connected") || error.message.includes("CLOSED") || error.message.includes("CLOSING"));
|
|
4614
|
+
if (isDisconnectError) {
|
|
4615
|
+
this.logger.debug(error, "Message send skipped - session disconnected");
|
|
4616
|
+
} else {
|
|
4617
|
+
this.logger.error(error, "Message send error");
|
|
4618
|
+
}
|
|
4419
4619
|
if (error instanceof Error) {
|
|
4420
4620
|
this.events.emit("error", error);
|
|
4421
4621
|
} else {
|
|
@@ -4968,10 +5168,10 @@ class AppServer {
|
|
|
4968
5168
|
});
|
|
4969
5169
|
});
|
|
4970
5170
|
}
|
|
4971
|
-
stop() {
|
|
5171
|
+
async stop() {
|
|
4972
5172
|
this.logger.info(`
|
|
4973
5173
|
\uD83D\uDED1 Shutting down...`);
|
|
4974
|
-
this.cleanup();
|
|
5174
|
+
await this.cleanup();
|
|
4975
5175
|
process.exit(0);
|
|
4976
5176
|
}
|
|
4977
5177
|
generateToken(userId, sessionId, secretKey) {
|
|
@@ -5175,10 +5375,20 @@ class AppServer {
|
|
|
5175
5375
|
process.on("SIGTERM", () => this.stop());
|
|
5176
5376
|
process.on("SIGINT", () => this.stop());
|
|
5177
5377
|
}
|
|
5178
|
-
cleanup() {
|
|
5378
|
+
async cleanup() {
|
|
5179
5379
|
for (const [sessionId, session] of this.activeSessions) {
|
|
5180
|
-
this.logger.info(`\uD83D\uDC4B Closing session ${sessionId}`);
|
|
5181
|
-
|
|
5380
|
+
this.logger.info(`\uD83D\uDC4B Closing session ${sessionId} with ownership release`);
|
|
5381
|
+
try {
|
|
5382
|
+
await session.disconnect({
|
|
5383
|
+
releaseOwnership: true,
|
|
5384
|
+
reason: "clean_shutdown"
|
|
5385
|
+
});
|
|
5386
|
+
} catch (error) {
|
|
5387
|
+
this.logger.error(error, `Error during cleanup of session ${sessionId}`);
|
|
5388
|
+
try {
|
|
5389
|
+
await session.disconnect();
|
|
5390
|
+
} catch {}
|
|
5391
|
+
}
|
|
5182
5392
|
}
|
|
5183
5393
|
this.activeSessions.clear();
|
|
5184
5394
|
this.activeSessionsByUserId.clear();
|
|
@@ -5332,6 +5542,7 @@ export {
|
|
|
5332
5542
|
isPhoneNotificationDismissed,
|
|
5333
5543
|
isPhoneNotification,
|
|
5334
5544
|
isPhoneBatteryUpdate,
|
|
5545
|
+
isOwnershipRelease,
|
|
5335
5546
|
isMicrophoneStateChange,
|
|
5336
5547
|
isManagedStreamStopRequest,
|
|
5337
5548
|
isManagedStreamStatus,
|
|
@@ -5400,6 +5611,7 @@ export {
|
|
|
5400
5611
|
PhotoStage,
|
|
5401
5612
|
PhotoErrorCode,
|
|
5402
5613
|
PermissionType,
|
|
5614
|
+
Observable,
|
|
5403
5615
|
LedModule,
|
|
5404
5616
|
LayoutType,
|
|
5405
5617
|
LEGACY_PERMISSION_MAP,
|
|
@@ -5408,6 +5620,7 @@ export {
|
|
|
5408
5620
|
GlassesToCloudMessageType,
|
|
5409
5621
|
GIVE_APP_CONTROL_OF_TOOL_RESPONSE,
|
|
5410
5622
|
EventTypes,
|
|
5623
|
+
DeviceState,
|
|
5411
5624
|
DashboardMode,
|
|
5412
5625
|
DashboardMessageTypes,
|
|
5413
5626
|
ControlActionTypes,
|
|
@@ -5425,4 +5638,4 @@ export {
|
|
|
5425
5638
|
AnimationUtils
|
|
5426
5639
|
};
|
|
5427
5640
|
|
|
5428
|
-
//# debugId=
|
|
5641
|
+
//# debugId=BBCA9D1CAB7CB76464756E2164756E21
|