@livedesk/hub 0.1.64 → 0.1.66

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livedesk/hub",
3
- "version": "0.1.64",
3
+ "version": "0.1.66",
4
4
  "description": "VuvoDesk local Hub API and browser frame bridge",
5
5
  "type": "module",
6
6
  "main": "src/server.js",
@@ -66,9 +66,27 @@ export function isVersionAtLeast(candidate, required) {
66
66
  return !!cleanVersion(candidate) && !!cleanVersion(required) && compareVersions(candidate, required) >= 0;
67
67
  }
68
68
 
69
- function isDedicatedBootstrapFailure(value) {
70
- return /^Update worker exited before terminal proof\b/i.test(String(value || '').trim());
71
- }
69
+ function isDedicatedBootstrapFailure(value) {
70
+ const failure = String(value || '').trim();
71
+ return /^Update worker exited before terminal proof\b/i.test(failure)
72
+ || failure === 'invalid-update-host-environment-entry';
73
+ }
74
+
75
+ function describeCommandResultEvidence(result) {
76
+ const data = result?.data;
77
+ if (!data || typeof data !== 'object') return '';
78
+ const evidence = [];
79
+ const shell = String(data.shell || '').trim().toLowerCase();
80
+ if (/^[a-z0-9._-]{1,32}$/.test(shell)) evidence.push(`shell=${shell}`);
81
+ const exitCode = Number(data.exitCode);
82
+ if (Number.isInteger(exitCode) && Math.abs(exitCode) <= 0x7fffffff) evidence.push(`exit=${exitCode}`);
83
+ if (typeof data.timedOut === 'boolean') evidence.push(`timedOut=${data.timedOut}`);
84
+ const durationMs = Number(data.durationMs);
85
+ if (Number.isFinite(durationMs) && durationMs >= 0 && durationMs <= 24 * 60 * 60_000) {
86
+ evidence.push(`durationMs=${Math.round(durationMs)}`);
87
+ }
88
+ return evidence.length > 0 ? ` [${evidence.join(' ')}]` : '';
89
+ }
72
90
 
73
91
  function encodePowerShell(value) {
74
92
  return Buffer.from(String(value || ''), 'utf16le').toString('base64');
@@ -451,16 +469,29 @@ export function createLiveDeskUpdateManager({
451
469
  return true;
452
470
  };
453
471
 
454
- const dispatchTarget = (target, device, options = {}) => {
455
- const dispatchedAtEpochMs = now();
456
- const requestedTimeoutMs = Math.max(
457
- 10_000,
458
- Math.min(
459
- effectiveTargetTimeoutMs,
460
- Number(options.updateTimeoutMs) || effectiveTargetTimeoutMs
461
- )
462
- );
463
- const updateDeadlineEpochMs = dispatchedAtEpochMs + requestedTimeoutMs;
472
+ const dispatchTarget = (target, device, options = {}) => {
473
+ const dispatchedAtEpochMs = now();
474
+ const requestedDeadlineEpochMs = Date.parse(String(options.deadlineAt || ''));
475
+ const usesExistingDeadline = Number.isFinite(requestedDeadlineEpochMs)
476
+ && requestedDeadlineEpochMs > 0;
477
+ const remainingExistingDeadlineMs = requestedDeadlineEpochMs - dispatchedAtEpochMs;
478
+ if (usesExistingDeadline && remainingExistingDeadlineMs < 10_000) {
479
+ target.state = 'failed';
480
+ target.error = 'client-update-deadline-insufficient';
481
+ return false;
482
+ }
483
+ const requestedTimeoutMs = usesExistingDeadline
484
+ ? Math.min(effectiveTargetTimeoutMs, remainingExistingDeadlineMs)
485
+ : Math.max(
486
+ 10_000,
487
+ Math.min(
488
+ effectiveTargetTimeoutMs,
489
+ Number(options.updateTimeoutMs) || effectiveTargetTimeoutMs
490
+ )
491
+ );
492
+ const updateDeadlineEpochMs = usesExistingDeadline
493
+ ? requestedDeadlineEpochMs
494
+ : dispatchedAtEpochMs + requestedTimeoutMs;
464
495
  const dispatchedSessionId = String(device?.sessionId || '').trim();
465
496
  if (!dispatchedSessionId) {
466
497
  target.state = 'failed';
@@ -784,12 +815,18 @@ export function createLiveDeskUpdateManager({
784
815
  if (!target
785
816
  || target.state !== 'waiting'
786
817
  || !['dedicated', 'legacy-command-run'].includes(target.method)) return;
787
- const result = event?.result;
788
- if (event?.error || result?.ok === false || result?.status === 'failed' || result?.status === 'rejected') {
789
- const failure = String(event.error || result?.error || result?.message || 'client-update-command-failed').slice(0, 500);
790
- if (target.method === 'dedicated'
791
- && target.bootstrapRetryCount === 0
792
- && isDedicatedBootstrapFailure(failure)) {
818
+ const result = event?.result;
819
+ if (event?.error || result?.ok === false || result?.status === 'failed' || result?.status === 'rejected') {
820
+ const failureReason = String(
821
+ event.error || result?.error || result?.message || 'client-update-command-failed'
822
+ ).slice(0, 500);
823
+ const failure = (
824
+ failureReason
825
+ + describeCommandResultEvidence(result)
826
+ ).slice(0, 500);
827
+ if (target.method === 'dedicated'
828
+ && target.bootstrapRetryCount === 0
829
+ && isDedicatedBootstrapFailure(failureReason)) {
793
830
  const retryDevice = connectedClientDevices()
794
831
  .find(device => String(device.deviceId || '') === target.deviceId);
795
832
  const remainingMs = Date.parse(target.deadlineAt || '') - now();
@@ -800,10 +837,10 @@ export function createLiveDeskUpdateManager({
800
837
  target.error = '';
801
838
  let retryResult;
802
839
  try {
803
- retryResult = dispatchTarget(target, retryDevice, {
804
- forceLegacy: true,
805
- updateTimeoutMs: remainingMs
806
- });
840
+ retryResult = dispatchTarget(target, retryDevice, {
841
+ forceLegacy: true,
842
+ deadlineAt: target.deadlineAt
843
+ });
807
844
  } catch (error) {
808
845
  target.error = String(error?.message || error || 'client-update-bootstrap-retry-failed').slice(0, 500);
809
846
  retryResult = false;
package/src/remote-hub.js CHANGED
@@ -9784,25 +9784,31 @@ export function createRemoteHub(options = {}) {
9784
9784
  return { ok: false, error: 'device-not-connected' };
9785
9785
  }
9786
9786
 
9787
- const command = safeString(options.command, 20000);
9788
- if (!command) return { ok: false, error: 'client-update-command-missing' };
9789
- const commandId = safeString(options.commandId, 128) || crypto.randomUUID();
9790
- const timeoutMs = clampNumber(options.timeoutMs, 1000, 120000, 30000);
9791
- const sent = writeJsonLine(device.socket, {
9787
+ const command = safeString(options.command, 20000);
9788
+ if (!command) return { ok: false, error: 'client-update-command-missing' };
9789
+ const commandId = safeString(options.commandId, 128) || crypto.randomUUID();
9790
+ const timeoutMs = clampNumber(options.timeoutMs, 1000, 120000, 30000);
9791
+ const legacyCommandShell = ['win32', 'windows']
9792
+ .includes(safeString(device.platform, 32).toLowerCase())
9793
+ ? 'cmd'
9794
+ : 'auto';
9795
+ const sent = writeJsonLine(device.socket, {
9792
9796
  type: 'command',
9793
9797
  commandId,
9794
- command: 'command.run',
9795
- payload: {
9796
- command,
9797
- timeoutMs,
9798
- permissionMode: 'full-access',
9798
+ command: 'command.run',
9799
+ payload: {
9800
+ command,
9801
+ shell: legacyCommandShell,
9802
+ timeoutMs,
9803
+ permissionMode: 'full-access',
9799
9804
  // RemoteFast releases before 0.1.170 read command.run inputs
9800
9805
  // from toolArguments, while Node and current Agents accept the
9801
9806
  // direct payload fields. Carry both during the update bridge.
9802
- toolArguments: {
9803
- command,
9804
- timeoutMs
9805
- }
9807
+ toolArguments: {
9808
+ command,
9809
+ shell: legacyCommandShell,
9810
+ timeoutMs
9811
+ }
9806
9812
  },
9807
9813
  issuedAt: new Date().toISOString()
9808
9814
  });