livedesk 0.1.605 → 0.1.607

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/electron/main.mjs CHANGED
@@ -6,7 +6,7 @@ import { spawn } from 'node:child_process';
6
6
  import { createRequire } from 'node:module';
7
7
  import { createProductUpdateManager } from './update-manager.mjs';
8
8
  import { createRecurringProductUpdateOwner } from './recurring-update-owner.mjs';
9
- import { createPkceAuthorizationUrl, exchangePkceCode, fetchSupabaseUser, parsePkceCallbackUrl, refreshSupabaseSession } from './oauth-pkce.mjs';
9
+ import { AUTH_REQUEST_TIMEOUT_MS, createPkceAuthorizationUrl, exchangePkceCode, fetchSupabaseUser, parsePkceCallbackUrl, refreshSupabaseSession } from './oauth-pkce.mjs';
10
10
  import { getRuntimeAuthEnvironment, resolveDesktopAuthConfig, resolveDesktopBuildFlavor } from './auth-config.mjs';
11
11
  import { createBoundedDesktopLogger } from './bounded-desktop-log.mjs';
12
12
  import {
@@ -671,46 +671,59 @@ async function prepareRuntimeForUpdateInstall() {
671
671
  throw new Error('desktop-update-install-already-started');
672
672
  }
673
673
  updateInstallSequenceStarted = true;
674
- isQuitting = true;
675
- recurringProductUpdates?.stop();
676
- try {
677
- await stopRuntime();
678
- } catch (error) {
679
- updateInstallSequenceStarted = false;
680
- isQuitting = false;
681
- throw error;
682
- }
674
+ isQuitting = true;
675
+ recurringProductUpdates?.stop();
676
+ try {
677
+ await quiesceDesktopAuthOwner('desktop-update-install');
678
+ await stopRuntime();
679
+ } catch (error) {
680
+ updateInstallSequenceStarted = false;
681
+ isQuitting = false;
682
+ scheduleDesktopAuthRefresh(readEncryptedSession());
683
+ throw error;
684
+ }
683
685
  }
684
686
 
685
687
  async function recoverRuntimeAfterUpdateInstallError(error) {
686
688
  log(`desktop update install launch failed after runtime drain: ${error?.message || error}`);
687
- updateInstallSequenceStarted = false;
688
- isQuitting = false;
689
- if (!runtimeChild) {
689
+ updateInstallSequenceStarted = false;
690
+ isQuitting = false;
691
+ scheduleDesktopAuthRefresh(readEncryptedSession());
692
+ if (!runtimeChild) {
690
693
  startRuntime();
691
694
  const ready = await waitForRuntime();
692
695
  if (ready) mainWindow?.loadURL(LOCAL_RUNTIME_URL);
693
696
  }
694
697
  }
695
698
 
696
- async function syncSessionToRuntime(session) {
697
- const normalized = normalizeSession(session);
698
- if (!normalized) throw new Error('oauth-session-invalid');
699
- const csrfToken = await getRuntimeCsrfToken();
700
- const response = await fetch(`${LOCAL_RUNTIME_URL}api/auth/session`, {
701
- method: 'POST',
702
- headers: {
703
- 'Content-Type': 'application/json',
704
- ...(csrfToken ? { 'X-LiveDesk-CSRF': csrfToken } : {})
705
- },
706
- body: JSON.stringify({
707
- accessToken: normalized.access_token,
708
- refreshToken: normalized.refresh_token,
709
- expiresAt: normalized.expires_at,
710
- userId: normalized.user?.id,
711
- user: { id: normalized.user?.id, email: normalized.user?.email }
712
- })
713
- });
699
+ async function syncSessionToRuntime(session) {
700
+ const normalized = normalizeSession(session);
701
+ if (!normalized) throw new Error('oauth-session-invalid');
702
+ const deadline = Date.now() + AUTH_REQUEST_TIMEOUT_MS + 2_500;
703
+ const csrfToken = await getRuntimeCsrfToken(Math.min(deadline, Date.now() + 2_500));
704
+ const response = await fetchBeforeRuntimeDeadline(
705
+ fetch,
706
+ `${LOCAL_RUNTIME_URL}api/auth/session`,
707
+ {
708
+ method: 'POST',
709
+ headers: {
710
+ 'Content-Type': 'application/json',
711
+ ...(csrfToken ? { 'X-LiveDesk-CSRF': csrfToken } : {})
712
+ },
713
+ body: JSON.stringify({
714
+ accessToken: normalized.access_token,
715
+ refreshToken: normalized.refresh_token,
716
+ expiresAt: normalized.expires_at,
717
+ userId: normalized.user?.id,
718
+ user: { id: normalized.user?.id, email: normalized.user?.email }
719
+ })
720
+ },
721
+ {
722
+ deadline,
723
+ maxDurationMs: Math.min(AUTH_REQUEST_TIMEOUT_MS, remainingRuntimeDeadlineMs(deadline)),
724
+ label: 'runtime-auth-sync-deadline'
725
+ }
726
+ );
714
727
  if (!response.ok) {
715
728
  const payload = await response.json().catch(() => ({}));
716
729
  throw new Error(String(payload?.error || `runtime-session-sync-failed:${response.status}`));
@@ -854,18 +867,29 @@ async function restoreEncryptedSessionOwned({ syncRuntime = true } = {}) {
854
867
  return session;
855
868
  }
856
869
 
857
- async function restoreEncryptedSession(options = {}) {
858
- if (desktopSessionRestorePromise) return desktopSessionRestorePromise;
859
- const operation = restoreEncryptedSessionOwned(options);
870
+ async function restoreEncryptedSession(options = {}) {
871
+ if (desktopSessionRestorePromise) return desktopSessionRestorePromise;
872
+ if (isQuitting) return readEncryptedSession();
873
+ const operation = restoreEncryptedSessionOwned(options);
860
874
  desktopSessionRestorePromise = operation;
861
875
  try {
862
876
  return await operation;
863
877
  } finally {
864
878
  if (desktopSessionRestorePromise === operation) desktopSessionRestorePromise = null;
865
- }
866
- }
867
-
868
- async function signOutDesktopSession() {
879
+ }
880
+ }
881
+
882
+ async function quiesceDesktopAuthOwner(reason = 'application-exit') {
883
+ clearDesktopAuthRefreshTimer();
884
+ while (desktopSessionRestorePromise) {
885
+ const pending = desktopSessionRestorePromise;
886
+ log(`waiting for secure auth owner before ${reason}`);
887
+ await pending;
888
+ if (desktopSessionRestorePromise === pending) break;
889
+ }
890
+ }
891
+
892
+ async function signOutDesktopSession() {
869
893
  desktopSessionEpoch += 1;
870
894
  desktopAuthInvalidationGuard.observeSuccess();
871
895
  clearEncryptedSession();
@@ -1071,19 +1095,21 @@ function registerIpc() {
1071
1095
  if (quitSequenceStarted || updateInstallSequenceStarted) {
1072
1096
  return { ok: false, error: 'application-exit-already-started' };
1073
1097
  }
1074
- quitSequenceStarted = true;
1075
- isQuitting = true;
1076
- try {
1077
- await stopRuntime();
1098
+ quitSequenceStarted = true;
1099
+ isQuitting = true;
1100
+ try {
1101
+ await quiesceDesktopAuthOwner('application-restart');
1102
+ await stopRuntime();
1078
1103
  await desktopClipboardOwner?.close();
1079
1104
  await closeDesktopLog();
1080
1105
  app.relaunch();
1081
1106
  app.exit(0);
1082
1107
  return { ok: true };
1083
- } catch (error) {
1084
- isQuitting = false;
1085
- quitSequenceStarted = false;
1086
- updateTray('Runtime cleanup blocked');
1108
+ } catch (error) {
1109
+ isQuitting = false;
1110
+ quitSequenceStarted = false;
1111
+ scheduleDesktopAuthRefresh(readEncryptedSession());
1112
+ updateTray('Runtime cleanup blocked');
1087
1113
  log(`runtime cleanup blocked application restart: ${error?.message || error}`);
1088
1114
  if (!runtimeChild) {
1089
1115
  startRuntime();
@@ -1277,19 +1303,22 @@ if (!app.requestSingleInstanceLock()) {
1277
1303
  if (quitSequenceStarted) return;
1278
1304
  event.preventDefault();
1279
1305
  recurringProductUpdates?.stop();
1280
- quitSequenceStarted = true;
1281
- isQuitting = true;
1282
- void stopRuntime().then(() => {
1283
- return desktopClipboardOwner?.close();
1306
+ quitSequenceStarted = true;
1307
+ isQuitting = true;
1308
+ void quiesceDesktopAuthOwner('application-quit').then(() => {
1309
+ return stopRuntime();
1310
+ }).then(() => {
1311
+ return desktopClipboardOwner?.close();
1284
1312
  }).then(() => {
1285
1313
  return closeDesktopLog();
1286
1314
  }).then(() => {
1287
1315
  app.quit();
1288
1316
  }).catch(error => {
1289
- isQuitting = false;
1290
- quitSequenceStarted = false;
1291
- updateInstallSequenceStarted = false;
1292
- updateTray('Runtime cleanup blocked');
1317
+ isQuitting = false;
1318
+ quitSequenceStarted = false;
1319
+ updateInstallSequenceStarted = false;
1320
+ scheduleDesktopAuthRefresh(readEncryptedSession());
1321
+ updateTray('Runtime cleanup blocked');
1293
1322
  log(`runtime cleanup blocked application quit: ${error?.message || error}`);
1294
1323
  });
1295
1324
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "livedesk",
3
- "version": "0.1.605",
4
- "livedeskClientVersion": "0.1.260",
3
+ "version": "0.1.607",
4
+ "livedeskClientVersion": "0.1.262",
5
5
  "buildFlavor": "production",
6
6
  "description": "VuvoDesk Hub and client launcher",
7
7
  "type": "module",
@@ -51,10 +51,10 @@
51
51
  "ws": "^8.18.3"
52
52
  },
53
53
  "optionalDependencies": {
54
- "@livedesk/fast-linux-x64": "0.1.457",
55
- "@livedesk/fast-osx-arm64": "0.1.457",
56
- "@livedesk/fast-osx-x64": "0.1.457",
57
- "@livedesk/fast-win-x64": "0.1.457"
54
+ "@livedesk/fast-linux-x64": "0.1.458",
55
+ "@livedesk/fast-osx-arm64": "0.1.458",
56
+ "@livedesk/fast-osx-x64": "0.1.458",
57
+ "@livedesk/fast-win-x64": "0.1.458"
58
58
  },
59
59
  "publishConfig": {
60
60
  "access": "public"
@@ -4,7 +4,7 @@
4
4
  "short_name": "VuvoDesk",
5
5
  "description": "Monitor and control your VuvoDesk computers from your phone.",
6
6
  "lang": "en",
7
- "start_url": "/app?pwa=0.1.605",
7
+ "start_url": "/app?pwa=0.1.607",
8
8
  "scope": "/",
9
9
  "display": "standalone",
10
10
  "orientation": "any",