@livedesk/client 0.1.23 → 0.1.25

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/README.md CHANGED
@@ -35,8 +35,10 @@ to `Desktop/LiveDeskFiles` unless the Hub sets another destination folder.
35
35
 
36
36
  By default, the launcher uses the packaged C# RemoteFast engine when supported.
37
37
  It falls back to the Node engine for AI assist or when a compatible RemoteFast
38
- runtime is unavailable. Linux currently uses the Node engine with native
39
- `node-screenshots` capture for thumbnails and live frames.
38
+ runtime is unavailable. macOS auto mode uses the Node engine by default because
39
+ the current RemoteFast macOS path shells out to `screencapture`, which can show
40
+ repeated Screen Recording privacy prompts. Use `--engine fast` on macOS only for
41
+ experimental testing.
40
42
 
41
43
  Useful flags:
42
44
 
@@ -6,7 +6,7 @@ import path from 'path';
6
6
  import crypto from 'crypto';
7
7
  import { promises as fs, statfsSync } from 'fs';
8
8
 
9
- const AGENT_VERSION = '0.1.23-livedesk.1';
9
+ const AGENT_VERSION = '0.1.24-livedesk.1';
10
10
  const DEFAULT_MANAGER = '127.0.0.1:5197';
11
11
  const DEFAULT_HEARTBEAT_MS = 5000;
12
12
  const DEFAULT_RECONNECT_MS = 5000;
@@ -17,6 +17,7 @@ const DEFAULT_AUTH_CALLBACK_HOST = '127.0.0.1';
17
17
  const DEFAULT_AUTH_CALLBACK_PORT = 5198;
18
18
  const DISCOVERY_RETRY_MS = 5000;
19
19
  const EXIT_INVALID_PAIR_TOKEN = 23;
20
+ const SESSION_REFRESH_SKEW_SECONDS = 60;
20
21
  const SUPABASE_URL = process.env.LIVEDESK_SUPABASE_URL || 'https://otbyfkjxrkngvjziawki.supabase.co';
21
22
  const SUPABASE_PUBLISHABLE_KEY = process.env.LIVEDESK_SUPABASE_PUBLISHABLE_KEY || 'sb_publishable_NpUs0RDJH2YnllsqTKO6TQ_1jTdSsNQ';
22
23
  const CLIENT_STATE_DIR = join(os.homedir(), '.livedesk-client');
@@ -60,7 +61,8 @@ Options:
60
61
  --help Show this help.
61
62
 
62
63
  Auto uses C# RemoteFast when supported and falls back to Node for AI assist or
63
- when a packaged RemoteFast runtime is unavailable.
64
+ when a packaged RemoteFast runtime is unavailable. On macOS, auto uses the Node
65
+ agent by default to avoid repeated screencapture privacy prompts.
64
66
  `.trimStart());
65
67
  }
66
68
 
@@ -73,6 +75,14 @@ function readPackageVersion() {
73
75
  }
74
76
  }
75
77
 
78
+ function isTruthy(value) {
79
+ return /^(1|true|yes|on)$/i.test(String(value || '').trim());
80
+ }
81
+
82
+ function isMacFastAutoEnabled() {
83
+ return isTruthy(process.env.LIVEDESK_CLIENT_MAC_FAST || process.env.MINDEXEC_REMOTE_MAC_FAST);
84
+ }
85
+
76
86
  function normalizeEngine(value) {
77
87
  const engine = String(value || 'auto').trim().toLowerCase();
78
88
  if (engine === 'c#' || engine === 'csharp' || engine === 'fast') {
@@ -278,7 +288,7 @@ async function createSupabaseClient() {
278
288
  const { createClient } = await import('@supabase/supabase-js');
279
289
  return createClient(SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY, {
280
290
  auth: {
281
- autoRefreshToken: true,
291
+ autoRefreshToken: false,
282
292
  persistSession: true,
283
293
  detectSessionInUrl: false,
284
294
  flowType: 'pkce',
@@ -288,6 +298,62 @@ async function createSupabaseClient() {
288
298
  });
289
299
  }
290
300
 
301
+ function getNestedErrorCode(error) {
302
+ let cursor = error;
303
+ for (let i = 0; i < 5 && cursor; i++) {
304
+ if (typeof cursor.code === 'string' && cursor.code.trim()) {
305
+ return cursor.code.trim();
306
+ }
307
+ cursor = cursor.cause;
308
+ }
309
+ return '';
310
+ }
311
+
312
+ function getNestedErrorMessage(error) {
313
+ const messages = [];
314
+ let cursor = error;
315
+ for (let i = 0; i < 5 && cursor; i++) {
316
+ if (typeof cursor.message === 'string' && cursor.message.trim()) {
317
+ messages.push(cursor.message.trim());
318
+ }
319
+ cursor = cursor.cause;
320
+ }
321
+ return messages.join(' ');
322
+ }
323
+
324
+ function isTransientNetworkError(error) {
325
+ const code = getNestedErrorCode(error).toUpperCase();
326
+ if (['ENOTFOUND', 'EAI_AGAIN', 'ECONNRESET', 'ECONNREFUSED', 'ETIMEDOUT', 'ENETUNREACH', 'EHOSTUNREACH'].includes(code)) {
327
+ return true;
328
+ }
329
+ return /fetch failed|network|dns|socket|connection|timeout|getaddrinfo/i.test(getNestedErrorMessage(error));
330
+ }
331
+
332
+ function formatDiscoveryError(error) {
333
+ if (isTransientNetworkError(error)) {
334
+ const code = getNestedErrorCode(error);
335
+ return `Network is not ready yet${code ? ` (${code})` : ''}. Waiting for DNS/Wi-Fi after sleep.`;
336
+ }
337
+ return error instanceof Error ? error.message : String(error);
338
+ }
339
+
340
+ async function refreshSessionIfNeeded(supabase) {
341
+ const { data: existing } = await supabase.auth.getSession();
342
+ const session = existing?.session;
343
+ if (!session?.access_token) {
344
+ return null;
345
+ }
346
+ const expiresAt = Number(session.expires_at || 0);
347
+ if (expiresAt <= 0 || expiresAt - Math.floor(Date.now() / 1000) > SESSION_REFRESH_SKEW_SECONDS) {
348
+ return session;
349
+ }
350
+ const { data, error } = await supabase.auth.refreshSession(session);
351
+ if (error) {
352
+ throw error;
353
+ }
354
+ return data?.session || session;
355
+ }
356
+
291
357
  function openBrowser(url) {
292
358
  if (os.platform() === 'win32') {
293
359
  spawn('rundll32.exe', ['url.dll,FileProtocolHandler', url], { detached: true, stdio: 'ignore', windowsHide: true }).unref();
@@ -533,6 +599,7 @@ function normalizeEndpointCandidates(target) {
533
599
  }
534
600
 
535
601
  async function resolveManagerFromSupabase(supabase, options = {}) {
602
+ await refreshSessionIfNeeded(supabase);
536
603
  const { data, error } = await supabase
537
604
  .from('livedesk_remote_host_targets')
538
605
  .select('endpoint, endpoint_candidates, pair_token, active, expires_at, updated_at, manager_version')
@@ -576,7 +643,7 @@ async function waitForManagerFromSupabase(supabase, options = {}) {
576
643
  try {
577
644
  return await resolveManagerFromSupabase(supabase, { requireReachable: true });
578
645
  } catch (err) {
579
- const message = err instanceof Error ? err.message : String(err);
646
+ const message = formatDiscoveryError(err);
580
647
  if (message !== lastMessage || attempts === 1 || attempts % 6 === 0) {
581
648
  const suffix = attempts === 1 ? '' : ` attempt ${attempts}`;
582
649
  console.log(`Still waiting for LiveDesk Hub${suffix}: ${message}`);
@@ -830,6 +897,9 @@ function shouldUseFast(parsed) {
830
897
  if (parsed.engine === 'fast') {
831
898
  return true;
832
899
  }
900
+ if (os.platform() === 'darwin' && !isMacFastAutoEnabled()) {
901
+ return false;
902
+ }
833
903
  return !parsed.nodeOnlyFeature;
834
904
  }
835
905
 
@@ -856,6 +926,9 @@ async function main() {
856
926
  const fastRuntime = getFastRuntime();
857
927
  const useFast = shouldTryFast(prepared, fastRuntime);
858
928
  let result;
929
+ if (!useFast && prepared.engine === 'auto' && os.platform() === 'darwin') {
930
+ console.warn('LiveDesk uses the Node engine on macOS to avoid repeated Screen Recording prompts. Use --engine fast only for experimental testing.');
931
+ }
859
932
  if (useFast) {
860
933
  const fastArgs = buildFastArgs(prepared.forwarded, prepared.fakeThumbnail);
861
934
  const fastLaunch = resolveFastLaunch(fastRuntime);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livedesk/client",
3
- "version": "0.1.23",
3
+ "version": "0.1.25",
4
4
  "description": "LiveDesk local remote client",
5
5
  "type": "module",
6
6
  "bin": {