@adhdev/session-host-core 1.0.82-rc.26 → 1.0.82-rc.29

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
@@ -16,6 +16,7 @@ var SESSION_HOST_SUPPORTED_REQUEST_TYPES = [
16
16
  "send_input",
17
17
  "resize_session",
18
18
  "stop_session",
19
+ "stop_mesh_writer",
19
20
  "delete_session",
20
21
  "resume_session",
21
22
  "acquire_write",
@@ -746,6 +747,19 @@ function disconnectSessionHostClients(endpoint, error) {
746
747
  }
747
748
  }
748
749
  }
750
+ function findConnectedSessionHostClient(endpoint) {
751
+ for (const ref of activeClientRefs) {
752
+ const client = ref.deref();
753
+ if (!client) {
754
+ activeClientRefs.delete(ref);
755
+ continue;
756
+ }
757
+ if ((!endpoint || client.endpoint.path === endpoint.path) && client.isConnected()) {
758
+ return client;
759
+ }
760
+ }
761
+ return null;
762
+ }
749
763
  var SessionHostClient = class {
750
764
  endpoint;
751
765
  socket = null;
@@ -763,11 +777,17 @@ var SessionHostClient = class {
763
777
  if (typeof options.requestTimeoutMs === "number" && options.requestTimeoutMs > 0) {
764
778
  this.requestTimeoutMs = options.requestTimeoutMs;
765
779
  }
766
- activeClientRefs.add(new WeakRef(this));
780
+ if (options.registerAsShared !== false) activeClientRefs.add(new WeakRef(this));
767
781
  }
768
782
  static disconnectClients(endpoint, error) {
769
783
  disconnectSessionHostClients(endpoint, error);
770
784
  }
785
+ static findConnectedClient(endpoint) {
786
+ return findConnectedSessionHostClient(endpoint);
787
+ }
788
+ isConnected() {
789
+ return Boolean(this.socket && !this.socket.destroyed && establishedSockets.has(this.socket));
790
+ }
771
791
  /**
772
792
  * Fires when an established connection to the session host goes away.
773
793
  *
@@ -829,18 +849,32 @@ var SessionHostClient = class {
829
849
  }
830
850
  }
831
851
  }
832
- sendRawRequestOnSocket(socket, request) {
852
+ sendRawRequestOnSocket(socket, request, timeoutMs = this.requestTimeoutMs) {
833
853
  const requestId = generateUUID();
834
854
  const envelope = {
835
855
  kind: "request",
836
856
  requestId,
837
857
  request
838
858
  };
839
- const timeoutMs = this.requestTimeoutMs > 0 ? this.requestTimeoutMs : 3e4;
840
859
  const timeoutLabel = timeoutMs % 1e3 === 0 ? `${timeoutMs / 1e3}s` : `${timeoutMs}ms`;
860
+ const startedAt = Date.now();
861
+ const startedMonotonicAt = performance.now();
841
862
  return new Promise((resolve3, reject) => {
842
863
  const timeout = setTimeout(() => {
843
- const timeoutError = new Error(`Session host request timed out after ${timeoutLabel} (${request.type})`);
864
+ const actualElapsedMs = Math.max(0, Math.round(performance.now() - startedMonotonicAt));
865
+ const wallElapsedMs = Math.max(0, Date.now() - startedAt);
866
+ const observerLagMs = Math.max(0, actualElapsedMs - timeoutMs);
867
+ const timeoutError = Object.assign(
868
+ new Error(`Session host request timed out after ${timeoutLabel} (${request.type})`),
869
+ {
870
+ code: "SESSION_HOST_REQUEST_TIMEOUT",
871
+ requestType: request.type,
872
+ scheduledTimeoutMs: timeoutMs,
873
+ actualElapsedMs,
874
+ wallElapsedMs,
875
+ observerLagMs
876
+ }
877
+ );
844
878
  if (this.socket === socket) {
845
879
  this.disconnect(timeoutError);
846
880
  } else {
@@ -866,7 +900,7 @@ var SessionHostClient = class {
866
900
  socket.write(serializeEnvelope(envelope));
867
901
  });
868
902
  }
869
- async connect() {
903
+ async connect(timeoutMs = this.requestTimeoutMs) {
870
904
  if (this.socket && !this.socket.destroyed) {
871
905
  if (this.connectPromise) await this.connectPromise;
872
906
  return;
@@ -914,11 +948,44 @@ var SessionHostClient = class {
914
948
  });
915
949
  const connectTask = (async () => {
916
950
  await new Promise((resolve3, reject) => {
917
- socket.once("connect", () => {
951
+ const cleanup = () => {
952
+ clearTimeout(timer);
953
+ socket.off("connect", connected);
954
+ socket.off("error", failed);
955
+ socket.off("close", closed);
956
+ };
957
+ const connected = () => {
958
+ cleanup();
918
959
  establishedSockets.add(socket);
919
960
  resolve3();
920
- });
921
- socket.once("error", reject);
961
+ };
962
+ const failed = (error) => {
963
+ cleanup();
964
+ reject(error);
965
+ };
966
+ const closed = () => failed(new Error("Session host connection closed before connect"));
967
+ const connectStartedAt = Date.now();
968
+ const connectStartedMonotonicAt = performance.now();
969
+ const timer = setTimeout(() => {
970
+ const actualElapsedMs = Math.max(0, Math.round(performance.now() - connectStartedMonotonicAt));
971
+ const wallElapsedMs = Math.max(0, Date.now() - connectStartedAt);
972
+ const observerLagMs = Math.max(0, actualElapsedMs - timeoutMs);
973
+ const error = Object.assign(
974
+ new Error(`Session host connect timed out after ${timeoutMs}ms`),
975
+ {
976
+ code: "SESSION_HOST_CONNECT_TIMEOUT",
977
+ scheduledTimeoutMs: timeoutMs,
978
+ actualElapsedMs,
979
+ wallElapsedMs,
980
+ observerLagMs
981
+ }
982
+ );
983
+ failed(error);
984
+ if (this.socket === socket) this.disconnect(error);
985
+ }, timeoutMs);
986
+ socket.once("connect", connected);
987
+ socket.once("error", failed);
988
+ socket.once("close", closed);
922
989
  });
923
990
  if (this.attachedSessions.size > 0 && this.socket === socket && !socket.destroyed) {
924
991
  for (const [sessionId, sub] of [...this.attachedSessions.entries()]) {
@@ -948,11 +1015,46 @@ var SessionHostClient = class {
948
1015
  this.eventListeners.delete(listener);
949
1016
  };
950
1017
  }
951
- async request(request) {
952
- await this.connect();
1018
+ async request(request, options = {}) {
1019
+ const timeoutMs = Math.max(1, options.timeoutMs ?? this.requestTimeoutMs);
1020
+ const deadline = Date.now() + timeoutMs;
1021
+ const alreadyConnected = this.isConnected() && !this.connectPromise;
1022
+ if (options.timeoutMs !== void 0) {
1023
+ let timer;
1024
+ const raceStartedAt = Date.now();
1025
+ const raceStartedMonotonicAt = performance.now();
1026
+ try {
1027
+ await Promise.race([
1028
+ this.connect(timeoutMs),
1029
+ new Promise((_resolve, reject) => {
1030
+ timer = setTimeout(() => {
1031
+ const actualElapsedMs = Math.max(0, Math.round(performance.now() - raceStartedMonotonicAt));
1032
+ const wallElapsedMs = Math.max(0, Date.now() - raceStartedAt);
1033
+ const observerLagMs = Math.max(0, actualElapsedMs - timeoutMs);
1034
+ const error = Object.assign(
1035
+ new Error(`Session host connect timed out after ${timeoutMs}ms`),
1036
+ {
1037
+ code: "SESSION_HOST_CONNECT_TIMEOUT",
1038
+ scheduledTimeoutMs: timeoutMs,
1039
+ actualElapsedMs,
1040
+ wallElapsedMs,
1041
+ observerLagMs
1042
+ }
1043
+ );
1044
+ reject(error);
1045
+ this.disconnect(error);
1046
+ }, timeoutMs);
1047
+ })
1048
+ ]);
1049
+ } finally {
1050
+ if (timer) clearTimeout(timer);
1051
+ }
1052
+ } else {
1053
+ await this.connect(timeoutMs);
1054
+ }
953
1055
  const requestSocket = this.socket;
954
1056
  if (!requestSocket) throw new Error("Session host socket unavailable");
955
- const response = await this.sendRawRequestOnSocket(requestSocket, request);
1057
+ const response = await this.sendRawRequestOnSocket(requestSocket, request, options.timeoutMs === void 0 || alreadyConnected ? timeoutMs : Math.max(1, deadline - Date.now()));
956
1058
  if (response.success) {
957
1059
  if (request.type === "attach_session") {
958
1060
  this.attachedSessions.set(request.payload.sessionId, {
@@ -1212,6 +1314,7 @@ export {
1212
1314
  createSessionHostControlPlane,
1213
1315
  disconnectSessionHostClients,
1214
1316
  ensureNodePtySpawnHelperPermissions,
1317
+ findConnectedSessionHostClient,
1215
1318
  formatRuntimeOwner,
1216
1319
  getDefaultSessionHostEndpoint,
1217
1320
  getSessionHostRecoveryLabel,