@livedesk/client 0.1.248 → 0.1.250

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.
@@ -20,6 +20,7 @@ import {
20
20
  } from '../src/runtime/agent-process-lifecycle.js';
21
21
  import { writeWindowsOwnedProcessManifest } from '../src/runtime/windows-owned-process-manifest.js';
22
22
  import { createHubWakeListener } from '../src/runtime/hub-wake-listener.js';
23
+ import { resolveSpawnablePackagedPath } from '../src/runtime/packaged-executable-path.js';
23
24
  import {
24
25
  inspectLinuxVideoAcceleration,
25
26
  installLinuxVideoAcceleration
@@ -620,13 +621,41 @@ export function preflightHubClientPort(port = resolveHubClientPort()) {
620
621
  });
621
622
  }
622
623
 
624
+ export async function recoverHubClientPortConflict(preflight, recoverPortOwner) {
625
+ if (preflight?.ok
626
+ || preflight?.code !== 'hub-client-port-in-use'
627
+ || typeof recoverPortOwner !== 'function') {
628
+ return preflight;
629
+ }
630
+
631
+ try {
632
+ await recoverPortOwner(Number(preflight.port || resolveHubClientPort()));
633
+ } catch (error) {
634
+ return {
635
+ ...preflight,
636
+ recoveryAttempted: true,
637
+ recoveryError: error instanceof Error ? error.message : String(error)
638
+ };
639
+ }
640
+
641
+ const verified = await preflightHubClientPort(preflight.port);
642
+ return {
643
+ ...verified,
644
+ recoveryAttempted: true,
645
+ recovered: verified.ok === true
646
+ };
647
+ }
648
+
623
649
  function hubClientPortPreflightError(preflight) {
624
650
  const port = Number(preflight?.port || resolveHubClientPort());
651
+ const recoveryError = String(preflight?.recoveryError || '').trim();
625
652
  return {
626
653
  ok: false,
627
654
  code: String(preflight?.code || 'hub-client-port-unavailable'),
628
655
  port,
629
- error: preflight?.code === 'hub-client-port-in-use'
656
+ error: recoveryError
657
+ ? `${recoveryError} The current Hub was not changed.`
658
+ : preflight?.code === 'hub-client-port-in-use'
630
659
  ? `LiveDesk cannot switch this computer to Hub because TCP ${port} is already in use. Change or stop the owning service, then try Switch to Hub again. The current Hub was not changed.`
631
660
  : `LiveDesk cannot switch this computer to Hub because TCP ${port} is unavailable. Check the local port configuration, then try again. The current Hub was not changed.`
632
661
  };
@@ -3082,9 +3111,13 @@ async function startConnectionChoiceServer(supabase, options = {}) {
3082
3111
  return;
3083
3112
  }
3084
3113
  const portPreflight = await preflightHubClientPort();
3085
- if (!portPreflight.ok) {
3114
+ const readyPortPreflight = await recoverHubClientPortConflict(
3115
+ portPreflight,
3116
+ options.recoverHubClientPort
3117
+ );
3118
+ if (!readyPortPreflight.ok) {
3086
3119
  res.writeHead(409, { 'Content-Type': 'application/json; charset=utf-8', 'Access-Control-Allow-Origin': '*' });
3087
- res.end(JSON.stringify(hubClientPortPreflightError(portPreflight)));
3120
+ res.end(JSON.stringify(hubClientPortPreflightError(readyPortPreflight)));
3088
3121
  return;
3089
3122
  }
3090
3123
  const session = await refreshSessionIfNeeded(supabase);
@@ -3403,8 +3436,12 @@ async function chooseClientConnection(supabase, options = {}) {
3403
3436
  return { ok: false, error: 'supabase-session-required' };
3404
3437
  }
3405
3438
  const portPreflight = await preflightHubClientPort();
3406
- if (!portPreflight.ok) {
3407
- return hubClientPortPreflightError(portPreflight);
3439
+ const readyPortPreflight = await recoverHubClientPortConflict(
3440
+ portPreflight,
3441
+ options.recoverHubClientPort
3442
+ );
3443
+ if (!readyPortPreflight.ok) {
3444
+ return hubClientPortPreflightError(readyPortPreflight);
3408
3445
  }
3409
3446
  const session = await refreshSessionIfNeeded(activeSupabase);
3410
3447
  if (!session?.access_token) {
@@ -4433,8 +4470,8 @@ function getFastRuntime() {
4433
4470
  return {
4434
4471
  rid,
4435
4472
  packageName,
4436
- executable: join(fastRoot, executableName),
4437
- dll: join(fastRoot, 'livedesk-client-fast.dll')
4473
+ executable: resolveSpawnablePackagedPath(join(fastRoot, executableName)),
4474
+ dll: resolveSpawnablePackagedPath(join(fastRoot, 'livedesk-client-fast.dll'))
4438
4475
  };
4439
4476
  } catch {
4440
4477
  return {
@@ -4573,7 +4610,9 @@ function clearFastPreflightCache() {
4573
4610
  function resolveBundledFfmpegPaths() {
4574
4611
  const paths = [];
4575
4612
  const addPath = (candidate) => {
4576
- const ffmpegPath = String(candidate || '').trim();
4613
+ // Electron can resolve ffmpeg-static through app.asar, but RemoteFast
4614
+ // is a native process and must receive the physical unpacked path.
4615
+ const ffmpegPath = resolveSpawnablePackagedPath(String(candidate || '').trim());
4577
4616
  if (ffmpegPath && existsSync(ffmpegPath) && !paths.includes(ffmpegPath)) {
4578
4617
  paths.push(ffmpegPath);
4579
4618
  }
@@ -5084,6 +5123,7 @@ export async function runClientRuntime(argv = process.argv.slice(2), runtimeOpti
5084
5123
  savedPin: null,
5085
5124
  allowRelayFallback: transportAllowsRelay(parsed.transport),
5086
5125
  relayEndpoint: parsed.relay,
5126
+ recoverHubClientPort: runtimeOptions.recoverHubClientPort,
5087
5127
  openBrowser: parsed.openBrowserOnStart,
5088
5128
  onStarted: page => {
5089
5129
  connectionPage = page;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livedesk/client",
3
- "version": "0.1.248",
3
+ "version": "0.1.250",
4
4
  "description": "LiveDesk local remote client",
5
5
  "type": "module",
6
6
  "bin": {
@@ -42,10 +42,10 @@
42
42
  "ws": "^8.18.3"
43
43
  },
44
44
  "optionalDependencies": {
45
- "@livedesk/fast-linux-x64": "0.1.445",
46
- "@livedesk/fast-osx-arm64": "0.1.445",
47
- "@livedesk/fast-osx-x64": "0.1.445",
48
- "@livedesk/fast-win-x64": "0.1.445"
45
+ "@livedesk/fast-linux-x64": "0.1.446",
46
+ "@livedesk/fast-osx-arm64": "0.1.446",
47
+ "@livedesk/fast-osx-x64": "0.1.446",
48
+ "@livedesk/fast-win-x64": "0.1.446"
49
49
  },
50
50
  "publishConfig": {
51
51
  "access": "public"
@@ -0,0 +1,23 @@
1
+ import { existsSync } from 'node:fs';
2
+
3
+ const ASAR_PATH_SEGMENT = /([\\/])app\.asar([\\/])/i;
4
+
5
+ /**
6
+ * Electron's Node loader can read JavaScript through the virtual app.asar
7
+ * path, but the operating system cannot spawn an executable from that virtual
8
+ * archive. electron-builder places executable payloads in the matching
9
+ * app.asar.unpacked tree, so only spawn targets cross that boundary.
10
+ */
11
+ export function resolveSpawnablePackagedPath(value, pathExists = existsSync) {
12
+ const candidate = String(value || '');
13
+ if (!candidate || !ASAR_PATH_SEGMENT.test(candidate)) {
14
+ return candidate;
15
+ }
16
+
17
+ const unpacked = candidate.replace(ASAR_PATH_SEGMENT, '$1app.asar.unpacked$2');
18
+ try {
19
+ return pathExists(unpacked) ? unpacked : candidate;
20
+ } catch {
21
+ return candidate;
22
+ }
23
+ }