@reactor-team/js-sdk 2.0.0 → 2.0.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.mjs CHANGED
@@ -50,6 +50,13 @@ var __async = (__this, __arguments, generator) => {
50
50
  });
51
51
  };
52
52
 
53
+ // src/types.ts
54
+ var ConflictError = class extends Error {
55
+ constructor(message) {
56
+ super(message);
57
+ }
58
+ };
59
+
53
60
  // src/core/CoordinatorClient.ts
54
61
  var INITIAL_BACKOFF_MS = 500;
55
62
  var MAX_BACKOFF_MS = 3e4;
@@ -68,6 +75,11 @@ var CoordinatorClient = class {
68
75
  Authorization: `Bearer ${this.jwtToken}`
69
76
  };
70
77
  }
78
+ getIceServers() {
79
+ return __async(this, null, function* () {
80
+ return [{ urls: "stun:stun.l.google.com:19302" }];
81
+ });
82
+ }
71
83
  /**
72
84
  * Creates a new session with the coordinator.
73
85
  * Expects a 200 response and stores the session ID.
@@ -304,6 +316,27 @@ var LocalCoordinatorClient = class extends CoordinatorClient {
304
316
  });
305
317
  this.localBaseUrl = baseUrl;
306
318
  }
319
+ /**
320
+ * Gets ICE servers from the local HTTP runtime.
321
+ * @returns The ICE server configuration
322
+ */
323
+ getIceServers() {
324
+ return __async(this, null, function* () {
325
+ console.debug("[LocalCoordinatorClient] Fetching ICE servers...");
326
+ const response = yield fetch(`${this.localBaseUrl}/ice_servers`, {
327
+ method: "GET"
328
+ });
329
+ if (!response.ok) {
330
+ throw new Error("Failed to get ICE servers from local coordinator.");
331
+ }
332
+ const data = yield response.json();
333
+ console.debug(
334
+ "[LocalCoordinatorClient] Received ICE servers:",
335
+ data.ice_servers
336
+ );
337
+ return data.ice_servers;
338
+ });
339
+ }
307
340
  /**
308
341
  * Creates a local session by posting to /start_session.
309
342
  * @returns always "local"
@@ -344,6 +377,9 @@ var LocalCoordinatorClient = class extends CoordinatorClient {
344
377
  body: JSON.stringify(sdpBody)
345
378
  });
346
379
  if (!response.ok) {
380
+ if (response.status === 409) {
381
+ throw new ConflictError("Connection superseded by newer request");
382
+ }
347
383
  throw new Error("Failed to get SDP answer from local coordinator.");
348
384
  }
349
385
  const sdpAnswer = yield response.json();
@@ -362,15 +398,12 @@ var LocalCoordinatorClient = class extends CoordinatorClient {
362
398
  };
363
399
 
364
400
  // src/utils/webrtc.ts
365
- var DEFAULT_ICE_SERVERS = [
366
- { urls: "stun:stun.l.google.com:19302" },
367
- { urls: "stun:stun1.l.google.com:19302" }
368
- ];
369
401
  var DEFAULT_DATA_CHANNEL_LABEL = "data";
402
+ var FORCE_RELAY_MODE = false;
370
403
  function createPeerConnection(config) {
371
- var _a;
372
404
  return new RTCPeerConnection({
373
- iceServers: (_a = config == null ? void 0 : config.iceServers) != null ? _a : DEFAULT_ICE_SERVERS
405
+ iceServers: config.iceServers,
406
+ iceTransportPolicy: FORCE_RELAY_MODE ? "relay" : "all"
374
407
  });
375
408
  }
376
409
  function createDataChannel(pc, label) {
@@ -451,7 +484,7 @@ var GPUMachineClient = class {
451
484
  constructor(config) {
452
485
  this.eventListeners = /* @__PURE__ */ new Map();
453
486
  this.status = "disconnected";
454
- this.config = config != null ? config : {};
487
+ this.config = config;
455
488
  }
456
489
  // ─────────────────────────────────────────────────────────────────────────────
457
490
  // Event Emitter API
@@ -848,9 +881,14 @@ var Reactor = class {
848
881
  console.warn("[Reactor] No active session to reconnect to.");
849
882
  return;
850
883
  }
884
+ if (this.status === "ready") {
885
+ console.warn("[Reactor] Already connected, no need to reconnect.");
886
+ return;
887
+ }
851
888
  this.setStatus("connecting");
852
889
  if (!this.machineClient) {
853
- this.machineClient = new GPUMachineClient();
890
+ const iceServers = yield this.coordinatorClient.getIceServers();
891
+ this.machineClient = new GPUMachineClient({ iceServers });
854
892
  this.setupMachineClientHandlers();
855
893
  }
856
894
  const sdpOffer = yield this.machineClient.createOffer();
@@ -862,8 +900,12 @@ var Reactor = class {
862
900
  yield this.machineClient.connect(sdpAnswer);
863
901
  this.setStatus("ready");
864
902
  } catch (error) {
903
+ let recoverable = false;
904
+ if (error instanceof ConflictError) {
905
+ recoverable = true;
906
+ }
865
907
  console.error("[Reactor] Failed to reconnect:", error);
866
- this.disconnect(false);
908
+ this.disconnect(recoverable);
867
909
  this.createError(
868
910
  "RECONNECTION_FAILED",
869
911
  `Failed to reconnect: ${error}`,
@@ -898,7 +940,8 @@ var Reactor = class {
898
940
  // Safe: validated on line 186-188
899
941
  model: this.model
900
942
  });
901
- this.machineClient = new GPUMachineClient();
943
+ const iceServers = yield this.coordinatorClient.getIceServers();
944
+ this.machineClient = new GPUMachineClient({ iceServers });
902
945
  this.setupMachineClientHandlers();
903
946
  const sdpOffer = yield this.machineClient.createOffer();
904
947
  const sessionId = yield this.coordinatorClient.createSession(sdpOffer);
@@ -2087,13 +2130,36 @@ function WebcamStream({
2087
2130
  }
2088
2131
  );
2089
2132
  }
2133
+
2134
+ // src/utils/tokens.ts
2135
+ function fetchInsecureJwtToken(_0) {
2136
+ return __async(this, arguments, function* (apiKey, coordinatorUrl = PROD_COORDINATOR_URL) {
2137
+ console.warn(
2138
+ "[Reactor] \u26A0\uFE0F SECURITY WARNING: fetchInsecureJwtToken() exposes your API key in client-side code. This should ONLY be used for local development or testing. In production, fetch tokens from your server instead."
2139
+ );
2140
+ const response = yield fetch(`${coordinatorUrl}/tokens`, {
2141
+ method: "GET",
2142
+ headers: {
2143
+ "X-API-Key": apiKey
2144
+ }
2145
+ });
2146
+ if (!response.ok) {
2147
+ const error = yield response.text();
2148
+ throw new Error(`Failed to create token: ${response.status} ${error}`);
2149
+ }
2150
+ const { jwt } = yield response.json();
2151
+ return jwt;
2152
+ });
2153
+ }
2090
2154
  export {
2155
+ ConflictError,
2091
2156
  PROD_COORDINATOR_URL,
2092
2157
  Reactor,
2093
2158
  ReactorController,
2094
2159
  ReactorProvider,
2095
2160
  ReactorView,
2096
2161
  WebcamStream,
2162
+ fetchInsecureJwtToken,
2097
2163
  useReactor,
2098
2164
  useReactorMessage,
2099
2165
  useReactorStore