@reactor-team/js-sdk 2.2.2 → 2.3.2
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/index.d.mts +44 -13
- package/dist/index.d.ts +44 -13
- package/dist/index.js +125 -51
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +125 -51
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -184,12 +184,13 @@ function waitForIceGathering(pc, timeoutMs = 5e3) {
|
|
|
184
184
|
}, timeoutMs);
|
|
185
185
|
});
|
|
186
186
|
}
|
|
187
|
-
function sendMessage(channel, command, data) {
|
|
187
|
+
function sendMessage(channel, command, data, scope = "application") {
|
|
188
188
|
if (channel.readyState !== "open") {
|
|
189
189
|
throw new Error(`Data channel not open: ${channel.readyState}`);
|
|
190
190
|
}
|
|
191
191
|
const jsonData = typeof data === "string" ? JSON.parse(data) : data;
|
|
192
|
-
const
|
|
192
|
+
const inner = { type: command, data: jsonData };
|
|
193
|
+
const payload = { scope, data: inner };
|
|
193
194
|
channel.send(JSON.stringify(payload));
|
|
194
195
|
}
|
|
195
196
|
function parseMessage(data) {
|
|
@@ -208,8 +209,9 @@ function closePeerConnection(pc) {
|
|
|
208
209
|
|
|
209
210
|
// src/core/CoordinatorClient.ts
|
|
210
211
|
var INITIAL_BACKOFF_MS = 500;
|
|
211
|
-
var MAX_BACKOFF_MS =
|
|
212
|
+
var MAX_BACKOFF_MS = 15e3;
|
|
212
213
|
var BACKOFF_MULTIPLIER = 2;
|
|
214
|
+
var DEFAULT_MAX_ATTEMPTS = 6;
|
|
213
215
|
var CoordinatorClient = class {
|
|
214
216
|
constructor(options) {
|
|
215
217
|
this.baseUrl = options.baseUrl;
|
|
@@ -402,13 +404,14 @@ var CoordinatorClient = class {
|
|
|
402
404
|
});
|
|
403
405
|
}
|
|
404
406
|
/**
|
|
405
|
-
* Polls for the SDP answer with
|
|
407
|
+
* Polls for the SDP answer with exponential backoff.
|
|
406
408
|
* Used for async reconnection when the answer is not immediately available.
|
|
407
409
|
* @param sessionId - The session ID to poll for
|
|
410
|
+
* @param maxAttempts - Optional maximum number of polling attempts before giving up
|
|
408
411
|
* @returns The SDP answer from the server
|
|
409
412
|
*/
|
|
410
|
-
pollSdpAnswer(
|
|
411
|
-
return __async(this,
|
|
413
|
+
pollSdpAnswer(_0) {
|
|
414
|
+
return __async(this, arguments, function* (sessionId, maxAttempts = DEFAULT_MAX_ATTEMPTS) {
|
|
412
415
|
console.debug(
|
|
413
416
|
"[CoordinatorClient] Polling for SDP answer for session:",
|
|
414
417
|
sessionId
|
|
@@ -416,9 +419,14 @@ var CoordinatorClient = class {
|
|
|
416
419
|
let backoffMs = INITIAL_BACKOFF_MS;
|
|
417
420
|
let attempt = 0;
|
|
418
421
|
while (true) {
|
|
422
|
+
if (attempt >= maxAttempts) {
|
|
423
|
+
throw new Error(
|
|
424
|
+
`SDP polling exceeded maximum attempts (${maxAttempts}) for session ${sessionId}`
|
|
425
|
+
);
|
|
426
|
+
}
|
|
419
427
|
attempt++;
|
|
420
428
|
console.debug(
|
|
421
|
-
`[CoordinatorClient] SDP poll attempt ${attempt} for session ${sessionId}`
|
|
429
|
+
`[CoordinatorClient] SDP poll attempt ${attempt}/${maxAttempts} for session ${sessionId}`
|
|
422
430
|
);
|
|
423
431
|
const response = yield fetch(
|
|
424
432
|
`${this.baseUrl}/sessions/${sessionId}/sdp_params`,
|
|
@@ -455,9 +463,10 @@ var CoordinatorClient = class {
|
|
|
455
463
|
* falls back to polling. If no sdpOffer is provided, goes directly to polling.
|
|
456
464
|
* @param sessionId - The session ID to connect to
|
|
457
465
|
* @param sdpOffer - Optional SDP offer from the local WebRTC peer connection
|
|
466
|
+
* @param maxAttempts - Optional maximum number of polling attempts before giving up
|
|
458
467
|
* @returns The SDP answer from the server
|
|
459
468
|
*/
|
|
460
|
-
connect(sessionId, sdpOffer) {
|
|
469
|
+
connect(sessionId, sdpOffer, maxAttempts) {
|
|
461
470
|
return __async(this, null, function* () {
|
|
462
471
|
console.debug("[CoordinatorClient] Connecting to session:", sessionId);
|
|
463
472
|
if (sdpOffer) {
|
|
@@ -466,7 +475,7 @@ var CoordinatorClient = class {
|
|
|
466
475
|
return answer;
|
|
467
476
|
}
|
|
468
477
|
}
|
|
469
|
-
return this.pollSdpAnswer(sessionId);
|
|
478
|
+
return this.pollSdpAnswer(sessionId, maxAttempts);
|
|
470
479
|
});
|
|
471
480
|
}
|
|
472
481
|
/**
|
|
@@ -571,6 +580,7 @@ var LocalCoordinatorClient = class extends CoordinatorClient {
|
|
|
571
580
|
};
|
|
572
581
|
|
|
573
582
|
// src/core/GPUMachineClient.ts
|
|
583
|
+
var PING_INTERVAL_MS = 5e3;
|
|
574
584
|
var GPUMachineClient = class {
|
|
575
585
|
constructor(config) {
|
|
576
586
|
this.eventListeners = /* @__PURE__ */ new Map();
|
|
@@ -653,6 +663,7 @@ var GPUMachineClient = class {
|
|
|
653
663
|
*/
|
|
654
664
|
disconnect() {
|
|
655
665
|
return __async(this, null, function* () {
|
|
666
|
+
this.stopPing();
|
|
656
667
|
if (this.publishedTrack) {
|
|
657
668
|
yield this.unpublishTrack();
|
|
658
669
|
}
|
|
@@ -693,13 +704,14 @@ var GPUMachineClient = class {
|
|
|
693
704
|
* Sends a command to the GPU machine via the data channel.
|
|
694
705
|
* @param command The command to send
|
|
695
706
|
* @param data The data to send with the command. These are the parameters for the command, matching the scheme in the capabilities dictionary.
|
|
707
|
+
* @param scope The message scope – "application" (default) for model commands, "runtime" for platform-level messages.
|
|
696
708
|
*/
|
|
697
|
-
sendCommand(command, data) {
|
|
709
|
+
sendCommand(command, data, scope = "application") {
|
|
698
710
|
if (!this.dataChannel) {
|
|
699
711
|
throw new Error("[GPUMachineClient] Data channel not available");
|
|
700
712
|
}
|
|
701
713
|
try {
|
|
702
|
-
sendMessage(this.dataChannel, command, data);
|
|
714
|
+
sendMessage(this.dataChannel, command, data, scope);
|
|
703
715
|
} catch (error) {
|
|
704
716
|
console.warn("[GPUMachineClient] Failed to send message:", error);
|
|
705
717
|
}
|
|
@@ -780,6 +792,34 @@ var GPUMachineClient = class {
|
|
|
780
792
|
return new MediaStream(tracks);
|
|
781
793
|
}
|
|
782
794
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
795
|
+
// Ping (Client Liveness)
|
|
796
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
797
|
+
/**
|
|
798
|
+
* Starts sending periodic "ping" messages on the runtime channel so the
|
|
799
|
+
* server can detect stale connections quickly.
|
|
800
|
+
*/
|
|
801
|
+
startPing() {
|
|
802
|
+
this.stopPing();
|
|
803
|
+
this.pingInterval = setInterval(() => {
|
|
804
|
+
var _a;
|
|
805
|
+
if (((_a = this.dataChannel) == null ? void 0 : _a.readyState) === "open") {
|
|
806
|
+
try {
|
|
807
|
+
sendMessage(this.dataChannel, "ping", {}, "runtime");
|
|
808
|
+
} catch (e) {
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
}, PING_INTERVAL_MS);
|
|
812
|
+
}
|
|
813
|
+
/**
|
|
814
|
+
* Stops the periodic ping.
|
|
815
|
+
*/
|
|
816
|
+
stopPing() {
|
|
817
|
+
if (this.pingInterval !== void 0) {
|
|
818
|
+
clearInterval(this.pingInterval);
|
|
819
|
+
this.pingInterval = void 0;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
783
823
|
// Private Helpers
|
|
784
824
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
785
825
|
setStatus(newStatus) {
|
|
@@ -833,18 +873,29 @@ var GPUMachineClient = class {
|
|
|
833
873
|
if (!this.dataChannel) return;
|
|
834
874
|
this.dataChannel.onopen = () => {
|
|
835
875
|
console.debug("[GPUMachineClient] Data channel open");
|
|
876
|
+
this.startPing();
|
|
836
877
|
};
|
|
837
878
|
this.dataChannel.onclose = () => {
|
|
838
879
|
console.debug("[GPUMachineClient] Data channel closed");
|
|
880
|
+
this.stopPing();
|
|
839
881
|
};
|
|
840
882
|
this.dataChannel.onerror = (error) => {
|
|
841
883
|
console.error("[GPUMachineClient] Data channel error:", error);
|
|
842
884
|
};
|
|
843
885
|
this.dataChannel.onmessage = (event) => {
|
|
844
|
-
const
|
|
845
|
-
console.debug("[GPUMachineClient] Received message:",
|
|
886
|
+
const rawData = parseMessage(event.data);
|
|
887
|
+
console.debug("[GPUMachineClient] Received message:", rawData);
|
|
846
888
|
try {
|
|
847
|
-
|
|
889
|
+
if ((rawData == null ? void 0 : rawData.scope) === "application" && (rawData == null ? void 0 : rawData.data) !== void 0) {
|
|
890
|
+
this.emit("message", rawData.data, "application");
|
|
891
|
+
} else if ((rawData == null ? void 0 : rawData.scope) === "runtime" && (rawData == null ? void 0 : rawData.data) !== void 0) {
|
|
892
|
+
this.emit("message", rawData.data, "runtime");
|
|
893
|
+
} else {
|
|
894
|
+
console.warn(
|
|
895
|
+
"[GPUMachineClient] Received message without envelope, treating as application"
|
|
896
|
+
);
|
|
897
|
+
this.emit("message", rawData, "application");
|
|
898
|
+
}
|
|
848
899
|
} catch (error) {
|
|
849
900
|
console.error(
|
|
850
901
|
"[GPUMachineClient] Failed to parse/validate message:",
|
|
@@ -894,11 +945,14 @@ var Reactor = class {
|
|
|
894
945
|
}
|
|
895
946
|
/**
|
|
896
947
|
* Public method to send a message to the machine.
|
|
897
|
-
*
|
|
898
|
-
* @param
|
|
948
|
+
* Wraps the message in the specified channel envelope (defaults to "application").
|
|
949
|
+
* @param command The command name to send.
|
|
950
|
+
* @param data The command payload.
|
|
951
|
+
* @param scope The envelope scope – "application" (default) for model commands,
|
|
952
|
+
* "runtime" for platform-level messages (e.g. requestCapabilities).
|
|
899
953
|
* @throws Error if not in ready state
|
|
900
954
|
*/
|
|
901
|
-
sendCommand(command, data) {
|
|
955
|
+
sendCommand(command, data, scope = "application") {
|
|
902
956
|
return __async(this, null, function* () {
|
|
903
957
|
var _a;
|
|
904
958
|
if (process.env.NODE_ENV !== "development" && this.status !== "ready") {
|
|
@@ -907,7 +961,7 @@ var Reactor = class {
|
|
|
907
961
|
return;
|
|
908
962
|
}
|
|
909
963
|
try {
|
|
910
|
-
(_a = this.machineClient) == null ? void 0 : _a.sendCommand(command, data);
|
|
964
|
+
(_a = this.machineClient) == null ? void 0 : _a.sendCommand(command, data, scope);
|
|
911
965
|
} catch (error) {
|
|
912
966
|
console.error("[Reactor] Failed to send message:", error);
|
|
913
967
|
this.createError(
|
|
@@ -965,8 +1019,9 @@ var Reactor = class {
|
|
|
965
1019
|
}
|
|
966
1020
|
/**
|
|
967
1021
|
* Public method for reconnecting to an existing session, that may have been interrupted but can be recovered.
|
|
1022
|
+
* @param options Optional connect options (e.g. maxAttempts for SDP polling)
|
|
968
1023
|
*/
|
|
969
|
-
reconnect() {
|
|
1024
|
+
reconnect(options) {
|
|
970
1025
|
return __async(this, null, function* () {
|
|
971
1026
|
if (!this.sessionId || !this.coordinatorClient) {
|
|
972
1027
|
console.warn("[Reactor] No active session to reconnect to.");
|
|
@@ -986,7 +1041,8 @@ var Reactor = class {
|
|
|
986
1041
|
try {
|
|
987
1042
|
const sdpAnswer = yield this.coordinatorClient.connect(
|
|
988
1043
|
this.sessionId,
|
|
989
|
-
sdpOffer
|
|
1044
|
+
sdpOffer,
|
|
1045
|
+
options == null ? void 0 : options.maxAttempts
|
|
990
1046
|
);
|
|
991
1047
|
yield this.machineClient.connect(sdpAnswer);
|
|
992
1048
|
this.setStatus("ready");
|
|
@@ -1010,8 +1066,10 @@ var Reactor = class {
|
|
|
1010
1066
|
* Connects to the coordinator and waits for a GPU to be assigned.
|
|
1011
1067
|
* Once a GPU is assigned, the Reactor will connect to the gpu machine via WebRTC.
|
|
1012
1068
|
* If no authentication is provided and not in local mode, an error is thrown.
|
|
1069
|
+
* @param jwtToken Optional JWT token for authentication
|
|
1070
|
+
* @param options Optional connect options (e.g. maxAttempts for SDP polling)
|
|
1013
1071
|
*/
|
|
1014
|
-
connect(jwtToken) {
|
|
1072
|
+
connect(jwtToken, options) {
|
|
1015
1073
|
return __async(this, null, function* () {
|
|
1016
1074
|
console.debug("[Reactor] Connecting, status:", this.status);
|
|
1017
1075
|
if (jwtToken == void 0 && !this.local) {
|
|
@@ -1028,7 +1086,7 @@ var Reactor = class {
|
|
|
1028
1086
|
this.coordinatorClient = this.local ? new LocalCoordinatorClient(this.coordinatorUrl) : new CoordinatorClient({
|
|
1029
1087
|
baseUrl: this.coordinatorUrl,
|
|
1030
1088
|
jwtToken,
|
|
1031
|
-
// Safe: validated
|
|
1089
|
+
// Safe: validated above
|
|
1032
1090
|
model: this.model
|
|
1033
1091
|
});
|
|
1034
1092
|
const iceServers = yield this.coordinatorClient.getIceServers();
|
|
@@ -1037,7 +1095,11 @@ var Reactor = class {
|
|
|
1037
1095
|
const sdpOffer = yield this.machineClient.createOffer();
|
|
1038
1096
|
const sessionId = yield this.coordinatorClient.createSession(sdpOffer);
|
|
1039
1097
|
this.setSessionId(sessionId);
|
|
1040
|
-
const sdpAnswer = yield this.coordinatorClient.connect(
|
|
1098
|
+
const sdpAnswer = yield this.coordinatorClient.connect(
|
|
1099
|
+
sessionId,
|
|
1100
|
+
void 0,
|
|
1101
|
+
options == null ? void 0 : options.maxAttempts
|
|
1102
|
+
);
|
|
1041
1103
|
yield this.machineClient.connect(sdpAnswer);
|
|
1042
1104
|
} catch (error) {
|
|
1043
1105
|
console.error("[Reactor] Connection failed:", error);
|
|
@@ -1047,7 +1109,14 @@ var Reactor = class {
|
|
|
1047
1109
|
"coordinator",
|
|
1048
1110
|
true
|
|
1049
1111
|
);
|
|
1050
|
-
|
|
1112
|
+
try {
|
|
1113
|
+
yield this.disconnect(false);
|
|
1114
|
+
} catch (disconnectError) {
|
|
1115
|
+
console.error(
|
|
1116
|
+
"[Reactor] Failed to clean up after connection failure:",
|
|
1117
|
+
disconnectError
|
|
1118
|
+
);
|
|
1119
|
+
}
|
|
1051
1120
|
throw error;
|
|
1052
1121
|
}
|
|
1053
1122
|
});
|
|
@@ -1057,12 +1126,8 @@ var Reactor = class {
|
|
|
1057
1126
|
*/
|
|
1058
1127
|
setupMachineClientHandlers() {
|
|
1059
1128
|
if (!this.machineClient) return;
|
|
1060
|
-
this.machineClient.on("
|
|
1061
|
-
|
|
1062
|
-
this.emit("newMessage", message.data);
|
|
1063
|
-
} else {
|
|
1064
|
-
this.emit("newMessage", message);
|
|
1065
|
-
}
|
|
1129
|
+
this.machineClient.on("message", (message, scope) => {
|
|
1130
|
+
this.emit("newMessage", message, scope);
|
|
1066
1131
|
});
|
|
1067
1132
|
this.machineClient.on("statusChanged", (status) => {
|
|
1068
1133
|
switch (status) {
|
|
@@ -1269,23 +1334,27 @@ var createReactorStore = (initProps, publicState = defaultInitState) => {
|
|
|
1269
1334
|
get().internal.reactor.off("newMessage", handler);
|
|
1270
1335
|
};
|
|
1271
1336
|
},
|
|
1272
|
-
sendCommand: (command, data) => __async(null, null, function* () {
|
|
1273
|
-
console.debug("[ReactorStore] Sending command", {
|
|
1337
|
+
sendCommand: (command, data, scope) => __async(null, null, function* () {
|
|
1338
|
+
console.debug("[ReactorStore] Sending command", {
|
|
1339
|
+
command,
|
|
1340
|
+
data,
|
|
1341
|
+
scope
|
|
1342
|
+
});
|
|
1274
1343
|
try {
|
|
1275
|
-
yield get().internal.reactor.sendCommand(command, data);
|
|
1344
|
+
yield get().internal.reactor.sendCommand(command, data, scope);
|
|
1276
1345
|
console.debug("[ReactorStore] Command sent successfully");
|
|
1277
1346
|
} catch (error) {
|
|
1278
1347
|
console.error("[ReactorStore] Failed to send command:", error);
|
|
1279
1348
|
throw error;
|
|
1280
1349
|
}
|
|
1281
1350
|
}),
|
|
1282
|
-
connect: (jwtToken) => __async(null, null, function* () {
|
|
1351
|
+
connect: (jwtToken, options) => __async(null, null, function* () {
|
|
1283
1352
|
if (jwtToken === void 0) {
|
|
1284
1353
|
jwtToken = get().jwtToken;
|
|
1285
1354
|
}
|
|
1286
1355
|
console.debug("[ReactorStore] Connect called.");
|
|
1287
1356
|
try {
|
|
1288
|
-
yield get().internal.reactor.connect(jwtToken);
|
|
1357
|
+
yield get().internal.reactor.connect(jwtToken, options);
|
|
1289
1358
|
console.debug("[ReactorStore] Connect completed successfully");
|
|
1290
1359
|
} catch (error) {
|
|
1291
1360
|
console.error("[ReactorStore] Connect failed:", error);
|
|
@@ -1330,10 +1399,10 @@ var createReactorStore = (initProps, publicState = defaultInitState) => {
|
|
|
1330
1399
|
throw error;
|
|
1331
1400
|
}
|
|
1332
1401
|
}),
|
|
1333
|
-
reconnect: () => __async(null, null, function* () {
|
|
1402
|
+
reconnect: (options) => __async(null, null, function* () {
|
|
1334
1403
|
console.debug("[ReactorStore] Reconnecting");
|
|
1335
1404
|
try {
|
|
1336
|
-
yield get().internal.reactor.reconnect();
|
|
1405
|
+
yield get().internal.reactor.reconnect(options);
|
|
1337
1406
|
console.debug("[ReactorStore] Reconnect completed successfully");
|
|
1338
1407
|
} catch (error) {
|
|
1339
1408
|
console.error("[ReactorStore] Failed to reconnect:", error);
|
|
@@ -1350,11 +1419,11 @@ import { jsx } from "react/jsx-runtime";
|
|
|
1350
1419
|
function ReactorProvider(_a) {
|
|
1351
1420
|
var _b = _a, {
|
|
1352
1421
|
children,
|
|
1353
|
-
|
|
1422
|
+
connectOptions,
|
|
1354
1423
|
jwtToken
|
|
1355
1424
|
} = _b, props = __objRest(_b, [
|
|
1356
1425
|
"children",
|
|
1357
|
-
"
|
|
1426
|
+
"connectOptions",
|
|
1358
1427
|
"jwtToken"
|
|
1359
1428
|
]);
|
|
1360
1429
|
const storeRef = useRef(void 0);
|
|
@@ -1369,14 +1438,16 @@ function ReactorProvider(_a) {
|
|
|
1369
1438
|
);
|
|
1370
1439
|
console.debug("[ReactorProvider] Reactor store created successfully");
|
|
1371
1440
|
}
|
|
1441
|
+
const _a2 = connectOptions != null ? connectOptions : {}, { autoConnect = false } = _a2, pollingOptions = __objRest(_a2, ["autoConnect"]);
|
|
1372
1442
|
const { coordinatorUrl, modelName, local } = props;
|
|
1443
|
+
const maxAttempts = pollingOptions.maxAttempts;
|
|
1373
1444
|
useEffect(() => {
|
|
1374
1445
|
const handleBeforeUnload = () => {
|
|
1375
|
-
var
|
|
1446
|
+
var _a3;
|
|
1376
1447
|
console.debug(
|
|
1377
1448
|
"[ReactorProvider] Page unloading, performing non-recoverable disconnect"
|
|
1378
1449
|
);
|
|
1379
|
-
(
|
|
1450
|
+
(_a3 = storeRef.current) == null ? void 0 : _a3.getState().internal.reactor.disconnect(false);
|
|
1380
1451
|
};
|
|
1381
1452
|
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
1382
1453
|
return () => {
|
|
@@ -1391,7 +1462,7 @@ function ReactorProvider(_a) {
|
|
|
1391
1462
|
console.debug(
|
|
1392
1463
|
"[ReactorProvider] Starting autoconnect in first render..."
|
|
1393
1464
|
);
|
|
1394
|
-
current2.getState().connect(jwtToken).then(() => {
|
|
1465
|
+
current2.getState().connect(jwtToken, pollingOptions).then(() => {
|
|
1395
1466
|
console.debug(
|
|
1396
1467
|
"[ReactorProvider] Autoconnect successful in first render"
|
|
1397
1468
|
);
|
|
@@ -1434,7 +1505,7 @@ function ReactorProvider(_a) {
|
|
|
1434
1505
|
);
|
|
1435
1506
|
if (autoConnect && current.getState().status === "disconnected" && jwtToken) {
|
|
1436
1507
|
console.debug("[ReactorProvider] Starting autoconnect...");
|
|
1437
|
-
current.getState().connect(jwtToken).then(() => {
|
|
1508
|
+
current.getState().connect(jwtToken, pollingOptions).then(() => {
|
|
1438
1509
|
console.debug("[ReactorProvider] Autoconnect successful");
|
|
1439
1510
|
}).catch((error) => {
|
|
1440
1511
|
console.error("[ReactorProvider] Failed to autoconnect:", error);
|
|
@@ -1450,7 +1521,7 @@ function ReactorProvider(_a) {
|
|
|
1450
1521
|
console.error("[ReactorProvider] Failed to disconnect:", error);
|
|
1451
1522
|
});
|
|
1452
1523
|
};
|
|
1453
|
-
}, [coordinatorUrl, modelName, autoConnect, local, jwtToken]);
|
|
1524
|
+
}, [coordinatorUrl, modelName, autoConnect, local, jwtToken, maxAttempts]);
|
|
1454
1525
|
return /* @__PURE__ */ jsx(ReactorContext.Provider, { value: storeRef.current, children });
|
|
1455
1526
|
}
|
|
1456
1527
|
function useReactorStore(selector) {
|
|
@@ -1475,9 +1546,12 @@ function useReactorMessage(handler) {
|
|
|
1475
1546
|
}, [handler]);
|
|
1476
1547
|
useEffect2(() => {
|
|
1477
1548
|
console.debug("[useReactorMessage] Setting up message subscription");
|
|
1478
|
-
const stableHandler = (message) => {
|
|
1479
|
-
console.debug("[useReactorMessage] Message received", {
|
|
1480
|
-
|
|
1549
|
+
const stableHandler = (message, scope) => {
|
|
1550
|
+
console.debug("[useReactorMessage] Message received", {
|
|
1551
|
+
message,
|
|
1552
|
+
scope
|
|
1553
|
+
});
|
|
1554
|
+
handlerRef.current(message, scope);
|
|
1481
1555
|
};
|
|
1482
1556
|
reactor.on("newMessage", stableHandler);
|
|
1483
1557
|
console.debug("[useReactorMessage] Message handler registered");
|
|
@@ -1606,7 +1680,7 @@ function ReactorController({
|
|
|
1606
1680
|
}, [status]);
|
|
1607
1681
|
const requestCapabilities = useCallback(() => {
|
|
1608
1682
|
if (status === "ready") {
|
|
1609
|
-
sendCommand("requestCapabilities", {});
|
|
1683
|
+
sendCommand("requestCapabilities", {}, "runtime");
|
|
1610
1684
|
}
|
|
1611
1685
|
}, [status, sendCommand]);
|
|
1612
1686
|
React.useEffect(() => {
|
|
@@ -1623,9 +1697,9 @@ function ReactorController({
|
|
|
1623
1697
|
}, 5e3);
|
|
1624
1698
|
return () => clearInterval(interval);
|
|
1625
1699
|
}, [status, commands, requestCapabilities]);
|
|
1626
|
-
useReactorMessage((message) => {
|
|
1627
|
-
if (message && typeof message === "object" && "commands" in message) {
|
|
1628
|
-
const commandsMessage = message;
|
|
1700
|
+
useReactorMessage((message, scope) => {
|
|
1701
|
+
if (scope === "runtime" && message && typeof message === "object" && message.type === "modelCapabilities" && message.data && "commands" in message.data) {
|
|
1702
|
+
const commandsMessage = message.data;
|
|
1629
1703
|
setCommands(commandsMessage.commands);
|
|
1630
1704
|
const initialValues = {};
|
|
1631
1705
|
const initialExpanded = {};
|