replicas-computer 0.3.16 → 0.3.17

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.js CHANGED
@@ -2937,7 +2937,7 @@ async function computerStatusCommand() {
2937
2937
  }
2938
2938
 
2939
2939
  // src/version.ts
2940
- var COMPUTER_USE_VERSION = "0.3.16";
2940
+ var COMPUTER_USE_VERSION = "0.3.17";
2941
2941
  var COMPUTER_USE_PROTOCOL_VERSION = 1;
2942
2942
 
2943
2943
  // src/program.ts
@@ -49,6 +49,7 @@ var DESKTOP_WEBRTC_FAILURE_REASONS = {
49
49
  full_chroma_undecodable: "WebRTC could not decode H.264 High 4:4:4"
50
50
  };
51
51
  var DESKTOP_QUALITY_MESSAGE = "replicas-desktop-quality";
52
+ var DESKTOP_CONNECTION_MESSAGE = "replicas-desktop-connection";
52
53
 
53
54
  // src/input-validation.ts
54
55
  var finite = (value) => typeof value === "number" && Number.isFinite(value);
@@ -102,6 +103,7 @@ var webrtcFirstFrameMs = 3e3;
102
103
  var webrtcRetryFirstMs = 15e3;
103
104
  var webrtcRetryMaxMs = 3e5;
104
105
  var webrtcRetryTimeoutMs = 1e4;
106
+ var hiddenDisconnectMs = 6e4;
105
107
  var webrtcSkipsAfterFailure = 3;
106
108
  var pointerIntervalMs = 4;
107
109
  var qualityWindowMs = 3e4;
@@ -113,8 +115,10 @@ try {
113
115
  } catch {
114
116
  }
115
117
  var socket;
116
- var standby = innerWidth === 0 || innerHeight === 0;
118
+ var intersecting = innerWidth > 0 && innerHeight > 0;
119
+ var standby = !intersecting;
117
120
  var standbyTimer;
121
+ var hiddenTimer;
118
122
  var config;
119
123
  var transport;
120
124
  var transportReason = "";
@@ -171,6 +175,24 @@ var restartQuality = () => {
171
175
  qualitySequence = 0;
172
176
  presentedAt = 0;
173
177
  };
178
+ var newConnectionTotals = () => ({
179
+ sampledAt: 0,
180
+ presented: 0,
181
+ shownMs: 0,
182
+ frames: 0,
183
+ samples: 0,
184
+ rung: 0,
185
+ worstRung: 0,
186
+ cpu: 0,
187
+ rtt: 0,
188
+ rttSamples: 0,
189
+ worstRtt: 0,
190
+ loss: 0,
191
+ lossSamples: 0,
192
+ longestGapMs: 0,
193
+ reportedAt: 0
194
+ });
195
+ var connectionTotals = newConnectionTotals();
174
196
  var state = {};
175
197
  globalThis.__replicasDesktop = state;
176
198
  var viewerEvents = [];
@@ -257,6 +279,31 @@ var announceTransport = () => {
257
279
  if (window.parent === window) return;
258
280
  window.parent.postMessage({ type: DESKTOP_TRANSPORT_MESSAGE, transport: transport ?? "connecting", reason: transportReason, path: route.path, codec: route.codec, failure, recovery }, "*");
259
281
  };
282
+ var average = (sum, count) => count ? Math.round(sum * 10 / count) / 10 : void 0;
283
+ var reportConnection = (ended) => {
284
+ if (window.parent === window || !config) return;
285
+ const totals = connectionTotals;
286
+ totals.reportedAt = performance.now();
287
+ window.parent.postMessage({
288
+ type: DESKTOP_CONNECTION_MESSAGE,
289
+ serverVersion: config.serverVersion ?? "unknown",
290
+ firstFrameMs: totals.firstFrameAt === void 0 ? void 0 : Math.round(totals.firstFrameAt - connectionStartedAt),
291
+ shownMs: Math.round(totals.shownMs),
292
+ displayedFps: totals.shownMs >= 1e3 ? Math.round(totals.frames * 1e4 / totals.shownMs) / 10 : void 0,
293
+ inputRttMs: average(totals.rtt, totals.rttSamples),
294
+ worstInputRttMs: totals.rttSamples ? Math.round(totals.worstRtt) : void 0,
295
+ qualityRung: average(totals.rung, totals.samples),
296
+ worstQualityRung: totals.samples ? totals.worstRung : void 0,
297
+ longestFrameGapMs: totals.samples ? Math.round(totals.longestGapMs) : void 0,
298
+ lossPercent: average(totals.loss, totals.lossSamples),
299
+ cpuLoadPercent: average(totals.cpu, totals.samples),
300
+ ended
301
+ }, "*");
302
+ };
303
+ var endConnection = (ended) => {
304
+ if (ended || connectionTotals.sampledAt) reportConnection(ended);
305
+ connectionTotals = newConnectionTotals();
306
+ };
260
307
  var closePeer = (preserveDeadline = false) => {
261
308
  cancelRetry();
262
309
  if (!preserveDeadline) {
@@ -310,9 +357,11 @@ var configureVideo = async (next, preferHardware = true) => {
310
357
  if (configuration !== videoConfiguration) return;
311
358
  const selectedConfig = support.supported && support.config ? support.config : decoderConfig(next);
312
359
  activeVideoConfig = selectedConfig;
360
+ const totals = connectionTotals;
313
361
  const decoder = new VideoDecoder({
314
362
  output: (frame) => {
315
363
  socketDecodedFrames += 1;
364
+ totals.firstFrameAt ??= performance.now();
316
365
  const writer = videoWriter;
317
366
  if (!writer || (writer.desiredSize ?? 1) <= 0) {
318
367
  frame.close();
@@ -776,6 +825,7 @@ var handleServerText = (data, viaSocket) => {
776
825
  };
777
826
  var connect = () => {
778
827
  if (document.visibilityState !== "visible") return;
828
+ endConnection();
779
829
  clearTimeout(reconnectTimer);
780
830
  closePeer();
781
831
  transport = void 0;
@@ -818,9 +868,12 @@ var connect = () => {
818
868
  });
819
869
  nextSocket.addEventListener("close", () => {
820
870
  if (socket !== nextSocket) return;
871
+ endConnection();
821
872
  closePeer();
822
- transport = void 0;
823
- announceTransport();
873
+ if (transport) {
874
+ transport = void 0;
875
+ announceTransport();
876
+ }
824
877
  reconnectAttempt += 1;
825
878
  const delay = Math.min(8e3, 250 * 2 ** Math.min(reconnectAttempt, 5)) * (0.85 + Math.random() * 0.3);
826
879
  note("socket closed", `reconnect in ${Math.round(delay)} ms`);
@@ -855,6 +908,8 @@ var observeFrame = () => {
855
908
  lastPresentedCount = metadata.presentedFrames;
856
909
  lastFrameAt = performance.now();
857
910
  if (presentedAt && qualityWindow) qualityWindow.longestGapMs = Math.max(qualityWindow.longestGapMs, lastFrameAt - Math.max(presentedAt, qualityWindow.at));
911
+ const { shownSince } = connectionTotals;
912
+ if (presentedAt && shownSince !== void 0) connectionTotals.longestGapMs = Math.max(connectionTotals.longestGapMs, lastFrameAt - Math.max(presentedAt, shownSince));
858
913
  presentedAt = lastFrameAt;
859
914
  status.classList.remove("visible");
860
915
  observeFrame();
@@ -909,22 +964,32 @@ var setStandby = (next) => {
909
964
  sendSocket({ type: "standby", standby });
910
965
  if (!standby && !audioPrompt.hidden) unlockAudio();
911
966
  };
912
- new IntersectionObserver(([entry]) => {
913
- if (entry.isIntersecting) setStandby(false);
967
+ var syncStandby = () => {
968
+ if (intersecting && document.visibilityState === "visible") setStandby(false);
914
969
  else if (!standby) {
915
970
  clearTimeout(standbyTimer);
916
971
  standbyTimer = setTimeout(() => setStandby(true), 1e3);
917
972
  }
973
+ };
974
+ new IntersectionObserver(([entry]) => {
975
+ intersecting = entry.isIntersecting;
976
+ syncStandby();
918
977
  }).observe(document.documentElement);
919
978
  window.addEventListener("online", connect);
920
979
  document.addEventListener("visibilitychange", () => {
980
+ clearTimeout(hiddenTimer);
981
+ syncStandby();
921
982
  if (document.visibilityState === "hidden") {
922
- disconnect();
983
+ hiddenTimer = setTimeout(() => {
984
+ endConnection("hidden");
985
+ disconnect();
986
+ }, hiddenDisconnectMs);
923
987
  return;
924
988
  }
925
- connect();
989
+ if (!socket || socket.readyState >= WebSocket.CLOSING) connect();
926
990
  });
927
991
  window.addEventListener("pagehide", () => {
992
+ endConnection();
928
993
  disconnect();
929
994
  screen.cancelVideoFrameCallback(frameCallback);
930
995
  videoDecoder?.close();
@@ -963,6 +1028,32 @@ var updateDiagnostics = (stats) => {
963
1028
  viewerEvents.length ? `viewer events: ${describeEvents(viewerEvents.slice(-5))}` : ""
964
1029
  ].filter(Boolean).join("\n");
965
1030
  };
1031
+ var sampleConnection = (stats, current, now) => {
1032
+ const totals = connectionTotals;
1033
+ const shown = !standby && document.visibilityState === "visible" && qualityUnseen.samples < 4;
1034
+ if (shown && totals.sampledAt) {
1035
+ totals.shownSince ??= totals.sampledAt;
1036
+ totals.shownMs += now - totals.sampledAt;
1037
+ totals.frames += presentedFrames - totals.presented;
1038
+ totals.samples += 1;
1039
+ totals.rung += current.encoder.rung;
1040
+ totals.worstRung = Math.max(totals.worstRung, current.encoder.rung);
1041
+ totals.cpu += current.cpu.load * 100 / current.cpu.cores;
1042
+ if (stats.rttMs > 0) {
1043
+ totals.rtt += stats.rttMs;
1044
+ totals.rttSamples += 1;
1045
+ totals.worstRtt = Math.max(totals.worstRtt, stats.rttMs);
1046
+ }
1047
+ if (stats.lossPercent !== void 0) {
1048
+ totals.loss += stats.lossPercent;
1049
+ totals.lossSamples += 1;
1050
+ }
1051
+ totals.longestGapMs = Math.max(totals.longestGapMs, now - Math.max(presentedAt, totals.shownSince));
1052
+ } else if (!shown) totals.shownSince = void 0;
1053
+ totals.sampledAt = now;
1054
+ totals.presented = presentedFrames;
1055
+ if (now - totals.reportedAt >= 2e3) reportConnection();
1056
+ };
966
1057
  var reportQuality = (stats) => {
967
1058
  if (window.parent === window || !transport || !config || !telemetry) return;
968
1059
  const now = performance.now();
@@ -970,6 +1061,7 @@ var reportQuality = (stats) => {
970
1061
  const decoded = transport === "webrtc" ? inbound?.framesDecoded ?? 0 : socketDecodedFrames;
971
1062
  qualityUnseen = { decoded, presented: presentedFrames, samples: decoded > qualityUnseen.decoded && presentedFrames === qualityUnseen.presented ? qualityUnseen.samples + 1 : 0 };
972
1063
  if (qualityUnseen.samples >= 4) current.unseen = true;
1064
+ sampleConnection(stats, telemetry, now);
973
1065
  qualitySamples.push({ stats, telemetry });
974
1066
  const windowMs = now - current.at;
975
1067
  if (windowMs < qualityWindowMs) return;
@@ -1048,6 +1140,7 @@ var reportWebRtcStats = async (pc, stats) => {
1048
1140
  };
1049
1141
  if (counters.framesDecoded > (inbound?.framesDecoded ?? 0)) {
1050
1142
  lastFrameAt = now;
1143
+ connectionTotals.firstFrameAt ??= now;
1051
1144
  clearTimeout(webrtcDeadline);
1052
1145
  webrtcDeadline = void 0;
1053
1146
  if (fullChromaFailure) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-computer",
3
- "version": "0.3.16",
3
+ "version": "0.3.17",
4
4
  "description": "Replicas workspace computer-use runtime",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",