@reactor-team/js-sdk 1.0.9 → 1.0.11

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 CHANGED
@@ -33,6 +33,7 @@ declare const OptionsSchema: z.ZodObject<{
33
33
  jwtToken: z.ZodOptional<z.ZodString>;
34
34
  coordinatorUrl: z.ZodDefault<z.ZodString>;
35
35
  modelName: z.ZodString;
36
+ queueing: z.ZodDefault<z.ZodBoolean>;
36
37
  }, z.core.$strip>;
37
38
  type Options = z.input<typeof OptionsSchema>;
38
39
  type EventHandler = (...args: any[]) => void;
@@ -48,6 +49,7 @@ declare class Reactor {
48
49
  private directConnection?;
49
50
  private modelName;
50
51
  private modelVersion;
52
+ private queueing;
51
53
  constructor(options: Options);
52
54
  private eventListeners;
53
55
  on(event: ReactorEvent, handler: EventHandler): void;
@@ -60,6 +62,16 @@ declare class Reactor {
60
62
  * @throws Error if not in ready state
61
63
  */
62
64
  sendMessage(message: any): Promise<void>;
65
+ /**
66
+ * Public method to publish a video stream to the machine.
67
+ * @param videoStream The video stream to send to the machine.
68
+ */
69
+ publishVideoStream(videoStream: MediaStream): Promise<void>;
70
+ /**
71
+ * Public method to unpublish video stream to the machine.
72
+ * This unpublishes the video track that was previously sent.
73
+ */
74
+ unpublishVideoStream(): Promise<void>;
63
75
  /**
64
76
  * Connects to the machine via LiveKit and waits for the gpu machine to be ready.
65
77
  * Once the machine is ready, the Reactor will establish the LiveKit connection.
package/dist/index.d.ts CHANGED
@@ -33,6 +33,7 @@ declare const OptionsSchema: z.ZodObject<{
33
33
  jwtToken: z.ZodOptional<z.ZodString>;
34
34
  coordinatorUrl: z.ZodDefault<z.ZodString>;
35
35
  modelName: z.ZodString;
36
+ queueing: z.ZodDefault<z.ZodBoolean>;
36
37
  }, z.core.$strip>;
37
38
  type Options = z.input<typeof OptionsSchema>;
38
39
  type EventHandler = (...args: any[]) => void;
@@ -48,6 +49,7 @@ declare class Reactor {
48
49
  private directConnection?;
49
50
  private modelName;
50
51
  private modelVersion;
52
+ private queueing;
51
53
  constructor(options: Options);
52
54
  private eventListeners;
53
55
  on(event: ReactorEvent, handler: EventHandler): void;
@@ -60,6 +62,16 @@ declare class Reactor {
60
62
  * @throws Error if not in ready state
61
63
  */
62
64
  sendMessage(message: any): Promise<void>;
65
+ /**
66
+ * Public method to publish a video stream to the machine.
67
+ * @param videoStream The video stream to send to the machine.
68
+ */
69
+ publishVideoStream(videoStream: MediaStream): Promise<void>;
70
+ /**
71
+ * Public method to unpublish video stream to the machine.
72
+ * This unpublishes the video track that was previously sent.
73
+ */
74
+ unpublishVideoStream(): Promise<void>;
63
75
  /**
64
76
  * Connects to the machine via LiveKit and waits for the gpu machine to be ready.
65
77
  * Once the machine is ready, the Reactor will establish the LiveKit connection.
package/dist/index.js CHANGED
@@ -142,7 +142,8 @@ var OptionsSchema = import_zod2.z.object({
142
142
  jwtToken: import_zod2.z.string().optional(),
143
143
  insecureApiKey: import_zod2.z.string().optional(),
144
144
  modelName: import_zod2.z.string(),
145
- modelVersion: import_zod2.z.string().default("latest")
145
+ modelVersion: import_zod2.z.string().default("latest"),
146
+ queueing: import_zod2.z.boolean().default(false)
146
147
  }).refine((data) => data.jwtToken || data.insecureApiKey, {
147
148
  message: "At least one of jwtToken or insecureApiKey must be provided."
148
149
  });
@@ -155,6 +156,7 @@ var CoordinatorClient = class {
155
156
  this.insecureApiKey = validatedOptions.insecureApiKey;
156
157
  this.modelName = validatedOptions.modelName;
157
158
  this.modelVersion = validatedOptions.modelVersion;
159
+ this.queueing = validatedOptions.queueing;
158
160
  }
159
161
  // Event Emitter API
160
162
  on(event, handler) {
@@ -189,6 +191,9 @@ var CoordinatorClient = class {
189
191
  } else if (this.insecureApiKey) {
190
192
  url.searchParams.set("api_key", this.insecureApiKey);
191
193
  }
194
+ if (this.queueing) {
195
+ url.searchParams.set("queueing", "true");
196
+ }
192
197
  console.debug("[CoordinatorClient] Connecting to", url.toString());
193
198
  this.websocket = new WebSocket(url.toString());
194
199
  this.websocket.onopen = () => {
@@ -382,6 +387,12 @@ var GPUMachineClient = class {
382
387
  */
383
388
  disconnect() {
384
389
  return __async(this, null, function* () {
390
+ if (this.publishedVideoTrack) {
391
+ yield this.unpublishVideoTrack();
392
+ }
393
+ if (this.publishedAudioTrack) {
394
+ yield this.unpublishAudioTrack();
395
+ }
385
396
  if (this.roomInstance) {
386
397
  console.debug("[GPUMachineClient] Closing LiveKit connection");
387
398
  yield this.roomInstance.disconnect();
@@ -404,6 +415,126 @@ var GPUMachineClient = class {
404
415
  getVideoStream() {
405
416
  return this.videoStream;
406
417
  }
418
+ /**
419
+ * Publishes a video track from the provided MediaStream to the LiveKit room.
420
+ * Only one video track can be published at a time. If a video track is already
421
+ * published, it will be unpublished first.
422
+ *
423
+ * @param mediaStream The MediaStream containing the video track to publish
424
+ * @throws Error if no video track is found in the MediaStream or room is not connected
425
+ */
426
+ publishVideoTrack(mediaStream) {
427
+ return __async(this, null, function* () {
428
+ if (!this.roomInstance) {
429
+ throw new Error(
430
+ "[GPUMachineClient] Cannot publish track - not connected to room"
431
+ );
432
+ }
433
+ const videoTracks = mediaStream.getVideoTracks();
434
+ if (videoTracks.length === 0) {
435
+ throw new Error("[GPUMachineClient] No video track found in MediaStream");
436
+ }
437
+ if (this.publishedVideoTrack) {
438
+ yield this.unpublishVideoTrack();
439
+ }
440
+ try {
441
+ const videoTrack = videoTracks[0];
442
+ const localVideoTrack = yield this.roomInstance.localParticipant.publishTrack(videoTrack, {
443
+ name: "client-video",
444
+ source: import_livekit_client.Track.Source.Camera
445
+ });
446
+ this.publishedVideoTrack = localVideoTrack.track;
447
+ console.debug("[GPUMachineClient] Video track published successfully");
448
+ } catch (error) {
449
+ console.error("[GPUMachineClient] Failed to publish video track:", error);
450
+ throw error;
451
+ }
452
+ });
453
+ }
454
+ /**
455
+ * Unpublishes the currently published video track.
456
+ */
457
+ unpublishVideoTrack() {
458
+ return __async(this, null, function* () {
459
+ if (!this.roomInstance || !this.publishedVideoTrack) {
460
+ return;
461
+ }
462
+ try {
463
+ yield this.roomInstance.localParticipant.unpublishTrack(
464
+ this.publishedVideoTrack
465
+ );
466
+ this.publishedVideoTrack.stop();
467
+ this.publishedVideoTrack = void 0;
468
+ console.debug("[GPUMachineClient] Video track unpublished successfully");
469
+ } catch (error) {
470
+ console.error(
471
+ "[GPUMachineClient] Failed to unpublish video track:",
472
+ error
473
+ );
474
+ throw error;
475
+ }
476
+ });
477
+ }
478
+ /**
479
+ * Publishes an audio track from the provided MediaStream to the LiveKit room.
480
+ * This is an internal method. Only one audio track can be published at a time.
481
+ *
482
+ * @param mediaStream The MediaStream containing the audio track to publish
483
+ * @private
484
+ */
485
+ publishAudioTrack(mediaStream) {
486
+ return __async(this, null, function* () {
487
+ if (!this.roomInstance) {
488
+ throw new Error(
489
+ "[GPUMachineClient] Cannot publish track - not connected to room"
490
+ );
491
+ }
492
+ const audioTracks = mediaStream.getAudioTracks();
493
+ if (audioTracks.length === 0) {
494
+ throw new Error("[GPUMachineClient] No audio track found in MediaStream");
495
+ }
496
+ if (this.publishedAudioTrack) {
497
+ yield this.unpublishAudioTrack();
498
+ }
499
+ try {
500
+ const audioTrack = audioTracks[0];
501
+ const localAudioTrack = yield this.roomInstance.localParticipant.publishTrack(audioTrack, {
502
+ name: "client-audio",
503
+ source: import_livekit_client.Track.Source.Microphone
504
+ });
505
+ this.publishedAudioTrack = localAudioTrack.track;
506
+ console.debug("[GPUMachineClient] Audio track published successfully");
507
+ } catch (error) {
508
+ console.error("[GPUMachineClient] Failed to publish audio track:", error);
509
+ throw error;
510
+ }
511
+ });
512
+ }
513
+ /**
514
+ * Unpublishes the currently published audio track.
515
+ * @private
516
+ */
517
+ unpublishAudioTrack() {
518
+ return __async(this, null, function* () {
519
+ if (!this.roomInstance || !this.publishedAudioTrack) {
520
+ return;
521
+ }
522
+ try {
523
+ yield this.roomInstance.localParticipant.unpublishTrack(
524
+ this.publishedAudioTrack
525
+ );
526
+ this.publishedAudioTrack.stop();
527
+ this.publishedAudioTrack = void 0;
528
+ console.debug("[GPUMachineClient] Audio track unpublished successfully");
529
+ } catch (error) {
530
+ console.error(
531
+ "[GPUMachineClient] Failed to unpublish audio track:",
532
+ error
533
+ );
534
+ throw error;
535
+ }
536
+ });
537
+ }
407
538
  };
408
539
 
409
540
  // src/core/Reactor.ts
@@ -416,7 +547,8 @@ var OptionsSchema2 = import_zod3.z.object({
416
547
  insecureApiKey: import_zod3.z.string().optional(),
417
548
  jwtToken: import_zod3.z.string().optional(),
418
549
  coordinatorUrl: import_zod3.z.string().default("wss://api.reactor.inc/ws"),
419
- modelName: import_zod3.z.string()
550
+ modelName: import_zod3.z.string(),
551
+ queueing: import_zod3.z.boolean().default(false)
420
552
  }).refine(
421
553
  (data) => data.directConnection || data.insecureApiKey || data.jwtToken,
422
554
  {
@@ -435,6 +567,7 @@ var Reactor = class {
435
567
  this.insecureApiKey = validatedOptions.insecureApiKey;
436
568
  this.directConnection = validatedOptions.directConnection;
437
569
  this.modelName = validatedOptions.modelName;
570
+ this.queueing = validatedOptions.queueing;
438
571
  this.modelVersion = "1.0.0";
439
572
  }
440
573
  // Event Emitter API
@@ -483,6 +616,51 @@ var Reactor = class {
483
616
  }
484
617
  });
485
618
  }
619
+ /**
620
+ * Public method to publish a video stream to the machine.
621
+ * @param videoStream The video stream to send to the machine.
622
+ */
623
+ publishVideoStream(videoStream) {
624
+ return __async(this, null, function* () {
625
+ var _a;
626
+ if (process.env.NODE_ENV !== "development" && this.status !== "ready") {
627
+ const errorMessage = `Cannot publish video stream, status is ${this.status}`;
628
+ console.error("[Reactor] Not ready, cannot publish video stream");
629
+ throw new Error(errorMessage);
630
+ }
631
+ try {
632
+ yield (_a = this.machineClient) == null ? void 0 : _a.publishVideoTrack(videoStream);
633
+ } catch (error) {
634
+ console.error("[Reactor] Failed to publish video:", error);
635
+ this.createError(
636
+ "VIDEO_PUBLISH_FAILED",
637
+ `Failed to publish video: ${error}`,
638
+ "gpu",
639
+ true
640
+ );
641
+ }
642
+ });
643
+ }
644
+ /**
645
+ * Public method to unpublish video stream to the machine.
646
+ * This unpublishes the video track that was previously sent.
647
+ */
648
+ unpublishVideoStream() {
649
+ return __async(this, null, function* () {
650
+ var _a;
651
+ try {
652
+ yield (_a = this.machineClient) == null ? void 0 : _a.unpublishVideoTrack();
653
+ } catch (error) {
654
+ console.error("[Reactor] Failed to unpublish video:", error);
655
+ this.createError(
656
+ "VIDEO_UNPUBLISH_FAILED",
657
+ `Failed to unpublish video: ${error}`,
658
+ "gpu",
659
+ true
660
+ );
661
+ }
662
+ });
663
+ }
486
664
  /**
487
665
  * Connects to the machine via LiveKit and waits for the gpu machine to be ready.
488
666
  * Once the machine is ready, the Reactor will establish the LiveKit connection.
@@ -557,7 +735,8 @@ var Reactor = class {
557
735
  jwtToken: this.jwtToken,
558
736
  insecureApiKey: this.insecureApiKey,
559
737
  modelName: this.modelName,
560
- modelVersion: this.modelVersion
738
+ modelVersion: this.modelVersion,
739
+ queueing: this.queueing
561
740
  });
562
741
  this.coordinatorClient.on(
563
742
  "gpu-machine-assigned",
@@ -835,34 +1014,98 @@ function ReactorProvider(_a) {
835
1014
  ]);
836
1015
  const storeRef = (0, import_react3.useRef)(void 0);
837
1016
  const firstRender = (0, import_react3.useRef)(true);
838
- function attemptAutoConnect() {
839
- var _a2, _b2;
840
- const status = (_a2 = storeRef.current) == null ? void 0 : _a2.getState().status;
841
- if (autoConnect && status === "disconnected") {
842
- console.debug("[ReactorProvider] Starting autoconnect...");
843
- (_b2 = storeRef.current) == null ? void 0 : _b2.getState().connect().then(() => {
844
- console.debug("[ReactorProvider] Autoconnect successful");
845
- }).catch((error) => {
846
- console.error("[ReactorProvider] Failed to autoconnect:", error);
847
- });
848
- }
849
- }
1017
+ const [_storeVersion, setStoreVersion] = (0, import_react3.useState)(0);
850
1018
  if (storeRef.current === void 0) {
851
1019
  console.debug("[ReactorProvider] Creating new reactor store");
852
1020
  storeRef.current = createReactorStore(initReactorStore(props));
853
1021
  console.debug("[ReactorProvider] Reactor store created successfully");
854
- attemptAutoConnect();
855
1022
  }
1023
+ const {
1024
+ coordinatorUrl,
1025
+ modelName,
1026
+ jwtToken,
1027
+ insecureApiKey,
1028
+ directConnection,
1029
+ queueing
1030
+ } = props;
856
1031
  (0, import_react3.useEffect)(() => {
857
1032
  if (firstRender.current) {
858
1033
  firstRender.current = false;
859
- return;
1034
+ const current2 = storeRef.current;
1035
+ if (autoConnect && current2.getState().status === "disconnected") {
1036
+ console.debug(
1037
+ "[ReactorProvider] Starting autoconnect in first render..."
1038
+ );
1039
+ current2.getState().connect().then(() => {
1040
+ console.debug(
1041
+ "[ReactorProvider] Autoconnect successful in first render"
1042
+ );
1043
+ }).catch((error) => {
1044
+ console.error(
1045
+ "[ReactorProvider] Failed to autoconnect in first render:",
1046
+ error
1047
+ );
1048
+ });
1049
+ }
1050
+ return () => {
1051
+ console.debug(
1052
+ "[ReactorProvider] Disconnecting in cleanup for first render"
1053
+ );
1054
+ current2.getState().disconnect().then(() => {
1055
+ console.debug(
1056
+ "[ReactorProvider] Disconnect completed successfully in cleanup for first render"
1057
+ );
1058
+ }).catch((error) => {
1059
+ console.error(
1060
+ "[ReactorProvider] Failed to disconnect in cleanup for first render:",
1061
+ error
1062
+ );
1063
+ });
1064
+ };
860
1065
  }
861
1066
  console.debug("[ReactorProvider] Updating reactor store");
862
- storeRef.current = createReactorStore(initReactorStore(props));
863
- console.debug("[ReactorProvider] Reactor store updated successfully");
864
- attemptAutoConnect();
865
- }, [props, autoConnect]);
1067
+ storeRef.current = createReactorStore(
1068
+ initReactorStore({
1069
+ coordinatorUrl,
1070
+ modelName,
1071
+ jwtToken,
1072
+ insecureApiKey,
1073
+ directConnection,
1074
+ queueing
1075
+ })
1076
+ );
1077
+ const current = storeRef.current;
1078
+ setStoreVersion((v) => v + 1);
1079
+ console.debug(
1080
+ "[ReactorProvider] Reactor store updated successfully, and increased version"
1081
+ );
1082
+ if (autoConnect && current.getState().status === "disconnected") {
1083
+ console.debug("[ReactorProvider] Starting autoconnect...");
1084
+ current.getState().connect().then(() => {
1085
+ console.debug("[ReactorProvider] Autoconnect successful");
1086
+ }).catch((error) => {
1087
+ console.error("[ReactorProvider] Failed to autoconnect:", error);
1088
+ });
1089
+ }
1090
+ return () => {
1091
+ console.debug("[ReactorProvider] Disconnecting in cleanup");
1092
+ current.getState().disconnect().then(() => {
1093
+ console.debug(
1094
+ "[ReactorProvider] Disconnect completed successfully in cleanup"
1095
+ );
1096
+ }).catch((error) => {
1097
+ console.error("[ReactorProvider] Failed to disconnect:", error);
1098
+ });
1099
+ };
1100
+ }, [
1101
+ coordinatorUrl,
1102
+ modelName,
1103
+ jwtToken,
1104
+ insecureApiKey,
1105
+ directConnection,
1106
+ queueing,
1107
+ autoConnect
1108
+ ]);
866
1109
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReactorContext.Provider, { value: storeRef.current, children });
867
1110
  }
868
1111
  function useReactorStore(selector) {