livedesk 0.1.428 → 0.1.429
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/client/README.md +3 -2
- package/client/bin/livedesk-client.js +4 -1
- package/client/package.json +5 -5
- package/client/src/runtime/client-runtime-server.js +128 -22
- package/package.json +6 -6
- package/web/dist/assets/{index-DGALRYcL.js → index-9sIpFaCy.js} +13 -13
- package/web/dist/index.html +1 -1
package/client/README.md
CHANGED
|
@@ -22,8 +22,9 @@ callback exchange, and saved authentication as one path. The browser only
|
|
|
22
22
|
shows progress; it does not maintain a second session that must be transferred
|
|
23
23
|
back to the Client. LiveDesk then waits for the active Hub published by the
|
|
24
24
|
same account and connects locally. If the Hub is not
|
|
25
|
-
ready yet, the client waits for a Hub-online event and
|
|
26
|
-
|
|
25
|
+
ready yet, the client waits for a Hub-online event and polls the registry at
|
|
26
|
+
most every six seconds as a fallback. Missing a wake event can never leave a
|
|
27
|
+
Client asleep for minutes. If a
|
|
27
28
|
connected Hub is replaced, the launcher also leaves the stale endpoint and
|
|
28
29
|
discovers the newly published Hub without another Google login. Omit the number
|
|
29
30
|
for first-available placement, or pass `1` to `999` to pin this machine to a
|
|
@@ -30,7 +30,10 @@ const DEFAULT_HUB_CLIENT_PORT = 5197;
|
|
|
30
30
|
const DEFAULT_AUTH_CALLBACK_HOST = '127.0.0.1';
|
|
31
31
|
const DEFAULT_AUTH_CALLBACK_PORT = 5179;
|
|
32
32
|
const ENDPOINT_PROBE_TIMEOUT_MS = 900;
|
|
33
|
-
|
|
33
|
+
// Hub-online delivery is an optimization, not a correctness dependency. A
|
|
34
|
+
// browser, proxy, or suspended network can miss the wake event, so registry
|
|
35
|
+
// polling must stay interactive instead of backing off to multi-minute waits.
|
|
36
|
+
const DISCOVERY_RETRY_SCHEDULE_MS = [750, 750, 750, 750, 750, 750, 750, 750, 5000];
|
|
34
37
|
const DISCOVERY_RETRY_JITTER_RATIO = 0.2;
|
|
35
38
|
const HUB_WAKE_QUERY_JITTER_MAX_MS = 2000;
|
|
36
39
|
const EXIT_INVALID_PAIR_TOKEN = 23;
|
package/client/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@livedesk/client",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.188",
|
|
4
4
|
"description": "LiveDesk local remote client",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -41,10 +41,10 @@
|
|
|
41
41
|
"ws": "^8.18.3"
|
|
42
42
|
},
|
|
43
43
|
"optionalDependencies": {
|
|
44
|
-
"@livedesk/fast-linux-x64": "0.1.
|
|
45
|
-
"@livedesk/fast-osx-arm64": "0.1.
|
|
46
|
-
"@livedesk/fast-osx-x64": "0.1.
|
|
47
|
-
"@livedesk/fast-win-x64": "0.1.
|
|
44
|
+
"@livedesk/fast-linux-x64": "0.1.395",
|
|
45
|
+
"@livedesk/fast-osx-arm64": "0.1.395",
|
|
46
|
+
"@livedesk/fast-osx-x64": "0.1.395",
|
|
47
|
+
"@livedesk/fast-win-x64": "0.1.395"
|
|
48
48
|
},
|
|
49
49
|
"publishConfig": {
|
|
50
50
|
"access": "public"
|
|
@@ -34,10 +34,84 @@ const READ_ONLY_CLIENT_API_PATHS = new Set([
|
|
|
34
34
|
'/api/auth/status'
|
|
35
35
|
]);
|
|
36
36
|
|
|
37
|
-
function normalizeString(value, maxLength = 1024) {
|
|
38
|
-
return String(value ?? '').replace(/[\r\n\t]/g, ' ').trim().slice(0, maxLength);
|
|
39
|
-
}
|
|
40
|
-
|
|
37
|
+
function normalizeString(value, maxLength = 1024) {
|
|
38
|
+
return String(value ?? '').replace(/[\r\n\t]/g, ' ').trim().slice(0, maxLength);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function normalizeAccountAvatarUrl(value) {
|
|
42
|
+
const candidate = normalizeString(value, 2048);
|
|
43
|
+
if (!candidate) return '';
|
|
44
|
+
try {
|
|
45
|
+
const url = new URL(candidate);
|
|
46
|
+
return url.protocol === 'https:' || url.protocol === 'http:' ? url.toString() : '';
|
|
47
|
+
} catch {
|
|
48
|
+
return '';
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function readClientAccountProfile(sessionOrUser) {
|
|
53
|
+
const user = sessionOrUser?.user && typeof sessionOrUser.user === 'object'
|
|
54
|
+
? sessionOrUser.user
|
|
55
|
+
: sessionOrUser && typeof sessionOrUser === 'object'
|
|
56
|
+
? sessionOrUser
|
|
57
|
+
: {};
|
|
58
|
+
const metadata = user.user_metadata && typeof user.user_metadata === 'object'
|
|
59
|
+
? user.user_metadata
|
|
60
|
+
: {};
|
|
61
|
+
const email = normalizeString(user.email, 320);
|
|
62
|
+
const name = normalizeString(
|
|
63
|
+
metadata.full_name
|
|
64
|
+
|| metadata.name
|
|
65
|
+
|| metadata.preferred_username
|
|
66
|
+
|| user.name
|
|
67
|
+
|| email
|
|
68
|
+
|| user.id,
|
|
69
|
+
160
|
|
70
|
+
);
|
|
71
|
+
const avatarUrl = normalizeAccountAvatarUrl(
|
|
72
|
+
metadata.avatar_url
|
|
73
|
+
|| metadata.picture
|
|
74
|
+
|| user.avatar_url
|
|
75
|
+
|| user.picture
|
|
76
|
+
);
|
|
77
|
+
return { email, name, avatarUrl };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function mergeClientAccountProfile(session, sourceUser) {
|
|
81
|
+
const profile = readClientAccountProfile(sourceUser);
|
|
82
|
+
const userMetadata = {
|
|
83
|
+
...(profile.name ? { full_name: profile.name } : {}),
|
|
84
|
+
...(profile.avatarUrl ? { avatar_url: profile.avatarUrl } : {})
|
|
85
|
+
};
|
|
86
|
+
return {
|
|
87
|
+
...session,
|
|
88
|
+
user: {
|
|
89
|
+
...(session?.user || {}),
|
|
90
|
+
...(Object.keys(userMetadata).length > 0 ? { user_metadata: userMetadata } : {})
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function hydrateClientAccountProfile(session) {
|
|
96
|
+
const accessToken = normalizeString(session?.access_token, 8192);
|
|
97
|
+
if (!accessToken) return session;
|
|
98
|
+
try {
|
|
99
|
+
const response = await fetch(`${SUPABASE_URL.replace(/\/+$/, '')}/auth/v1/user`, {
|
|
100
|
+
headers: {
|
|
101
|
+
apikey: SUPABASE_PUBLISHABLE_KEY,
|
|
102
|
+
Authorization: `Bearer ${accessToken}`,
|
|
103
|
+
Accept: 'application/json'
|
|
104
|
+
},
|
|
105
|
+
signal: AbortSignal.timeout(5_000)
|
|
106
|
+
});
|
|
107
|
+
if (!response.ok) return session;
|
|
108
|
+
const verifiedUser = await response.json().catch(() => null);
|
|
109
|
+
return verifiedUser ? mergeClientAccountProfile(session, verifiedUser) : session;
|
|
110
|
+
} catch {
|
|
111
|
+
return session;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
41
115
|
function normalizePort(value, fallback = DEFAULT_PORT) {
|
|
42
116
|
const port = Number(value);
|
|
43
117
|
return Number.isInteger(port) && port >= 1 && port <= 65535 ? port : fallback;
|
|
@@ -538,6 +612,7 @@ export function createClientRuntimeServer(options = {}) {
|
|
|
538
612
|
const appVersion = normalizeString(options.appVersion || process.env.LIVEDESK_NPM_LAUNCHER_VERSION, 80) || 'dev';
|
|
539
613
|
const savedSession = options.savedSession?.refresh_token ? options.savedSession : readSavedSession();
|
|
540
614
|
const savedSessionPersisted = Boolean(savedSession?.refresh_token);
|
|
615
|
+
const savedAccountProfile = readClientAccountProfile(savedSession);
|
|
541
616
|
let previousCpuTotals = readCpuTotals();
|
|
542
617
|
let hardwareSnapshot = { gpus: [], disks: [], collectedAt: '', collecting: true };
|
|
543
618
|
let networkSnapshot = { interfaces: activeNetworkInterfaces(), receivedBytes: 0, sentBytes: 0, receiveBytesPerSecond: 0, sendBytesPerSecond: 0, sampledAt: '' };
|
|
@@ -570,7 +645,9 @@ export function createClientRuntimeServer(options = {}) {
|
|
|
570
645
|
lastResult: '',
|
|
571
646
|
lastError: '',
|
|
572
647
|
persisted: savedSessionPersisted,
|
|
573
|
-
email:
|
|
648
|
+
email: savedAccountProfile.email,
|
|
649
|
+
name: savedAccountProfile.name,
|
|
650
|
+
avatarUrl: savedAccountProfile.avatarUrl
|
|
574
651
|
}
|
|
575
652
|
};
|
|
576
653
|
let completed = false;
|
|
@@ -586,8 +663,8 @@ export function createClientRuntimeServer(options = {}) {
|
|
|
586
663
|
const runtime = createRuntimeManager({
|
|
587
664
|
role: 'client',
|
|
588
665
|
state: RuntimeState.WAITING_AUTH,
|
|
589
|
-
authenticated: Boolean(
|
|
590
|
-
userId:
|
|
666
|
+
authenticated: Boolean(savedSession?.access_token),
|
|
667
|
+
userId: savedSession?.user?.id || '',
|
|
591
668
|
deviceId,
|
|
592
669
|
appVersion,
|
|
593
670
|
startedAt: new Date().toISOString(),
|
|
@@ -673,6 +750,7 @@ export function createClientRuntimeServer(options = {}) {
|
|
|
673
750
|
lastChoice = choice || lastChoice;
|
|
674
751
|
if (completed) {
|
|
675
752
|
if (choice?.session?.access_token) {
|
|
753
|
+
const accountProfile = readClientAccountProfile(choice.session);
|
|
676
754
|
runtime.setAuthenticated(true, choice.session.user?.id || '');
|
|
677
755
|
state.auth = {
|
|
678
756
|
...state.auth,
|
|
@@ -680,7 +758,9 @@ export function createClientRuntimeServer(options = {}) {
|
|
|
680
758
|
lastResult: 'accepted',
|
|
681
759
|
lastError: '',
|
|
682
760
|
persisted: true,
|
|
683
|
-
email:
|
|
761
|
+
email: accountProfile.email || state.auth.email,
|
|
762
|
+
name: accountProfile.name || state.auth.name,
|
|
763
|
+
avatarUrl: accountProfile.avatarUrl || state.auth.avatarUrl
|
|
684
764
|
};
|
|
685
765
|
}
|
|
686
766
|
return;
|
|
@@ -695,12 +775,15 @@ export function createClientRuntimeServer(options = {}) {
|
|
|
695
775
|
: [];
|
|
696
776
|
state.message = message;
|
|
697
777
|
state.lastError = '';
|
|
778
|
+
const accountProfile = readClientAccountProfile(choice?.session);
|
|
698
779
|
state.auth = {
|
|
699
780
|
...state.auth,
|
|
700
781
|
lastAttemptAt: state.connectedAt,
|
|
701
782
|
lastResult: 'accepted',
|
|
702
783
|
lastError: '',
|
|
703
|
-
email:
|
|
784
|
+
email: accountProfile.email || state.auth.email,
|
|
785
|
+
name: accountProfile.name || state.auth.name,
|
|
786
|
+
avatarUrl: accountProfile.avatarUrl || state.auth.avatarUrl
|
|
704
787
|
};
|
|
705
788
|
runtime.setAuthenticated(Boolean(choice?.session?.access_token), choice?.session?.user?.id || '');
|
|
706
789
|
runtime.update({ state: RuntimeState.RESOLVING_ROLE }, 'credentials-accepted');
|
|
@@ -727,6 +810,8 @@ export function createClientRuntimeServer(options = {}) {
|
|
|
727
810
|
authenticated: snapshot.authenticated,
|
|
728
811
|
userId: snapshot.userId || '',
|
|
729
812
|
userEmail: state.auth.email || '',
|
|
813
|
+
userName: state.auth.name || '',
|
|
814
|
+
userAvatarUrl: state.auth.avatarUrl || '',
|
|
730
815
|
appVersion,
|
|
731
816
|
runtimeId: `client-${snapshot.runtimePid}-${snapshot.startedAt}`,
|
|
732
817
|
runtimePid: snapshot.runtimePid,
|
|
@@ -845,6 +930,8 @@ export function createClientRuntimeServer(options = {}) {
|
|
|
845
930
|
authenticated: snapshot.authenticated,
|
|
846
931
|
userId: snapshot.userId || null,
|
|
847
932
|
userEmail: state.auth.email || null,
|
|
933
|
+
userName: state.auth.name || null,
|
|
934
|
+
userAvatarUrl: state.auth.avatarUrl || null,
|
|
848
935
|
role: 'client'
|
|
849
936
|
});
|
|
850
937
|
return;
|
|
@@ -896,7 +983,10 @@ export function createClientRuntimeServer(options = {}) {
|
|
|
896
983
|
const sessionCandidate = exchanged?.session || exchanged;
|
|
897
984
|
const normalized = normalizeRuntimeAuthSession(sessionCandidate, { requireRefreshToken: true });
|
|
898
985
|
if (!normalized.ok) throw new Error(normalized.error);
|
|
899
|
-
const session = {
|
|
986
|
+
const session = {
|
|
987
|
+
...sessionCandidate,
|
|
988
|
+
...mergeClientAccountProfile(normalized.session, sessionCandidate?.user)
|
|
989
|
+
};
|
|
900
990
|
if (!writeSavedSession(session)) throw new Error('refresh-token-required');
|
|
901
991
|
state.auth.persisted = true;
|
|
902
992
|
complete({ type: 'google', session }, 'Signed in. Finding the LiveDesk Hub.');
|
|
@@ -928,11 +1018,15 @@ export function createClientRuntimeServer(options = {}) {
|
|
|
928
1018
|
respondJson(401, { ok: false, error: `supabase-user-verification-failed:${verifyResponse.status}` });
|
|
929
1019
|
return;
|
|
930
1020
|
}
|
|
931
|
-
const verifiedUser = await verifyResponse.json().catch(() => ({}));
|
|
932
|
-
const
|
|
1021
|
+
const verifiedUser = await verifyResponse.json().catch(() => ({}));
|
|
1022
|
+
const verifiedSession = {
|
|
933
1023
|
...normalized.session,
|
|
934
|
-
user: {
|
|
1024
|
+
user: {
|
|
1025
|
+
id: normalizeString(verifiedUser?.id || body.user?.id || body.userId, 160),
|
|
1026
|
+
email: normalizeString(verifiedUser?.email || body.user?.email || body.email, 320)
|
|
1027
|
+
}
|
|
935
1028
|
};
|
|
1029
|
+
const session = mergeClientAccountProfile(verifiedSession, verifiedUser);
|
|
936
1030
|
try {
|
|
937
1031
|
if (!writeSavedSession(session)) throw new Error('refresh-token-required');
|
|
938
1032
|
state.auth.persisted = true;
|
|
@@ -954,7 +1048,15 @@ export function createClientRuntimeServer(options = {}) {
|
|
|
954
1048
|
state.manager = '';
|
|
955
1049
|
state.message = 'Saved sign-in was cleared. Sign in again to start the Client.';
|
|
956
1050
|
state.lastError = '';
|
|
957
|
-
state.auth = {
|
|
1051
|
+
state.auth = {
|
|
1052
|
+
lastAttemptAt: new Date().toISOString(),
|
|
1053
|
+
lastResult: 'cleared',
|
|
1054
|
+
lastError: '',
|
|
1055
|
+
persisted: false,
|
|
1056
|
+
email: '',
|
|
1057
|
+
name: '',
|
|
1058
|
+
avatarUrl: ''
|
|
1059
|
+
};
|
|
958
1060
|
runtime.setAuthenticated(false);
|
|
959
1061
|
respondJson(200, { ok: true, authenticated: false, role: 'client' });
|
|
960
1062
|
return;
|
|
@@ -1168,16 +1270,20 @@ export function createClientRuntimeServer(options = {}) {
|
|
|
1168
1270
|
complete(initialChoice, initialChoiceMessage);
|
|
1169
1271
|
});
|
|
1170
1272
|
} else {
|
|
1171
|
-
const saved = normalizeRuntimeAuthSession(
|
|
1273
|
+
const saved = normalizeRuntimeAuthSession(savedSession, { requireRefreshToken: true });
|
|
1172
1274
|
if (saved.ok) {
|
|
1173
1275
|
setImmediate(() => {
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1276
|
+
void (async () => {
|
|
1277
|
+
try {
|
|
1278
|
+
const normalizedSession = mergeClientAccountProfile(saved.session, savedSession?.user);
|
|
1279
|
+
const hydratedSession = await hydrateClientAccountProfile(normalizedSession);
|
|
1280
|
+
if (!writeSavedSession(hydratedSession)) throw new Error('refresh-token-required');
|
|
1281
|
+
state.auth.persisted = true;
|
|
1282
|
+
complete({ type: 'google', session: hydratedSession }, 'Saved sign-in found. Finding the LiveDesk Hub.');
|
|
1283
|
+
} catch (error) {
|
|
1284
|
+
recordAuthAttempt('rejected', `session-persistence-failed:${normalizeString(error?.message || error, 160)}`);
|
|
1285
|
+
}
|
|
1286
|
+
})();
|
|
1181
1287
|
});
|
|
1182
1288
|
}
|
|
1183
1289
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "livedesk",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"livedeskClientVersion": "0.1.
|
|
3
|
+
"version": "0.1.429",
|
|
4
|
+
"livedeskClientVersion": "0.1.188",
|
|
5
5
|
"buildFlavor": "production",
|
|
6
6
|
"description": "LiveDesk Hub and client launcher",
|
|
7
7
|
"type": "module",
|
|
@@ -50,10 +50,10 @@
|
|
|
50
50
|
"ws": "^8.18.3"
|
|
51
51
|
},
|
|
52
52
|
"optionalDependencies": {
|
|
53
|
-
"@livedesk/fast-linux-x64": "0.1.
|
|
54
|
-
"@livedesk/fast-osx-arm64": "0.1.
|
|
55
|
-
"@livedesk/fast-osx-x64": "0.1.
|
|
56
|
-
"@livedesk/fast-win-x64": "0.1.
|
|
53
|
+
"@livedesk/fast-linux-x64": "0.1.395",
|
|
54
|
+
"@livedesk/fast-osx-arm64": "0.1.395",
|
|
55
|
+
"@livedesk/fast-osx-x64": "0.1.395",
|
|
56
|
+
"@livedesk/fast-win-x64": "0.1.395"
|
|
57
57
|
},
|
|
58
58
|
"publishConfig": {
|
|
59
59
|
"access": "public"
|