livedesk 0.1.125 → 0.1.127

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/README.md CHANGED
@@ -10,6 +10,8 @@ npx -y livedesk hub
10
10
  ```
11
11
 
12
12
  This starts the LiveDesk Hub, opens the local screen wall, and accepts clients.
13
+ The Hub UI/API listens on `127.0.0.1` by default while the client endpoint
14
+ continues to listen on the LAN.
13
15
 
14
16
  ## Client
15
17
 
@@ -22,6 +24,14 @@ The client signs in with Google, discovers the active Hub, and connects to the w
22
24
  On Windows, enable **Start with Windows** on the connection page to reconnect
23
25
  automatically after reboot.
24
26
 
27
+ ## Plans
28
+
29
+ - Free: 5 personal devices with a standard wall banner ad.
30
+ - Plus LTD launch: 30 personal devices, no ads, USD 79 one-time.
31
+ - Pro LTD launch: commercial use and larger walls, no ads, USD 199 one-time.
32
+
33
+ Monthly and yearly subscriptions are planned after the LTD launch.
34
+
25
35
  ## Free ad slot
26
36
 
27
37
  Free accounts show a standard bottom ad slot. Set these Vite build variables
package/bin/livedesk.js CHANGED
@@ -34,7 +34,7 @@ Commands:
34
34
  Hub options:
35
35
  --no-open Do not open the browser automatically.
36
36
  --url <url> Browser URL to open. Default: ${DEFAULT_MANAGER_URL}
37
- --host <host> Hub HTTP host. Default: 0.0.0.0
37
+ --host <host> Hub HTTP host. Default: 127.0.0.1
38
38
  --port <port> Hub HTTP port. Default: 5179
39
39
  --remote-port <port>
40
40
  Client connection port. Default: 5197
@@ -304,7 +304,7 @@ async function runManager(args) {
304
304
  const packagedWebDist = resolve(packageRoot, 'web', 'dist');
305
305
  const env = {
306
306
  ...process.env,
307
- LIVEDESK_HUB_HTTP_HOST: options.host || process.env.LIVEDESK_HUB_HTTP_HOST || '0.0.0.0',
307
+ LIVEDESK_HUB_HTTP_HOST: options.host || process.env.LIVEDESK_HUB_HTTP_HOST || '127.0.0.1',
308
308
  LIVEDESK_HUB_HTTP_PORT: String(httpPort),
309
309
  REMOTE_HUB_PORT: String(remotePort),
310
310
  REMOTE_HUB_PAIR_TOKEN: pairToken,
@@ -17,11 +17,11 @@ const MAX_AGENT_TASK_CHARS = 4000;
17
17
  const MAX_AGENT_TASK_RESULT_CHARS = 3000;
18
18
  const RECENT_TASK_LIMIT = 12;
19
19
  const RECENT_TASK_BATCH_LIMIT = 16;
20
- const RECENT_FRAME_CACHE_TTL_MS = 12000;
21
- const RECENT_THUMBNAIL_FRAME_CACHE_LIMIT = 4;
22
- const RECENT_LIVE_FRAME_CACHE_LIMIT = 80;
23
- const RECENT_THUMBNAIL_FRAME_CACHE_MAX_BYTES = 3 * 1024 * 1024;
24
- const RECENT_LIVE_FRAME_CACHE_MAX_BYTES = 24 * 1024 * 1024;
20
+ const RECENT_FRAME_CACHE_TTL_MS = 4000;
21
+ const RECENT_THUMBNAIL_FRAME_CACHE_LIMIT = 2;
22
+ const RECENT_LIVE_FRAME_CACHE_LIMIT = 1;
23
+ const RECENT_THUMBNAIL_FRAME_CACHE_MAX_BYTES = 2 * 1024 * 1024;
24
+ const RECENT_LIVE_FRAME_CACHE_MAX_BYTES = 3 * 1024 * 1024;
25
25
  const LIVE_STREAM_PENDING_REUSE_MS = 5000;
26
26
  const LIVE_STREAM_MIN_FRESH_MS = 3000;
27
27
  const LIVE_STREAM_MAX_FRESH_MS = 12000;
@@ -1339,7 +1339,9 @@ export function createRemoteHub(options = {}) {
1339
1339
  const inputSockets = new Map();
1340
1340
  const frameSockets = new Map();
1341
1341
  const allSockets = new Set();
1342
+ const agentBinaryIngressLanes = [];
1342
1343
  const duplicateDeviceLogAt = new Map();
1344
+ let agentBinaryIngressDraining = false;
1343
1345
  let server = null;
1344
1346
  let started = false;
1345
1347
  let boundPort = requestedPort;
@@ -2838,7 +2840,7 @@ export function createRemoteHub(options = {}) {
2838
2840
 
2839
2841
  function buildFramePayloadBuffer(framePayload, frameData) {
2840
2842
  if (Buffer.isBuffer(framePayload)) {
2841
- return Buffer.from(framePayload);
2843
+ return framePayload;
2842
2844
  }
2843
2845
 
2844
2846
  const raw = String(frameData || '');
@@ -2929,6 +2931,8 @@ export function createRemoteHub(options = {}) {
2929
2931
  durationUs: Number.isFinite(Number(message.durationUs)) ? Number(message.durationUs) : 0,
2930
2932
  sourceGapMs: Number.isFinite(Number(message.sourceGapMs)) ? Number(message.sourceGapMs) : 0,
2931
2933
  agentSendGapMs: Number.isFinite(Number(message.agentSendGapMs)) ? Number(message.agentSendGapMs) : 0,
2934
+ agentSendDurationMs: Number.isFinite(Number(message.agentSendDurationMs)) ? Number(message.agentSendDurationMs) : 0,
2935
+ agentSendDurationFrameSeq: Number.isFinite(Number(message.agentSendDurationFrameSeq)) ? Number(message.agentSendDurationFrameSeq) : 0,
2932
2936
  agentPaceMs: Number.isFinite(Number(message.agentPaceMs)) ? Number(message.agentPaceMs) : 0,
2933
2937
  droppedByAgent: Number.isFinite(Number(message.droppedByAgent)) ? Number(message.droppedByAgent) : 0,
2934
2938
  hardwareEncoder: safeString(message.hardwareEncoder, 80),
@@ -3111,6 +3115,8 @@ export function createRemoteHub(options = {}) {
3111
3115
  durationUs: Number.isFinite(Number(message.durationUs)) ? Number(message.durationUs) : 0,
3112
3116
  sourceGapMs: Number.isFinite(Number(message.sourceGapMs)) ? Number(message.sourceGapMs) : 0,
3113
3117
  agentSendGapMs: Number.isFinite(Number(message.agentSendGapMs)) ? Number(message.agentSendGapMs) : 0,
3118
+ agentSendDurationMs: Number.isFinite(Number(message.agentSendDurationMs)) ? Number(message.agentSendDurationMs) : 0,
3119
+ agentSendDurationFrameSeq: Number.isFinite(Number(message.agentSendDurationFrameSeq)) ? Number(message.agentSendDurationFrameSeq) : 0,
3114
3120
  agentPaceMs: Number.isFinite(Number(message.agentPaceMs)) ? Number(message.agentPaceMs) : 0,
3115
3121
  droppedByAgent: Number.isFinite(Number(message.droppedByAgent)) ? Number(message.droppedByAgent) : 0,
3116
3122
  hardwareEncoder: safeString(message.hardwareEncoder, 80),
@@ -3305,29 +3311,56 @@ export function createRemoteHub(options = {}) {
3305
3311
  state.binaryQueueDrops = Number(state.binaryQueueDrops || 0) + 1;
3306
3312
  }
3307
3313
 
3308
- function drainQueuedAgentBinaryFrames(socket, state) {
3309
- if (state.binaryQueueDraining || state.closed) {
3314
+ function scheduleAgentBinaryIngress(socket, state) {
3315
+ if (!state || state.closed || !socket || socket.destroyed) {
3310
3316
  return;
3311
3317
  }
3312
- state.binaryQueueDraining = true;
3313
- setImmediate(() => {
3314
- state.binaryQueueDraining = false;
3315
- if (state.closed || socket.destroyed) {
3316
- state.binaryQueue.length = 0;
3317
- return;
3318
- }
3319
3318
 
3319
+ state.binaryQueueSocket = socket;
3320
+ if (state.binaryQueueScheduled !== true) {
3321
+ state.binaryQueueScheduled = true;
3322
+ agentBinaryIngressLanes.push(state);
3323
+ }
3324
+ drainAgentBinaryIngressRoundRobin();
3325
+ }
3326
+
3327
+ function drainAgentBinaryIngressRoundRobin() {
3328
+ if (agentBinaryIngressDraining) {
3329
+ return;
3330
+ }
3331
+ agentBinaryIngressDraining = true;
3332
+ setImmediate(() => {
3333
+ agentBinaryIngressDraining = false;
3320
3334
  let processed = 0;
3321
- while (state.binaryQueue.length > 0 && processed < AGENT_BINARY_INGRESS_DRAIN_BUDGET_PACKETS) {
3335
+ while (agentBinaryIngressLanes.length > 0 && processed < AGENT_BINARY_INGRESS_DRAIN_BUDGET_PACKETS) {
3336
+ const state = agentBinaryIngressLanes.shift();
3337
+ if (!state) {
3338
+ continue;
3339
+ }
3340
+
3341
+ state.binaryQueueScheduled = false;
3342
+ const socket = state.binaryQueueSocket;
3343
+ if (state.closed || !socket || socket.destroyed) {
3344
+ if (Array.isArray(state.binaryQueue)) {
3345
+ state.binaryQueue.length = 0;
3346
+ }
3347
+ continue;
3348
+ }
3349
+
3322
3350
  const item = state.binaryQueue.shift();
3323
3351
  if (item) {
3324
3352
  handleAgentBinaryFrame(socket, state, item.header, item.payload);
3325
3353
  processed += 1;
3326
3354
  }
3355
+
3356
+ if (state.binaryQueue.length > 0 && !state.closed && !socket.destroyed) {
3357
+ state.binaryQueueScheduled = true;
3358
+ agentBinaryIngressLanes.push(state);
3359
+ }
3327
3360
  }
3328
3361
 
3329
- if (state.binaryQueue.length > 0 && !state.closed && !socket.destroyed) {
3330
- drainQueuedAgentBinaryFrames(socket, state);
3362
+ if (agentBinaryIngressLanes.length > 0) {
3363
+ drainAgentBinaryIngressRoundRobin();
3331
3364
  }
3332
3365
  });
3333
3366
  }
@@ -3343,7 +3376,7 @@ export function createRemoteHub(options = {}) {
3343
3376
  dropQueuedAgentBinaryFrame(state);
3344
3377
  }
3345
3378
  state.binaryQueue.push({ header, payload });
3346
- drainQueuedAgentBinaryFrames(socket, state);
3379
+ scheduleAgentBinaryIngress(socket, state);
3347
3380
  }
3348
3381
 
3349
3382
  function handleAgentMessage(socket, state, message) {
@@ -3489,6 +3522,8 @@ export function createRemoteHub(options = {}) {
3489
3522
  frameOnly: false,
3490
3523
  binaryQueue: [],
3491
3524
  binaryQueueDraining: false,
3525
+ binaryQueueScheduled: false,
3526
+ binaryQueueSocket: null,
3492
3527
  binaryQueueDrops: 0,
3493
3528
  closed: false
3494
3529
  };
@@ -3541,6 +3576,8 @@ export function createRemoteHub(options = {}) {
3541
3576
  const detach = reason => {
3542
3577
  state.closed = true;
3543
3578
  state.binaryQueue.length = 0;
3579
+ state.binaryQueueScheduled = false;
3580
+ state.binaryQueueSocket = null;
3544
3581
  allSockets.delete(socket);
3545
3582
  clearTimeout(helloTimer);
3546
3583
  detachInputSocket(socket, reason);
@@ -3640,6 +3677,8 @@ export function createRemoteHub(options = {}) {
3640
3677
  pendingBinaryFrame: null,
3641
3678
  binaryQueue: [],
3642
3679
  binaryQueueDraining: false,
3680
+ binaryQueueScheduled: false,
3681
+ binaryQueueSocket: null,
3643
3682
  binaryQueueDrops: 0,
3644
3683
  closed: false
3645
3684
  };
@@ -3770,6 +3809,8 @@ export function createRemoteHub(options = {}) {
3770
3809
  socket.on('close', () => {
3771
3810
  state.closed = true;
3772
3811
  state.binaryQueue.length = 0;
3812
+ state.binaryQueueScheduled = false;
3813
+ state.binaryQueueSocket = null;
3773
3814
  allSockets.delete(socket);
3774
3815
  clearTimeout(helloTimer);
3775
3816
  detachInputSocket(socket);
@@ -3779,6 +3820,8 @@ export function createRemoteHub(options = {}) {
3779
3820
  socket.on('error', err => {
3780
3821
  state.closed = true;
3781
3822
  state.binaryQueue.length = 0;
3823
+ state.binaryQueueScheduled = false;
3824
+ state.binaryQueueSocket = null;
3782
3825
  allSockets.delete(socket);
3783
3826
  clearTimeout(helloTimer);
3784
3827
  detachInputSocket(socket, err?.message || 'socket-error');