untunneled.dev 0.2.8 → 0.3.0
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/dist/index.js +78 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -199,7 +199,7 @@ var TunnelUI = ({
|
|
|
199
199
|
/* @__PURE__ */ jsx4(Text4, { color: "cyan", bold: true, children: LOGO }),
|
|
200
200
|
/* @__PURE__ */ jsxs4(Box4, { justifyContent: "space-between", paddingX: 1, children: [
|
|
201
201
|
/* @__PURE__ */ jsx4(ConnectionStatus, { status: connectionStatus }),
|
|
202
|
-
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "v0.
|
|
202
|
+
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "v0.3.0" })
|
|
203
203
|
] })
|
|
204
204
|
] }),
|
|
205
205
|
/* @__PURE__ */ jsxs4(
|
|
@@ -345,6 +345,7 @@ var RelayTunnel = class extends EventEmitter {
|
|
|
345
345
|
reconnectAttempts = 0;
|
|
346
346
|
isClosing = false;
|
|
347
347
|
pingInterval = null;
|
|
348
|
+
activeWebSockets = /* @__PURE__ */ new Map();
|
|
348
349
|
constructor(options) {
|
|
349
350
|
super();
|
|
350
351
|
this.options = options;
|
|
@@ -399,24 +400,40 @@ var RelayTunnel = class extends EventEmitter {
|
|
|
399
400
|
}
|
|
400
401
|
});
|
|
401
402
|
this.ws.on("message", async (data) => {
|
|
402
|
-
await this.
|
|
403
|
+
await this.handleRelayFrame(data);
|
|
403
404
|
});
|
|
404
405
|
} catch (error) {
|
|
405
406
|
reject(error);
|
|
406
407
|
}
|
|
407
408
|
});
|
|
408
409
|
}
|
|
409
|
-
async
|
|
410
|
-
|
|
411
|
-
let request;
|
|
410
|
+
async handleRelayFrame(data) {
|
|
411
|
+
let frame;
|
|
412
412
|
try {
|
|
413
|
-
|
|
413
|
+
frame = JSON.parse(String(data));
|
|
414
414
|
} catch {
|
|
415
415
|
if (this.options.verbose) {
|
|
416
|
-
console.error("[Relay] Invalid
|
|
416
|
+
console.error("[Relay] Invalid frame data");
|
|
417
417
|
}
|
|
418
418
|
return;
|
|
419
419
|
}
|
|
420
|
+
switch (frame.type) {
|
|
421
|
+
case "http_request":
|
|
422
|
+
await this.handleHttpRequest(frame);
|
|
423
|
+
break;
|
|
424
|
+
case "ws_open":
|
|
425
|
+
await this.handleWsOpen(frame);
|
|
426
|
+
break;
|
|
427
|
+
case "ws_message":
|
|
428
|
+
await this.handleWsMessage(frame);
|
|
429
|
+
break;
|
|
430
|
+
case "ws_close":
|
|
431
|
+
await this.handleWsClose(frame);
|
|
432
|
+
break;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
async handleHttpRequest(request) {
|
|
436
|
+
const startTime = Date.now();
|
|
420
437
|
try {
|
|
421
438
|
const url = `http://127.0.0.1:${this.localPort}${request.path}`;
|
|
422
439
|
const fetchOptions = {
|
|
@@ -430,6 +447,7 @@ var RelayTunnel = class extends EventEmitter {
|
|
|
430
447
|
const body = await response.text();
|
|
431
448
|
const duration = Date.now() - startTime;
|
|
432
449
|
const relayResponse = {
|
|
450
|
+
type: "http_response",
|
|
433
451
|
id: request.id,
|
|
434
452
|
status: response.status,
|
|
435
453
|
statusText: response.statusText,
|
|
@@ -453,6 +471,7 @@ var RelayTunnel = class extends EventEmitter {
|
|
|
453
471
|
console.error(`[Relay] Proxy error: ${err.message}`);
|
|
454
472
|
}
|
|
455
473
|
const errorResponse = {
|
|
474
|
+
type: "http_response",
|
|
456
475
|
id: request.id,
|
|
457
476
|
status: 502,
|
|
458
477
|
statusText: "Bad Gateway",
|
|
@@ -462,6 +481,53 @@ var RelayTunnel = class extends EventEmitter {
|
|
|
462
481
|
this.ws?.send(JSON.stringify(errorResponse));
|
|
463
482
|
}
|
|
464
483
|
}
|
|
484
|
+
async handleWsOpen(frame) {
|
|
485
|
+
const localUrl = `ws://127.0.0.1:${this.localPort}${frame.url}`;
|
|
486
|
+
if (this.options.verbose) {
|
|
487
|
+
console.log(`[Relay] Opening WebSocket to ${localUrl}`);
|
|
488
|
+
}
|
|
489
|
+
const localWs = new WebSocket(localUrl, {
|
|
490
|
+
headers: frame.headers
|
|
491
|
+
});
|
|
492
|
+
this.activeWebSockets.set(frame.id, localWs);
|
|
493
|
+
localWs.on("message", (data, isBinary) => {
|
|
494
|
+
const message = {
|
|
495
|
+
type: "ws_message",
|
|
496
|
+
id: frame.id,
|
|
497
|
+
data: isBinary ? data.toString("base64") : data.toString(),
|
|
498
|
+
isBinary
|
|
499
|
+
};
|
|
500
|
+
this.ws?.send(JSON.stringify(message));
|
|
501
|
+
});
|
|
502
|
+
localWs.on("close", () => {
|
|
503
|
+
const closeFrame = {
|
|
504
|
+
type: "ws_close",
|
|
505
|
+
id: frame.id
|
|
506
|
+
};
|
|
507
|
+
this.ws?.send(JSON.stringify(closeFrame));
|
|
508
|
+
this.activeWebSockets.delete(frame.id);
|
|
509
|
+
});
|
|
510
|
+
localWs.on("error", (err) => {
|
|
511
|
+
if (this.options.verbose) {
|
|
512
|
+
console.error(`[Relay] Local WebSocket error (${frame.id}):`, err.message);
|
|
513
|
+
}
|
|
514
|
+
localWs.close();
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
async handleWsMessage(frame) {
|
|
518
|
+
const localWs = this.activeWebSockets.get(frame.id);
|
|
519
|
+
if (localWs && localWs.readyState === WebSocket.OPEN) {
|
|
520
|
+
const data = frame.isBinary ? Buffer.from(frame.data, "base64") : frame.data;
|
|
521
|
+
localWs.send(data);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
async handleWsClose(frame) {
|
|
525
|
+
const localWs = this.activeWebSockets.get(frame.id);
|
|
526
|
+
if (localWs) {
|
|
527
|
+
localWs.close();
|
|
528
|
+
this.activeWebSockets.delete(frame.id);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
465
531
|
setupPing() {
|
|
466
532
|
this.pingInterval = setInterval(() => {
|
|
467
533
|
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
@@ -496,6 +562,10 @@ var RelayTunnel = class extends EventEmitter {
|
|
|
496
562
|
async disconnect() {
|
|
497
563
|
this.isClosing = true;
|
|
498
564
|
this.clearPing();
|
|
565
|
+
for (const ws of this.activeWebSockets.values()) {
|
|
566
|
+
ws.close();
|
|
567
|
+
}
|
|
568
|
+
this.activeWebSockets.clear();
|
|
499
569
|
if (this.ws) {
|
|
500
570
|
this.ws.close(1e3, "Client closing");
|
|
501
571
|
this.ws = null;
|
|
@@ -648,7 +718,7 @@ async function stopCommand(tunnelId) {
|
|
|
648
718
|
|
|
649
719
|
// src/index.ts
|
|
650
720
|
var program = new Command();
|
|
651
|
-
program.name("untunneled").description("Fast, free localhost tunneling").version("0.
|
|
721
|
+
program.name("untunneled").description("Fast, free localhost tunneling").version("0.3.0");
|
|
652
722
|
program.argument("<port>", "Local port to tunnel").option("--qr", "Show QR code for mobile access").option("--password <password>", "Password protect the tunnel").option("--allow <emails>", "Email whitelist (comma-separated)").option("--verbose", "Show detailed logs").option("--no-telemetry", "Disable anonymous usage tracking").action(startCommand);
|
|
653
723
|
program.command("list").description("List active tunnels").action(listCommand);
|
|
654
724
|
program.command("stop").argument("<tunnel-id>", "Tunnel ID to stop").description("Stop a running tunnel").action(stopCommand);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/commands/start.ts","../src/ui/TunnelUI.tsx","../src/ui/QRCode.tsx","../src/ui/RequestLog.tsx","../src/ui/ConnectionStatus.tsx","../src/tunnel/relay.ts","../src/config.ts","../src/analytics.ts","../src/commands/list.ts","../src/commands/stop.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { startCommand, listCommand, stopCommand } from './commands/index.js';\n\nconst program = new Command();\n\nprogram.name('untunneled').description('Fast, free localhost tunneling').version('0.2.8');\n\nprogram\n .argument('<port>', 'Local port to tunnel')\n .option('--qr', 'Show QR code for mobile access')\n .option('--password <password>', 'Password protect the tunnel')\n .option('--allow <emails>', 'Email whitelist (comma-separated)')\n .option('--verbose', 'Show detailed logs')\n .option('--no-telemetry', 'Disable anonymous usage tracking')\n .action(startCommand);\n\nprogram.command('list').description('List active tunnels').action(listCommand);\n\nprogram\n .command('stop')\n .argument('<tunnel-id>', 'Tunnel ID to stop')\n .description('Stop a running tunnel')\n .action(stopCommand);\n\nprogram.parse();\n","import { render } from 'ink';\nimport React from 'react';\nimport { TunnelUI } from '../ui/TunnelUI.js';\nimport { RelayTunnel } from '../tunnel/relay.js';\nimport { Analytics } from '../analytics.js';\nimport {\n generateTunnelId,\n getProjectName,\n getTunnelUrl,\n validatePort,\n parseAllowList,\n} from '../config.js';\nimport type { StartOptions } from '../types.js';\n\nexport async function startCommand(port: string, options: StartOptions): Promise<void> {\n let localPort: number;\n\n try {\n localPort = validatePort(port);\n } catch (error) {\n console.error((error as Error).message);\n process.exit(1);\n }\n\n const projectName = getProjectName();\n const tunnelId = generateTunnelId(projectName);\n const tunnelUrl = getTunnelUrl(tunnelId);\n\n const analytics = new Analytics({\n enabled: options.telemetry !== false,\n tunnelId,\n });\n\n await analytics.track('tunnel_start', {\n has_auth: !!options.password || !!options.allow,\n node_version: process.version,\n platform: process.platform,\n });\n\n const relayTunnel = new RelayTunnel({\n localPort,\n tunnelId,\n tunnelUrl,\n password: options.password,\n allowList: parseAllowList(options.allow),\n verbose: options.verbose,\n });\n\n try {\n await relayTunnel.connect();\n\n if (options.verbose) {\n console.log(`Tunnel URL: ${tunnelUrl}`);\n console.log(`Forwarding to localhost:${localPort}`);\n }\n } catch (error) {\n console.error('Failed to connect to relay server:', (error as Error).message);\n await analytics.shutdown();\n process.exit(1);\n }\n\n const { waitUntilExit } = render(\n React.createElement(TunnelUI, {\n tunnelUrl,\n localPort,\n showQR: options.qr,\n relayTunnel,\n })\n );\n\n const cleanup = async () => {\n const duration = relayTunnel.getDuration();\n const requests = relayTunnel.getRequestCount();\n\n await analytics.track('tunnel_end', {\n duration_seconds: duration,\n requests: requests,\n });\n\n await relayTunnel.disconnect();\n await analytics.shutdown();\n };\n\n process.on('SIGINT', async () => {\n await cleanup();\n process.exit(0);\n });\n\n process.on('SIGTERM', async () => {\n await cleanup();\n process.exit(0);\n });\n\n await waitUntilExit();\n}\n","import React, { useState, useEffect } from 'react';\nimport { Box, Text, useApp } from 'ink';\nimport { QRCode } from './QRCode.js';\nimport { RequestLog } from './RequestLog.js';\nimport { ConnectionStatus } from './ConnectionStatus.js';\nimport type { RelayTunnel } from '../tunnel/relay.js';\nimport type { RequestLog as RequestLogType, TunnelStats } from '../types.js';\n\ninterface TunnelUIProps {\n tunnelUrl: string;\n localPort: number;\n showQR?: boolean;\n relayTunnel: RelayTunnel;\n}\n\nconst MAX_LOGS = 10;\n\nconst LOGO = `\n ╦ ╦╔╗╔╔╦╗╦ ╦╔╗╔╔╗╔╔═╗╦ ╔═╗╔╦╗ ╔╦╗╔═╗╦ ╦\n ║ ║║║║ ║ ║ ║║║║║║║║╣ ║ ║╣ ║║ ║║║╣ ╚╗╔╝\n ╚═╝╝╚╝ ╩ ╚═╝╝╚╝╝╚╝╚═╝╩═╝╚═╝═╩╝o═╩╝╚═╝ ╚╝ \n`;\n\nconst formatUptime = (ms: number) => {\n const seconds = Math.floor(ms / 1000);\n const minutes = Math.floor(seconds / 60);\n const hours = Math.floor(minutes / 60);\n return `${hours}h ${minutes % 60}m ${seconds % 60}s`;\n};\n\nexport const TunnelUI: React.FC<TunnelUIProps> = ({\n tunnelUrl,\n localPort,\n showQR,\n relayTunnel,\n}) => {\n useApp();\n const [requests, setRequests] = useState<RequestLogType[]>([]);\n const [stats, setStats] = useState<TunnelStats>({\n total: 0,\n startTime: Date.now(),\n });\n const [relayConnected, setRelayConnected] = useState(relayTunnel.isConnected());\n const [uptime, setUptime] = useState(0);\n\n useEffect(() => {\n const handleRequest = (req: RequestLogType) => {\n setRequests((prev) => [...prev.slice(-(MAX_LOGS - 1)), req]);\n setStats((prev) => ({\n ...prev,\n total: prev.total + 1,\n }));\n };\n\n const handleRelayConnected = () => setRelayConnected(true);\n const handleRelayDisconnected = () => setRelayConnected(false);\n\n relayTunnel.on('request', handleRequest);\n relayTunnel.on('connected', handleRelayConnected);\n relayTunnel.on('disconnected', handleRelayDisconnected);\n\n const uptimeTimer = setInterval(() => {\n setUptime(Date.now() - stats.startTime);\n }, 1000);\n\n return () => {\n relayTunnel.off('request', handleRequest);\n relayTunnel.off('connected', handleRelayConnected);\n relayTunnel.off('disconnected', handleRelayDisconnected);\n clearInterval(uptimeTimer);\n };\n }, [relayTunnel, stats.startTime]);\n\n const connectionStatus = relayConnected ? 'connected' : 'connecting';\n const accentColor = relayConnected ? 'cyan' : 'yellow';\n\n return (\n <Box flexDirection=\"column\" paddingX={2} paddingY={1}>\n {/* Header */}\n <Box flexDirection=\"column\" marginBottom={1}>\n <Text color=\"cyan\" bold>\n {LOGO}\n </Text>\n <Box justifyContent=\"space-between\" paddingX={1}>\n <ConnectionStatus status={connectionStatus} />\n <Text dimColor>v0.2.8</Text>\n </Box>\n </Box>\n\n {/* Main Connection Info */}\n <Box\n borderStyle=\"round\"\n borderColor={accentColor}\n paddingX={2}\n paddingY={1}\n flexDirection=\"column\"\n >\n <Box>\n <Text bold>Public URL: </Text>\n <Text bold color=\"black\" backgroundColor=\"cyan\">\n {' '}\n {tunnelUrl}{' '}\n </Text>\n </Box>\n <Box marginTop={1}>\n <Text dimColor>Forwarding: </Text>\n <Text color=\"yellow\">http://localhost:{localPort}</Text>\n <Text dimColor> → </Text>\n <Text color=\"cyan\">{tunnelUrl}</Text>\n </Box>\n </Box>\n\n {/* Stats Bar */}\n <Box marginTop={1} paddingX={1} gap={4}>\n <Box>\n <Text dimColor>Requests: </Text>\n <Text color=\"white\" bold>\n {stats.total}\n </Text>\n </Box>\n <Box>\n <Text dimColor>Uptime: </Text>\n <Text color=\"white\" bold>\n {formatUptime(uptime)}\n </Text>\n </Box>\n <Box>\n <Text dimColor>Latency: </Text>\n <Text color=\"white\" bold>\n {requests.length > 0 ? `${requests[requests.length - 1]?.duration}ms` : '-'}\n </Text>\n </Box>\n </Box>\n\n {/* QR Code */}\n {showQR && (\n <Box\n marginY={1}\n borderStyle=\"single\"\n borderColor=\"gray\"\n paddingX={1}\n alignSelf=\"flex-start\"\n >\n <QRCode url={tunnelUrl} />\n </Box>\n )}\n\n {/* Request Log */}\n <Box marginTop={1} flexDirection=\"column\">\n <Box marginBottom={1}>\n <Text bold color=\"cyan\">\n ─── Recent Requests ───\n </Text>\n </Box>\n <RequestLog requests={requests} />\n </Box>\n\n {/* Footer */}\n <Box\n marginTop={1}\n borderStyle=\"single\"\n borderTop={true}\n borderBottom={false}\n borderLeft={false}\n borderRight={false}\n borderColor=\"gray\"\n paddingTop={1}\n >\n <Box flexGrow={1}>\n <Text dimColor>Press </Text>\n <Text color=\"yellow\" bold>\n Ctrl+C\n </Text>\n <Text dimColor> to stop tunnel</Text>\n </Box>\n <Box>\n <Text dimColor>Sponsor: </Text>\n <Text color=\"magenta\" bold>\n untunneled.dev/sponsor\n </Text>\n </Box>\n </Box>\n </Box>\n );\n};\n","import React, { useState, useEffect } from 'react';\nimport { Box, Text } from 'ink';\nimport qrcode from 'qrcode-terminal';\n\ninterface QRCodeProps {\n url: string;\n}\n\nexport const QRCode: React.FC<QRCodeProps> = ({ url }) => {\n const [qrString, setQrString] = useState('');\n\n useEffect(() => {\n qrcode.generate(url, { small: true }, (qr: string) => {\n setQrString(qr);\n });\n }, [url]);\n\n if (!qrString) {\n return (\n <Box>\n <Text dimColor>Generating QR code...</Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" borderStyle=\"single\" paddingX={1}>\n <Text dimColor>Scan with mobile:</Text>\n <Text>{qrString}</Text>\n </Box>\n );\n};\n","import React from 'react';\nimport { Box, Text } from 'ink';\nimport type { RequestLog as RequestLogType } from '../types.js';\n\ninterface RequestLogProps {\n requests: RequestLogType[];\n}\n\nconst getStatusColor = (status: number): string => {\n if (status >= 500) return 'red';\n if (status >= 400) return 'yellow';\n if (status >= 300) return 'cyan';\n if (status >= 200) return 'green';\n return 'gray';\n};\n\nconst getMethodColor = (method: string): string => {\n switch (method.toUpperCase()) {\n case 'GET':\n return 'cyan';\n case 'POST':\n return 'green';\n case 'PUT':\n return 'yellow';\n case 'DELETE':\n return 'red';\n case 'PATCH':\n return 'magenta';\n default:\n return 'white';\n }\n};\n\nexport const RequestLog: React.FC<RequestLogProps> = ({ requests }) => {\n if (requests.length === 0) {\n return (\n <Box\n borderStyle=\"single\"\n borderColor=\"gray\"\n flexDirection=\"column\"\n alignItems=\"center\"\n paddingX={3}\n paddingY={1}\n width={40}\n alignSelf=\"center\"\n >\n <Text color=\"cyan\">⏳ Waiting for requests...</Text>\n <Box height={1} />\n <Text dimColor>Open the URL in your browser</Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\">\n {requests.map((req) => {\n const time = new Date(req.timestamp).toLocaleTimeString([], {\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false,\n });\n\n return (\n <Box key={req.id} gap={1}>\n <Text dimColor>[{time}]</Text>\n <Box width={8}>\n <Text color={getMethodColor(req.method)} bold>\n {req.method.padEnd(7)}\n </Text>\n </Box>\n <Box flexGrow={1}>\n <Text>{req.path.length > 40 ? req.path.substring(0, 37) + '...' : req.path}</Text>\n </Box>\n <Box width={6}>\n <Text color={getStatusColor(req.status)} bold>\n {req.status}\n </Text>\n </Box>\n <Box width={10} justifyContent=\"flex-end\">\n <Text color={req.duration > 500 ? 'yellow' : 'gray'}>\n {req.duration.toString().padStart(4)}ms\n </Text>\n </Box>\n </Box>\n );\n })}\n </Box>\n );\n};\n","import React, { useState, useEffect } from 'react';\nimport { Box, Text } from 'ink';\nimport type { ConnectionStatus as ConnectionStatusType } from '../types.js';\n\ninterface ConnectionStatusProps {\n status: ConnectionStatusType;\n}\n\nconst SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];\n\nconst getStatusIndicator = (\n status: ConnectionStatusType,\n frame: string\n): { color: string; symbol: string; label: string } => {\n switch (status) {\n case 'connected':\n return { color: 'green', symbol: '✔', label: 'CONNECTED' };\n case 'connecting':\n return { color: 'yellow', symbol: frame, label: 'CONNECTING' };\n case 'disconnected':\n return { color: 'gray', symbol: '○', label: 'DISCONNECTED' };\n case 'error':\n return { color: 'red', symbol: '✘', label: 'ERROR' };\n default:\n return { color: 'white', symbol: '?', label: String(status).toUpperCase() };\n }\n};\n\nexport const ConnectionStatus: React.FC<ConnectionStatusProps> = ({ status }) => {\n const [frameIndex, setFrameIndex] = useState(0);\n\n useEffect(() => {\n if (status !== 'connecting') return;\n\n const timer = setInterval(() => {\n setFrameIndex((prev) => (prev + 1) % SPINNER_FRAMES.length);\n }, 80);\n\n return () => clearInterval(timer);\n }, [status]);\n\n const frame = SPINNER_FRAMES[frameIndex] ?? ' ';\n const indicator = getStatusIndicator(status, frame);\n\n return (\n <Box>\n <Text color={indicator.color} bold>\n {indicator.symbol} {indicator.label}\n </Text>\n </Box>\n );\n};\n","import { WebSocket } from 'ws';\nimport { EventEmitter } from 'node:events';\nimport { getRelayUrl, TIMEOUTS } from '../config.js';\nimport type { RelayRequest, RelayResponse, RequestLog } from '../types.js';\n\ninterface RelayTunnelOptions {\n localPort: number;\n tunnelId: string;\n tunnelUrl: string;\n password?: string;\n allowList?: string[];\n verbose?: boolean;\n}\n\nexport class RelayTunnel extends EventEmitter {\n private ws: WebSocket | null = null;\n private localPort: number;\n private tunnelId: string;\n private tunnelUrl: string;\n private options: RelayTunnelOptions;\n private startTime: number = 0;\n private requestCount: number = 0;\n private reconnectAttempts: number = 0;\n private isClosing: boolean = false;\n private pingInterval: NodeJS.Timeout | null = null;\n\n constructor(options: RelayTunnelOptions) {\n super();\n this.options = options;\n this.localPort = options.localPort;\n this.tunnelId = options.tunnelId;\n this.tunnelUrl = options.tunnelUrl;\n }\n\n async connect(): Promise<void> {\n this.startTime = Date.now();\n this.isClosing = false;\n\n const wsUrl = getRelayUrl(this.tunnelId);\n\n if (this.options.verbose) {\n console.log(`[Relay] Connecting to ${wsUrl}`);\n }\n\n return new Promise((resolve, reject) => {\n try {\n this.ws = new WebSocket(wsUrl, {\n headers: {\n 'X-Tunnel-Auth': this.options.password || '',\n 'X-Tunnel-Allow': this.options.allowList?.join(',') || '',\n },\n });\n\n const connectionTimeout = setTimeout(() => {\n if (this.ws && this.ws.readyState !== WebSocket.OPEN) {\n this.ws.terminate();\n reject(new Error('Connection timeout'));\n }\n }, TIMEOUTS.RELAY_REQUEST);\n\n this.ws.on('open', () => {\n clearTimeout(connectionTimeout);\n this.reconnectAttempts = 0;\n\n if (this.options.verbose) {\n console.log('[Relay] Connected');\n }\n\n this.setupPing();\n this.emit('connected');\n resolve();\n });\n\n this.ws.on('error', (err) => {\n clearTimeout(connectionTimeout);\n\n if (this.options.verbose) {\n console.error('[Relay] WebSocket error:', err.message);\n }\n\n this.emit('error', err);\n reject(err);\n });\n\n this.ws.on('close', () => {\n this.clearPing();\n\n if (!this.isClosing) {\n this.emit('disconnected');\n this.attemptReconnect();\n }\n });\n\n this.ws.on('message', async (data) => {\n await this.handleRelayRequest(data);\n });\n } catch (error) {\n reject(error);\n }\n });\n }\n\n private async handleRelayRequest(data: Buffer | ArrayBuffer | Buffer[]): Promise<void> {\n const startTime = Date.now();\n let request: RelayRequest;\n\n try {\n request = JSON.parse(String(data)) as RelayRequest;\n } catch {\n if (this.options.verbose) {\n console.error('[Relay] Invalid request data');\n }\n return;\n }\n\n try {\n const url = `http://127.0.0.1:${this.localPort}${request.path}`;\n\n const fetchOptions: RequestInit = {\n method: request.method,\n headers: request.headers,\n };\n\n if (request.body && !['GET', 'HEAD'].includes(request.method)) {\n fetchOptions.body = request.body;\n }\n\n const response = await fetch(url, fetchOptions);\n const body = await response.text();\n const duration = Date.now() - startTime;\n\n const relayResponse: RelayResponse = {\n id: request.id,\n status: response.status,\n statusText: response.statusText,\n headers: Object.fromEntries(response.headers.entries()),\n body,\n };\n\n this.ws?.send(JSON.stringify(relayResponse));\n\n const log: RequestLog = {\n id: request.id,\n timestamp: Date.now(),\n method: request.method,\n path: request.path,\n status: response.status,\n duration,\n };\n\n this.emit('request', log);\n this.requestCount++;\n } catch (error) {\n const err = error as any;\n\n if (this.options.verbose || err.code === 'ECONNREFUSED') {\n console.error(`[Relay] Proxy error: ${err.message}`);\n }\n\n const errorResponse: RelayResponse = {\n id: request.id,\n status: 502,\n statusText: 'Bad Gateway',\n headers: {},\n body: `Error: ${err.message}`,\n };\n\n this.ws?.send(JSON.stringify(errorResponse));\n }\n }\n\n private setupPing(): void {\n this.pingInterval = setInterval(() => {\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n this.ws.ping();\n }\n }, TIMEOUTS.WEBSOCKET_PING);\n }\n\n private clearPing(): void {\n if (this.pingInterval) {\n clearInterval(this.pingInterval);\n this.pingInterval = null;\n }\n }\n\n private async attemptReconnect(): Promise<void> {\n if (this.isClosing) return;\n\n this.reconnectAttempts++;\n const delay = Math.min(\n TIMEOUTS.RECONNECT_BASE * Math.pow(2, this.reconnectAttempts - 1),\n TIMEOUTS.RECONNECT_MAX\n );\n\n if (this.options.verbose) {\n console.log(`[Relay] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);\n }\n\n setTimeout(async () => {\n if (this.isClosing) return;\n\n try {\n await this.connect();\n } catch (_) {}\n }, delay);\n }\n\n async disconnect(): Promise<void> {\n this.isClosing = true;\n this.clearPing();\n\n if (this.ws) {\n this.ws.close(1000, 'Client closing');\n this.ws = null;\n }\n }\n\n getDuration(): number {\n if (this.startTime === 0) return 0;\n return Math.floor((Date.now() - this.startTime) / 1000);\n }\n\n getRequestCount(): number {\n return this.requestCount;\n }\n\n isConnected(): boolean {\n return this.ws !== null && this.ws.readyState === WebSocket.OPEN;\n }\n\n getTunnelUrl(): string {\n return this.tunnelUrl;\n }\n}\n","import { nanoid } from 'nanoid';\nimport path from 'node:path';\n\nexport const RELAY_DOMAIN = 'relay.untunneled.dev';\nexport const TUNNEL_DOMAIN = 'untunneled.dev';\n\nexport const getRelayUrl = (tunnelId: string): string => {\n const host = process.env['UNTUNNELED_RELAY_HOST'] || RELAY_DOMAIN;\n const protocol = host.includes('localhost') ? 'ws' : 'wss';\n return `${protocol}://${host}/${tunnelId}`;\n};\n\nexport const getTunnelUrl = (tunnelId: string): string => {\n const domain = process.env['UNTUNNELED_TUNNEL_DOMAIN'] || TUNNEL_DOMAIN;\n return `https://${tunnelId}.${domain}`;\n};\n\nexport function getProjectName(): string {\n const cwd = process.cwd();\n const folderName = path.basename(cwd);\n\n return (\n folderName\n .toLowerCase()\n .replace(/[^a-z0-9-]/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '')\n .slice(0, 20) || 'tunnel'\n );\n}\n\nexport function generateTunnelId(projectName?: string): string {\n const name = projectName || getProjectName();\n const suffix = nanoid(6).toLowerCase();\n return `${name}-${suffix}`;\n}\n\nexport function validatePort(port: string | number): number {\n const portNum = typeof port === 'string' ? parseInt(port, 10) : port;\n\n if (isNaN(portNum) || portNum < 1 || portNum > 65535) {\n throw new Error(`Invalid port number: ${port}. Must be between 1 and 65535.`);\n }\n\n return portNum;\n}\n\nexport function parseAllowList(allow?: string): string[] | undefined {\n if (!allow) return undefined;\n\n return allow\n .split(',')\n .map((email) => email.trim().toLowerCase())\n .filter((email) => email.length > 0);\n}\n\nexport const TIMEOUTS = {\n RELAY_REQUEST: 30000,\n WEBSOCKET_PING: 30000,\n RECONNECT_BASE: 1000,\n RECONNECT_MAX: 30000,\n};\n","import { PostHog } from 'posthog-node';\n\nconst POSTHOG_API_KEY = process.env['POSTHOG_API_KEY'] || 'phc_placeholder';\nconst POSTHOG_HOST = 'https://app.posthog.com';\n\ninterface AnalyticsOptions {\n enabled: boolean;\n tunnelId: string;\n}\n\ntype EventName = 'tunnel_start' | 'tunnel_end' | 'request_handled';\n\ninterface EventProperties {\n tunnel_start: {\n has_auth: boolean;\n node_version: string;\n platform: string;\n };\n tunnel_end: {\n duration_seconds: number;\n requests: number;\n };\n request_handled: {\n method: string;\n status: number;\n duration_ms: number;\n };\n}\n\nexport class Analytics {\n private posthog: PostHog | null = null;\n private tunnelId: string;\n private enabled: boolean;\n\n constructor(options: AnalyticsOptions) {\n this.tunnelId = options.tunnelId;\n this.enabled = options.enabled;\n\n if (this.enabled && POSTHOG_API_KEY !== 'phc_placeholder') {\n this.posthog = new PostHog(POSTHOG_API_KEY, {\n host: POSTHOG_HOST,\n flushAt: 10,\n flushInterval: 10000,\n });\n }\n }\n\n async track<E extends EventName>(event: E, properties?: EventProperties[E]): Promise<void> {\n if (!this.posthog || !this.enabled) return;\n\n try {\n this.posthog.capture({\n distinctId: this.tunnelId,\n event,\n properties: {\n ...properties,\n version: '0.1.0',\n cli: true,\n },\n });\n } catch (_) {}\n }\n\n async shutdown(): Promise<void> {\n if (!this.posthog) return;\n\n try {\n await this.posthog.shutdown();\n } catch (_) {}\n }\n\n isEnabled(): boolean {\n return this.enabled && this.posthog !== null;\n }\n}\n","export async function listCommand(): Promise<void> {\n console.log('Active tunnels:');\n console.log(' (No active tunnels)');\n console.log('');\n console.log('Note: Tunnel list feature coming soon.');\n}\n","export async function stopCommand(tunnelId: string): Promise<void> {\n console.log(`Stopping tunnel: ${tunnelId}`);\n console.log('');\n console.log('Note: Remote stop feature coming soon.');\n}\n"],"mappings":"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","names":["React","useState","useEffect","Box","Text","Box","Text","jsx","jsxs","useState","useEffect","Box","Text","jsx","jsxs","jsx","jsxs","useState","useEffect","Box","Text","React"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/commands/start.ts","../src/ui/TunnelUI.tsx","../src/ui/QRCode.tsx","../src/ui/RequestLog.tsx","../src/ui/ConnectionStatus.tsx","../src/tunnel/relay.ts","../src/config.ts","../src/analytics.ts","../src/commands/list.ts","../src/commands/stop.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { startCommand, listCommand, stopCommand } from './commands/index.js';\n\nconst program = new Command();\n\nprogram.name('untunneled').description('Fast, free localhost tunneling').version('0.3.0');\n\nprogram\n .argument('<port>', 'Local port to tunnel')\n .option('--qr', 'Show QR code for mobile access')\n .option('--password <password>', 'Password protect the tunnel')\n .option('--allow <emails>', 'Email whitelist (comma-separated)')\n .option('--verbose', 'Show detailed logs')\n .option('--no-telemetry', 'Disable anonymous usage tracking')\n .action(startCommand);\n\nprogram.command('list').description('List active tunnels').action(listCommand);\n\nprogram\n .command('stop')\n .argument('<tunnel-id>', 'Tunnel ID to stop')\n .description('Stop a running tunnel')\n .action(stopCommand);\n\nprogram.parse();\n","import { render } from 'ink';\nimport React from 'react';\nimport { TunnelUI } from '../ui/TunnelUI.js';\nimport { RelayTunnel } from '../tunnel/relay.js';\nimport { Analytics } from '../analytics.js';\nimport {\n generateTunnelId,\n getProjectName,\n getTunnelUrl,\n validatePort,\n parseAllowList,\n} from '../config.js';\nimport type { StartOptions } from '../types.js';\n\nexport async function startCommand(port: string, options: StartOptions): Promise<void> {\n let localPort: number;\n\n try {\n localPort = validatePort(port);\n } catch (error) {\n console.error((error as Error).message);\n process.exit(1);\n }\n\n const projectName = getProjectName();\n const tunnelId = generateTunnelId(projectName);\n const tunnelUrl = getTunnelUrl(tunnelId);\n\n const analytics = new Analytics({\n enabled: options.telemetry !== false,\n tunnelId,\n });\n\n await analytics.track('tunnel_start', {\n has_auth: !!options.password || !!options.allow,\n node_version: process.version,\n platform: process.platform,\n });\n\n const relayTunnel = new RelayTunnel({\n localPort,\n tunnelId,\n tunnelUrl,\n password: options.password,\n allowList: parseAllowList(options.allow),\n verbose: options.verbose,\n });\n\n try {\n await relayTunnel.connect();\n\n if (options.verbose) {\n console.log(`Tunnel URL: ${tunnelUrl}`);\n console.log(`Forwarding to localhost:${localPort}`);\n }\n } catch (error) {\n console.error('Failed to connect to relay server:', (error as Error).message);\n await analytics.shutdown();\n process.exit(1);\n }\n\n const { waitUntilExit } = render(\n React.createElement(TunnelUI, {\n tunnelUrl,\n localPort,\n showQR: options.qr,\n relayTunnel,\n })\n );\n\n const cleanup = async () => {\n const duration = relayTunnel.getDuration();\n const requests = relayTunnel.getRequestCount();\n\n await analytics.track('tunnel_end', {\n duration_seconds: duration,\n requests: requests,\n });\n\n await relayTunnel.disconnect();\n await analytics.shutdown();\n };\n\n process.on('SIGINT', async () => {\n await cleanup();\n process.exit(0);\n });\n\n process.on('SIGTERM', async () => {\n await cleanup();\n process.exit(0);\n });\n\n await waitUntilExit();\n}\n","import React, { useState, useEffect } from 'react';\nimport { Box, Text, useApp } from 'ink';\nimport { QRCode } from './QRCode.js';\nimport { RequestLog } from './RequestLog.js';\nimport { ConnectionStatus } from './ConnectionStatus.js';\nimport type { RelayTunnel } from '../tunnel/relay.js';\nimport type { RequestLog as RequestLogType, TunnelStats } from '../types.js';\n\ninterface TunnelUIProps {\n tunnelUrl: string;\n localPort: number;\n showQR?: boolean;\n relayTunnel: RelayTunnel;\n}\n\nconst MAX_LOGS = 10;\n\nconst LOGO = `\n ╦ ╦╔╗╔╔╦╗╦ ╦╔╗╔╔╗╔╔═╗╦ ╔═╗╔╦╗ ╔╦╗╔═╗╦ ╦\n ║ ║║║║ ║ ║ ║║║║║║║║╣ ║ ║╣ ║║ ║║║╣ ╚╗╔╝\n ╚═╝╝╚╝ ╩ ╚═╝╝╚╝╝╚╝╚═╝╩═╝╚═╝═╩╝o═╩╝╚═╝ ╚╝ \n`;\n\nconst formatUptime = (ms: number) => {\n const seconds = Math.floor(ms / 1000);\n const minutes = Math.floor(seconds / 60);\n const hours = Math.floor(minutes / 60);\n return `${hours}h ${minutes % 60}m ${seconds % 60}s`;\n};\n\nexport const TunnelUI: React.FC<TunnelUIProps> = ({\n tunnelUrl,\n localPort,\n showQR,\n relayTunnel,\n}) => {\n useApp();\n const [requests, setRequests] = useState<RequestLogType[]>([]);\n const [stats, setStats] = useState<TunnelStats>({\n total: 0,\n startTime: Date.now(),\n });\n const [relayConnected, setRelayConnected] = useState(relayTunnel.isConnected());\n const [uptime, setUptime] = useState(0);\n\n useEffect(() => {\n const handleRequest = (req: RequestLogType) => {\n setRequests((prev) => [...prev.slice(-(MAX_LOGS - 1)), req]);\n setStats((prev) => ({\n ...prev,\n total: prev.total + 1,\n }));\n };\n\n const handleRelayConnected = () => setRelayConnected(true);\n const handleRelayDisconnected = () => setRelayConnected(false);\n\n relayTunnel.on('request', handleRequest);\n relayTunnel.on('connected', handleRelayConnected);\n relayTunnel.on('disconnected', handleRelayDisconnected);\n\n const uptimeTimer = setInterval(() => {\n setUptime(Date.now() - stats.startTime);\n }, 1000);\n\n return () => {\n relayTunnel.off('request', handleRequest);\n relayTunnel.off('connected', handleRelayConnected);\n relayTunnel.off('disconnected', handleRelayDisconnected);\n clearInterval(uptimeTimer);\n };\n }, [relayTunnel, stats.startTime]);\n\n const connectionStatus = relayConnected ? 'connected' : 'connecting';\n const accentColor = relayConnected ? 'cyan' : 'yellow';\n\n return (\n <Box flexDirection=\"column\" paddingX={2} paddingY={1}>\n {/* Header */}\n <Box flexDirection=\"column\" marginBottom={1}>\n <Text color=\"cyan\" bold>\n {LOGO}\n </Text>\n <Box justifyContent=\"space-between\" paddingX={1}>\n <ConnectionStatus status={connectionStatus} />\n <Text dimColor>v0.3.0</Text>\n </Box>\n </Box>\n\n {/* Main Connection Info */}\n <Box\n borderStyle=\"round\"\n borderColor={accentColor}\n paddingX={2}\n paddingY={1}\n flexDirection=\"column\"\n >\n <Box>\n <Text bold>Public URL: </Text>\n <Text bold color=\"black\" backgroundColor=\"cyan\">\n {' '}\n {tunnelUrl}{' '}\n </Text>\n </Box>\n <Box marginTop={1}>\n <Text dimColor>Forwarding: </Text>\n <Text color=\"yellow\">http://localhost:{localPort}</Text>\n <Text dimColor> → </Text>\n <Text color=\"cyan\">{tunnelUrl}</Text>\n </Box>\n </Box>\n\n {/* Stats Bar */}\n <Box marginTop={1} paddingX={1} gap={4}>\n <Box>\n <Text dimColor>Requests: </Text>\n <Text color=\"white\" bold>\n {stats.total}\n </Text>\n </Box>\n <Box>\n <Text dimColor>Uptime: </Text>\n <Text color=\"white\" bold>\n {formatUptime(uptime)}\n </Text>\n </Box>\n <Box>\n <Text dimColor>Latency: </Text>\n <Text color=\"white\" bold>\n {requests.length > 0 ? `${requests[requests.length - 1]?.duration}ms` : '-'}\n </Text>\n </Box>\n </Box>\n\n {/* QR Code */}\n {showQR && (\n <Box\n marginY={1}\n borderStyle=\"single\"\n borderColor=\"gray\"\n paddingX={1}\n alignSelf=\"flex-start\"\n >\n <QRCode url={tunnelUrl} />\n </Box>\n )}\n\n {/* Request Log */}\n <Box marginTop={1} flexDirection=\"column\">\n <Box marginBottom={1}>\n <Text bold color=\"cyan\">\n ─── Recent Requests ───\n </Text>\n </Box>\n <RequestLog requests={requests} />\n </Box>\n\n {/* Footer */}\n <Box\n marginTop={1}\n borderStyle=\"single\"\n borderTop={true}\n borderBottom={false}\n borderLeft={false}\n borderRight={false}\n borderColor=\"gray\"\n paddingTop={1}\n >\n <Box flexGrow={1}>\n <Text dimColor>Press </Text>\n <Text color=\"yellow\" bold>\n Ctrl+C\n </Text>\n <Text dimColor> to stop tunnel</Text>\n </Box>\n <Box>\n <Text dimColor>Sponsor: </Text>\n <Text color=\"magenta\" bold>\n untunneled.dev/sponsor\n </Text>\n </Box>\n </Box>\n </Box>\n );\n};\n","import React, { useState, useEffect } from 'react';\nimport { Box, Text } from 'ink';\nimport qrcode from 'qrcode-terminal';\n\ninterface QRCodeProps {\n url: string;\n}\n\nexport const QRCode: React.FC<QRCodeProps> = ({ url }) => {\n const [qrString, setQrString] = useState('');\n\n useEffect(() => {\n qrcode.generate(url, { small: true }, (qr: string) => {\n setQrString(qr);\n });\n }, [url]);\n\n if (!qrString) {\n return (\n <Box>\n <Text dimColor>Generating QR code...</Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" borderStyle=\"single\" paddingX={1}>\n <Text dimColor>Scan with mobile:</Text>\n <Text>{qrString}</Text>\n </Box>\n );\n};\n","import React from 'react';\nimport { Box, Text } from 'ink';\nimport type { RequestLog as RequestLogType } from '../types.js';\n\ninterface RequestLogProps {\n requests: RequestLogType[];\n}\n\nconst getStatusColor = (status: number): string => {\n if (status >= 500) return 'red';\n if (status >= 400) return 'yellow';\n if (status >= 300) return 'cyan';\n if (status >= 200) return 'green';\n return 'gray';\n};\n\nconst getMethodColor = (method: string): string => {\n switch (method.toUpperCase()) {\n case 'GET':\n return 'cyan';\n case 'POST':\n return 'green';\n case 'PUT':\n return 'yellow';\n case 'DELETE':\n return 'red';\n case 'PATCH':\n return 'magenta';\n default:\n return 'white';\n }\n};\n\nexport const RequestLog: React.FC<RequestLogProps> = ({ requests }) => {\n if (requests.length === 0) {\n return (\n <Box\n borderStyle=\"single\"\n borderColor=\"gray\"\n flexDirection=\"column\"\n alignItems=\"center\"\n paddingX={3}\n paddingY={1}\n width={40}\n alignSelf=\"center\"\n >\n <Text color=\"cyan\">⏳ Waiting for requests...</Text>\n <Box height={1} />\n <Text dimColor>Open the URL in your browser</Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\">\n {requests.map((req) => {\n const time = new Date(req.timestamp).toLocaleTimeString([], {\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false,\n });\n\n return (\n <Box key={req.id} gap={1}>\n <Text dimColor>[{time}]</Text>\n <Box width={8}>\n <Text color={getMethodColor(req.method)} bold>\n {req.method.padEnd(7)}\n </Text>\n </Box>\n <Box flexGrow={1}>\n <Text>{req.path.length > 40 ? req.path.substring(0, 37) + '...' : req.path}</Text>\n </Box>\n <Box width={6}>\n <Text color={getStatusColor(req.status)} bold>\n {req.status}\n </Text>\n </Box>\n <Box width={10} justifyContent=\"flex-end\">\n <Text color={req.duration > 500 ? 'yellow' : 'gray'}>\n {req.duration.toString().padStart(4)}ms\n </Text>\n </Box>\n </Box>\n );\n })}\n </Box>\n );\n};\n","import React, { useState, useEffect } from 'react';\nimport { Box, Text } from 'ink';\nimport type { ConnectionStatus as ConnectionStatusType } from '../types.js';\n\ninterface ConnectionStatusProps {\n status: ConnectionStatusType;\n}\n\nconst SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];\n\nconst getStatusIndicator = (\n status: ConnectionStatusType,\n frame: string\n): { color: string; symbol: string; label: string } => {\n switch (status) {\n case 'connected':\n return { color: 'green', symbol: '✔', label: 'CONNECTED' };\n case 'connecting':\n return { color: 'yellow', symbol: frame, label: 'CONNECTING' };\n case 'disconnected':\n return { color: 'gray', symbol: '○', label: 'DISCONNECTED' };\n case 'error':\n return { color: 'red', symbol: '✘', label: 'ERROR' };\n default:\n return { color: 'white', symbol: '?', label: String(status).toUpperCase() };\n }\n};\n\nexport const ConnectionStatus: React.FC<ConnectionStatusProps> = ({ status }) => {\n const [frameIndex, setFrameIndex] = useState(0);\n\n useEffect(() => {\n if (status !== 'connecting') return;\n\n const timer = setInterval(() => {\n setFrameIndex((prev) => (prev + 1) % SPINNER_FRAMES.length);\n }, 80);\n\n return () => clearInterval(timer);\n }, [status]);\n\n const frame = SPINNER_FRAMES[frameIndex] ?? ' ';\n const indicator = getStatusIndicator(status, frame);\n\n return (\n <Box>\n <Text color={indicator.color} bold>\n {indicator.symbol} {indicator.label}\n </Text>\n </Box>\n );\n};\n","import { WebSocket } from 'ws';\nimport { EventEmitter } from 'node:events';\nimport { getRelayUrl, TIMEOUTS } from '../config.js';\nimport type {\n RelayFrame,\n WsOpen,\n WsMessage,\n WsClose,\n RelayResponse,\n RequestLog,\n} from '../types.js';\n\ninterface RelayTunnelOptions {\n localPort: number;\n tunnelId: string;\n tunnelUrl: string;\n password?: string;\n allowList?: string[];\n verbose?: boolean;\n}\n\nexport class RelayTunnel extends EventEmitter {\n private ws: WebSocket | null = null;\n private localPort: number;\n private tunnelId: string;\n private tunnelUrl: string;\n private options: RelayTunnelOptions;\n private startTime: number = 0;\n private requestCount: number = 0;\n private reconnectAttempts: number = 0;\n private isClosing: boolean = false;\n private pingInterval: NodeJS.Timeout | null = null;\n private activeWebSockets: Map<string, WebSocket> = new Map();\n\n constructor(options: RelayTunnelOptions) {\n super();\n this.options = options;\n this.localPort = options.localPort;\n this.tunnelId = options.tunnelId;\n this.tunnelUrl = options.tunnelUrl;\n }\n\n async connect(): Promise<void> {\n this.startTime = Date.now();\n this.isClosing = false;\n\n const wsUrl = getRelayUrl(this.tunnelId);\n\n if (this.options.verbose) {\n console.log(`[Relay] Connecting to ${wsUrl}`);\n }\n\n return new Promise((resolve, reject) => {\n try {\n this.ws = new WebSocket(wsUrl, {\n headers: {\n 'X-Tunnel-Auth': this.options.password || '',\n 'X-Tunnel-Allow': this.options.allowList?.join(',') || '',\n },\n });\n\n const connectionTimeout = setTimeout(() => {\n if (this.ws && this.ws.readyState !== WebSocket.OPEN) {\n this.ws.terminate();\n reject(new Error('Connection timeout'));\n }\n }, TIMEOUTS.RELAY_REQUEST);\n\n this.ws.on('open', () => {\n clearTimeout(connectionTimeout);\n this.reconnectAttempts = 0;\n\n if (this.options.verbose) {\n console.log('[Relay] Connected');\n }\n\n this.setupPing();\n this.emit('connected');\n resolve();\n });\n\n this.ws.on('error', (err) => {\n clearTimeout(connectionTimeout);\n\n if (this.options.verbose) {\n console.error('[Relay] WebSocket error:', err.message);\n }\n\n this.emit('error', err);\n reject(err);\n });\n\n this.ws.on('close', () => {\n this.clearPing();\n\n if (!this.isClosing) {\n this.emit('disconnected');\n this.attemptReconnect();\n }\n });\n\n this.ws.on('message', async (data) => {\n await this.handleRelayFrame(data);\n });\n } catch (error) {\n reject(error);\n }\n });\n }\n\n private async handleRelayFrame(data: Buffer | ArrayBuffer | Buffer[]): Promise<void> {\n let frame: RelayFrame;\n\n try {\n frame = JSON.parse(String(data)) as RelayFrame;\n } catch {\n if (this.options.verbose) {\n console.error('[Relay] Invalid frame data');\n }\n return;\n }\n\n switch (frame.type) {\n case 'http_request':\n await this.handleHttpRequest(frame);\n break;\n case 'ws_open':\n await this.handleWsOpen(frame);\n break;\n case 'ws_message':\n await this.handleWsMessage(frame);\n break;\n case 'ws_close':\n await this.handleWsClose(frame);\n break;\n }\n }\n\n private async handleHttpRequest(request: RelayFrame & { type: 'http_request' }): Promise<void> {\n const startTime = Date.now();\n\n try {\n const url = `http://127.0.0.1:${this.localPort}${request.path}`;\n\n const fetchOptions: RequestInit = {\n method: request.method,\n headers: request.headers,\n };\n\n if (request.body && !['GET', 'HEAD'].includes(request.method)) {\n fetchOptions.body = request.body;\n }\n\n const response = await fetch(url, fetchOptions);\n const body = await response.text();\n const duration = Date.now() - startTime;\n\n const relayResponse: RelayResponse = {\n type: 'http_response',\n id: request.id,\n status: response.status,\n statusText: response.statusText,\n headers: Object.fromEntries(response.headers.entries()),\n body,\n };\n\n this.ws?.send(JSON.stringify(relayResponse));\n\n const log: RequestLog = {\n id: request.id,\n timestamp: Date.now(),\n method: request.method,\n path: request.path,\n status: response.status,\n duration,\n };\n\n this.emit('request', log);\n this.requestCount++;\n } catch (error) {\n const err = error as any;\n\n if (this.options.verbose || err.code === 'ECONNREFUSED') {\n console.error(`[Relay] Proxy error: ${err.message}`);\n }\n\n const errorResponse: RelayResponse = {\n type: 'http_response',\n id: request.id,\n status: 502,\n statusText: 'Bad Gateway',\n headers: {},\n body: `Error: ${err.message}`,\n };\n\n this.ws?.send(JSON.stringify(errorResponse));\n }\n }\n\n private async handleWsOpen(frame: WsOpen): Promise<void> {\n const localUrl = `ws://127.0.0.1:${this.localPort}${frame.url}`;\n\n if (this.options.verbose) {\n console.log(`[Relay] Opening WebSocket to ${localUrl}`);\n }\n\n const localWs = new WebSocket(localUrl, {\n headers: frame.headers,\n });\n\n this.activeWebSockets.set(frame.id, localWs);\n\n localWs.on('message', (data, isBinary) => {\n const message: WsMessage = {\n type: 'ws_message',\n id: frame.id,\n data: isBinary ? (data as Buffer).toString('base64') : data.toString(),\n isBinary,\n };\n this.ws?.send(JSON.stringify(message));\n });\n\n localWs.on('close', () => {\n const closeFrame: WsClose = {\n type: 'ws_close',\n id: frame.id,\n };\n this.ws?.send(JSON.stringify(closeFrame));\n this.activeWebSockets.delete(frame.id);\n });\n\n localWs.on('error', (err) => {\n if (this.options.verbose) {\n console.error(`[Relay] Local WebSocket error (${frame.id}):`, err.message);\n }\n localWs.close();\n });\n }\n\n private async handleWsMessage(frame: WsMessage): Promise<void> {\n const localWs = this.activeWebSockets.get(frame.id);\n if (localWs && localWs.readyState === WebSocket.OPEN) {\n const data = frame.isBinary ? Buffer.from(frame.data, 'base64') : frame.data;\n localWs.send(data);\n }\n }\n\n private async handleWsClose(frame: WsClose): Promise<void> {\n const localWs = this.activeWebSockets.get(frame.id);\n if (localWs) {\n localWs.close();\n this.activeWebSockets.delete(frame.id);\n }\n }\n\n private setupPing(): void {\n this.pingInterval = setInterval(() => {\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n this.ws.ping();\n }\n }, TIMEOUTS.WEBSOCKET_PING);\n }\n\n private clearPing(): void {\n if (this.pingInterval) {\n clearInterval(this.pingInterval);\n this.pingInterval = null;\n }\n }\n\n private async attemptReconnect(): Promise<void> {\n if (this.isClosing) return;\n\n this.reconnectAttempts++;\n const delay = Math.min(\n TIMEOUTS.RECONNECT_BASE * Math.pow(2, this.reconnectAttempts - 1),\n TIMEOUTS.RECONNECT_MAX\n );\n\n if (this.options.verbose) {\n console.log(`[Relay] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);\n }\n\n setTimeout(async () => {\n if (this.isClosing) return;\n\n try {\n await this.connect();\n } catch (_) {}\n }, delay);\n }\n\n async disconnect(): Promise<void> {\n this.isClosing = true;\n this.clearPing();\n\n for (const ws of this.activeWebSockets.values()) {\n ws.close();\n }\n this.activeWebSockets.clear();\n\n if (this.ws) {\n this.ws.close(1000, 'Client closing');\n this.ws = null;\n }\n }\n\n getDuration(): number {\n if (this.startTime === 0) return 0;\n return Math.floor((Date.now() - this.startTime) / 1000);\n }\n\n getRequestCount(): number {\n return this.requestCount;\n }\n\n isConnected(): boolean {\n return this.ws !== null && this.ws.readyState === WebSocket.OPEN;\n }\n\n getTunnelUrl(): string {\n return this.tunnelUrl;\n }\n}\n","import { nanoid } from 'nanoid';\nimport path from 'node:path';\n\nexport const RELAY_DOMAIN = 'relay.untunneled.dev';\nexport const TUNNEL_DOMAIN = 'untunneled.dev';\n\nexport const getRelayUrl = (tunnelId: string): string => {\n const host = process.env['UNTUNNELED_RELAY_HOST'] || RELAY_DOMAIN;\n const protocol = host.includes('localhost') ? 'ws' : 'wss';\n return `${protocol}://${host}/${tunnelId}`;\n};\n\nexport const getTunnelUrl = (tunnelId: string): string => {\n const domain = process.env['UNTUNNELED_TUNNEL_DOMAIN'] || TUNNEL_DOMAIN;\n return `https://${tunnelId}.${domain}`;\n};\n\nexport function getProjectName(): string {\n const cwd = process.cwd();\n const folderName = path.basename(cwd);\n\n return (\n folderName\n .toLowerCase()\n .replace(/[^a-z0-9-]/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '')\n .slice(0, 20) || 'tunnel'\n );\n}\n\nexport function generateTunnelId(projectName?: string): string {\n const name = projectName || getProjectName();\n const suffix = nanoid(6).toLowerCase();\n return `${name}-${suffix}`;\n}\n\nexport function validatePort(port: string | number): number {\n const portNum = typeof port === 'string' ? parseInt(port, 10) : port;\n\n if (isNaN(portNum) || portNum < 1 || portNum > 65535) {\n throw new Error(`Invalid port number: ${port}. Must be between 1 and 65535.`);\n }\n\n return portNum;\n}\n\nexport function parseAllowList(allow?: string): string[] | undefined {\n if (!allow) return undefined;\n\n return allow\n .split(',')\n .map((email) => email.trim().toLowerCase())\n .filter((email) => email.length > 0);\n}\n\nexport const TIMEOUTS = {\n RELAY_REQUEST: 30000,\n WEBSOCKET_PING: 30000,\n RECONNECT_BASE: 1000,\n RECONNECT_MAX: 30000,\n};\n","import { PostHog } from 'posthog-node';\n\nconst POSTHOG_API_KEY = process.env['POSTHOG_API_KEY'] || 'phc_placeholder';\nconst POSTHOG_HOST = 'https://app.posthog.com';\n\ninterface AnalyticsOptions {\n enabled: boolean;\n tunnelId: string;\n}\n\ntype EventName = 'tunnel_start' | 'tunnel_end' | 'request_handled';\n\ninterface EventProperties {\n tunnel_start: {\n has_auth: boolean;\n node_version: string;\n platform: string;\n };\n tunnel_end: {\n duration_seconds: number;\n requests: number;\n };\n request_handled: {\n method: string;\n status: number;\n duration_ms: number;\n };\n}\n\nexport class Analytics {\n private posthog: PostHog | null = null;\n private tunnelId: string;\n private enabled: boolean;\n\n constructor(options: AnalyticsOptions) {\n this.tunnelId = options.tunnelId;\n this.enabled = options.enabled;\n\n if (this.enabled && POSTHOG_API_KEY !== 'phc_placeholder') {\n this.posthog = new PostHog(POSTHOG_API_KEY, {\n host: POSTHOG_HOST,\n flushAt: 10,\n flushInterval: 10000,\n });\n }\n }\n\n async track<E extends EventName>(event: E, properties?: EventProperties[E]): Promise<void> {\n if (!this.posthog || !this.enabled) return;\n\n try {\n this.posthog.capture({\n distinctId: this.tunnelId,\n event,\n properties: {\n ...properties,\n version: '0.1.0',\n cli: true,\n },\n });\n } catch (_) {}\n }\n\n async shutdown(): Promise<void> {\n if (!this.posthog) return;\n\n try {\n await this.posthog.shutdown();\n } catch (_) {}\n }\n\n isEnabled(): boolean {\n return this.enabled && this.posthog !== null;\n }\n}\n","export async function listCommand(): Promise<void> {\n console.log('Active tunnels:');\n console.log(' (No active tunnels)');\n console.log('');\n console.log('Note: Tunnel list feature coming soon.');\n}\n","export async function stopCommand(tunnelId: string): Promise<void> {\n console.log(`Stopping tunnel: ${tunnelId}`);\n console.log('');\n console.log('Note: Remote stop feature coming soon.');\n}\n"],"mappings":"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","names":["React","useState","useEffect","Box","Text","Box","Text","jsx","jsxs","useState","useEffect","Box","Text","jsx","jsxs","jsx","jsxs","useState","useEffect","Box","Text","React"]}
|