@livedesk/hub 0.1.44 → 0.1.46

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livedesk/hub",
3
- "version": "0.1.44",
3
+ "version": "0.1.46",
4
4
  "description": "LiveDesk local Hub API and browser frame bridge",
5
5
  "type": "module",
6
6
  "main": "src/server.js",
@@ -107,9 +107,10 @@ export function createHubConsoleRelay(options = {}) {
107
107
  let lastError = relayUrl instanceof URL ? '' : relayUrl === '' ? '' : 'invalid-relay-url';
108
108
  let droppedBinaryMessages = 0;
109
109
 
110
- const sendRelay = payload => {
110
+ const sendRelay = (payload, bypassBackpressure = false) => {
111
111
  if (!relaySocket || relaySocket.readyState !== WebSocketImpl.OPEN) return false;
112
- if (Number(relaySocket.bufferedAmount || 0) > MAX_RELAY_BUFFERED_BYTES) return false;
112
+ if (!bypassBackpressure
113
+ && Number(relaySocket.bufferedAmount || 0) > MAX_RELAY_BUFFERED_BYTES) return false;
113
114
  relaySocket.send(typeof payload === 'string' || Buffer.isBuffer(payload) ? payload : JSON.stringify(payload));
114
115
  return true;
115
116
  };
@@ -232,6 +233,7 @@ export function createHubConsoleRelay(options = {}) {
232
233
  if (!relaySocket || relaySocket.readyState !== WebSocketImpl.OPEN
233
234
  || Number(relaySocket.bufferedAmount || 0) > MAX_RELAY_BUFFERED_BYTES) {
234
235
  droppedBinaryMessages += 1;
236
+ closeLocalSocket(key, 1013, 'console-relay-backpressure');
235
237
  return;
236
238
  }
237
239
  const header = Buffer.from(`B${consoleId}${channelId}`, 'ascii');
@@ -246,7 +248,14 @@ export function createHubConsoleRelay(options = {}) {
246
248
  });
247
249
  socket.once('close', (code, reason) => {
248
250
  if (localSockets.get(key) === socket) localSockets.delete(key);
249
- sendRelay({ type: 'ws-closed', consoleId, channelId, code, reason: String(reason || '').slice(0, 120) });
251
+ const closeReason = String(reason || '').slice(0, 120);
252
+ // Do not silently punch a hole in an H.264 GOP. A tiny terminal control
253
+ // message is allowed behind the already-bounded video backlog so the
254
+ // browser replaces only this logical lane and starts again on a key.
255
+ sendRelay(
256
+ { type: 'ws-closed', consoleId, channelId, code, reason: closeReason },
257
+ closeReason === 'console-relay-backpressure'
258
+ );
250
259
  });
251
260
  socket.once('error', error => {
252
261
  sendError(consoleId, channelId, error instanceof Error ? error.message : error, 'ws-error');
package/src/server.js CHANGED
@@ -513,11 +513,19 @@ async function getRuntimeAccessToken() {
513
513
  const accessToken = String(runtimeAccessToken || '').trim();
514
514
  const expiresSoon = runtimeAccessTokenExpiresAt > 0
515
515
  && runtimeAccessTokenExpiresAt <= Date.now() + HUB_ACCESS_TOKEN_REFRESH_SKEW_MS;
516
- if (accessToken && !expiresSoon) {
517
- return accessToken;
518
- }
519
-
520
- const refreshToken = String(runtimeRefreshToken || '').trim();
516
+ if (accessToken && !expiresSoon) {
517
+ return accessToken;
518
+ }
519
+
520
+ // The packaged Electron main process owns the encrypted refresh token and
521
+ // proactively sends each rotation to this child runtime. A second refresh
522
+ // owner here could consume the one-time token first and make the desktop
523
+ // shell appear signed out after sleep.
524
+ if (desktopMainAuthConfig) {
525
+ return accessToken;
526
+ }
527
+
528
+ const refreshToken = String(runtimeRefreshToken || '').trim();
521
529
  if (!refreshToken) {
522
530
  return accessToken;
523
531
  }
@@ -621,9 +629,9 @@ async function queryAuthoritativeRuntimeRole() {
621
629
  },
622
630
  SUPABASE_AUTH_TIMEOUT_MS
623
631
  );
624
- if (response.status === 401 || response.status === 403) {
625
- clearRuntimeSession();
626
- throw new Error(`hub-role-query-not-authenticated:${response.status}`);
632
+ if (response.status === 401 || response.status === 403) {
633
+ if (!desktopMainAuthConfig) clearRuntimeSession();
634
+ throw new Error(`hub-role-query-not-authenticated:${response.status}`);
627
635
  }
628
636
  if (!response.ok) {
629
637
  throw new Error(`hub-role-query-failed:${response.status}`);
@@ -4601,9 +4609,9 @@ async function callHubSupabaseRpc(name, payload, accessToken) {
4601
4609
  );
4602
4610
  const data = await response.json().catch(() => null);
4603
4611
  const result = Array.isArray(data) ? data[0] : data;
4604
- if (response.status === 401 || response.status === 403) {
4605
- clearRuntimeSession();
4606
- throw new Error(`${name}-not-authenticated:${response.status}`);
4612
+ if (response.status === 401 || response.status === 403) {
4613
+ if (!desktopMainAuthConfig) clearRuntimeSession();
4614
+ throw new Error(`${name}-not-authenticated:${response.status}`);
4607
4615
  }
4608
4616
  if (!response.ok) {
4609
4617
  throw new Error(`${name}-failed:${response.status}`);