shell-mirror
Version:
Access your Mac shell from any device securely. Perfect for mobile coding with Claude Code CLI, Gemini CLI, and any shell tool.
145 lines (129 loc) • 4.38 kB
JavaScript
const os = require('os');
/**
* Network utilities for local WebSocket server setup
*/
class NetworkUtils {
/**
* Get the local IP address for WebSocket server binding
* Prioritizes Wi-Fi and Ethernet interfaces over others
*/
static getLocalIP() {
const interfaces = os.networkInterfaces();
// Priority order for interface selection
const priorityOrder = [
'Wi-Fi', // macOS Wi-Fi
'en0', // macOS primary interface
'en1', // macOS secondary interface
'eth0', // Linux Ethernet
'wlan0', // Linux Wi-Fi
'Ethernet', // Windows Ethernet
'Wi-Fi' // Windows Wi-Fi (might be different name)
];
// First, try priority interfaces
for (const interfaceName of priorityOrder) {
const addresses = interfaces[interfaceName];
if (addresses) {
for (const addr of addresses) {
if (addr.family === 'IPv4' && !addr.internal) {
console.log(`🌐 Selected network interface: ${interfaceName} (${addr.address})`);
return addr.address;
}
}
}
}
// Fallback: find any non-internal IPv4 address
for (const interfaceName in interfaces) {
const addresses = interfaces[interfaceName];
for (const addr of addresses) {
if (addr.family === 'IPv4' && !addr.internal) {
console.log(`🌐 Fallback network interface: ${interfaceName} (${addr.address})`);
return addr.address;
}
}
}
// Last resort: localhost (won't work for remote clients)
console.warn('⚠️ No external network interface found, using localhost');
return '127.0.0.1';
}
/**
* Find an available port for the WebSocket server
* @param {number} startPort - Starting port to check
* @param {number} maxAttempts - Maximum number of ports to try
*/
static async findAvailablePort(startPort = 8080, maxAttempts = 10) {
const net = require('net');
for (let i = 0; i < maxAttempts; i++) {
const port = startPort + i;
const isAvailable = await this.checkPortAvailable(port);
if (isAvailable) {
console.log(`🔌 Found available port: ${port}`);
return port;
}
}
throw new Error(`No available ports found in range ${startPort}-${startPort + maxAttempts - 1}`);
}
/**
* Check if a specific port is available
* @param {number} port - Port to check
*/
static checkPortAvailable(port) {
return new Promise((resolve) => {
const net = require('net');
const server = net.createServer();
server.listen(port, (err) => {
if (err) {
resolve(false);
} else {
server.once('close', () => resolve(true));
server.close();
}
});
server.on('error', () => resolve(false));
});
}
/**
* Generate WebSocket server configuration
* @param {number} port - Port for WebSocket server
*/
static generateWebSocketConfig(port) {
const localIP = this.getLocalIP();
return {
host: '0.0.0.0', // Bind to all interfaces
port: port,
localIP: localIP,
wsUrl: `ws://${localIP}:${port}`,
httpsWsUrl: `wss://${localIP}:${port}` // For future HTTPS support
};
}
/**
* Display network connectivity information
* @param {object} config - WebSocket configuration
*/
static displayNetworkInfo(config) {
console.log('');
console.log('🌐 Network Configuration:');
console.log(` Local IP: ${config.localIP}`);
console.log(` WebSocket URL: ${config.wsUrl}`);
console.log(` Binding: ${config.host}:${config.port}`);
console.log('');
console.log('📱 Access from other devices:');
console.log(` Use this URL in browser: https://shellmirror.app`);
console.log(` Mac agent WebSocket: ${config.wsUrl}`);
console.log('');
}
/**
* Validate WebSocket URL format
* @param {string} wsUrl - WebSocket URL to validate
*/
static validateWebSocketURL(wsUrl) {
try {
const url = new URL(wsUrl);
return (url.protocol === 'ws:' || url.protocol === 'wss:') &&
url.hostname &&
url.port;
} catch (error) {
return false;
}
}
}
module.exports = NetworkUtils;