@livedesk/client 0.1.19 → 0.1.21

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.
@@ -6,9 +6,11 @@ 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.19-livedesk.1';
9
+ const AGENT_VERSION = '0.1.21-livedesk.1';
10
10
  const DEFAULT_MANAGER = '127.0.0.1:5197';
11
11
  const DEFAULT_HEARTBEAT_MS = 5000;
12
+ const DEFAULT_RECONNECT_MS = 5000;
13
+ const EXIT_INVALID_PAIR_TOKEN = 23;
12
14
  const DEFAULT_AI_MODEL = 'gpt-5.4-mini';
13
15
  const DEFAULT_LIVE_FPS = 20;
14
16
  const MAX_LIVE_FPS = 24;
@@ -44,6 +46,7 @@ Options:
44
46
  --fake-ai Use deterministic AI assist responses for smoke tests.
45
47
  --fake-thumbnail Use generated thumbnail frames for smoke tests.
46
48
  --exit-on-disconnect Exit after manager disconnect. Useful for tests.
49
+ --exit-on-invalid-pair Exit when the manager rejects the pair token.
47
50
  --once Connect, send one status packet, and exit after welcome.
48
51
  --version Show the agent version.
49
52
  --help Show this help.
@@ -90,6 +93,7 @@ function parseArgs(argv) {
90
93
  fakeAi: isTruthy(process.env.LIVEDESK_CLIENT_FAKE_AI || process.env.MINDEXEC_REMOTE_FAKE_AI),
91
94
  fakeThumbnail: isTruthy(process.env.LIVEDESK_CLIENT_FAKE_THUMBNAIL || process.env.MINDEXEC_REMOTE_FAKE_THUMBNAIL),
92
95
  exitOnDisconnect: false,
96
+ exitOnInvalidPair: false,
93
97
  once: false,
94
98
  version: false,
95
99
  help: false
@@ -164,6 +168,9 @@ function parseArgs(argv) {
164
168
  case '--exit-on-disconnect':
165
169
  result.exitOnDisconnect = true;
166
170
  break;
171
+ case '--exit-on-invalid-pair':
172
+ result.exitOnInvalidPair = true;
173
+ break;
167
174
  case '--once':
168
175
  result.once = true;
169
176
  break;
@@ -1106,7 +1113,11 @@ function connectOnce(options, deviceId) {
1106
1113
  finish();
1107
1114
  return;
1108
1115
  } else if (message.type === 'error') {
1109
- finish(new Error(message.error || 'RemoteHub rejected the connection.'));
1116
+ const remoteError = new Error(message.error || 'RemoteHub rejected the connection.');
1117
+ if (message.error === 'invalid-pair-token' && options.exitOnInvalidPair) {
1118
+ remoteError.exitCode = EXIT_INVALID_PAIR_TOKEN;
1119
+ }
1120
+ finish(remoteError);
1110
1121
  return;
1111
1122
  }
1112
1123
  }
@@ -1143,7 +1154,10 @@ async function connectWithRetry(options) {
1143
1154
  attempt = 0;
1144
1155
  } catch (err) {
1145
1156
  attempt += 1;
1146
- const delayMs = Math.min(10000, 500 * attempt);
1157
+ if (err?.exitCode === EXIT_INVALID_PAIR_TOKEN) {
1158
+ throw err;
1159
+ }
1160
+ const delayMs = DEFAULT_RECONNECT_MS;
1147
1161
  console.error(`RemoteHub connection failed: ${err?.message || err}. Retrying in ${delayMs}ms.`);
1148
1162
  await wait(delayMs);
1149
1163
  }
@@ -1171,5 +1185,5 @@ async function main() {
1171
1185
 
1172
1186
  main().catch(err => {
1173
1187
  console.error(err?.message || err);
1174
- process.exit(1);
1188
+ process.exit(Number.isInteger(err?.exitCode) ? err.exitCode : 1);
1175
1189
  });
@@ -15,6 +15,8 @@ const FAST_PREFLIGHT_TIMEOUT_MS = 5000;
15
15
  const DEFAULT_MANAGER = '127.0.0.1:5197';
16
16
  const DEFAULT_AUTH_CALLBACK_HOST = '127.0.0.1';
17
17
  const DEFAULT_AUTH_CALLBACK_PORT = 5198;
18
+ const DISCOVERY_RETRY_MS = 5000;
19
+ const EXIT_INVALID_PAIR_TOKEN = 23;
18
20
  const SUPABASE_URL = process.env.LIVEDESK_SUPABASE_URL || 'https://otbyfkjxrkngvjziawki.supabase.co';
19
21
  const SUPABASE_PUBLISHABLE_KEY = process.env.LIVEDESK_SUPABASE_PUBLISHABLE_KEY || 'sb_publishable_NpUs0RDJH2YnllsqTKO6TQ_1jTdSsNQ';
20
22
  const CLIENT_STATE_DIR = join(os.homedir(), '.livedesk-client');
@@ -233,6 +235,13 @@ function upsertForwardedOption(args, flag, value) {
233
235
  return next;
234
236
  }
235
237
 
238
+ function appendForwardedFlag(args, flag) {
239
+ if (args.includes(flag)) {
240
+ return args;
241
+ }
242
+ return [...args, flag];
243
+ }
244
+
236
245
  function createFileStorage(filePath) {
237
246
  function readState() {
238
247
  try {
@@ -611,12 +620,16 @@ async function prepareLoginConnection(parsed) {
611
620
  if (slot) {
612
621
  forwarded = upsertForwardedOption(forwarded, '--slot', slot);
613
622
  }
623
+ if (shouldLogin) {
624
+ forwarded = appendForwardedFlag(forwarded, '--exit-on-invalid-pair');
625
+ }
614
626
  return {
615
627
  ...parsed,
616
628
  manager,
617
629
  pair,
618
630
  slot,
619
- forwarded
631
+ forwarded,
632
+ rediscoverOnInvalidPair: shouldLogin
620
633
  };
621
634
  }
622
635
 
@@ -799,16 +812,14 @@ function spawnAgent(command, args) {
799
812
  windowsHide: true
800
813
  });
801
814
 
802
- child.once('error', error => {
803
- console.error(`Failed to launch ${command}: ${error.message}`);
804
- process.exitCode = 1;
805
- });
806
- child.once('exit', (code, signal) => {
807
- if (signal) {
808
- process.kill(process.pid, signal);
809
- return;
810
- }
811
- process.exit(code ?? 0);
815
+ return new Promise(resolve => {
816
+ child.once('error', error => {
817
+ console.error(`Failed to launch ${command}: ${error.message}`);
818
+ resolve({ code: 1, signal: null });
819
+ });
820
+ child.once('exit', (code, signal) => {
821
+ resolve({ code: code ?? 0, signal });
822
+ });
812
823
  });
813
824
  }
814
825
 
@@ -840,23 +851,36 @@ async function main() {
840
851
  return;
841
852
  }
842
853
 
843
- const prepared = await prepareLoginConnection(parsed);
844
- const fastRuntime = getFastRuntime();
845
- const useFast = shouldTryFast(prepared, fastRuntime);
846
- if (useFast) {
847
- const fastArgs = buildFastArgs(prepared.forwarded, prepared.fakeThumbnail);
848
- const fastLaunch = resolveFastLaunch(fastRuntime);
849
- if (fastLaunch.ok) {
850
- spawnAgent(fastLaunch.command, [...fastLaunch.argsPrefix, ...fastArgs]);
851
- } else if (prepared.engine === 'fast') {
852
- console.error(`C# RemoteFast is unavailable: ${fastLaunch.reason}. Use --engine node to run the legacy Node agent.`);
853
- process.exit(2);
854
+ while (true) {
855
+ const prepared = await prepareLoginConnection(parsed);
856
+ const fastRuntime = getFastRuntime();
857
+ const useFast = shouldTryFast(prepared, fastRuntime);
858
+ let result;
859
+ if (useFast) {
860
+ const fastArgs = buildFastArgs(prepared.forwarded, prepared.fakeThumbnail);
861
+ const fastLaunch = resolveFastLaunch(fastRuntime);
862
+ if (fastLaunch.ok) {
863
+ result = await spawnAgent(fastLaunch.command, [...fastLaunch.argsPrefix, ...fastArgs]);
864
+ } else if (prepared.engine === 'fast') {
865
+ console.error(`C# RemoteFast is unavailable: ${fastLaunch.reason}. Use --engine node to run the legacy Node agent.`);
866
+ process.exit(2);
867
+ } else {
868
+ console.warn(`C# RemoteFast is unavailable (${fastLaunch.reason}). Falling back to the Node remote agent.`);
869
+ result = await spawnAgent(process.execPath, [nodeAgentPath, ...prepared.forwarded]);
870
+ }
854
871
  } else {
855
- console.warn(`C# RemoteFast is unavailable (${fastLaunch.reason}). Falling back to the Node remote agent.`);
856
- spawnAgent(process.execPath, [nodeAgentPath, ...prepared.forwarded]);
872
+ result = await spawnAgent(process.execPath, [nodeAgentPath, ...prepared.forwarded]);
873
+ }
874
+ if (result?.signal) {
875
+ process.kill(process.pid, result.signal);
876
+ return;
877
+ }
878
+ if (prepared.rediscoverOnInvalidPair && result?.code === EXIT_INVALID_PAIR_TOKEN) {
879
+ console.error(`LiveDesk manager pair token changed. Refreshing manager discovery in ${DISCOVERY_RETRY_MS}ms.`);
880
+ await sleep(DISCOVERY_RETRY_MS);
881
+ continue;
857
882
  }
858
- } else {
859
- spawnAgent(process.execPath, [nodeAgentPath, ...prepared.forwarded]);
883
+ process.exit(result?.code ?? 0);
860
884
  }
861
885
  }
862
886
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livedesk/client",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
4
4
  "description": "LiveDesk local remote client",
5
5
  "type": "module",
6
6
  "bin": {