livedesk 0.1.257 → 0.1.259

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/bin/livedesk.js CHANGED
@@ -220,28 +220,45 @@ function waitMilliseconds(milliseconds) {
220
220
  return new Promise(resolve => setTimeout(resolve, milliseconds));
221
221
  }
222
222
 
223
- function probePortBind(port, host = '0.0.0.0') {
224
- return new Promise(resolve => {
225
- const server = net.createServer();
226
- let settled = false;
227
- const finish = value => {
228
- if (settled) return;
223
+ function probePortBind(port, host = '0.0.0.0') {
224
+ return new Promise(resolve => {
225
+ const server = net.createServer();
226
+ let settled = false;
227
+ const finish = value => {
228
+ if (settled) return;
229
229
  settled = true;
230
230
  resolve(value);
231
231
  };
232
- server.once('error', error => finish({
233
- ok: false,
234
- error: error?.code || error?.message || 'bind-failed'
235
- }));
236
- server.listen(port, host, () => {
237
- server.close(() => finish({ ok: true, error: '' }));
238
- });
239
- });
240
- }
241
-
242
- async function canBindPort(port, host = '0.0.0.0') {
243
- return (await probePortBind(port, host)).ok;
244
- }
232
+ server.once('error', error => finish({
233
+ ok: false,
234
+ error: error?.code || error?.message || 'bind-failed'
235
+ }));
236
+ server.listen(port, host, () => {
237
+ server.close(() => finish({ ok: true, error: '' }));
238
+ });
239
+ });
240
+ }
241
+
242
+ async function canBindPort(port, host = '0.0.0.0') {
243
+ return (await probePortBind(port, host)).ok;
244
+ }
245
+
246
+ async function waitForPortBindable(port, host = '0.0.0.0', timeoutMs = PORT_CLEANUP_TIMEOUT_MS) {
247
+ const deadline = Date.now() + timeoutMs;
248
+ let lastProbe = { ok: false, error: 'bind-not-tested' };
249
+ do {
250
+ lastProbe = await probePortBind(port, host);
251
+ if (lastProbe.ok) {
252
+ return lastProbe;
253
+ }
254
+ const remainingMs = deadline - Date.now();
255
+ if (remainingMs <= 0) {
256
+ break;
257
+ }
258
+ await waitMilliseconds(Math.min(PORT_CLEANUP_POLL_MS, remainingMs));
259
+ } while (true);
260
+ return lastProbe;
261
+ }
245
262
 
246
263
  function isKnownLiveDeskProcess(processName, commandLine) {
247
264
  const normalizedName = String(processName || '').trim();
@@ -270,7 +287,7 @@ function assertPortCleanupReady(finalRecords, portStates) {
270
287
  const owners = finalRecords
271
288
  .filter(record => Number(record?.Port) === Number(port?.Port))
272
289
  .map(formatPortOwner);
273
- return `TCP ${port.Port}[available=${port.PortAvailable === true},bindable=${port.PortBindable === true},bind-error=${port.BindError || 'none'},owners=${owners.length ? owners.join('; ') : 'owner not visible'}`;
290
+ return `TCP ${port.Port}[available=${port.PortAvailable === true},bindable=${port.PortBindable === true},bind-error=${port.BindError || 'none'},owners=${owners.length ? owners.join('; ') : 'owner not visible'}`;
274
291
  }).join('], ');
275
292
  throw new Error(`LiveDesk Hub startup cleanup could not release the configured port(s): ${summary}]. Unrelated processes are preserved; stop the owning process manually or choose an explicit --port/--remote-port value.`);
276
293
  }
@@ -388,7 +405,7 @@ async function stopProcessesOnPorts(ports) {
388
405
  ' [pscustomobject]@{ Port = [int]$port; Pid = $owner; ProcessName = $processName; CommandLine = $commandLine; KnownLiveDesk = $knownLiveDesk; Action = "preserved"; KillAttempted = $false }',
389
406
  ' }',
390
407
  '}',
391
- '$records = @($ports | ForEach-Object { Get-PortRecords $_ } | Sort-Object Port,Pid -Unique);',
408
+ '$records = @($ports | ForEach-Object { Get-PortRecords $_ } | Sort-Object Port,Pid -Unique);',
392
409
  '$killableOwners = @($records | Where-Object { $_.KnownLiveDesk } | Group-Object Pid | ForEach-Object { @($_.Group)[0] });',
393
410
  'foreach ($owner in $killableOwners) {',
394
411
  ' Stop-Process -Id $owner.Pid -Force -ErrorAction SilentlyContinue;',
@@ -406,8 +423,8 @@ async function stopProcessesOnPorts(ports) {
406
423
  ' $portStates = @($ports | ForEach-Object {',
407
424
  ' $port = [int]$_;',
408
425
  ' $busy = $busyPorts -contains $port;',
409
- ' $bindable = [bool](-not $busy);',
410
- ' [pscustomobject]@{ Port = $port; PortAvailable = [bool](-not $busy); PortBindable = $bindable; Ready = [bool](-not $busy) }',
426
+ ' $bindable = [bool](-not $busy);',
427
+ ' [pscustomobject]@{ Port = $port; PortAvailable = [bool](-not $busy); PortBindable = $bindable; Ready = [bool](-not $busy) }',
411
428
  ' });',
412
429
  ' if ((@($portStates | Where-Object { -not $_.Ready }).Count -eq 0) -or (Get-Date).ToUniversalTime() -ge $deadline) { break }',
413
430
  ` Start-Sleep -Milliseconds ${PORT_CLEANUP_POLL_MS};`,
@@ -431,22 +448,22 @@ async function stopProcessesOnPorts(ports) {
431
448
  }
432
449
  const records = Array.isArray(payload?.Records) ? payload.Records : [];
433
450
  const finalRecords = Array.isArray(payload?.FinalRecords) ? payload.FinalRecords : [];
434
- const rawPortStates = Array.isArray(payload?.Ports) ? payload.Ports : [];
435
- const rawStatesByPort = new Map(rawPortStates.map(state => [Number(state?.Port), state]));
436
- const portStates = [];
437
- for (const port of uniquePorts) {
438
- const rawState = rawStatesByPort.get(Number(port)) || {};
439
- const probe = rawState.PortAvailable === true
440
- ? await probePortBind(port)
441
- : { ok: false, error: 'port-in-use' };
442
- portStates.push({
443
- ...rawState,
444
- Port: Number(port),
445
- PortBindable: probe.ok,
446
- BindError: probe.error || '',
447
- Ready: rawState.PortAvailable === true && probe.ok
448
- });
449
- }
451
+ const rawPortStates = Array.isArray(payload?.Ports) ? payload.Ports : [];
452
+ const rawStatesByPort = new Map(rawPortStates.map(state => [Number(state?.Port), state]));
453
+ const portStates = [];
454
+ for (const port of uniquePorts) {
455
+ const rawState = rawStatesByPort.get(Number(port)) || {};
456
+ const probe = rawState.PortAvailable === true
457
+ ? await waitForPortBindable(port)
458
+ : { ok: false, error: 'port-in-use' };
459
+ portStates.push({
460
+ ...rawState,
461
+ Port: Number(port),
462
+ PortBindable: probe.ok,
463
+ BindError: probe.error || '',
464
+ Ready: rawState.PortAvailable === true && probe.ok
465
+ });
466
+ }
450
467
  const stopped = records.filter(record => record?.Action === 'stopped');
451
468
  if (stopped.length > 0) {
452
469
  const stoppedOwners = new Map();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "livedesk",
3
- "version": "0.1.257",
3
+ "version": "0.1.259",
4
4
  "description": "LiveDesk Hub and client launcher",
5
5
  "type": "module",
6
6
  "bin": {