@reactor-team/js-sdk 2.2.1 → 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,10 +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
- }
1106
+ this.machineClient.on("message", (message, scope) => {
1107
+ this.emit("newMessage", message, scope);
1064
1108
  });
1065
1109
  this.machineClient.on("statusChanged", (status) => {
1066
1110
  switch (status) {
@@ -1267,10 +1311,14 @@ var createReactorStore = (initProps, publicState = defaultInitState) => {
1267
1311
  get().internal.reactor.off("newMessage", handler);
1268
1312
  };
1269
1313
  },
1270
- sendCommand: (command, data) => __async(null, null, function* () {
1271
- 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
+ });
1272
1320
  try {
1273
- yield get().internal.reactor.sendCommand(command, data);
1321
+ yield get().internal.reactor.sendCommand(command, data, scope);
1274
1322
  console.debug("[ReactorStore] Command sent successfully");
1275
1323
  } catch (error) {
1276
1324
  console.error("[ReactorStore] Failed to send command:", error);
@@ -1473,9 +1521,12 @@ function useReactorMessage(handler) {
1473
1521
  }, [handler]);
1474
1522
  useEffect2(() => {
1475
1523
  console.debug("[useReactorMessage] Setting up message subscription");
1476
- const stableHandler = (message) => {
1477
- console.debug("[useReactorMessage] Message received", { message });
1478
- handlerRef.current(message);
1524
+ const stableHandler = (message, scope) => {
1525
+ console.debug("[useReactorMessage] Message received", {
1526
+ message,
1527
+ scope
1528
+ });
1529
+ handlerRef.current(message, scope);
1479
1530
  };
1480
1531
  reactor.on("newMessage", stableHandler);
1481
1532
  console.debug("[useReactorMessage] Message handler registered");
@@ -1604,7 +1655,7 @@ function ReactorController({
1604
1655
  }, [status]);
1605
1656
  const requestCapabilities = useCallback(() => {
1606
1657
  if (status === "ready") {
1607
- sendCommand("requestCapabilities", {});
1658
+ sendCommand("requestCapabilities", {}, "runtime");
1608
1659
  }
1609
1660
  }, [status, sendCommand]);
1610
1661
  React.useEffect(() => {
@@ -1621,9 +1672,9 @@ function ReactorController({
1621
1672
  }, 5e3);
1622
1673
  return () => clearInterval(interval);
1623
1674
  }, [status, commands, requestCapabilities]);
1624
- useReactorMessage((message) => {
1625
- if (message && typeof message === "object" && "commands" in message) {
1626
- 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;
1627
1678
  setCommands(commandsMessage.commands);
1628
1679
  const initialValues = {};
1629
1680
  const initialExpanded = {};