@oussema_mili/test-pkg-123 1.1.42 → 1.1.44
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/containerManager.js +60 -7
- package/daemon/agentRunner.js +42 -23
- package/dist/styles.css +3 -0
- package/docker-actions/volumes.js +2 -2
- package/index.html +6 -2
- package/package.json +1 -1
- package/store/configStore.js +11 -4
- package/utils/envSetup.js +5 -2
- package/utils/portUtils.js +42 -2
- package/postcss.config.mjs +0 -5
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, isPortAvailable } 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,37 @@ 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, 100, [WS_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
|
+
|
|
186
|
+
// If the desired container port conflicts with the WebSocket port or is unavailable, pick another port
|
|
187
|
+
if (APP_PORT === WS_PORT || !(await isPortAvailable(APP_PORT))) {
|
|
188
|
+
try {
|
|
189
|
+
const newPort = await findAvailableContainerPort(DEFAULT_CONTAINER_PORT, 100, [WS_PORT]);
|
|
190
|
+
console.log(chalk.yellow(`⚠️ Desired container port ${APP_PORT} is unavailable or conflicts with WebSocket port ${WS_PORT}. Using ${newPort} instead.`));
|
|
191
|
+
APP_PORT = newPort;
|
|
192
|
+
} catch (err) {
|
|
193
|
+
throw new Error(`Failed to resolve container port conflict: ${err.message}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
153
197
|
// Check Docker availability
|
|
154
198
|
const dockerAvailable = await this.checkDockerAvailable();
|
|
155
199
|
if (!dockerAvailable) {
|
|
@@ -223,13 +267,14 @@ class ContainerManager {
|
|
|
223
267
|
runProcess.on('close', (code) => {
|
|
224
268
|
if (code === 0) {
|
|
225
269
|
this.isRunning = true;
|
|
270
|
+
this.currentContainerPort = APP_PORT; // Store the actual port used
|
|
226
271
|
console.log(chalk.green('✅ Container started successfully'));
|
|
227
272
|
console.log(chalk.gray(` Image: ${DOCKER_IMAGE}`));
|
|
228
273
|
console.log(chalk.gray(` Container: ${CONTAINER_NAME}`));
|
|
229
274
|
console.log(chalk.gray(` Port: ${APP_PORT}`));
|
|
230
275
|
console.log(chalk.gray(` Data volume: ${HOST_DATA_DIR} -> ${CONTAINER_DATA_DIR}`));
|
|
231
276
|
console.log(chalk.gray(` Token volume: ${HOST_FW_DIR} -> ${CONTAINER_FW_DIR}`));
|
|
232
|
-
resolve();
|
|
277
|
+
resolve(APP_PORT); // Return the actual port used
|
|
233
278
|
} else {
|
|
234
279
|
console.error(chalk.red('❌ Failed to start container:'));
|
|
235
280
|
if (errorOutput) {
|
|
@@ -275,11 +320,19 @@ class ContainerManager {
|
|
|
275
320
|
return {
|
|
276
321
|
isRunning: running,
|
|
277
322
|
containerName: CONTAINER_NAME,
|
|
278
|
-
port:
|
|
323
|
+
port: this.currentContainerPort || getCurrentContainerPort(),
|
|
279
324
|
dataDirectory: HOST_DATA_DIR,
|
|
280
325
|
};
|
|
281
326
|
}
|
|
282
327
|
|
|
328
|
+
/**
|
|
329
|
+
* Get the current container port
|
|
330
|
+
* @returns {number} The current container port
|
|
331
|
+
*/
|
|
332
|
+
getContainerPort() {
|
|
333
|
+
return this.currentContainerPort || getCurrentContainerPort();
|
|
334
|
+
}
|
|
335
|
+
|
|
283
336
|
/**
|
|
284
337
|
* Show container logs
|
|
285
338
|
*/
|
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,16 +313,6 @@ export async function runAgent(options = {}) {
|
|
|
305
313
|
// Initialize registry store
|
|
306
314
|
await registryStore.initialize();
|
|
307
315
|
|
|
308
|
-
// Start container
|
|
309
|
-
log('Starting container...');
|
|
310
|
-
try {
|
|
311
|
-
await containerManager.startContainer();
|
|
312
|
-
log('Container started successfully');
|
|
313
|
-
} catch (containerError) {
|
|
314
|
-
log(`Failed to start container: ${containerError.message}`, 'warn');
|
|
315
|
-
log('Starting agent without container...');
|
|
316
|
-
}
|
|
317
|
-
|
|
318
316
|
// Create HTTP server with download endpoint handler
|
|
319
317
|
const agentId = uuidv4();
|
|
320
318
|
const existingWsToken = readWsToken();
|
|
@@ -388,13 +386,14 @@ export async function runAgent(options = {}) {
|
|
|
388
386
|
// Initialize agent start time
|
|
389
387
|
await initializeAgentStartTime();
|
|
390
388
|
|
|
391
|
-
// Save daemon state
|
|
389
|
+
// Save daemon pid and preliminary state (containerPort updated after container start)
|
|
392
390
|
saveDaemonPid(process.pid);
|
|
393
391
|
saveDaemonState({
|
|
394
392
|
pid: process.pid,
|
|
395
393
|
port: actualPort,
|
|
394
|
+
containerPort: null,
|
|
396
395
|
startTime: new Date().toISOString(),
|
|
397
|
-
status: '
|
|
396
|
+
status: 'starting',
|
|
398
397
|
userEntityRef: session.userEntityRef,
|
|
399
398
|
isDaemon,
|
|
400
399
|
});
|
|
@@ -428,14 +427,34 @@ export async function runAgent(options = {}) {
|
|
|
428
427
|
pollInterval,
|
|
429
428
|
};
|
|
430
429
|
|
|
430
|
+
// Now that WebSocket is bound, start container (so it cannot take the WS port)
|
|
431
|
+
log('Starting container (after WebSocket bind)...');
|
|
432
|
+
let actualContainerPort = preferredContainerPort;
|
|
433
|
+
try {
|
|
434
|
+
actualContainerPort = await containerManager.startContainer({
|
|
435
|
+
wsPort: actualPort,
|
|
436
|
+
containerPort: preferredContainerPort,
|
|
437
|
+
});
|
|
438
|
+
log(`Container started successfully on port ${actualContainerPort}`);
|
|
439
|
+
} catch (containerError) {
|
|
440
|
+
log(`Failed to start container: ${containerError.message}`, 'warn');
|
|
441
|
+
log('Continuing without container...');
|
|
442
|
+
actualContainerPort = preferredContainerPort || DEFAULT_CONTAINER_PORT;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Update daemon state now that containerPort is known
|
|
446
|
+
updateDaemonState({
|
|
447
|
+
port: actualPort,
|
|
448
|
+
containerPort: actualContainerPort,
|
|
449
|
+
status: 'running',
|
|
450
|
+
});
|
|
451
|
+
|
|
431
452
|
// Display startup info
|
|
432
|
-
console.log(
|
|
433
|
-
chalk.green('\n' + '='.repeat(66))
|
|
434
|
-
);
|
|
453
|
+
console.log(chalk.green('\n' + '='.repeat(66)));
|
|
435
454
|
console.log(chalk.green(' Fenwave Agent Started Successfully'));
|
|
436
455
|
console.log(chalk.green('='.repeat(66) + '\n'));
|
|
437
456
|
console.log(chalk.white(' Fenwave DevApp Dashboard:'));
|
|
438
|
-
console.log(chalk.cyan(` http://localhost:${
|
|
457
|
+
console.log(chalk.cyan(` http://localhost:${actualContainerPort}\n`));
|
|
439
458
|
console.log(chalk.white(' WebSocket Server:'));
|
|
440
459
|
console.log(chalk.cyan(` ws://localhost:${actualPort}\n`));
|
|
441
460
|
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/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,44 @@ 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 = 100, excludePorts = []) {
|
|
66
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
67
|
+
const port = startPort + i;
|
|
68
|
+
if (excludePorts && excludePorts.includes(port)) continue;
|
|
69
|
+
if (await isPortAvailable(port)) {
|
|
70
|
+
return port;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
throw new Error(`No available container port found in range ${startPort}-${startPort + maxAttempts - 1}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Find a random available port in the ephemeral range
|
|
78
|
+
* @returns {Promise<number>} Available port number
|
|
79
|
+
*/
|
|
80
|
+
export async function findRandomAvailablePort() {
|
|
81
|
+
return new Promise((resolve, reject) => {
|
|
82
|
+
const server = net.createServer();
|
|
83
|
+
server.listen(0, '127.0.0.1', () => {
|
|
84
|
+
const { port } = server.address();
|
|
85
|
+
server.close(() => resolve(port));
|
|
86
|
+
});
|
|
87
|
+
server.on('error', reject);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
55
91
|
export default {
|
|
56
92
|
isPortAvailable,
|
|
57
93
|
findAvailablePort,
|
|
94
|
+
findAvailableContainerPort,
|
|
95
|
+
findRandomAvailablePort,
|
|
58
96
|
isPortInUse,
|
|
97
|
+
DEFAULT_WS_PORT,
|
|
98
|
+
DEFAULT_CONTAINER_PORT,
|
|
59
99
|
};
|