untunneled.dev 0.2.2 → 0.2.4

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 CHANGED
@@ -61,19 +61,24 @@ var getMethodColor = (method) => {
61
61
  };
62
62
  var RequestLog = ({ requests }) => {
63
63
  if (requests.length === 0) {
64
- return /* @__PURE__ */ jsxs2(Box2, { paddingY: 1, flexDirection: "column", alignItems: "center", children: [
65
- /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510" }),
66
- /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "\u2502 \u2502" }),
67
- /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
68
- "\u2502 ",
69
- /* @__PURE__ */ jsx2(Text2, { color: "cyan", children: "\u23F3" }),
70
- " Waiting for requests... \u2502"
71
- ] }),
72
- /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "\u2502 \u2502" }),
73
- /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "\u2502 Open the URL in your browser \u2502" }),
74
- /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "\u2502 \u2502" }),
75
- /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518" })
76
- ] });
64
+ return /* @__PURE__ */ jsxs2(
65
+ Box2,
66
+ {
67
+ borderStyle: "single",
68
+ borderColor: "gray",
69
+ flexDirection: "column",
70
+ alignItems: "center",
71
+ paddingX: 3,
72
+ paddingY: 1,
73
+ width: 40,
74
+ alignSelf: "center",
75
+ children: [
76
+ /* @__PURE__ */ jsx2(Text2, { color: "cyan", children: "\u23F3 Waiting for requests..." }),
77
+ /* @__PURE__ */ jsx2(Box2, { height: 1 }),
78
+ /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "Open the URL in your browser" })
79
+ ]
80
+ }
81
+ );
77
82
  }
78
83
  return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: requests.map((req) => {
79
84
  const time = new Date(req.timestamp).toLocaleTimeString([], {
@@ -194,7 +199,7 @@ var TunnelUI = ({
194
199
  /* @__PURE__ */ jsx4(Text4, { color: "cyan", bold: true, children: LOGO }),
195
200
  /* @__PURE__ */ jsxs4(Box4, { justifyContent: "space-between", paddingX: 1, children: [
196
201
  /* @__PURE__ */ jsx4(ConnectionStatus, { status: connectionStatus }),
197
- /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "v0.2.2" })
202
+ /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "v0.2.4" })
198
203
  ] })
199
204
  ] }),
200
205
  /* @__PURE__ */ jsxs4(
@@ -434,7 +439,7 @@ var RelayTunnel = class extends EventEmitter {
434
439
  this.ws?.send(JSON.stringify(relayResponse));
435
440
  const log = {
436
441
  id: request.id,
437
- timestamp: (/* @__PURE__ */ new Date()).toISOString().substring(11, 19),
442
+ timestamp: Date.now(),
438
443
  method: request.method,
439
444
  path: request.path,
440
445
  status: response.status,
@@ -643,7 +648,7 @@ async function stopCommand(tunnelId) {
643
648
 
644
649
  // src/index.ts
645
650
  var program = new Command();
646
- program.name("untunneled").description("Fast, free localhost tunneling").version("0.2.2");
651
+ program.name("untunneled").description("Fast, free localhost tunneling").version("0.2.4");
647
652
  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("--team <name>", "Team tunnel name").option("--verbose", "Show detailed logs").option("--no-telemetry", "Disable anonymous usage tracking").action(startCommand);
648
653
  program.command("list").description("List active tunnels").action(listCommand);
649
654
  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.2');\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('--team <name>', 'Team tunnel name')\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.2</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 paddingY={1} flexDirection=\"column\" alignItems=\"center\">\n <Text dimColor>┌─────────────────────────────────────┐</Text>\n <Text dimColor>│ │</Text>\n <Text dimColor>\n │ <Text color=\"cyan\">⏳</Text> Waiting for requests... │\n </Text>\n <Text dimColor>│ │</Text>\n <Text dimColor>│ Open the URL in your browser │</Text>\n <Text dimColor>│ │</Text>\n <Text dimColor>└─────────────────────────────────────┘</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://localhost:${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: new Date().toISOString().substring(11, 19),\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 Error;\n\n if (this.options.verbose) {\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.2.4');\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('--team <name>', 'Team tunnel name')\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.4</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://localhost:${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 Error;\n\n if (this.options.verbose) {\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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "untunneled.dev",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Fast, free localhost tunneling for developers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",