@oussema_mili/test-pkg-123 1.1.41 → 1.1.43
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/cli-commands.js +1 -17
- package/containerManager.js +49 -7
- package/daemon/agentRunner.js +25 -11
- package/dist/styles.css +3 -0
- package/docker-actions/volumes.js +2 -2
- package/index.html +6 -2
- package/package.json +1 -1
- package/setup/setupWizard.js +2 -30
- package/store/configStore.js +11 -4
- package/utils/envSetup.js +5 -2
- package/utils/portUtils.js +35 -2
- package/postcss.config.mjs +0 -5
package/cli-commands.js
CHANGED
|
@@ -893,27 +893,11 @@ function setupCLICommands(program, startServerFunction) {
|
|
|
893
893
|
{ timeout: 10000 },
|
|
894
894
|
);
|
|
895
895
|
|
|
896
|
-
// Debug: Log the actual response
|
|
897
|
-
console.log(chalk.gray('\n Debug - Backend response:'), JSON.stringify(response.data, null, 2));
|
|
898
|
-
|
|
899
|
-
// Try to get userEntityRef from backend, fallback to session, then "unknown"
|
|
900
|
-
let userEntityRef = response.data.userEntityRef;
|
|
901
|
-
|
|
902
|
-
if (!userEntityRef || userEntityRef === 'unknown') {
|
|
903
|
-
const session = loadSession();
|
|
904
|
-
if (session && isSessionValid(session) && session.userEntityRef) {
|
|
905
|
-
userEntityRef = session.userEntityRef;
|
|
906
|
-
console.log(chalk.gray(' Using userEntityRef from session: ' + userEntityRef));
|
|
907
|
-
} else {
|
|
908
|
-
userEntityRef = "unknown";
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
|
|
912
896
|
// Save credentials
|
|
913
897
|
saveDeviceCredential({
|
|
914
898
|
deviceId: response.data.deviceId,
|
|
915
899
|
deviceCredential: response.data.deviceCredential,
|
|
916
|
-
userEntityRef: userEntityRef,
|
|
900
|
+
userEntityRef: response.data.userEntityRef || "unknown",
|
|
917
901
|
deviceName: deviceMetadata.deviceName,
|
|
918
902
|
platform: deviceMetadata.platform,
|
|
919
903
|
agentVersion: deviceMetadata.agentVersion,
|
package/containerManager.js
CHANGED
|
@@ -2,9 +2,11 @@ import os from 'os';
|
|
|
2
2
|
import { spawn, exec } from 'child_process';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
|
-
import { loadConfig } from './store/configStore.js';
|
|
5
|
+
import { loadConfig, DEFAULT_WS_PORT, DEFAULT_CONTAINER_PORT } from './store/configStore.js';
|
|
6
|
+
import { getDaemonState } from './store/daemonStore.js';
|
|
7
|
+
import { findAvailableContainerPort } from './utils/portUtils.js';
|
|
6
8
|
|
|
7
|
-
// Load configuration
|
|
9
|
+
// Load static configuration (ports are resolved dynamically)
|
|
8
10
|
const config = loadConfig();
|
|
9
11
|
const CONTAINER_NAME = config.containerName;
|
|
10
12
|
const AGENT_ROOT_DIR = config.agentRootDir;
|
|
@@ -13,10 +15,24 @@ const HOST_DATA_DIR = path.join(os.homedir(), AGENT_ROOT_DIR, REGISTRIES_DIR);
|
|
|
13
15
|
const HOST_FW_DIR = path.join(os.homedir(), AGENT_ROOT_DIR);
|
|
14
16
|
const CONTAINER_DATA_DIR = config.containerDataDir;
|
|
15
17
|
const CONTAINER_FW_DIR = '/app/.fenwave';
|
|
16
|
-
const APP_PORT = config.containerPort;
|
|
17
|
-
const WS_PORT = config.wsPort;
|
|
18
18
|
const DOCKER_IMAGE = config.dockerImage;
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Get the current WebSocket port (from daemon state or config)
|
|
22
|
+
*/
|
|
23
|
+
function getCurrentWsPort() {
|
|
24
|
+
const state = getDaemonState();
|
|
25
|
+
return state?.port || config.wsPort || DEFAULT_WS_PORT;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Get the current container port (from daemon state or config)
|
|
30
|
+
*/
|
|
31
|
+
function getCurrentContainerPort() {
|
|
32
|
+
const state = getDaemonState();
|
|
33
|
+
return state?.containerPort || config.containerPort || DEFAULT_CONTAINER_PORT;
|
|
34
|
+
}
|
|
35
|
+
|
|
20
36
|
/**
|
|
21
37
|
* Container Manager for Fenwave DevApp
|
|
22
38
|
*/
|
|
@@ -147,9 +163,26 @@ class ContainerManager {
|
|
|
147
163
|
|
|
148
164
|
/**
|
|
149
165
|
* Start the Fenwave DevApp container
|
|
166
|
+
* @param {Object} options - Start options
|
|
167
|
+
* @param {number} options.wsPort - WebSocket port to connect to
|
|
168
|
+
* @param {number} options.containerPort - Port to expose the container on (0 for auto-assign)
|
|
150
169
|
*/
|
|
151
|
-
async startContainer() {
|
|
170
|
+
async startContainer(options = {}) {
|
|
152
171
|
try {
|
|
172
|
+
// Get ports - use provided values, daemon state, config, or defaults
|
|
173
|
+
const WS_PORT = options.wsPort || getCurrentWsPort();
|
|
174
|
+
let APP_PORT = options.containerPort || getCurrentContainerPort();
|
|
175
|
+
|
|
176
|
+
// If containerPort is 0, find an available port
|
|
177
|
+
if (APP_PORT === 0) {
|
|
178
|
+
try {
|
|
179
|
+
APP_PORT = await findAvailableContainerPort(DEFAULT_CONTAINER_PORT);
|
|
180
|
+
console.log(chalk.blue(`📍 Auto-assigned container port: ${APP_PORT}`));
|
|
181
|
+
} catch (error) {
|
|
182
|
+
throw new Error(`Failed to find available container port: ${error.message}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
153
186
|
// Check Docker availability
|
|
154
187
|
const dockerAvailable = await this.checkDockerAvailable();
|
|
155
188
|
if (!dockerAvailable) {
|
|
@@ -223,13 +256,14 @@ class ContainerManager {
|
|
|
223
256
|
runProcess.on('close', (code) => {
|
|
224
257
|
if (code === 0) {
|
|
225
258
|
this.isRunning = true;
|
|
259
|
+
this.currentContainerPort = APP_PORT; // Store the actual port used
|
|
226
260
|
console.log(chalk.green('✅ Container started successfully'));
|
|
227
261
|
console.log(chalk.gray(` Image: ${DOCKER_IMAGE}`));
|
|
228
262
|
console.log(chalk.gray(` Container: ${CONTAINER_NAME}`));
|
|
229
263
|
console.log(chalk.gray(` Port: ${APP_PORT}`));
|
|
230
264
|
console.log(chalk.gray(` Data volume: ${HOST_DATA_DIR} -> ${CONTAINER_DATA_DIR}`));
|
|
231
265
|
console.log(chalk.gray(` Token volume: ${HOST_FW_DIR} -> ${CONTAINER_FW_DIR}`));
|
|
232
|
-
resolve();
|
|
266
|
+
resolve(APP_PORT); // Return the actual port used
|
|
233
267
|
} else {
|
|
234
268
|
console.error(chalk.red('❌ Failed to start container:'));
|
|
235
269
|
if (errorOutput) {
|
|
@@ -275,11 +309,19 @@ class ContainerManager {
|
|
|
275
309
|
return {
|
|
276
310
|
isRunning: running,
|
|
277
311
|
containerName: CONTAINER_NAME,
|
|
278
|
-
port:
|
|
312
|
+
port: this.currentContainerPort || getCurrentContainerPort(),
|
|
279
313
|
dataDirectory: HOST_DATA_DIR,
|
|
280
314
|
};
|
|
281
315
|
}
|
|
282
316
|
|
|
317
|
+
/**
|
|
318
|
+
* Get the current container port
|
|
319
|
+
* @returns {number} The current container port
|
|
320
|
+
*/
|
|
321
|
+
getContainerPort() {
|
|
322
|
+
return this.currentContainerPort || getCurrentContainerPort();
|
|
323
|
+
}
|
|
324
|
+
|
|
283
325
|
/**
|
|
284
326
|
* Show container logs
|
|
285
327
|
*/
|
package/daemon/agentRunner.js
CHANGED
|
@@ -10,7 +10,7 @@ import { setupWebSocketServer, readWsToken } from '../websocket-server.js';
|
|
|
10
10
|
import containerManager from '../containerManager.js';
|
|
11
11
|
import registryStore from '../store/registryStore.js';
|
|
12
12
|
import agentStore from '../store/agentStore.js';
|
|
13
|
-
import { loadConfig } from '../store/configStore.js';
|
|
13
|
+
import { loadConfig, DEFAULT_WS_PORT, DEFAULT_CONTAINER_PORT } from '../store/configStore.js';
|
|
14
14
|
import { initializeAgentStartTime } from '../docker-actions/general.js';
|
|
15
15
|
import { checkAppHasBeenRun } from '../docker-actions/apps.js';
|
|
16
16
|
import {
|
|
@@ -34,7 +34,6 @@ import { createLogger, writeLog } from './logManager.js';
|
|
|
34
34
|
// Load configuration
|
|
35
35
|
const config = loadConfig();
|
|
36
36
|
const BACKEND_URL = config.backendUrl;
|
|
37
|
-
const CONTAINER_PORT = config.containerPort;
|
|
38
37
|
|
|
39
38
|
// Store active connections
|
|
40
39
|
const clients = new Map();
|
|
@@ -261,12 +260,17 @@ async function gracefulShutdown(signal = 'SIGTERM', drainTimeout = 10000) {
|
|
|
261
260
|
/**
|
|
262
261
|
* Run the agent in the current process
|
|
263
262
|
* @param {Object} options - Options
|
|
264
|
-
* @param {number} options.preferredPort - Preferred WebSocket port
|
|
263
|
+
* @param {number} options.preferredPort - Preferred WebSocket port (0 for auto-assign)
|
|
264
|
+
* @param {number} options.preferredContainerPort - Preferred container port (0 for auto-assign)
|
|
265
265
|
* @param {boolean} options.isDaemon - Whether running as daemon
|
|
266
266
|
* @returns {Promise<Object>} Server instances
|
|
267
267
|
*/
|
|
268
268
|
export async function runAgent(options = {}) {
|
|
269
|
-
const {
|
|
269
|
+
const {
|
|
270
|
+
preferredPort = config.wsPort || DEFAULT_WS_PORT,
|
|
271
|
+
preferredContainerPort = config.containerPort || DEFAULT_CONTAINER_PORT,
|
|
272
|
+
isDaemon = false
|
|
273
|
+
} = options;
|
|
270
274
|
|
|
271
275
|
// Check for existing session
|
|
272
276
|
log('Checking for existing session...');
|
|
@@ -290,12 +294,16 @@ export async function runAgent(options = {}) {
|
|
|
290
294
|
throw new Error('Another instance of the agent is already running.');
|
|
291
295
|
}
|
|
292
296
|
|
|
293
|
-
// Find available port
|
|
297
|
+
// Find available port for WebSocket
|
|
294
298
|
let actualPort;
|
|
295
299
|
try {
|
|
296
|
-
|
|
297
|
-
|
|
300
|
+
// If preferredPort is 0, find any available port starting from default
|
|
301
|
+
const startPort = preferredPort === 0 ? DEFAULT_WS_PORT : preferredPort;
|
|
302
|
+
actualPort = await findAvailablePort(startPort);
|
|
303
|
+
if (actualPort !== preferredPort && preferredPort !== 0) {
|
|
298
304
|
log(`Port ${preferredPort} is in use, using port ${actualPort}`, 'warn');
|
|
305
|
+
} else if (preferredPort === 0) {
|
|
306
|
+
log(`Auto-assigned WebSocket port: ${actualPort}`);
|
|
299
307
|
}
|
|
300
308
|
} catch (error) {
|
|
301
309
|
releaseLock();
|
|
@@ -305,14 +313,19 @@ export async function runAgent(options = {}) {
|
|
|
305
313
|
// Initialize registry store
|
|
306
314
|
await registryStore.initialize();
|
|
307
315
|
|
|
308
|
-
// Start container
|
|
316
|
+
// Start container with dynamic ports
|
|
309
317
|
log('Starting container...');
|
|
318
|
+
let actualContainerPort = preferredContainerPort;
|
|
310
319
|
try {
|
|
311
|
-
await containerManager.startContainer(
|
|
312
|
-
|
|
320
|
+
actualContainerPort = await containerManager.startContainer({
|
|
321
|
+
wsPort: actualPort,
|
|
322
|
+
containerPort: preferredContainerPort,
|
|
323
|
+
});
|
|
324
|
+
log(`Container started successfully on port ${actualContainerPort}`);
|
|
313
325
|
} catch (containerError) {
|
|
314
326
|
log(`Failed to start container: ${containerError.message}`, 'warn');
|
|
315
327
|
log('Starting agent without container...');
|
|
328
|
+
actualContainerPort = preferredContainerPort || DEFAULT_CONTAINER_PORT;
|
|
316
329
|
}
|
|
317
330
|
|
|
318
331
|
// Create HTTP server with download endpoint handler
|
|
@@ -393,6 +406,7 @@ export async function runAgent(options = {}) {
|
|
|
393
406
|
saveDaemonState({
|
|
394
407
|
pid: process.pid,
|
|
395
408
|
port: actualPort,
|
|
409
|
+
containerPort: actualContainerPort,
|
|
396
410
|
startTime: new Date().toISOString(),
|
|
397
411
|
status: 'running',
|
|
398
412
|
userEntityRef: session.userEntityRef,
|
|
@@ -435,7 +449,7 @@ export async function runAgent(options = {}) {
|
|
|
435
449
|
console.log(chalk.green(' Fenwave Agent Started Successfully'));
|
|
436
450
|
console.log(chalk.green('='.repeat(66) + '\n'));
|
|
437
451
|
console.log(chalk.white(' Fenwave DevApp Dashboard:'));
|
|
438
|
-
console.log(chalk.cyan(` http://localhost:${
|
|
452
|
+
console.log(chalk.cyan(` http://localhost:${actualContainerPort}\n`));
|
|
439
453
|
console.log(chalk.white(' WebSocket Server:'));
|
|
440
454
|
console.log(chalk.cyan(` ws://localhost:${actualPort}\n`));
|
|
441
455
|
console.log(chalk.white(' User:'));
|
package/dist/styles.css
CHANGED
|
@@ -5,7 +5,7 @@ import util from 'util';
|
|
|
5
5
|
import fs from 'fs';
|
|
6
6
|
import path from 'path';
|
|
7
7
|
import os from 'os';
|
|
8
|
-
import { loadConfig } from '../store/configStore.js';
|
|
8
|
+
import { loadConfig, DEFAULT_WS_PORT } from '../store/configStore.js';
|
|
9
9
|
import { getDaemonState } from '../store/daemonStore.js';
|
|
10
10
|
|
|
11
11
|
const execPromise = util.promisify(exec);
|
|
@@ -23,7 +23,7 @@ function getAgentPort() {
|
|
|
23
23
|
return state.port;
|
|
24
24
|
}
|
|
25
25
|
const config = loadConfig();
|
|
26
|
-
return config.wsPort ||
|
|
26
|
+
return config.wsPort || DEFAULT_WS_PORT;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
// Cache for docker system df result to avoid concurrent operations
|
package/index.html
CHANGED
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
<script>
|
|
13
13
|
// Store WebSocket token in localStorage for App Builder to use
|
|
14
14
|
// These values are injected by the agent server
|
|
15
|
+
// Default ports: WS=3001, Container=3003 (may vary if ports are in use)
|
|
16
|
+
const DEFAULT_WS_PORT = "3001";
|
|
17
|
+
const DEFAULT_CONTAINER_PORT = "3003";
|
|
18
|
+
|
|
15
19
|
const wsToken = new URLSearchParams(window.location.search).get(
|
|
16
20
|
"wsToken",
|
|
17
21
|
);
|
|
@@ -22,8 +26,8 @@
|
|
|
22
26
|
|
|
23
27
|
if (wsToken) {
|
|
24
28
|
localStorage.setItem("fenwave-ws-token", wsToken);
|
|
25
|
-
localStorage.setItem("fenwave-ws-port", wsPort ||
|
|
26
|
-
localStorage.setItem("fenwave-container-port", containerPort ||
|
|
29
|
+
localStorage.setItem("fenwave-ws-port", wsPort || DEFAULT_WS_PORT);
|
|
30
|
+
localStorage.setItem("fenwave-container-port", containerPort || DEFAULT_CONTAINER_PORT);
|
|
27
31
|
}
|
|
28
32
|
</script>
|
|
29
33
|
<style>
|
package/package.json
CHANGED
package/setup/setupWizard.js
CHANGED
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
displayPrerequisites,
|
|
7
7
|
getMissingPrerequisites,
|
|
8
8
|
} from "../utils/prerequisites.js";
|
|
9
|
-
import { loadSession, isSessionValid } from "../auth.js";
|
|
10
9
|
import {
|
|
11
10
|
promptRegistrationToken,
|
|
12
11
|
promptConfirmation,
|
|
@@ -255,27 +254,11 @@ export class SetupWizard {
|
|
|
255
254
|
{ timeout: 10000 }
|
|
256
255
|
);
|
|
257
256
|
|
|
258
|
-
// Debug: Log the actual response to understand what backend returns
|
|
259
|
-
console.log(chalk.gray(' Debug - Backend response:'), JSON.stringify(response.data, null, 2));
|
|
260
|
-
|
|
261
|
-
// Try to get userEntityRef from backend, fallback to session, then "unknown"
|
|
262
|
-
let userEntityRef = response.data.userEntityRef;
|
|
263
|
-
|
|
264
|
-
if (!userEntityRef || userEntityRef === 'unknown') {
|
|
265
|
-
const session = loadSession();
|
|
266
|
-
if (session && isSessionValid(session) && session.userEntityRef) {
|
|
267
|
-
userEntityRef = session.userEntityRef;
|
|
268
|
-
console.log(chalk.gray(' Using userEntityRef from session: ' + userEntityRef));
|
|
269
|
-
} else {
|
|
270
|
-
userEntityRef = "unknown";
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
257
|
return {
|
|
275
258
|
deviceId: response.data.deviceId,
|
|
276
259
|
deviceCredential: response.data.deviceCredential,
|
|
277
260
|
registryConfig: response.data.registryConfig,
|
|
278
|
-
userEntityRef: userEntityRef,
|
|
261
|
+
userEntityRef: response.data.userEntityRef || "unknown",
|
|
279
262
|
};
|
|
280
263
|
} catch (error) {
|
|
281
264
|
if (error.response?.status === 401) {
|
|
@@ -384,22 +367,11 @@ export class SetupWizard {
|
|
|
384
367
|
console.log("");
|
|
385
368
|
displayHeader("Setup Summary");
|
|
386
369
|
|
|
387
|
-
// Use session userEntityRef if device credential has unknown
|
|
388
|
-
let displayUser = registrationData.userEntityRef;
|
|
389
|
-
if (!displayUser || displayUser === "unknown") {
|
|
390
|
-
const session = loadSession();
|
|
391
|
-
if (session && isSessionValid(session) && session.userEntityRef) {
|
|
392
|
-
displayUser = session.userEntityRef;
|
|
393
|
-
} else {
|
|
394
|
-
displayUser = "N/A";
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
|
|
398
370
|
displayKeyValue({
|
|
399
371
|
"Device ID": registrationData.deviceId,
|
|
400
372
|
"Device Name": registrationData.deviceName || "N/A",
|
|
401
373
|
Platform: registrationData.platform || "N/A",
|
|
402
|
-
User:
|
|
374
|
+
User: registrationData.userEntityRef || "N/A",
|
|
403
375
|
});
|
|
404
376
|
|
|
405
377
|
console.log("");
|
package/store/configStore.js
CHANGED
|
@@ -6,6 +6,13 @@ const FENWAVE_DIR = path.join(os.homedir(), ".fenwave");
|
|
|
6
6
|
const CONFIG_DIR = path.join(FENWAVE_DIR, "config");
|
|
7
7
|
const CONFIG_FILE = path.join(CONFIG_DIR, "agent.json");
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Default port values (used when no port is specified)
|
|
11
|
+
* Set to 0 to use a random available port
|
|
12
|
+
*/
|
|
13
|
+
export const DEFAULT_WS_PORT = 3001;
|
|
14
|
+
export const DEFAULT_CONTAINER_PORT = 3003;
|
|
15
|
+
|
|
9
16
|
/**
|
|
10
17
|
* Default configuration values
|
|
11
18
|
*/
|
|
@@ -16,16 +23,16 @@ const DEFAULT_CONFIG = {
|
|
|
16
23
|
|
|
17
24
|
// Container Configuration
|
|
18
25
|
containerName: "fenwave-devapp",
|
|
19
|
-
containerPort:
|
|
20
|
-
wsPort:
|
|
26
|
+
containerPort: DEFAULT_CONTAINER_PORT,
|
|
27
|
+
wsPort: DEFAULT_WS_PORT,
|
|
21
28
|
|
|
22
29
|
// Directory Configuration
|
|
23
30
|
agentRootDir: ".fenwave",
|
|
24
31
|
registriesDir: "registries",
|
|
25
32
|
containerDataDir: "/data",
|
|
26
33
|
|
|
27
|
-
// Docker Image (
|
|
28
|
-
dockerImage: "
|
|
34
|
+
// Docker Image (Docker Hub - testing, will be moved to GHCR later)
|
|
35
|
+
dockerImage: "oussemamili/dev-app:latest",
|
|
29
36
|
|
|
30
37
|
// Other Settings
|
|
31
38
|
authTimeoutMs: 60000,
|
package/utils/envSetup.js
CHANGED
|
@@ -22,10 +22,13 @@ export function ensureEnvironmentFiles(agentDir, showFoundMessages = false) {
|
|
|
22
22
|
// Check and create .env.agent for agent if not exists
|
|
23
23
|
const agentEnvPath = path.join(fenwareConfigDir, ".env.agent");
|
|
24
24
|
if (!fs.existsSync(agentEnvPath)) {
|
|
25
|
+
// Note: WS_PORT and CONTAINER_PORT can be set to 0 for auto-assignment
|
|
26
|
+
// or left empty to use defaults (3001 and 3003)
|
|
25
27
|
const defaultAgentEnv = `BACKEND_URL=http://localhost:7007
|
|
26
28
|
FRONTEND_URL=http://localhost:3000
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
# Port configuration (leave empty for defaults: 3001/3003, set to 0 for random available port)
|
|
30
|
+
WS_PORT=
|
|
31
|
+
CONTAINER_PORT=
|
|
29
32
|
AGENT_ROOT_DIR=.fenwave
|
|
30
33
|
REGISTRIES_DIR=registries
|
|
31
34
|
AUTH_TIMEOUT_MS=60000
|
package/utils/portUtils.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import net from 'net';
|
|
2
2
|
|
|
3
|
+
// Default ports (can be overridden via environment or config)
|
|
4
|
+
export const DEFAULT_WS_PORT = 3001;
|
|
5
|
+
export const DEFAULT_CONTAINER_PORT = 3003;
|
|
6
|
+
|
|
3
7
|
/**
|
|
4
8
|
* Check if a port is available
|
|
5
9
|
* @param {number} port - Port number to check
|
|
@@ -28,12 +32,12 @@ export async function isPortAvailable(port) {
|
|
|
28
32
|
|
|
29
33
|
/**
|
|
30
34
|
* Find an available port starting from a given port
|
|
31
|
-
* @param {number} startPort - Starting port number (default:
|
|
35
|
+
* @param {number} startPort - Starting port number (default: DEFAULT_WS_PORT)
|
|
32
36
|
* @param {number} maxAttempts - Maximum number of ports to try (default: 10)
|
|
33
37
|
* @returns {Promise<number>} Available port number
|
|
34
38
|
* @throws {Error} If no available port found in range
|
|
35
39
|
*/
|
|
36
|
-
export async function findAvailablePort(startPort =
|
|
40
|
+
export async function findAvailablePort(startPort = DEFAULT_WS_PORT, maxAttempts = 10) {
|
|
37
41
|
for (let i = 0; i < maxAttempts; i++) {
|
|
38
42
|
const port = startPort + i;
|
|
39
43
|
if (await isPortAvailable(port)) {
|
|
@@ -52,8 +56,37 @@ export async function isPortInUse(port) {
|
|
|
52
56
|
return !(await isPortAvailable(port));
|
|
53
57
|
}
|
|
54
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Find an available port for container, starting from a given port
|
|
61
|
+
* @param {number} startPort - Starting port number (default: DEFAULT_CONTAINER_PORT)
|
|
62
|
+
* @param {number} maxAttempts - Maximum number of ports to try (default: 10)
|
|
63
|
+
* @returns {Promise<number>} Available port number
|
|
64
|
+
*/
|
|
65
|
+
export async function findAvailableContainerPort(startPort = DEFAULT_CONTAINER_PORT, maxAttempts = 10) {
|
|
66
|
+
return findAvailablePort(startPort, maxAttempts);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Find a random available port in the ephemeral range
|
|
71
|
+
* @returns {Promise<number>} Available port number
|
|
72
|
+
*/
|
|
73
|
+
export async function findRandomAvailablePort() {
|
|
74
|
+
return new Promise((resolve, reject) => {
|
|
75
|
+
const server = net.createServer();
|
|
76
|
+
server.listen(0, '127.0.0.1', () => {
|
|
77
|
+
const { port } = server.address();
|
|
78
|
+
server.close(() => resolve(port));
|
|
79
|
+
});
|
|
80
|
+
server.on('error', reject);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
55
84
|
export default {
|
|
56
85
|
isPortAvailable,
|
|
57
86
|
findAvailablePort,
|
|
87
|
+
findAvailableContainerPort,
|
|
88
|
+
findRandomAvailablePort,
|
|
58
89
|
isPortInUse,
|
|
90
|
+
DEFAULT_WS_PORT,
|
|
91
|
+
DEFAULT_CONTAINER_PORT,
|
|
59
92
|
};
|