@reactor-team/js-sdk 2.2.2 → 2.3.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/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 payload = { type: command, data: jsonData };
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) {
@@ -571,6 +572,7 @@ var LocalCoordinatorClient = class extends CoordinatorClient {
571
572
  };
572
573
 
573
574
  // src/core/GPUMachineClient.ts
575
+ var PING_INTERVAL_MS = 5e3;
574
576
  var GPUMachineClient = class {
575
577
  constructor(config) {
576
578
  this.eventListeners = /* @__PURE__ */ new Map();
@@ -653,6 +655,7 @@ var GPUMachineClient = class {
653
655
  */
654
656
  disconnect() {
655
657
  return __async(this, null, function* () {
658
+ this.stopPing();
656
659
  if (this.publishedTrack) {
657
660
  yield this.unpublishTrack();
658
661
  }
@@ -693,13 +696,14 @@ var GPUMachineClient = class {
693
696
  * Sends a command to the GPU machine via the data channel.
694
697
  * @param command The command to send
695
698
  * @param data The data to send with the command. These are the parameters for the command, matching the scheme in the capabilities dictionary.
699
+ * @param scope The message scope – "application" (default) for model commands, "runtime" for platform-level messages.
696
700
  */
697
- sendCommand(command, data) {
701
+ sendCommand(command, data, scope = "application") {
698
702
  if (!this.dataChannel) {
699
703
  throw new Error("[GPUMachineClient] Data channel not available");
700
704
  }
701
705
  try {
702
- sendMessage(this.dataChannel, command, data);
706
+ sendMessage(this.dataChannel, command, data, scope);
703
707
  } catch (error) {
704
708
  console.warn("[GPUMachineClient] Failed to send message:", error);
705
709
  }
@@ -780,6 +784,34 @@ var GPUMachineClient = class {
780
784
  return new MediaStream(tracks);
781
785
  }
782
786
  // ─────────────────────────────────────────────────────────────────────────────
787
+ // Ping (Client Liveness)
788
+ // ─────────────────────────────────────────────────────────────────────────────
789
+ /**
790
+ * Starts sending periodic "ping" messages on the runtime channel so the
791
+ * server can detect stale connections quickly.
792
+ */
793
+ startPing() {
794
+ this.stopPing();
795
+ this.pingInterval = setInterval(() => {
796
+ var _a;
797
+ if (((_a = this.dataChannel) == null ? void 0 : _a.readyState) === "open") {
798
+ try {
799
+ sendMessage(this.dataChannel, "ping", {}, "runtime");
800
+ } catch (e) {
801
+ }
802
+ }
803
+ }, PING_INTERVAL_MS);
804
+ }
805
+ /**
806
+ * Stops the periodic ping.
807
+ */
808
+ stopPing() {
809
+ if (this.pingInterval !== void 0) {
810
+ clearInterval(this.pingInterval);
811
+ this.pingInterval = void 0;
812
+ }
813
+ }
814
+ // ─────────────────────────────────────────────────────────────────────────────
783
815
  // Private Helpers
784
816
  // ─────────────────────────────────────────────────────────────────────────────
785
817
  setStatus(newStatus) {
@@ -833,18 +865,29 @@ var GPUMachineClient = class {
833
865
  if (!this.dataChannel) return;
834
866
  this.dataChannel.onopen = () => {
835
867
  console.debug("[GPUMachineClient] Data channel open");
868
+ this.startPing();
836
869
  };
837
870
  this.dataChannel.onclose = () => {
838
871
  console.debug("[GPUMachineClient] Data channel closed");
872
+ this.stopPing();
839
873
  };
840
874
  this.dataChannel.onerror = (error) => {
841
875
  console.error("[GPUMachineClient] Data channel error:", error);
842
876
  };
843
877
  this.dataChannel.onmessage = (event) => {
844
- const data = parseMessage(event.data);
845
- console.debug("[GPUMachineClient] Received message:", data);
878
+ const rawData = parseMessage(event.data);
879
+ console.debug("[GPUMachineClient] Received message:", rawData);
846
880
  try {
847
- this.emit("application", data);
881
+ if ((rawData == null ? void 0 : rawData.scope) === "application" && (rawData == null ? void 0 : rawData.data) !== void 0) {
882
+ this.emit("message", rawData.data, "application");
883
+ } else if ((rawData == null ? void 0 : rawData.scope) === "runtime" && (rawData == null ? void 0 : rawData.data) !== void 0) {
884
+ this.emit("message", rawData.data, "runtime");
885
+ } else {
886
+ console.warn(
887
+ "[GPUMachineClient] Received message without envelope, treating as application"
888
+ );
889
+ this.emit("message", rawData, "application");
890
+ }
848
891
  } catch (error) {
849
892
  console.error(
850
893
  "[GPUMachineClient] Failed to parse/validate message:",
@@ -894,11 +937,14 @@ var Reactor = class {
894
937
  }
895
938
  /**
896
939
  * Public method to send a message to the machine.
897
- * Automatically wraps the message in an application message.
898
- * @param message The message to send to the machine.
940
+ * Wraps the message in the specified channel envelope (defaults to "application").
941
+ * @param command The command name to send.
942
+ * @param data The command payload.
943
+ * @param scope The envelope scope – "application" (default) for model commands,
944
+ * "runtime" for platform-level messages (e.g. requestCapabilities).
899
945
  * @throws Error if not in ready state
900
946
  */
901
- sendCommand(command, data) {
947
+ sendCommand(command, data, scope = "application") {
902
948
  return __async(this, null, function* () {
903
949
  var _a;
904
950
  if (process.env.NODE_ENV !== "development" && this.status !== "ready") {
@@ -907,7 +953,7 @@ var Reactor = class {
907
953
  return;
908
954
  }
909
955
  try {
910
- (_a = this.machineClient) == null ? void 0 : _a.sendCommand(command, data);
956
+ (_a = this.machineClient) == null ? void 0 : _a.sendCommand(command, data, scope);
911
957
  } catch (error) {
912
958
  console.error("[Reactor] Failed to send message:", error);
913
959
  this.createError(
@@ -1057,12 +1103,8 @@ var Reactor = class {
1057
1103
  */
1058
1104
  setupMachineClientHandlers() {
1059
1105
  if (!this.machineClient) return;
1060
- this.machineClient.on("application", (message) => {
1061
- if ((message == null ? void 0 : message.type) === "application" && (message == null ? void 0 : message.data) !== void 0) {
1062
- this.emit("newMessage", message.data);
1063
- } else {
1064
- this.emit("newMessage", message);
1065
- }
1106
+ this.machineClient.on("message", (message, scope) => {
1107
+ this.emit("newMessage", message, scope);
1066
1108
  });
1067
1109
  this.machineClient.on("statusChanged", (status) => {
1068
1110
  switch (status) {
@@ -1269,10 +1311,14 @@ var createReactorStore = (initProps, publicState = defaultInitState) => {
1269
1311
  get().internal.reactor.off("newMessage", handler);
1270
1312
  };
1271
1313
  },
1272
- sendCommand: (command, data) => __async(null, null, function* () {
1273
- console.debug("[ReactorStore] Sending command", { command, data });
1314
+ sendCommand: (command, data, scope) => __async(null, null, function* () {
1315
+ console.debug("[ReactorStore] Sending command", {
1316
+ command,
1317
+ data,
1318
+ scope
1319
+ });
1274
1320
  try {
1275
- yield get().internal.reactor.sendCommand(command, data);
1321
+ yield get().internal.reactor.sendCommand(command, data, scope);
1276
1322
  console.debug("[ReactorStore] Command sent successfully");
1277
1323
  } catch (error) {
1278
1324
  console.error("[ReactorStore] Failed to send command:", error);
@@ -1475,9 +1521,12 @@ function useReactorMessage(handler) {
1475
1521
  }, [handler]);
1476
1522
  useEffect2(() => {
1477
1523
  console.debug("[useReactorMessage] Setting up message subscription");
1478
- const stableHandler = (message) => {
1479
- console.debug("[useReactorMessage] Message received", { message });
1480
- handlerRef.current(message);
1524
+ const stableHandler = (message, scope) => {
1525
+ console.debug("[useReactorMessage] Message received", {
1526
+ message,
1527
+ scope
1528
+ });
1529
+ handlerRef.current(message, scope);
1481
1530
  };
1482
1531
  reactor.on("newMessage", stableHandler);
1483
1532
  console.debug("[useReactorMessage] Message handler registered");
@@ -1606,7 +1655,7 @@ function ReactorController({
1606
1655
  }, [status]);
1607
1656
  const requestCapabilities = useCallback(() => {
1608
1657
  if (status === "ready") {
1609
- sendCommand("requestCapabilities", {});
1658
+ sendCommand("requestCapabilities", {}, "runtime");
1610
1659
  }
1611
1660
  }, [status, sendCommand]);
1612
1661
  React.useEffect(() => {
@@ -1623,9 +1672,9 @@ function ReactorController({
1623
1672
  }, 5e3);
1624
1673
  return () => clearInterval(interval);
1625
1674
  }, [status, commands, requestCapabilities]);
1626
- useReactorMessage((message) => {
1627
- if (message && typeof message === "object" && "commands" in message) {
1628
- const commandsMessage = message;
1675
+ useReactorMessage((message, scope) => {
1676
+ if (scope === "runtime" && message && typeof message === "object" && message.type === "modelCapabilities" && message.data && "commands" in message.data) {
1677
+ const commandsMessage = message.data;
1629
1678
  setCommands(commandsMessage.commands);
1630
1679
  const initialValues = {};
1631
1680
  const initialExpanded = {};