livedesk 0.1.365 → 0.1.366

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.
@@ -617,11 +617,12 @@ function normalizeRemoteInputEvent(value = {}) {
617
617
  const location = Number(input.location ?? input.Location);
618
618
  const monitorIndex = Number(input.monitorIndex ?? input.MonitorIndex ?? input.screenIndex ?? input.ScreenIndex ?? input.displayIndex ?? input.DisplayIndex);
619
619
  const inputSeq = Number(input.inputSeq ?? input.InputSeq);
620
- const issuedAtEpochMs = Number(input.issuedAtEpochMs ?? input.IssuedAtEpochMs);
621
- const hubReceivedAtEpochMs = Number(input.hubReceivedAtEpochMs ?? input.HubReceivedAtEpochMs);
622
- return {
623
- type,
624
- monitorIndex: Number.isFinite(monitorIndex) ? normalizeMonitorIndex(monitorIndex) : 0,
620
+ const issuedAtEpochMs = Number(input.issuedAtEpochMs ?? input.IssuedAtEpochMs);
621
+ const hubReceivedAtEpochMs = Number(input.hubReceivedAtEpochMs ?? input.HubReceivedAtEpochMs);
622
+ const captureGeneration = Number(input.captureGeneration ?? input.CaptureGeneration);
623
+ return {
624
+ type,
625
+ monitorIndex: Number.isFinite(monitorIndex) ? normalizeMonitorIndex(monitorIndex) : 0,
625
626
  normalizedX: Number.isFinite(normalizedX) ? Math.max(0, Math.min(1, normalizedX)) : undefined,
626
627
  normalizedY: Number.isFinite(normalizedY) ? Math.max(0, Math.min(1, normalizedY)) : undefined,
627
628
  button: safeString(input.button || input.Button, 24),
@@ -636,9 +637,13 @@ function normalizeRemoteInputEvent(value = {}) {
636
637
  shiftKey: input.shiftKey === true || input.ShiftKey === true,
637
638
  ctrlKey: input.ctrlKey === true || input.ControlKey === true || input.CtrlKey === true,
638
639
  altKey: input.altKey === true || input.AltKey === true,
639
- metaKey: input.metaKey === true || input.MetaKey === true || input.commandKey === true || input.CommandKey === true,
640
- controlLeaseId: safeString(input.controlLeaseId || input.ControlLeaseId, 128),
641
- inputSeq: Number.isSafeInteger(inputSeq) && inputSeq > 0 ? inputSeq : 0,
640
+ metaKey: input.metaKey === true || input.MetaKey === true || input.commandKey === true || input.CommandKey === true,
641
+ controlLeaseId: safeString(input.controlLeaseId || input.ControlLeaseId, 128),
642
+ controlSessionId: safeString(input.controlSessionId || input.sessionId || input.ControlSessionId || input.SessionId, 160),
643
+ controlCommandId: safeString(input.controlCommandId || input.commandId || input.ControlCommandId || input.CommandId, 128),
644
+ captureGeneration: Number.isSafeInteger(captureGeneration) && captureGeneration > 0 ? captureGeneration : 0,
645
+ hubConnectionId: safeString(input.hubConnectionId || input.HubConnectionId, 128),
646
+ inputSeq: Number.isSafeInteger(inputSeq) && inputSeq > 0 ? inputSeq : 0,
642
647
  requestAck: input.requestAck !== false && input.RequestAck !== false,
643
648
  issuedAtEpochMs: Number.isFinite(issuedAtEpochMs) ? issuedAtEpochMs : 0,
644
649
  hubReceivedAtEpochMs: Number.isFinite(hubReceivedAtEpochMs) ? hubReceivedAtEpochMs : 0,
@@ -3397,9 +3402,9 @@ export function createRemoteHub(options = {}) {
3397
3402
  const activatingPendingGeneration = isPendingGeneration && commandId !== activeCommandId;
3398
3403
  const transfer = buildRemoteFrameTransferDescriptorFromMessage(message, streamState.mode || DEFAULT_REMOTE_FRAME_MODE);
3399
3404
  const openedAt = safeString(message.openedAt, 80) || device.lastSeenAt || new Date().toISOString();
3400
- const nextStreamState = {
3401
- ...streamState,
3402
- commandId: activatingPendingGeneration ? commandId : streamState.commandId,
3405
+ const nextStreamState = {
3406
+ ...streamState,
3407
+ commandId: activatingPendingGeneration ? commandId : streamState.commandId,
3403
3408
  pendingCommandId: activatingPendingGeneration ? '' : streamState.pendingCommandId,
3404
3409
  pendingStartedAt: activatingPendingGeneration ? '' : streamState.pendingStartedAt,
3405
3410
  pendingRestartToken: activatingPendingGeneration ? '' : streamState.pendingRestartToken,
@@ -3419,10 +3424,14 @@ export function createRemoteHub(options = {}) {
3419
3424
  height: Number.isFinite(Number(message.height)) ? Number(message.height) : Number(streamState.height || 0),
3420
3425
  sourceWidth: Number.isFinite(Number(message.sourceWidth)) ? Number(message.sourceWidth) : Number(streamState.sourceWidth || 0),
3421
3426
  sourceHeight: Number.isFinite(Number(message.sourceHeight)) ? Number(message.sourceHeight) : Number(streamState.sourceHeight || 0),
3422
- monitorIndex: Number.isFinite(Number(message.monitorIndex)) ? normalizeMonitorIndex(message.monitorIndex) : Number(streamState.monitorIndex || 0),
3423
- monitorCount: Number.isFinite(Number(message.monitorCount)) ? clampNumber(message.monitorCount, 1, 64, 1) : Number(streamState.monitorCount || 1),
3424
- openedFrameSeq: Number.isFinite(Number(message.frameSeq)) ? Number(message.frameSeq) : Number(streamState.openedFrameSeq || 0)
3425
- };
3427
+ monitorIndex: Number.isFinite(Number(message.monitorIndex)) ? normalizeMonitorIndex(message.monitorIndex) : Number(streamState.monitorIndex || 0),
3428
+ monitorCount: Number.isFinite(Number(message.monitorCount)) ? clampNumber(message.monitorCount, 1, 64, 1) : Number(streamState.monitorCount || 1),
3429
+ streamPurpose: safeString(message.streamPurpose || streamState.streamPurpose, 24) || 'wall',
3430
+ captureGeneration: Number.isSafeInteger(Number(message.captureGeneration)) && Number(message.captureGeneration) > 0
3431
+ ? Number(message.captureGeneration)
3432
+ : Number(streamState.captureGeneration || 0),
3433
+ openedFrameSeq: Number.isFinite(Number(message.frameSeq)) ? Number(message.frameSeq) : Number(streamState.openedFrameSeq || 0)
3434
+ };
3426
3435
  setDeviceLiveStream(device, nextStreamState);
3427
3436
  emitRemoteEvent('RemoteLiveStreamOpened', device, {
3428
3437
  streamId,
@@ -3457,9 +3466,9 @@ export function createRemoteHub(options = {}) {
3457
3466
  });
3458
3467
  return false;
3459
3468
  }
3460
- const activeCommandId = safeString(streamState.commandId, 128);
3461
- const pendingCommandId = safeString(streamState.pendingCommandId, 128);
3462
- const isActiveGeneration = !commandId || !activeCommandId || commandId === activeCommandId;
3469
+ const activeCommandId = safeString(streamState.commandId, 128);
3470
+ const pendingCommandId = safeString(streamState.pendingCommandId, 128);
3471
+ const isActiveGeneration = !commandId || !activeCommandId || commandId === activeCommandId;
3463
3472
  if (!isActiveGeneration) {
3464
3473
  device.counters.liveFramesDropped += 1;
3465
3474
  emitRemoteEvent('RemoteFrameDropped', device, {
@@ -3471,8 +3480,25 @@ export function createRemoteHub(options = {}) {
3471
3480
  frameSeq: Number.isFinite(frameSeq) ? frameSeq : null,
3472
3481
  transport
3473
3482
  });
3474
- return false;
3475
- }
3483
+ return false;
3484
+ }
3485
+ const frameCaptureGeneration = Number(message.captureGeneration || streamState.captureGeneration || 0);
3486
+ const activeCaptureGeneration = Number(streamState.captureGeneration || 0);
3487
+ if (activeCaptureGeneration > 0
3488
+ && frameCaptureGeneration > 0
3489
+ && frameCaptureGeneration !== activeCaptureGeneration) {
3490
+ device.counters.liveFramesDropped += 1;
3491
+ emitRemoteEvent('RemoteFrameDropped', device, {
3492
+ reason: 'stale-capture-generation-frame',
3493
+ streamId,
3494
+ commandId,
3495
+ captureGeneration: frameCaptureGeneration,
3496
+ activeCaptureGeneration,
3497
+ frameSeq: Number.isFinite(frameSeq) ? frameSeq : null,
3498
+ transport
3499
+ });
3500
+ return false;
3501
+ }
3476
3502
 
3477
3503
  const byteLength = normalizeFrameByteLength(framePayload, frameData);
3478
3504
  if ((!Buffer.isBuffer(framePayload) && !frameData)
@@ -3511,9 +3537,12 @@ export function createRemoteHub(options = {}) {
3511
3537
  device.latestLiveFrame = {
3512
3538
  streamId,
3513
3539
  frameSeq,
3514
- streamFrameSeq: Number.isFinite(Number(message.streamFrameSeq)) ? Number(message.streamFrameSeq) : frameSeq,
3515
- commandId,
3516
- width: Number.isFinite(Number(message.width)) ? Number(message.width) : 0,
3540
+ streamFrameSeq: Number.isFinite(Number(message.streamFrameSeq)) ? Number(message.streamFrameSeq) : frameSeq,
3541
+ commandId,
3542
+ sessionId: device.sessionId,
3543
+ captureGeneration: activeCaptureGeneration || frameCaptureGeneration,
3544
+ streamPurpose: safeString(message.streamPurpose || streamState.streamPurpose, 24) || 'wall',
3545
+ width: Number.isFinite(Number(message.width)) ? Number(message.width) : 0,
3517
3546
  height: Number.isFinite(Number(message.height)) ? Number(message.height) : 0,
3518
3547
  sourceWidth: Number.isFinite(Number(message.sourceWidth)) ? Number(message.sourceWidth) : Number(message.width || 0),
3519
3548
  sourceHeight: Number.isFinite(Number(message.sourceHeight)) ? Number(message.sourceHeight) : Number(message.height || 0),
@@ -4667,17 +4696,49 @@ export function createRemoteHub(options = {}) {
4667
4696
  const denied = policyError(device, 'allowControl');
4668
4697
  if (denied) return { ok: false, error: denied };
4669
4698
 
4670
- if (!readCapabilityFlag(device.capabilities, 'control')) {
4671
- return { ok: false, error: 'device-input-control-unavailable' };
4672
- }
4673
-
4674
- const normalized = normalizeRemoteInputEvent(input);
4675
- if (!normalized.type) {
4676
- return { ok: false, error: 'missing-input-type' };
4677
- }
4678
-
4679
- const inputSocket = device.inputSocket;
4680
- if (inputSocket && !inputSocket.destroyed) {
4699
+ if (!readCapabilityFlag(device.capabilities, 'control')) {
4700
+ const permissionMissing = device.capabilities?.controlPermission === false
4701
+ || device.capabilities?.accessibilityPermission === false;
4702
+ return { ok: false, error: permissionMissing ? 'INPUT_PERMISSION_DENIED' : 'device-input-control-unavailable' };
4703
+ }
4704
+
4705
+ const normalized = normalizeRemoteInputEvent(input);
4706
+ if (!normalized.type) {
4707
+ return { ok: false, error: 'missing-input-type' };
4708
+ }
4709
+ const controlStream = getActiveControlStream(device);
4710
+ if (!controlStream
4711
+ || controlStream.open !== true
4712
+ || !safeString(controlStream.commandId, 128)
4713
+ || Number(controlStream.captureGeneration || 0) <= 0) {
4714
+ return { ok: false, error: 'CONTROL_NOT_READY' };
4715
+ }
4716
+ if (!normalized.controlSessionId
4717
+ || normalized.controlSessionId !== safeString(device.sessionId, 160)) {
4718
+ return {
4719
+ ok: false,
4720
+ error: 'STALE_CONTROL_SESSION',
4721
+ activeSessionId: safeString(device.sessionId, 160)
4722
+ };
4723
+ }
4724
+ if (!normalized.controlCommandId
4725
+ || normalized.controlCommandId !== safeString(controlStream.commandId, 128)) {
4726
+ return {
4727
+ ok: false,
4728
+ error: 'STALE_CONTROL_SESSION',
4729
+ activeCommandId: safeString(controlStream.commandId, 128)
4730
+ };
4731
+ }
4732
+ if (normalized.captureGeneration !== Number(controlStream.captureGeneration || 0)) {
4733
+ return {
4734
+ ok: false,
4735
+ error: 'STALE_CAPTURE_GENERATION',
4736
+ activeCaptureGeneration: Number(controlStream.captureGeneration || 0)
4737
+ };
4738
+ }
4739
+
4740
+ const inputSocket = device.inputSocket;
4741
+ if (inputSocket && !inputSocket.destroyed) {
4681
4742
  const sent = writeJsonLine(inputSocket, {
4682
4743
  type: 'input.control',
4683
4744
  payload: {
@@ -4687,22 +4748,21 @@ export function createRemoteHub(options = {}) {
4687
4748
  }
4688
4749
  });
4689
4750
  if (sent) {
4690
- device.counters.commandsSent += 1;
4691
- device.inputLastSeenAt = new Date().toISOString();
4692
- return { ok: true, inputSocket: true };
4693
- }
4751
+ device.counters.commandsSent += 1;
4752
+ device.inputLastSeenAt = new Date().toISOString();
4753
+ return {
4754
+ ok: true,
4755
+ inputSocket: true,
4756
+ sessionId: device.sessionId,
4757
+ commandId: controlStream.commandId,
4758
+ captureGeneration: controlStream.captureGeneration
4759
+ };
4760
+ }
4694
4761
 
4695
4762
  detachInputSocket(inputSocket, 'input-socket-write-failed');
4696
4763
  }
4697
4764
 
4698
- return sendCommand(deviceId, {
4699
- command: 'input.control',
4700
- payload: {
4701
- ...normalized,
4702
- hubForwardedAtEpochMs: Date.now(),
4703
- issuedAt: normalized.issuedAt || new Date().toISOString()
4704
- }
4705
- });
4765
+ return { ok: false, error: 'CONTROL_NOT_READY', detail: 'input-channel-not-connected' };
4706
4766
  }
4707
4767
 
4708
4768
  function notifyAgentProgress(deviceIds, progress = {}) {
@@ -5061,11 +5121,83 @@ export function createRemoteHub(options = {}) {
5061
5121
  }
5062
5122
  }
5063
5123
 
5064
- function makeStableLiveStreamId(deviceId, purpose = 'wall') {
5065
- const hash = crypto.createHash('sha1').update(String(deviceId || 'device')).digest('hex').slice(0, 16);
5066
- const role = safeString(purpose, 24).toLowerCase().replace(/[^a-z0-9_-]+/g, '-') || 'wall';
5067
- return `${role}-${hash}`;
5068
- }
5124
+ function makeStableLiveStreamId(deviceId, purpose = 'wall') {
5125
+ const hash = crypto.createHash('sha1').update(String(deviceId || 'device')).digest('hex').slice(0, 16);
5126
+ const role = safeString(purpose, 24).toLowerCase().replace(/[^a-z0-9_-]+/g, '-') || 'wall';
5127
+ return `${role}-${hash}`;
5128
+ }
5129
+
5130
+ function getActiveLiveStreams(device) {
5131
+ if (!device || typeof device !== 'object') {
5132
+ return [];
5133
+ }
5134
+ return [...ensureDeviceLiveStreams(device).values()].filter(stream => stream?.active === true);
5135
+ }
5136
+
5137
+ function getActiveControlStream(device) {
5138
+ return getActiveLiveStreams(device).find(stream =>
5139
+ safeString(stream?.streamPurpose, 24).toLowerCase() === 'control') || null;
5140
+ }
5141
+
5142
+ function stopCompetingMacCaptureStreams(deviceId, device, nextStreamId, nextPurpose) {
5143
+ if (normalizeLivePlatform(device?.platform) !== 'macos') {
5144
+ return { ok: true, stopped: [] };
5145
+ }
5146
+
5147
+ const activeControl = getActiveControlStream(device);
5148
+ if (nextPurpose !== 'control'
5149
+ && activeControl
5150
+ && activeControl.streamId !== nextStreamId) {
5151
+ return {
5152
+ ok: true,
5153
+ suppressed: true,
5154
+ reason: 'CONTROL_CAPTURE_OWNS_DEVICE',
5155
+ ownerStreamId: activeControl.streamId,
5156
+ ownerCommandId: activeControl.commandId,
5157
+ captureGeneration: Number(activeControl.captureGeneration || 0)
5158
+ };
5159
+ }
5160
+
5161
+ const stopped = [];
5162
+ for (const stream of getActiveLiveStreams(device)) {
5163
+ if (!stream?.streamId || stream.streamId === nextStreamId) {
5164
+ continue;
5165
+ }
5166
+ const stopCommandId = crypto.randomUUID();
5167
+ const result = sendCommand(deviceId, {
5168
+ command: 'stream.stop',
5169
+ commandId: stopCommandId,
5170
+ payload: {
5171
+ streamId: stream.streamId,
5172
+ requestedAt: new Date().toISOString(),
5173
+ reason: `mac-capture-owner-transition:${nextPurpose}`
5174
+ }
5175
+ });
5176
+ if (!result.ok) {
5177
+ return {
5178
+ ok: false,
5179
+ error: 'CAPTURE_TRANSITION_IN_PROGRESS',
5180
+ streamId: stream.streamId,
5181
+ detail: result.error || 'stream-stop-dispatch-failed'
5182
+ };
5183
+ }
5184
+ stream.active = false;
5185
+ stream.pendingCommandId = '';
5186
+ stream.pendingStartedAt = '';
5187
+ stream.pendingRestartToken = '';
5188
+ stream.stoppedAt = new Date().toISOString();
5189
+ stream.stopReason = `mac-capture-owner-transition:${nextPurpose}`;
5190
+ stopped.push({ streamId: stream.streamId, commandId: stopCommandId });
5191
+ }
5192
+ return { ok: true, stopped };
5193
+ }
5194
+
5195
+ function nextCaptureGeneration(device) {
5196
+ const current = Number(device?.captureGenerationCounter || 0);
5197
+ const next = Number.isSafeInteger(current) && current > 0 ? current + 1 : 1;
5198
+ device.captureGenerationCounter = next;
5199
+ return next;
5200
+ }
5069
5201
 
5070
5202
  function normalizeLivePlatform(value) {
5071
5203
  const normalized = safeString(value, 32).toLowerCase();
@@ -5158,11 +5290,11 @@ export function createRemoteHub(options = {}) {
5158
5290
  }
5159
5291
 
5160
5292
  const now = new Date().toISOString();
5161
- const normalized = normalizeLiveStreamStartOptions({ fps: 2, ...options, platform: device.platform });
5162
- const { fps, maxWidth, maxHeight, quality, monitorIndex, transfer, streamPurpose } = normalized;
5163
- const streamId = safeString(options.streamId, 128) || makeStableLiveStreamId(deviceId, streamPurpose);
5164
- const activeLiveStream = getDeviceLiveStream(device, streamId);
5165
- const commandId = safeString(options.commandId, 128) || crypto.randomUUID();
5293
+ const normalized = normalizeLiveStreamStartOptions({ fps: 2, ...options, platform: device.platform });
5294
+ const { fps, maxWidth, maxHeight, quality, monitorIndex, transfer, streamPurpose } = normalized;
5295
+ const streamId = safeString(options.streamId, 128) || makeStableLiveStreamId(deviceId, streamPurpose);
5296
+ const activeLiveStream = getDeviceLiveStream(device, streamId);
5297
+ const commandId = safeString(options.commandId, 128) || crypto.randomUUID();
5166
5298
  if (activeLiveStream
5167
5299
  && liveStreamMatchesOptions(activeLiveStream, normalized)
5168
5300
  && (activeLiveStream.open === true || liveStreamStartStillPending(activeLiveStream))) {
@@ -5173,13 +5305,15 @@ export function createRemoteHub(options = {}) {
5173
5305
  fps,
5174
5306
  mode: transfer.mode,
5175
5307
  frameMode: transfer.frameMode,
5176
- monitorIndex,
5177
- synthetic: true,
5178
- reused: true
5179
- };
5180
- }
5181
- device.counters.commandsSent += 1;
5182
- setDeviceLiveStream(device, {
5308
+ monitorIndex,
5309
+ captureGeneration: Number(activeLiveStream.captureGeneration || 0),
5310
+ synthetic: true,
5311
+ reused: true
5312
+ };
5313
+ }
5314
+ const captureGeneration = nextCaptureGeneration(device);
5315
+ device.counters.commandsSent += 1;
5316
+ setDeviceLiveStream(device, {
5183
5317
  streamId,
5184
5318
  commandId,
5185
5319
  active: true,
@@ -5204,11 +5338,12 @@ export function createRemoteHub(options = {}) {
5204
5338
  startedAt: now,
5205
5339
  stoppedAt: '',
5206
5340
  stopReason: '',
5207
- lastFrameAt: '',
5208
- lastFrameSeq: 0,
5209
- framesReceived: 0,
5210
- streamPurpose
5211
- });
5341
+ lastFrameAt: '',
5342
+ lastFrameSeq: 0,
5343
+ framesReceived: 0,
5344
+ streamPurpose,
5345
+ captureGeneration
5346
+ });
5212
5347
  device.counters.liveStreamsStarted += 1;
5213
5348
  applySyntheticLiveFrame(device, streamId, {
5214
5349
  commandId,
@@ -5224,7 +5359,7 @@ export function createRemoteHub(options = {}) {
5224
5359
  mode: transfer.mode,
5225
5360
  synthetic: true
5226
5361
  });
5227
- return { ok: true, commandId, streamId, fps, mode: transfer.mode, frameMode: transfer.frameMode, monitorIndex, synthetic: true };
5362
+ return { ok: true, commandId, streamId, fps, mode: transfer.mode, frameMode: transfer.frameMode, monitorIndex, captureGeneration, synthetic: true };
5228
5363
  }
5229
5364
 
5230
5365
  if (!device?.socket || device.socket.destroyed || !device.connected) {
@@ -5235,11 +5370,35 @@ export function createRemoteHub(options = {}) {
5235
5370
  }
5236
5371
 
5237
5372
  const now = new Date().toISOString();
5238
- const normalized = normalizeLiveStreamStartOptions({ ...options, platform: device.platform });
5239
- const { fps, maxWidth, maxHeight, quality, monitorIndex, transfer, streamPurpose } = normalized;
5240
- const streamId = safeString(options.streamId, 128) || makeStableLiveStreamId(deviceId, streamPurpose);
5241
- const activeLiveStream = getDeviceLiveStream(device, streamId);
5242
- if (activeLiveStream?.pendingCommandId) {
5373
+ const normalized = normalizeLiveStreamStartOptions({ ...options, platform: device.platform });
5374
+ const { fps, maxWidth, maxHeight, quality, monitorIndex, transfer, streamPurpose } = normalized;
5375
+ const streamId = safeString(options.streamId, 128) || makeStableLiveStreamId(deviceId, streamPurpose);
5376
+ const activeLiveStream = getDeviceLiveStream(device, streamId);
5377
+ const macTransition = stopCompetingMacCaptureStreams(deviceId, device, streamId, streamPurpose);
5378
+ if (!macTransition.ok) {
5379
+ return macTransition;
5380
+ }
5381
+ if (macTransition.suppressed) {
5382
+ emitRemoteEvent('RemoteLiveStreamStartSuppressed', device, {
5383
+ streamId,
5384
+ streamPurpose,
5385
+ reason: macTransition.reason,
5386
+ ownerStreamId: macTransition.ownerStreamId,
5387
+ ownerCommandId: macTransition.ownerCommandId,
5388
+ captureGeneration: macTransition.captureGeneration
5389
+ });
5390
+ return {
5391
+ ok: true,
5392
+ suppressed: true,
5393
+ streamId,
5394
+ streamPurpose,
5395
+ reason: macTransition.reason,
5396
+ ownerStreamId: macTransition.ownerStreamId,
5397
+ ownerCommandId: macTransition.ownerCommandId,
5398
+ captureGeneration: macTransition.captureGeneration
5399
+ };
5400
+ }
5401
+ if (activeLiveStream?.pendingCommandId) {
5243
5402
  if (liveStreamReplacementStillPending(activeLiveStream)) {
5244
5403
  emitRemoteEvent('RemoteLiveStreamRestartPending', device, {
5245
5404
  streamId,
@@ -5253,11 +5412,12 @@ export function createRemoteHub(options = {}) {
5253
5412
  streamId,
5254
5413
  fps: Number(activeLiveStream.fps || fps),
5255
5414
  mode: activeLiveStream.mode || transfer.mode,
5256
- frameMode: activeLiveStream.frameMode || transfer.frameMode,
5257
- monitorIndex: Number(activeLiveStream.monitorIndex || monitorIndex),
5258
- pending: true,
5259
- reused: true
5260
- };
5415
+ frameMode: activeLiveStream.frameMode || transfer.frameMode,
5416
+ monitorIndex: Number(activeLiveStream.monitorIndex || monitorIndex),
5417
+ captureGeneration: Number(activeLiveStream.captureGeneration || 0),
5418
+ pending: true,
5419
+ reused: true
5420
+ };
5261
5421
  }
5262
5422
  activeLiveStream.pendingCommandId = '';
5263
5423
  activeLiveStream.pendingStartedAt = '';
@@ -5275,10 +5435,11 @@ export function createRemoteHub(options = {}) {
5275
5435
  streamId,
5276
5436
  fps: Number(activeLiveStream.fps || fps),
5277
5437
  mode: activeLiveStream.mode || transfer.mode,
5278
- frameMode: activeLiveStream.frameMode || transfer.frameMode,
5279
- monitorIndex: Number(activeLiveStream.monitorIndex || monitorIndex),
5280
- reused: true
5281
- };
5438
+ frameMode: activeLiveStream.frameMode || transfer.frameMode,
5439
+ monitorIndex: Number(activeLiveStream.monitorIndex || monitorIndex),
5440
+ captureGeneration: Number(activeLiveStream.captureGeneration || 0),
5441
+ reused: true
5442
+ };
5282
5443
  }
5283
5444
  if (activeLiveStream
5284
5445
  && options.forceRestart !== true
@@ -5299,10 +5460,11 @@ export function createRemoteHub(options = {}) {
5299
5460
  streamId,
5300
5461
  fps,
5301
5462
  mode: transfer.mode,
5302
- frameMode: transfer.frameMode,
5303
- monitorIndex,
5304
- reused: true
5305
- };
5463
+ frameMode: transfer.frameMode,
5464
+ monitorIndex,
5465
+ captureGeneration: Number(activeLiveStream.captureGeneration || 0),
5466
+ reused: true
5467
+ };
5306
5468
  }
5307
5469
  if (options.forceRestart === true && activeLiveStream) {
5308
5470
  emitRemoteEvent('RemoteLiveStreamForcedRestart', device, {
@@ -5329,10 +5491,11 @@ export function createRemoteHub(options = {}) {
5329
5491
  lastFrameAt: activeLiveStream.lastFrameAt || ''
5330
5492
  });
5331
5493
  }
5332
- const result = sendCommand(deviceId, {
5333
- command: 'stream.start',
5334
- commandId,
5335
- payload: {
5494
+ const captureGeneration = nextCaptureGeneration(device);
5495
+ const result = sendCommand(deviceId, {
5496
+ command: 'stream.start',
5497
+ commandId,
5498
+ payload: {
5336
5499
  streamId,
5337
5500
  mode: transfer.mode,
5338
5501
  frameMode: transfer.frameMode,
@@ -5347,10 +5510,12 @@ export function createRemoteHub(options = {}) {
5347
5510
  maxWidth,
5348
5511
  maxHeight,
5349
5512
  quality,
5350
- monitorIndex,
5351
- streamPurpose,
5352
- requestedAt: now
5353
- }
5513
+ monitorIndex,
5514
+ streamPurpose,
5515
+ captureGeneration,
5516
+ sessionId: device.sessionId,
5517
+ requestedAt: now
5518
+ }
5354
5519
  });
5355
5520
 
5356
5521
  if (!result.ok) {
@@ -5392,9 +5557,10 @@ export function createRemoteHub(options = {}) {
5392
5557
  stopReason: '',
5393
5558
  lastFrameAt: '',
5394
5559
  lastFrameSeq: 0,
5395
- framesReceived: 0,
5396
- streamPurpose
5397
- });
5560
+ framesReceived: 0,
5561
+ streamPurpose,
5562
+ captureGeneration
5563
+ });
5398
5564
  device.counters.liveStreamsStarted += 1;
5399
5565
  emitRemoteEvent('RemoteLiveStreamStarted', device, {
5400
5566
  streamId,
@@ -5407,7 +5573,7 @@ export function createRemoteHub(options = {}) {
5407
5573
  monitorIndex
5408
5574
  });
5409
5575
 
5410
- return { ok: true, commandId, streamId, fps, mode: transfer.mode, frameMode: transfer.frameMode, monitorIndex };
5576
+ return { ok: true, commandId, streamId, fps, mode: transfer.mode, frameMode: transfer.frameMode, monitorIndex, captureGeneration };
5411
5577
  }
5412
5578
 
5413
5579
  function stopLiveStream(deviceId, options = {}) {
package/hub/src/server.js CHANGED
@@ -3844,10 +3844,10 @@ inputWss.on('connection', ws => {
3844
3844
  sendJson(ws, { type: 'RemoteInputError', error: 'invalid-json' });
3845
3845
  return;
3846
3846
  }
3847
- const deviceId = String(payload?.deviceId || '').trim();
3848
- const input = payload?.input && typeof payload.input === 'object'
3849
- ? { ...payload.input, hubReceivedAtEpochMs: Date.now() }
3850
- : { ...payload, hubReceivedAtEpochMs: Date.now() };
3847
+ const deviceId = String(payload?.deviceId || '').trim();
3848
+ const input = payload?.input && typeof payload.input === 'object'
3849
+ ? { ...payload.input, hubConnectionId: ws.liveDeskInputClientId, hubReceivedAtEpochMs: Date.now() }
3850
+ : { ...payload, hubConnectionId: ws.liveDeskInputClientId, hubReceivedAtEpochMs: Date.now() };
3851
3851
  const result = remoteHub.sendInputControl(deviceId, input);
3852
3852
  const inputType = String(input?.type || '').toLowerCase();
3853
3853
  const fireAndForget = payload?.fireAndForget === true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "livedesk",
3
- "version": "0.1.365",
3
+ "version": "0.1.366",
4
4
  "buildFlavor": "production",
5
5
  "description": "LiveDesk Hub and client launcher",
6
6
  "type": "module",
@@ -48,10 +48,10 @@
48
48
  "ws": "^8.18.3"
49
49
  },
50
50
  "optionalDependencies": {
51
- "@livedesk/fast-linux-x64": "0.1.365",
52
- "@livedesk/fast-osx-arm64": "0.1.365",
53
- "@livedesk/fast-osx-x64": "0.1.365",
54
- "@livedesk/fast-win-x64": "0.1.365"
51
+ "@livedesk/fast-linux-x64": "0.1.366",
52
+ "@livedesk/fast-osx-arm64": "0.1.366",
53
+ "@livedesk/fast-osx-x64": "0.1.366",
54
+ "@livedesk/fast-win-x64": "0.1.366"
55
55
  },
56
56
  "publishConfig": {
57
57
  "access": "public"