livedesk 0.1.2 → 0.1.3

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.
Files changed (2) hide show
  1. package/bin/livedesk.js +105 -16
  2. package/package.json +1 -1
package/bin/livedesk.js CHANGED
@@ -3,14 +3,16 @@
3
3
  import { createRequire } from 'node:module';
4
4
  import { dirname, resolve } from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
- import { spawn } from 'node:child_process';
6
+ import { execFile, spawn } from 'node:child_process';
7
7
  import { existsSync } from 'node:fs';
8
8
  import os from 'node:os';
9
9
 
10
10
  const require = createRequire(import.meta.url);
11
11
  const __dirname = dirname(fileURLToPath(import.meta.url));
12
12
  const packageRoot = resolve(__dirname, '..');
13
- const DEFAULT_MANAGER_URL = 'http://127.0.0.1:5179';
13
+ const DEFAULT_MANAGER_HTTP_PORT = 5179;
14
+ const DEFAULT_REMOTE_HUB_PORT = 5197;
15
+ const DEFAULT_MANAGER_URL = `http://127.0.0.1:${DEFAULT_MANAGER_HTTP_PORT}`;
14
16
 
15
17
  function printHelp() {
16
18
  process.stdout.write(`
@@ -31,6 +33,9 @@ Manager options:
31
33
  --url <url> Browser URL to open. Default: ${DEFAULT_MANAGER_URL}
32
34
  --host <host> Manager HTTP host. Default: 0.0.0.0
33
35
  --port <port> Manager HTTP port. Default: 5179
36
+ --remote-port <port>
37
+ Client connection port. Default: 5197
38
+ --no-clean Do not stop existing processes on the manager ports.
34
39
 
35
40
  Common:
36
41
  --help Show this help.
@@ -72,19 +77,27 @@ function readVersion() {
72
77
 
73
78
  function parseManagerArgs(args) {
74
79
  const forwarded = [];
80
+ let cleanPortsOnStart = true;
75
81
  let openBrowserOnStart = true;
76
82
  let openUrl = DEFAULT_MANAGER_URL;
83
+ let openUrlExplicit = false;
77
84
  let host = '';
78
85
  let port = '';
86
+ let remotePort = '';
79
87
 
80
88
  for (let index = 0; index < args.length; index += 1) {
81
89
  const arg = args[index];
90
+ if (arg === '--no-clean') {
91
+ cleanPortsOnStart = false;
92
+ continue;
93
+ }
82
94
  if (arg === '--no-open') {
83
95
  openBrowserOnStart = false;
84
96
  continue;
85
97
  }
86
98
  if (arg === '--url') {
87
99
  openUrl = args[index + 1] || openUrl;
100
+ openUrlExplicit = true;
88
101
  index += 1;
89
102
  continue;
90
103
  }
@@ -98,18 +111,34 @@ function parseManagerArgs(args) {
98
111
  index += 1;
99
112
  continue;
100
113
  }
114
+ if (arg === '--remote-port' || arg === '--client-port') {
115
+ remotePort = args[index + 1] || remotePort;
116
+ index += 1;
117
+ continue;
118
+ }
101
119
  forwarded.push(arg);
102
120
  }
103
121
 
104
122
  return {
123
+ cleanPortsOnStart,
105
124
  forwarded,
106
125
  host,
107
126
  openBrowserOnStart,
127
+ openUrlExplicit,
108
128
  openUrl,
109
- port
129
+ port,
130
+ remotePort
110
131
  };
111
132
  }
112
133
 
134
+ function normalizePort(value, fallback) {
135
+ const number = Number(String(value || '').trim());
136
+ if (!Number.isInteger(number) || number < 1 || number > 65535) {
137
+ return fallback;
138
+ }
139
+ return number;
140
+ }
141
+
113
142
  function openBrowser(url) {
114
143
  if (!url) {
115
144
  return;
@@ -135,6 +164,63 @@ async function isManagerAlreadyRunning(url = DEFAULT_MANAGER_URL) {
135
164
  }
136
165
  }
137
166
 
167
+ function runQuiet(command, args) {
168
+ return new Promise(resolve => {
169
+ execFile(command, args, { windowsHide: true }, (error, stdout, stderr) => {
170
+ resolve({
171
+ ok: !error,
172
+ stdout: String(stdout || '').trim(),
173
+ stderr: String(stderr || '').trim(),
174
+ error
175
+ });
176
+ });
177
+ });
178
+ }
179
+
180
+ async function stopProcessesOnPorts(ports) {
181
+ const uniquePorts = [...new Set(ports.map(port => normalizePort(port, 0)).filter(Boolean))];
182
+ if (uniquePorts.length === 0) {
183
+ return;
184
+ }
185
+
186
+ if (os.platform() === 'win32') {
187
+ const script = [
188
+ '$ErrorActionPreference = "SilentlyContinue";',
189
+ `$ports = @(${uniquePorts.join(',')});`,
190
+ '$owners = foreach ($port in $ports) {',
191
+ ' Get-NetTCPConnection -LocalPort $port -ErrorAction SilentlyContinue | Select-Object -ExpandProperty OwningProcess -Unique',
192
+ '}',
193
+ '$owners = $owners | Where-Object { $_ -and $_ -ne $PID } | Sort-Object -Unique;',
194
+ 'foreach ($owner in $owners) {',
195
+ ' $proc = Get-Process -Id $owner -ErrorAction SilentlyContinue;',
196
+ ' if ($proc) {',
197
+ ' Write-Output "$($proc.Id):$($proc.ProcessName)";',
198
+ ' Stop-Process -Id $owner -Force -ErrorAction SilentlyContinue;',
199
+ ' }',
200
+ '}'
201
+ ].join(' ');
202
+ const result = await runQuiet('powershell.exe', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', script]);
203
+ if (result.stdout) {
204
+ console.log(`Restarting LiveDesk manager. Cleared port owner(s): ${result.stdout.replace(/\r?\n/g, ', ')}`);
205
+ }
206
+ return;
207
+ }
208
+
209
+ const script = uniquePorts.map(port => [
210
+ `pids="$(lsof -ti tcp:${port} -sTCP:LISTEN 2>/dev/null || true)"`,
211
+ 'for pid in $pids; do',
212
+ ' if [ "$pid" != "$$" ]; then',
213
+ ' kill "$pid" 2>/dev/null || true',
214
+ ' echo "$pid"',
215
+ ' fi',
216
+ 'done'
217
+ ].join('; ')).join('; ');
218
+ const result = await runQuiet('sh', ['-c', script]);
219
+ if (result.stdout) {
220
+ console.log(`Restarting LiveDesk manager. Cleared port owner(s): ${result.stdout.replace(/\r?\n/g, ', ')}`);
221
+ }
222
+ }
223
+
138
224
  async function waitForManager(url = DEFAULT_MANAGER_URL, timeoutMs = 10000) {
139
225
  const startedAt = Date.now();
140
226
  while (Date.now() - startedAt < timeoutMs) {
@@ -156,16 +242,20 @@ function resolvePackageEntry(packageName, fallback) {
156
242
 
157
243
  async function runManager(args) {
158
244
  const options = parseManagerArgs(args);
159
- const openUrl = options.port && options.openUrl === DEFAULT_MANAGER_URL
160
- ? `http://127.0.0.1:${options.port}`
161
- : (options.openUrl || DEFAULT_MANAGER_URL);
162
-
163
- if (await isManagerAlreadyRunning(openUrl)) {
164
- console.log(`LiveDesk manager is already running at ${openUrl}`);
165
- if (options.openBrowserOnStart) {
166
- openBrowser(openUrl);
167
- }
168
- return;
245
+ const httpPort = normalizePort(
246
+ options.port || process.env.LIVEDESK_HUB_HTTP_PORT || process.env.PORT,
247
+ DEFAULT_MANAGER_HTTP_PORT
248
+ );
249
+ const remotePort = normalizePort(
250
+ options.remotePort || process.env.REMOTE_HUB_PORT,
251
+ DEFAULT_REMOTE_HUB_PORT
252
+ );
253
+ const openUrl = options.openUrlExplicit
254
+ ? (options.openUrl || `http://127.0.0.1:${httpPort}`)
255
+ : `http://127.0.0.1:${httpPort}`;
256
+
257
+ if (options.cleanPortsOnStart) {
258
+ await stopProcessesOnPorts([httpPort, remotePort]);
169
259
  }
170
260
 
171
261
  const internalHubEntry = resolve(packageRoot, 'hub', 'src', 'server.js');
@@ -176,11 +266,10 @@ async function runManager(args) {
176
266
  const env = {
177
267
  ...process.env,
178
268
  LIVEDESK_HUB_HTTP_HOST: options.host || process.env.LIVEDESK_HUB_HTTP_HOST || '0.0.0.0',
269
+ LIVEDESK_HUB_HTTP_PORT: String(httpPort),
270
+ REMOTE_HUB_PORT: String(remotePort),
179
271
  LIVEDESK_WEB_DIST: existsSync(resolve(packagedWebDist, 'index.html')) ? packagedWebDist : (process.env.LIVEDESK_WEB_DIST || '')
180
272
  };
181
- if (options.port) {
182
- env.LIVEDESK_HUB_HTTP_PORT = options.port;
183
- }
184
273
 
185
274
  const child = spawn(process.execPath, [hubEntry, ...options.forwarded], {
186
275
  env,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "livedesk",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "LiveDesk manager and client launcher",
5
5
  "type": "module",
6
6
  "bin": {