@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.js
CHANGED
|
@@ -227,12 +227,13 @@ function waitForIceGathering(pc, timeoutMs = 5e3) {
|
|
|
227
227
|
}, timeoutMs);
|
|
228
228
|
});
|
|
229
229
|
}
|
|
230
|
-
function sendMessage(channel, command, data) {
|
|
230
|
+
function sendMessage(channel, command, data, scope = "application") {
|
|
231
231
|
if (channel.readyState !== "open") {
|
|
232
232
|
throw new Error(`Data channel not open: ${channel.readyState}`);
|
|
233
233
|
}
|
|
234
234
|
const jsonData = typeof data === "string" ? JSON.parse(data) : data;
|
|
235
|
-
const
|
|
235
|
+
const inner = { type: command, data: jsonData };
|
|
236
|
+
const payload = { scope, data: inner };
|
|
236
237
|
channel.send(JSON.stringify(payload));
|
|
237
238
|
}
|
|
238
239
|
function parseMessage(data) {
|
|
@@ -251,8 +252,9 @@ function closePeerConnection(pc) {
|
|
|
251
252
|
|
|
252
253
|
// src/core/CoordinatorClient.ts
|
|
253
254
|
var INITIAL_BACKOFF_MS = 500;
|
|
254
|
-
var MAX_BACKOFF_MS =
|
|
255
|
+
var MAX_BACKOFF_MS = 15e3;
|
|
255
256
|
var BACKOFF_MULTIPLIER = 2;
|
|
257
|
+
var DEFAULT_MAX_ATTEMPTS = 6;
|
|
256
258
|
var CoordinatorClient = class {
|
|
257
259
|
constructor(options) {
|
|
258
260
|
this.baseUrl = options.baseUrl;
|
|
@@ -445,13 +447,14 @@ var CoordinatorClient = class {
|
|
|
445
447
|
});
|
|
446
448
|
}
|
|
447
449
|
/**
|
|
448
|
-
* Polls for the SDP answer with
|
|
450
|
+
* Polls for the SDP answer with exponential backoff.
|
|
449
451
|
* Used for async reconnection when the answer is not immediately available.
|
|
450
452
|
* @param sessionId - The session ID to poll for
|
|
453
|
+
* @param maxAttempts - Optional maximum number of polling attempts before giving up
|
|
451
454
|
* @returns The SDP answer from the server
|
|
452
455
|
*/
|
|
453
|
-
pollSdpAnswer(
|
|
454
|
-
return __async(this,
|
|
456
|
+
pollSdpAnswer(_0) {
|
|
457
|
+
return __async(this, arguments, function* (sessionId, maxAttempts = DEFAULT_MAX_ATTEMPTS) {
|
|
455
458
|
console.debug(
|
|
456
459
|
"[CoordinatorClient] Polling for SDP answer for session:",
|
|
457
460
|
sessionId
|
|
@@ -459,9 +462,14 @@ var CoordinatorClient = class {
|
|
|
459
462
|
let backoffMs = INITIAL_BACKOFF_MS;
|
|
460
463
|
let attempt = 0;
|
|
461
464
|
while (true) {
|
|
465
|
+
if (attempt >= maxAttempts) {
|
|
466
|
+
throw new Error(
|
|
467
|
+
`SDP polling exceeded maximum attempts (${maxAttempts}) for session ${sessionId}`
|
|
468
|
+
);
|
|
469
|
+
}
|
|
462
470
|
attempt++;
|
|
463
471
|
console.debug(
|
|
464
|
-
`[CoordinatorClient] SDP poll attempt ${attempt} for session ${sessionId}`
|
|
472
|
+
`[CoordinatorClient] SDP poll attempt ${attempt}/${maxAttempts} for session ${sessionId}`
|
|
465
473
|
);
|
|
466
474
|
const response = yield fetch(
|
|
467
475
|
`${this.baseUrl}/sessions/${sessionId}/sdp_params`,
|
|
@@ -498,9 +506,10 @@ var CoordinatorClient = class {
|
|
|
498
506
|
* falls back to polling. If no sdpOffer is provided, goes directly to polling.
|
|
499
507
|
* @param sessionId - The session ID to connect to
|
|
500
508
|
* @param sdpOffer - Optional SDP offer from the local WebRTC peer connection
|
|
509
|
+
* @param maxAttempts - Optional maximum number of polling attempts before giving up
|
|
501
510
|
* @returns The SDP answer from the server
|
|
502
511
|
*/
|
|
503
|
-
connect(sessionId, sdpOffer) {
|
|
512
|
+
connect(sessionId, sdpOffer, maxAttempts) {
|
|
504
513
|
return __async(this, null, function* () {
|
|
505
514
|
console.debug("[CoordinatorClient] Connecting to session:", sessionId);
|
|
506
515
|
if (sdpOffer) {
|
|
@@ -509,7 +518,7 @@ var CoordinatorClient = class {
|
|
|
509
518
|
return answer;
|
|
510
519
|
}
|
|
511
520
|
}
|
|
512
|
-
return this.pollSdpAnswer(sessionId);
|
|
521
|
+
return this.pollSdpAnswer(sessionId, maxAttempts);
|
|
513
522
|
});
|
|
514
523
|
}
|
|
515
524
|
/**
|
|
@@ -614,6 +623,7 @@ var LocalCoordinatorClient = class extends CoordinatorClient {
|
|
|
614
623
|
};
|
|
615
624
|
|
|
616
625
|
// src/core/GPUMachineClient.ts
|
|
626
|
+
var PING_INTERVAL_MS = 5e3;
|
|
617
627
|
var GPUMachineClient = class {
|
|
618
628
|
constructor(config) {
|
|
619
629
|
this.eventListeners = /* @__PURE__ */ new Map();
|
|
@@ -696,6 +706,7 @@ var GPUMachineClient = class {
|
|
|
696
706
|
*/
|
|
697
707
|
disconnect() {
|
|
698
708
|
return __async(this, null, function* () {
|
|
709
|
+
this.stopPing();
|
|
699
710
|
if (this.publishedTrack) {
|
|
700
711
|
yield this.unpublishTrack();
|
|
701
712
|
}
|
|
@@ -736,13 +747,14 @@ var GPUMachineClient = class {
|
|
|
736
747
|
* Sends a command to the GPU machine via the data channel.
|
|
737
748
|
* @param command The command to send
|
|
738
749
|
* @param data The data to send with the command. These are the parameters for the command, matching the scheme in the capabilities dictionary.
|
|
750
|
+
* @param scope The message scope – "application" (default) for model commands, "runtime" for platform-level messages.
|
|
739
751
|
*/
|
|
740
|
-
sendCommand(command, data) {
|
|
752
|
+
sendCommand(command, data, scope = "application") {
|
|
741
753
|
if (!this.dataChannel) {
|
|
742
754
|
throw new Error("[GPUMachineClient] Data channel not available");
|
|
743
755
|
}
|
|
744
756
|
try {
|
|
745
|
-
sendMessage(this.dataChannel, command, data);
|
|
757
|
+
sendMessage(this.dataChannel, command, data, scope);
|
|
746
758
|
} catch (error) {
|
|
747
759
|
console.warn("[GPUMachineClient] Failed to send message:", error);
|
|
748
760
|
}
|
|
@@ -823,6 +835,34 @@ var GPUMachineClient = class {
|
|
|
823
835
|
return new MediaStream(tracks);
|
|
824
836
|
}
|
|
825
837
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
838
|
+
// Ping (Client Liveness)
|
|
839
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
840
|
+
/**
|
|
841
|
+
* Starts sending periodic "ping" messages on the runtime channel so the
|
|
842
|
+
* server can detect stale connections quickly.
|
|
843
|
+
*/
|
|
844
|
+
startPing() {
|
|
845
|
+
this.stopPing();
|
|
846
|
+
this.pingInterval = setInterval(() => {
|
|
847
|
+
var _a;
|
|
848
|
+
if (((_a = this.dataChannel) == null ? void 0 : _a.readyState) === "open") {
|
|
849
|
+
try {
|
|
850
|
+
sendMessage(this.dataChannel, "ping", {}, "runtime");
|
|
851
|
+
} catch (e) {
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
}, PING_INTERVAL_MS);
|
|
855
|
+
}
|
|
856
|
+
/**
|
|
857
|
+
* Stops the periodic ping.
|
|
858
|
+
*/
|
|
859
|
+
stopPing() {
|
|
860
|
+
if (this.pingInterval !== void 0) {
|
|
861
|
+
clearInterval(this.pingInterval);
|
|
862
|
+
this.pingInterval = void 0;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
826
866
|
// Private Helpers
|
|
827
867
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
828
868
|
setStatus(newStatus) {
|
|
@@ -876,18 +916,29 @@ var GPUMachineClient = class {
|
|
|
876
916
|
if (!this.dataChannel) return;
|
|
877
917
|
this.dataChannel.onopen = () => {
|
|
878
918
|
console.debug("[GPUMachineClient] Data channel open");
|
|
919
|
+
this.startPing();
|
|
879
920
|
};
|
|
880
921
|
this.dataChannel.onclose = () => {
|
|
881
922
|
console.debug("[GPUMachineClient] Data channel closed");
|
|
923
|
+
this.stopPing();
|
|
882
924
|
};
|
|
883
925
|
this.dataChannel.onerror = (error) => {
|
|
884
926
|
console.error("[GPUMachineClient] Data channel error:", error);
|
|
885
927
|
};
|
|
886
928
|
this.dataChannel.onmessage = (event) => {
|
|
887
|
-
const
|
|
888
|
-
console.debug("[GPUMachineClient] Received message:",
|
|
929
|
+
const rawData = parseMessage(event.data);
|
|
930
|
+
console.debug("[GPUMachineClient] Received message:", rawData);
|
|
889
931
|
try {
|
|
890
|
-
|
|
932
|
+
if ((rawData == null ? void 0 : rawData.scope) === "application" && (rawData == null ? void 0 : rawData.data) !== void 0) {
|
|
933
|
+
this.emit("message", rawData.data, "application");
|
|
934
|
+
} else if ((rawData == null ? void 0 : rawData.scope) === "runtime" && (rawData == null ? void 0 : rawData.data) !== void 0) {
|
|
935
|
+
this.emit("message", rawData.data, "runtime");
|
|
936
|
+
} else {
|
|
937
|
+
console.warn(
|
|
938
|
+
"[GPUMachineClient] Received message without envelope, treating as application"
|
|
939
|
+
);
|
|
940
|
+
this.emit("message", rawData, "application");
|
|
941
|
+
}
|
|
891
942
|
} catch (error) {
|
|
892
943
|
console.error(
|
|
893
944
|
"[GPUMachineClient] Failed to parse/validate message:",
|
|
@@ -937,11 +988,14 @@ var Reactor = class {
|
|
|
937
988
|
}
|
|
938
989
|
/**
|
|
939
990
|
* Public method to send a message to the machine.
|
|
940
|
-
*
|
|
941
|
-
* @param
|
|
991
|
+
* Wraps the message in the specified channel envelope (defaults to "application").
|
|
992
|
+
* @param command The command name to send.
|
|
993
|
+
* @param data The command payload.
|
|
994
|
+
* @param scope The envelope scope – "application" (default) for model commands,
|
|
995
|
+
* "runtime" for platform-level messages (e.g. requestCapabilities).
|
|
942
996
|
* @throws Error if not in ready state
|
|
943
997
|
*/
|
|
944
|
-
sendCommand(command, data) {
|
|
998
|
+
sendCommand(command, data, scope = "application") {
|
|
945
999
|
return __async(this, null, function* () {
|
|
946
1000
|
var _a;
|
|
947
1001
|
if (process.env.NODE_ENV !== "development" && this.status !== "ready") {
|
|
@@ -950,7 +1004,7 @@ var Reactor = class {
|
|
|
950
1004
|
return;
|
|
951
1005
|
}
|
|
952
1006
|
try {
|
|
953
|
-
(_a = this.machineClient) == null ? void 0 : _a.sendCommand(command, data);
|
|
1007
|
+
(_a = this.machineClient) == null ? void 0 : _a.sendCommand(command, data, scope);
|
|
954
1008
|
} catch (error) {
|
|
955
1009
|
console.error("[Reactor] Failed to send message:", error);
|
|
956
1010
|
this.createError(
|
|
@@ -1008,8 +1062,9 @@ var Reactor = class {
|
|
|
1008
1062
|
}
|
|
1009
1063
|
/**
|
|
1010
1064
|
* Public method for reconnecting to an existing session, that may have been interrupted but can be recovered.
|
|
1065
|
+
* @param options Optional connect options (e.g. maxAttempts for SDP polling)
|
|
1011
1066
|
*/
|
|
1012
|
-
reconnect() {
|
|
1067
|
+
reconnect(options) {
|
|
1013
1068
|
return __async(this, null, function* () {
|
|
1014
1069
|
if (!this.sessionId || !this.coordinatorClient) {
|
|
1015
1070
|
console.warn("[Reactor] No active session to reconnect to.");
|
|
@@ -1029,7 +1084,8 @@ var Reactor = class {
|
|
|
1029
1084
|
try {
|
|
1030
1085
|
const sdpAnswer = yield this.coordinatorClient.connect(
|
|
1031
1086
|
this.sessionId,
|
|
1032
|
-
sdpOffer
|
|
1087
|
+
sdpOffer,
|
|
1088
|
+
options == null ? void 0 : options.maxAttempts
|
|
1033
1089
|
);
|
|
1034
1090
|
yield this.machineClient.connect(sdpAnswer);
|
|
1035
1091
|
this.setStatus("ready");
|
|
@@ -1053,8 +1109,10 @@ var Reactor = class {
|
|
|
1053
1109
|
* Connects to the coordinator and waits for a GPU to be assigned.
|
|
1054
1110
|
* Once a GPU is assigned, the Reactor will connect to the gpu machine via WebRTC.
|
|
1055
1111
|
* If no authentication is provided and not in local mode, an error is thrown.
|
|
1112
|
+
* @param jwtToken Optional JWT token for authentication
|
|
1113
|
+
* @param options Optional connect options (e.g. maxAttempts for SDP polling)
|
|
1056
1114
|
*/
|
|
1057
|
-
connect(jwtToken) {
|
|
1115
|
+
connect(jwtToken, options) {
|
|
1058
1116
|
return __async(this, null, function* () {
|
|
1059
1117
|
console.debug("[Reactor] Connecting, status:", this.status);
|
|
1060
1118
|
if (jwtToken == void 0 && !this.local) {
|
|
@@ -1071,7 +1129,7 @@ var Reactor = class {
|
|
|
1071
1129
|
this.coordinatorClient = this.local ? new LocalCoordinatorClient(this.coordinatorUrl) : new CoordinatorClient({
|
|
1072
1130
|
baseUrl: this.coordinatorUrl,
|
|
1073
1131
|
jwtToken,
|
|
1074
|
-
// Safe: validated
|
|
1132
|
+
// Safe: validated above
|
|
1075
1133
|
model: this.model
|
|
1076
1134
|
});
|
|
1077
1135
|
const iceServers = yield this.coordinatorClient.getIceServers();
|
|
@@ -1080,7 +1138,11 @@ var Reactor = class {
|
|
|
1080
1138
|
const sdpOffer = yield this.machineClient.createOffer();
|
|
1081
1139
|
const sessionId = yield this.coordinatorClient.createSession(sdpOffer);
|
|
1082
1140
|
this.setSessionId(sessionId);
|
|
1083
|
-
const sdpAnswer = yield this.coordinatorClient.connect(
|
|
1141
|
+
const sdpAnswer = yield this.coordinatorClient.connect(
|
|
1142
|
+
sessionId,
|
|
1143
|
+
void 0,
|
|
1144
|
+
options == null ? void 0 : options.maxAttempts
|
|
1145
|
+
);
|
|
1084
1146
|
yield this.machineClient.connect(sdpAnswer);
|
|
1085
1147
|
} catch (error) {
|
|
1086
1148
|
console.error("[Reactor] Connection failed:", error);
|
|
@@ -1090,7 +1152,14 @@ var Reactor = class {
|
|
|
1090
1152
|
"coordinator",
|
|
1091
1153
|
true
|
|
1092
1154
|
);
|
|
1093
|
-
|
|
1155
|
+
try {
|
|
1156
|
+
yield this.disconnect(false);
|
|
1157
|
+
} catch (disconnectError) {
|
|
1158
|
+
console.error(
|
|
1159
|
+
"[Reactor] Failed to clean up after connection failure:",
|
|
1160
|
+
disconnectError
|
|
1161
|
+
);
|
|
1162
|
+
}
|
|
1094
1163
|
throw error;
|
|
1095
1164
|
}
|
|
1096
1165
|
});
|
|
@@ -1100,12 +1169,8 @@ var Reactor = class {
|
|
|
1100
1169
|
*/
|
|
1101
1170
|
setupMachineClientHandlers() {
|
|
1102
1171
|
if (!this.machineClient) return;
|
|
1103
|
-
this.machineClient.on("
|
|
1104
|
-
|
|
1105
|
-
this.emit("newMessage", message.data);
|
|
1106
|
-
} else {
|
|
1107
|
-
this.emit("newMessage", message);
|
|
1108
|
-
}
|
|
1172
|
+
this.machineClient.on("message", (message, scope) => {
|
|
1173
|
+
this.emit("newMessage", message, scope);
|
|
1109
1174
|
});
|
|
1110
1175
|
this.machineClient.on("statusChanged", (status) => {
|
|
1111
1176
|
switch (status) {
|
|
@@ -1312,23 +1377,27 @@ var createReactorStore = (initProps, publicState = defaultInitState) => {
|
|
|
1312
1377
|
get().internal.reactor.off("newMessage", handler);
|
|
1313
1378
|
};
|
|
1314
1379
|
},
|
|
1315
|
-
sendCommand: (command, data) => __async(null, null, function* () {
|
|
1316
|
-
console.debug("[ReactorStore] Sending command", {
|
|
1380
|
+
sendCommand: (command, data, scope) => __async(null, null, function* () {
|
|
1381
|
+
console.debug("[ReactorStore] Sending command", {
|
|
1382
|
+
command,
|
|
1383
|
+
data,
|
|
1384
|
+
scope
|
|
1385
|
+
});
|
|
1317
1386
|
try {
|
|
1318
|
-
yield get().internal.reactor.sendCommand(command, data);
|
|
1387
|
+
yield get().internal.reactor.sendCommand(command, data, scope);
|
|
1319
1388
|
console.debug("[ReactorStore] Command sent successfully");
|
|
1320
1389
|
} catch (error) {
|
|
1321
1390
|
console.error("[ReactorStore] Failed to send command:", error);
|
|
1322
1391
|
throw error;
|
|
1323
1392
|
}
|
|
1324
1393
|
}),
|
|
1325
|
-
connect: (jwtToken) => __async(null, null, function* () {
|
|
1394
|
+
connect: (jwtToken, options) => __async(null, null, function* () {
|
|
1326
1395
|
if (jwtToken === void 0) {
|
|
1327
1396
|
jwtToken = get().jwtToken;
|
|
1328
1397
|
}
|
|
1329
1398
|
console.debug("[ReactorStore] Connect called.");
|
|
1330
1399
|
try {
|
|
1331
|
-
yield get().internal.reactor.connect(jwtToken);
|
|
1400
|
+
yield get().internal.reactor.connect(jwtToken, options);
|
|
1332
1401
|
console.debug("[ReactorStore] Connect completed successfully");
|
|
1333
1402
|
} catch (error) {
|
|
1334
1403
|
console.error("[ReactorStore] Connect failed:", error);
|
|
@@ -1373,10 +1442,10 @@ var createReactorStore = (initProps, publicState = defaultInitState) => {
|
|
|
1373
1442
|
throw error;
|
|
1374
1443
|
}
|
|
1375
1444
|
}),
|
|
1376
|
-
reconnect: () => __async(null, null, function* () {
|
|
1445
|
+
reconnect: (options) => __async(null, null, function* () {
|
|
1377
1446
|
console.debug("[ReactorStore] Reconnecting");
|
|
1378
1447
|
try {
|
|
1379
|
-
yield get().internal.reactor.reconnect();
|
|
1448
|
+
yield get().internal.reactor.reconnect(options);
|
|
1380
1449
|
console.debug("[ReactorStore] Reconnect completed successfully");
|
|
1381
1450
|
} catch (error) {
|
|
1382
1451
|
console.error("[ReactorStore] Failed to reconnect:", error);
|
|
@@ -1393,11 +1462,11 @@ var import_jsx_runtime = require("react/jsx-runtime");
|
|
|
1393
1462
|
function ReactorProvider(_a) {
|
|
1394
1463
|
var _b = _a, {
|
|
1395
1464
|
children,
|
|
1396
|
-
|
|
1465
|
+
connectOptions,
|
|
1397
1466
|
jwtToken
|
|
1398
1467
|
} = _b, props = __objRest(_b, [
|
|
1399
1468
|
"children",
|
|
1400
|
-
"
|
|
1469
|
+
"connectOptions",
|
|
1401
1470
|
"jwtToken"
|
|
1402
1471
|
]);
|
|
1403
1472
|
const storeRef = (0, import_react3.useRef)(void 0);
|
|
@@ -1412,14 +1481,16 @@ function ReactorProvider(_a) {
|
|
|
1412
1481
|
);
|
|
1413
1482
|
console.debug("[ReactorProvider] Reactor store created successfully");
|
|
1414
1483
|
}
|
|
1484
|
+
const _a2 = connectOptions != null ? connectOptions : {}, { autoConnect = false } = _a2, pollingOptions = __objRest(_a2, ["autoConnect"]);
|
|
1415
1485
|
const { coordinatorUrl, modelName, local } = props;
|
|
1486
|
+
const maxAttempts = pollingOptions.maxAttempts;
|
|
1416
1487
|
(0, import_react3.useEffect)(() => {
|
|
1417
1488
|
const handleBeforeUnload = () => {
|
|
1418
|
-
var
|
|
1489
|
+
var _a3;
|
|
1419
1490
|
console.debug(
|
|
1420
1491
|
"[ReactorProvider] Page unloading, performing non-recoverable disconnect"
|
|
1421
1492
|
);
|
|
1422
|
-
(
|
|
1493
|
+
(_a3 = storeRef.current) == null ? void 0 : _a3.getState().internal.reactor.disconnect(false);
|
|
1423
1494
|
};
|
|
1424
1495
|
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
1425
1496
|
return () => {
|
|
@@ -1434,7 +1505,7 @@ function ReactorProvider(_a) {
|
|
|
1434
1505
|
console.debug(
|
|
1435
1506
|
"[ReactorProvider] Starting autoconnect in first render..."
|
|
1436
1507
|
);
|
|
1437
|
-
current2.getState().connect(jwtToken).then(() => {
|
|
1508
|
+
current2.getState().connect(jwtToken, pollingOptions).then(() => {
|
|
1438
1509
|
console.debug(
|
|
1439
1510
|
"[ReactorProvider] Autoconnect successful in first render"
|
|
1440
1511
|
);
|
|
@@ -1477,7 +1548,7 @@ function ReactorProvider(_a) {
|
|
|
1477
1548
|
);
|
|
1478
1549
|
if (autoConnect && current.getState().status === "disconnected" && jwtToken) {
|
|
1479
1550
|
console.debug("[ReactorProvider] Starting autoconnect...");
|
|
1480
|
-
current.getState().connect(jwtToken).then(() => {
|
|
1551
|
+
current.getState().connect(jwtToken, pollingOptions).then(() => {
|
|
1481
1552
|
console.debug("[ReactorProvider] Autoconnect successful");
|
|
1482
1553
|
}).catch((error) => {
|
|
1483
1554
|
console.error("[ReactorProvider] Failed to autoconnect:", error);
|
|
@@ -1493,7 +1564,7 @@ function ReactorProvider(_a) {
|
|
|
1493
1564
|
console.error("[ReactorProvider] Failed to disconnect:", error);
|
|
1494
1565
|
});
|
|
1495
1566
|
};
|
|
1496
|
-
}, [coordinatorUrl, modelName, autoConnect, local, jwtToken]);
|
|
1567
|
+
}, [coordinatorUrl, modelName, autoConnect, local, jwtToken, maxAttempts]);
|
|
1497
1568
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReactorContext.Provider, { value: storeRef.current, children });
|
|
1498
1569
|
}
|
|
1499
1570
|
function useReactorStore(selector) {
|
|
@@ -1518,9 +1589,12 @@ function useReactorMessage(handler) {
|
|
|
1518
1589
|
}, [handler]);
|
|
1519
1590
|
(0, import_react4.useEffect)(() => {
|
|
1520
1591
|
console.debug("[useReactorMessage] Setting up message subscription");
|
|
1521
|
-
const stableHandler = (message) => {
|
|
1522
|
-
console.debug("[useReactorMessage] Message received", {
|
|
1523
|
-
|
|
1592
|
+
const stableHandler = (message, scope) => {
|
|
1593
|
+
console.debug("[useReactorMessage] Message received", {
|
|
1594
|
+
message,
|
|
1595
|
+
scope
|
|
1596
|
+
});
|
|
1597
|
+
handlerRef.current(message, scope);
|
|
1524
1598
|
};
|
|
1525
1599
|
reactor.on("newMessage", stableHandler);
|
|
1526
1600
|
console.debug("[useReactorMessage] Message handler registered");
|
|
@@ -1649,7 +1723,7 @@ function ReactorController({
|
|
|
1649
1723
|
}, [status]);
|
|
1650
1724
|
const requestCapabilities = (0, import_react6.useCallback)(() => {
|
|
1651
1725
|
if (status === "ready") {
|
|
1652
|
-
sendCommand("requestCapabilities", {});
|
|
1726
|
+
sendCommand("requestCapabilities", {}, "runtime");
|
|
1653
1727
|
}
|
|
1654
1728
|
}, [status, sendCommand]);
|
|
1655
1729
|
import_react6.default.useEffect(() => {
|
|
@@ -1666,9 +1740,9 @@ function ReactorController({
|
|
|
1666
1740
|
}, 5e3);
|
|
1667
1741
|
return () => clearInterval(interval);
|
|
1668
1742
|
}, [status, commands, requestCapabilities]);
|
|
1669
|
-
useReactorMessage((message) => {
|
|
1670
|
-
if (message && typeof message === "object" && "commands" in message) {
|
|
1671
|
-
const commandsMessage = message;
|
|
1743
|
+
useReactorMessage((message, scope) => {
|
|
1744
|
+
if (scope === "runtime" && message && typeof message === "object" && message.type === "modelCapabilities" && message.data && "commands" in message.data) {
|
|
1745
|
+
const commandsMessage = message.data;
|
|
1672
1746
|
setCommands(commandsMessage.commands);
|
|
1673
1747
|
const initialValues = {};
|
|
1674
1748
|
const initialExpanded = {};
|