livedesk 0.1.80 → 0.1.82

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.
@@ -535,6 +535,8 @@ function normalizeRemoteInputEvent(value = {}) {
535
535
  const normalizedY = Number(input.normalizedY ?? input.NormalizedY);
536
536
  const deltaX = Number(input.deltaX ?? input.DeltaX);
537
537
  const deltaY = Number(input.deltaY ?? input.DeltaY);
538
+ const keyCode = Number(input.keyCode ?? input.KeyCode);
539
+ const location = Number(input.location ?? input.Location);
538
540
  return {
539
541
  type,
540
542
  normalizedX: Number.isFinite(normalizedX) ? Math.max(0, Math.min(1, normalizedX)) : undefined,
@@ -544,6 +546,8 @@ function normalizeRemoteInputEvent(value = {}) {
544
546
  deltaY: Number.isFinite(deltaY) ? Math.max(-4096, Math.min(4096, deltaY)) : 0,
545
547
  key: safeString(input.key || input.Key, 80),
546
548
  code: safeString(input.code || input.Code, 80),
549
+ keyCode: Number.isFinite(keyCode) ? Math.max(0, Math.min(65535, Math.trunc(keyCode))) : 0,
550
+ location: Number.isFinite(location) ? Math.max(0, Math.min(255, Math.trunc(location))) : 0,
547
551
  text: safeText(input.text || input.Text, 256),
548
552
  repeat: input.repeat === true || input.Repeat === true,
549
553
  controlLeaseId: safeString(input.controlLeaseId || input.ControlLeaseId, 128),
@@ -1201,6 +1205,7 @@ export function createRemoteHub(options = {}) {
1201
1205
  const devices = new Map();
1202
1206
  const taskBatches = new Map();
1203
1207
  const sockets = new Map();
1208
+ const inputSockets = new Map();
1204
1209
  const allSockets = new Set();
1205
1210
  const duplicateDeviceLogAt = new Map();
1206
1211
  let server = null;
@@ -1734,6 +1739,7 @@ export function createRemoteHub(options = {}) {
1734
1739
 
1735
1740
  function closeExistingDeviceSocket(deviceId, nextSessionId) {
1736
1741
  const existing = devices.get(deviceId);
1742
+ closeInputSocket(existing, 'replaced-by-new-session');
1737
1743
  if (!existing?.socket || existing.socket.destroyed) {
1738
1744
  return;
1739
1745
  }
@@ -1748,6 +1754,43 @@ export function createRemoteHub(options = {}) {
1748
1754
  existing.socket.destroy();
1749
1755
  }
1750
1756
 
1757
+ function closeInputSocket(device, reason = 'input-socket-closed') {
1758
+ const socket = device?.inputSocket;
1759
+ if (!socket) {
1760
+ return;
1761
+ }
1762
+
1763
+ device.inputSocket = null;
1764
+ inputSockets.delete(socket);
1765
+ try {
1766
+ writeJsonLine(socket, { type: 'disconnect', reason });
1767
+ } catch {
1768
+ // Best-effort notice before closing the side channel.
1769
+ }
1770
+ try {
1771
+ socket.destroy?.();
1772
+ } catch {
1773
+ // Ignore close failures.
1774
+ }
1775
+ }
1776
+
1777
+ function detachInputSocket(socket, reason = 'input-socket-closed') {
1778
+ const deviceId = inputSockets.get(socket);
1779
+ inputSockets.delete(socket);
1780
+ if (!deviceId) {
1781
+ return;
1782
+ }
1783
+
1784
+ const device = devices.get(deviceId);
1785
+ if (!device || device.inputSocket !== socket) {
1786
+ return;
1787
+ }
1788
+
1789
+ device.inputSocket = null;
1790
+ device.inputLastSeenAt = new Date().toISOString();
1791
+ emitRemoteEvent('RemoteInputSocketDisconnected', device, { reason });
1792
+ }
1793
+
1751
1794
  function getDeviceActivityMs(device) {
1752
1795
  return Math.max(
1753
1796
  Date.parse(device?.lastSeenAt || '') || 0,
@@ -2132,6 +2175,7 @@ export function createRemoteHub(options = {}) {
2132
2175
 
2133
2176
  const device = {
2134
2177
  socket,
2178
+ inputSocket: null,
2135
2179
  deviceId,
2136
2180
  sessionId,
2137
2181
  deviceName: safeString(hello.deviceName || hello.hostname || deviceId, 120),
@@ -2148,6 +2192,8 @@ export function createRemoteHub(options = {}) {
2148
2192
  capabilities,
2149
2193
  connected: true,
2150
2194
  connectedAt: now,
2195
+ inputConnectedAt: '',
2196
+ inputLastSeenAt: '',
2151
2197
  disconnectedAt: '',
2152
2198
  lastSeenAt: now,
2153
2199
  lastStatusAt: '',
@@ -2191,6 +2237,37 @@ export function createRemoteHub(options = {}) {
2191
2237
  return device;
2192
2238
  }
2193
2239
 
2240
+ function attachInputSocket(socket, hello) {
2241
+ const deviceId = normalizeDeviceId(hello.deviceId);
2242
+ const device = devices.get(deviceId);
2243
+ if (!device || !device.connected || !device.socket || device.socket.destroyed) {
2244
+ writeJsonLine(socket, { type: 'error', error: 'device-not-connected' });
2245
+ socket.destroy();
2246
+ return null;
2247
+ }
2248
+
2249
+ closeInputSocket(device, 'replaced-by-new-input-socket');
2250
+
2251
+ const now = new Date().toISOString();
2252
+ device.inputSocket = socket;
2253
+ device.inputConnectedAt = now;
2254
+ device.inputLastSeenAt = now;
2255
+ inputSockets.set(socket, deviceId);
2256
+
2257
+ writeJsonLine(socket, {
2258
+ type: 'welcome',
2259
+ channel: 'input',
2260
+ protocol: REMOTE_AGENT_PROTOCOL,
2261
+ protocolVersion: REMOTE_PROTOCOL_VERSION,
2262
+ sessionId: device.sessionId,
2263
+ deviceId,
2264
+ serverTime: now
2265
+ });
2266
+
2267
+ emitRemoteEvent('RemoteInputSocketConnected', device);
2268
+ return device;
2269
+ }
2270
+
2194
2271
  function detachSocket(socket, reason = 'socket-closed') {
2195
2272
  const deviceId = sockets.get(socket);
2196
2273
  sockets.delete(socket);
@@ -2205,6 +2282,7 @@ export function createRemoteHub(options = {}) {
2205
2282
 
2206
2283
  device.connected = false;
2207
2284
  device.socket = null;
2285
+ closeInputSocket(device, reason);
2208
2286
  device.disconnectedAt = new Date().toISOString();
2209
2287
  device.lastDisconnectReason = reason;
2210
2288
  if (device.activeLiveStream) {
@@ -2969,6 +3047,12 @@ export function createRemoteHub(options = {}) {
2969
3047
  }
2970
3048
 
2971
3049
  const device = state.device;
3050
+ if (state.inputOnly) {
3051
+ writeJsonLine(socket, { type: 'error', error: 'input-channel-binary-not-supported' });
3052
+ socket.destroy();
3053
+ return;
3054
+ }
3055
+
2972
3056
  device.counters.messagesReceived += 1;
2973
3057
  device.lastSeenAt = new Date().toISOString();
2974
3058
 
@@ -3012,6 +3096,19 @@ export function createRemoteHub(options = {}) {
3012
3096
  return;
3013
3097
  }
3014
3098
 
3099
+ const channel = safeString(message.channel || message.Channel, 40).toLowerCase();
3100
+ if (channel === 'input') {
3101
+ const device = attachInputSocket(socket, message);
3102
+ if (!device) {
3103
+ return;
3104
+ }
3105
+
3106
+ state.authenticated = true;
3107
+ state.device = device;
3108
+ state.inputOnly = true;
3109
+ return;
3110
+ }
3111
+
3015
3112
  const device = attachDevice(socket, message);
3016
3113
  if (!device) {
3017
3114
  return;
@@ -3028,6 +3125,11 @@ export function createRemoteHub(options = {}) {
3028
3125
  return;
3029
3126
  }
3030
3127
 
3128
+ if (state.inputOnly) {
3129
+ device.inputLastSeenAt = new Date().toISOString();
3130
+ return;
3131
+ }
3132
+
3031
3133
  device.counters.messagesReceived += 1;
3032
3134
  device.lastSeenAt = new Date().toISOString();
3033
3135
 
@@ -3099,7 +3201,8 @@ export function createRemoteHub(options = {}) {
3099
3201
 
3100
3202
  const state = {
3101
3203
  authenticated: false,
3102
- device: null
3204
+ device: null,
3205
+ inputOnly: false
3103
3206
  };
3104
3207
 
3105
3208
  const helloTimer = setTimeout(() => {
@@ -3150,6 +3253,7 @@ export function createRemoteHub(options = {}) {
3150
3253
  const detach = reason => {
3151
3254
  allSockets.delete(socket);
3152
3255
  clearTimeout(helloTimer);
3256
+ detachInputSocket(socket, reason);
3153
3257
  detachSocket(socket, reason);
3154
3258
  };
3155
3259
 
@@ -3239,6 +3343,7 @@ export function createRemoteHub(options = {}) {
3239
3343
  const state = {
3240
3344
  authenticated: false,
3241
3345
  device: null,
3346
+ inputOnly: false,
3242
3347
  buffer: Buffer.alloc(0),
3243
3348
  pendingBinaryFrame: null
3244
3349
  };
@@ -3319,11 +3424,13 @@ export function createRemoteHub(options = {}) {
3319
3424
  socket.on('close', () => {
3320
3425
  allSockets.delete(socket);
3321
3426
  clearTimeout(helloTimer);
3427
+ detachInputSocket(socket);
3322
3428
  detachSocket(socket);
3323
3429
  });
3324
3430
  socket.on('error', err => {
3325
3431
  allSockets.delete(socket);
3326
3432
  clearTimeout(helloTimer);
3433
+ detachInputSocket(socket, err?.message || 'socket-error');
3327
3434
  detachSocket(socket, err?.message || 'socket-error');
3328
3435
  });
3329
3436
  }
@@ -3510,6 +3617,24 @@ export function createRemoteHub(options = {}) {
3510
3617
  return { ok: false, error: 'missing-input-type' };
3511
3618
  }
3512
3619
 
3620
+ const inputSocket = device.inputSocket;
3621
+ if (inputSocket && !inputSocket.destroyed) {
3622
+ const sent = writeJsonLine(inputSocket, {
3623
+ type: 'input.control',
3624
+ payload: {
3625
+ ...normalized,
3626
+ issuedAt: normalized.issuedAt || new Date().toISOString()
3627
+ }
3628
+ });
3629
+ if (sent) {
3630
+ device.counters.commandsSent += 1;
3631
+ device.inputLastSeenAt = new Date().toISOString();
3632
+ return { ok: true, inputSocket: true };
3633
+ }
3634
+
3635
+ detachInputSocket(inputSocket, 'input-socket-write-failed');
3636
+ }
3637
+
3513
3638
  return sendCommand(deviceId, {
3514
3639
  command: 'input.control',
3515
3640
  payload: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "livedesk",
3
- "version": "0.1.80",
3
+ "version": "0.1.82",
4
4
  "description": "LiveDesk Hub and client launcher",
5
5
  "type": "module",
6
6
  "bin": {
@@ -30,7 +30,7 @@
30
30
  "node": ">=20"
31
31
  },
32
32
  "dependencies": {
33
- "@livedesk/client": "0.1.60",
33
+ "@livedesk/client": "0.1.62",
34
34
  "cors": "^2.8.5",
35
35
  "express": "^4.21.2",
36
36
  "ws": "^8.18.3"