untunneled.dev 0.1.0 → 0.1.1

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
@@ -504,6 +504,7 @@ var RelayTunnel = class extends EventEmitter {
504
504
 
505
505
  // src/tunnel/p2p.ts
506
506
  import SimplePeer from "simple-peer";
507
+ import wrtc from "@roamhq/wrtc";
507
508
  import { WebSocket as WebSocket2 } from "ws";
508
509
  import { EventEmitter as EventEmitter2 } from "events";
509
510
  var P2PTunnel = class extends EventEmitter2 {
@@ -546,7 +547,8 @@ var P2PTunnel = class extends EventEmitter2 {
546
547
  this.peer = new SimplePeer({
547
548
  initiator: false,
548
549
  trickle: false,
549
- config: { iceServers: ICE_SERVERS }
550
+ config: { iceServers: ICE_SERVERS },
551
+ wrtc
550
552
  });
551
553
  this.peer.on("signal", (signal) => {
552
554
  if (this.options.verbose) {
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/tunnel/p2p.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\n .name('untunneled')\n .description('Privacy-first localhost tunneling - P2P by default')\n .version('0.1.0');\n\nprogram\n .argument('<port>', 'Local port to tunnel')\n .option('-r, --relay-fallback', 'Enable HTTP relay fallback if P2P fails')\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 { P2PTunnel } from '../tunnel/p2p.js';\nimport { Analytics } from '../analytics.js';\nimport {\n generateTunnelId,\n getProjectName,\n getTunnelUrl,\n validatePort,\n parseAllowList,\n} from '../config.js';\nimport type { StartOptions, TunnelMode } 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 const mode: TunnelMode = options.relayFallback ? 'p2p-with-fallback' : 'p2p-only';\n\n const analytics = new Analytics({\n enabled: options.telemetry !== false,\n tunnelId,\n });\n\n await analytics.track('tunnel_start', {\n mode,\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 mode,\n });\n\n let p2pTunnel: P2PTunnel | null = null;\n let p2pError: Error | null = null;\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 p2pTunnel = new P2PTunnel({\n localPort,\n tunnelId,\n verbose: options.verbose,\n });\n\n try {\n await p2pTunnel.connect();\n await analytics.track('p2p_success', { mode });\n } catch (error) {\n p2pError = error as Error;\n await analytics.track('p2p_failed', { mode, reason: p2pError.message });\n\n if (!options.relayFallback) {\n console.error('');\n console.error('P2P connection failed:', p2pError.message);\n console.error('');\n console.error('Subsequent requests from the browser will fail.');\n console.error('');\n console.error('Options:');\n console.error(' 1. Use --relay-fallback for automatic fallback');\n console.error(' 2. Try a different network (mobile hotspot, home WiFi)');\n console.error('');\n }\n }\n\n const { waitUntilExit } = render(\n React.createElement(TunnelUI, {\n tunnelUrl,\n localPort,\n showQR: options.qr,\n mode,\n relayTunnel,\n p2pTunnel: p2pTunnel?.isConnected() ? p2pTunnel : null,\n p2pError,\n })\n );\n\n const cleanup = async () => {\n const duration = relayTunnel.getDuration();\n const relayRequests = relayTunnel.getRequestCount();\n const p2pRequests = p2pTunnel?.getRequestCount() ?? 0;\n\n await analytics.track('tunnel_end', {\n duration_seconds: duration,\n requests_relay: relayRequests,\n requests_p2p: p2pRequests,\n p2p_success: p2pTunnel?.isConnected() ?? false,\n });\n\n await relayTunnel.disconnect();\n p2pTunnel?.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, Newline, 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 { P2PTunnel } from '../tunnel/p2p.js';\nimport type { RequestLog as RequestLogType, TunnelMode, TunnelStats } from '../types.js';\n\ninterface TunnelUIProps {\n tunnelUrl: string;\n localPort: number;\n showQR?: boolean;\n mode: TunnelMode;\n relayTunnel: RelayTunnel;\n p2pTunnel?: P2PTunnel | null;\n p2pError?: Error | null;\n}\n\nconst MAX_LOGS = 10;\n\nexport const TunnelUI: React.FC<TunnelUIProps> = ({\n tunnelUrl,\n localPort,\n showQR,\n mode,\n relayTunnel,\n p2pTunnel,\n p2pError,\n}) => {\n useApp();\n const [requests, setRequests] = useState<RequestLogType[]>([]);\n const [stats, setStats] = useState<TunnelStats>({\n total: 0,\n p2p: 0,\n relay: 0,\n startTime: Date.now(),\n });\n const [relayConnected, setRelayConnected] = useState(relayTunnel.isConnected());\n const [p2pConnected, setP2pConnected] = useState(p2pTunnel?.isConnected() ?? false);\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 p2p: prev.p2p + (req.type === 'p2p' ? 1 : 0),\n relay: prev.relay + (req.type === 'relay' ? 1 : 0),\n }));\n };\n\n const handleRelayConnected = () => setRelayConnected(true);\n const handleRelayDisconnected = () => setRelayConnected(false);\n const handleP2PConnected = () => setP2pConnected(true);\n const handleP2PDisconnected = () => setP2pConnected(false);\n\n relayTunnel.on('request', handleRequest);\n relayTunnel.on('connected', handleRelayConnected);\n relayTunnel.on('disconnected', handleRelayDisconnected);\n\n if (p2pTunnel) {\n p2pTunnel.on('request', handleRequest);\n p2pTunnel.on('connected', handleP2PConnected);\n p2pTunnel.on('disconnected', handleP2PDisconnected);\n }\n\n return () => {\n relayTunnel.off('request', handleRequest);\n relayTunnel.off('connected', handleRelayConnected);\n relayTunnel.off('disconnected', handleRelayDisconnected);\n\n if (p2pTunnel) {\n p2pTunnel.off('request', handleRequest);\n p2pTunnel.off('connected', handleP2PConnected);\n p2pTunnel.off('disconnected', handleP2PDisconnected);\n }\n };\n }, [relayTunnel, p2pTunnel]);\n\n const connectionStatus = relayConnected || p2pConnected ? 'connected' : 'connecting';\n const borderColor = p2pConnected ? 'green' : relayConnected ? 'yellow' : 'gray';\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box borderStyle=\"round\" borderColor={borderColor} padding={1} flexDirection=\"column\">\n <Text bold color={borderColor}>\n {p2pConnected ? '🔒' : '⚡'} untunneled.dev\n </Text>\n <Newline />\n <ConnectionStatus\n status={connectionStatus}\n mode={mode}\n p2pConnected={p2pConnected}\n relayConnected={relayConnected}\n />\n <Newline />\n <Box>\n <Text>URL: </Text>\n <Text bold color=\"cyan\">\n {tunnelUrl}\n </Text>\n </Box>\n <Box>\n <Text dimColor>→ localhost:{localPort}</Text>\n </Box>\n </Box>\n\n {p2pError && mode === 'p2p-only' && (\n <Box\n marginTop={1}\n borderStyle=\"single\"\n borderColor=\"red\"\n padding={1}\n flexDirection=\"column\"\n >\n <Text color=\"red\" bold>\n P2P Connection Failed\n </Text>\n <Text dimColor>{p2pError.message}</Text>\n <Newline />\n <Text dimColor>Use --relay-fallback for guaranteed compatibility</Text>\n </Box>\n )}\n\n {showQR && (\n <Box marginTop={1}>\n <QRCode url={tunnelUrl} />\n </Box>\n )}\n\n <Box marginTop={1} gap={2}>\n <Text>\n Requests: <Text color=\"cyan\">{stats.total}</Text>\n </Text>\n {mode !== 'relay' && stats.total > 0 && (\n <>\n <Text>\n P2P: <Text color=\"green\">{stats.p2p}</Text>\n </Text>\n <Text>\n Relay: <Text color=\"yellow\">{stats.relay}</Text>\n </Text>\n <Text dimColor>({Math.round((stats.p2p / stats.total) * 100)}% private)</Text>\n </>\n )}\n </Box>\n\n <Box marginTop={1} flexDirection=\"column\">\n <Text dimColor>Recent requests:</Text>\n <RequestLog requests={requests} />\n </Box>\n\n <Box marginTop={1}>\n <Text dimColor>Press Ctrl+C to stop</Text>\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 'blue';\n return 'green';\n};\n\nconst getMethodColor = (method: string): string => {\n switch (method) {\n case 'GET':\n return 'green';\n case 'POST':\n return 'yellow';\n case 'PUT':\n return 'blue';\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 <Text dimColor> Waiting for requests...</Text>;\n }\n\n return (\n <Box flexDirection=\"column\">\n {requests.map((req) => (\n <Box key={req.id} gap={1}>\n <Text dimColor>{req.timestamp}</Text>\n <Box width={7}>\n <Text color={getMethodColor(req.method)}>{req.method.padEnd(6)}</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={5}>\n <Text color={getStatusColor(req.status)}>{req.status}</Text>\n </Box>\n <Box width={8}>\n <Text dimColor>{req.duration}ms</Text>\n </Box>\n <Box width={7}>\n <Text color={req.type === 'p2p' ? 'green' : 'yellow'}>[{req.type.toUpperCase()}]</Text>\n </Box>\n </Box>\n ))}\n </Box>\n );\n};\n","import React from 'react';\nimport { Box, Text } from 'ink';\nimport type { ConnectionStatus as ConnectionStatusType, TunnelMode } from '../types.js';\n\ninterface ConnectionStatusProps {\n status: ConnectionStatusType;\n mode: TunnelMode;\n p2pConnected: boolean;\n relayConnected: boolean;\n}\n\nconst getStatusIndicator = (status: ConnectionStatusType): { color: string; symbol: string } => {\n switch (status) {\n case 'connected':\n return { color: 'green', symbol: '●' };\n case 'connecting':\n return { color: 'yellow', symbol: '◐' };\n case 'disconnected':\n return { color: 'red', symbol: '○' };\n case 'error':\n return { color: 'red', symbol: '✗' };\n }\n};\n\nconst getModeLabel = (mode: TunnelMode, p2pConnected: boolean): string => {\n if (mode === 'relay') return 'HTTP Relay';\n if (mode === 'p2p-only') return p2pConnected ? 'P2P Direct' : 'P2P (connecting...)';\n return p2pConnected ? 'P2P + Relay Fallback' : 'Relay (P2P pending...)';\n};\n\nexport const ConnectionStatus: React.FC<ConnectionStatusProps> = ({\n status,\n mode,\n p2pConnected,\n relayConnected,\n}) => {\n const indicator = getStatusIndicator(status);\n const modeLabel = getModeLabel(mode, p2pConnected);\n\n return (\n <Box gap={2}>\n <Box>\n <Text color={indicator.color}>{indicator.symbol}</Text>\n <Text> </Text>\n <Text color={indicator.color}>{status.toUpperCase()}</Text>\n </Box>\n <Box>\n <Text dimColor>Mode: </Text>\n <Text color={p2pConnected ? 'green' : 'yellow'}>{modeLabel}</Text>\n </Box>\n {mode !== 'relay' && (\n <Box>\n <Text dimColor>P2P: </Text>\n <Text color={p2pConnected ? 'green' : 'yellow'}>\n {p2pConnected ? 'Active' : 'Pending'}\n </Text>\n </Box>\n )}\n <Box>\n <Text dimColor>Relay: </Text>\n <Text color={relayConnected ? 'green' : 'red'}>\n {relayConnected ? 'Connected' : 'Disconnected'}\n </Text>\n </Box>\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, TunnelMode } 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 mode?: TunnelMode;\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 'X-Tunnel-Mode': this.options.mode || 'relay',\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.P2P_CONNECTION);\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 type: 'relay',\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 SIGNALING_DOMAIN = 'signal.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 getSignalingUrl = (): string => {\n const host = process.env['UNTUNNELED_SIGNALING_HOST'] || SIGNALING_DOMAIN;\n const protocol = host.includes('localhost') ? 'ws' : 'wss';\n return `${protocol}://${host}`;\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 ICE_SERVERS = [\n { urls: 'stun:stun.l.google.com:19302' },\n { urls: 'stun:stun1.l.google.com:19302' },\n { urls: 'stun:stun2.l.google.com:19302' },\n { urls: 'stun:global.stun.twilio.com:3478' },\n];\n\nexport const TIMEOUTS = {\n P2P_CONNECTION: 10000,\n RELAY_REQUEST: 30000,\n WEBSOCKET_PING: 30000,\n RECONNECT_BASE: 1000,\n RECONNECT_MAX: 30000,\n};\n","import SimplePeer from 'simple-peer';\nimport { WebSocket } from 'ws';\nimport { EventEmitter } from 'node:events';\nimport { getSignalingUrl, ICE_SERVERS, TIMEOUTS } from '../config.js';\nimport type { RelayRequest, RelayResponse, RequestLog, SignalingMessage } from '../types.js';\n\ninterface P2PTunnelOptions {\n localPort: number;\n tunnelId: string;\n verbose?: boolean;\n}\n\nexport class P2PTunnel extends EventEmitter {\n private peer: SimplePeer.Instance | null = null;\n private ws: WebSocket | null = null;\n private localPort: number;\n private tunnelId: string;\n private options: P2PTunnelOptions;\n private connected: boolean = false;\n private requestCount: number = 0;\n\n constructor(options: P2PTunnelOptions) {\n super();\n this.options = options;\n this.localPort = options.localPort;\n this.tunnelId = options.tunnelId;\n }\n\n async connect(): Promise<void> {\n if (this.options.verbose) {\n console.log('[P2P] Attempting connection...');\n }\n\n return new Promise((resolve, reject) => {\n const signalingUrl = getSignalingUrl();\n this.ws = new WebSocket(signalingUrl);\n\n const connectionTimeout = setTimeout(() => {\n if (!this.connected) {\n this.cleanup();\n reject(new Error('P2P connection timeout'));\n }\n }, TIMEOUTS.P2P_CONNECTION);\n\n this.ws.on('open', () => {\n if (this.options.verbose) {\n console.log('[P2P] Signaling connected');\n }\n\n const registerMsg: SignalingMessage = {\n type: 'register',\n tunnelId: this.tunnelId,\n role: 'cli',\n };\n this.ws!.send(JSON.stringify(registerMsg));\n\n this.peer = new SimplePeer({\n initiator: false,\n trickle: false,\n config: { iceServers: ICE_SERVERS },\n });\n\n this.peer.on('signal', (signal) => {\n if (this.options.verbose) {\n console.log('[P2P] Sending signal to browser');\n }\n\n const signalMsg: SignalingMessage = {\n type: 'signal',\n from: 'cli',\n to: 'browser',\n tunnelId: this.tunnelId,\n signal,\n };\n this.ws!.send(JSON.stringify(signalMsg));\n });\n\n this.peer.on('connect', () => {\n clearTimeout(connectionTimeout);\n this.connected = true;\n this.emit('connected');\n\n if (this.options.verbose) {\n console.log('[P2P] Connection established');\n }\n\n resolve();\n });\n\n this.peer.on('data', async (data) => {\n await this.handleP2PRequest(data);\n });\n\n this.peer.on('error', (err) => {\n clearTimeout(connectionTimeout);\n this.connected = false;\n this.emit('error', err);\n\n if (this.options.verbose) {\n console.error('[P2P] Error:', err.message);\n }\n\n reject(err);\n });\n\n this.peer.on('close', () => {\n this.connected = false;\n this.emit('disconnected');\n });\n });\n\n this.ws.on('message', (data) => {\n const msg = JSON.parse(data.toString()) as SignalingMessage;\n\n if (msg.type === 'signal' && msg.from === 'browser' && this.peer) {\n if (this.options.verbose) {\n console.log('[P2P] Received signal from browser');\n }\n this.peer.signal(msg.signal as SimplePeer.SignalData);\n }\n });\n\n this.ws.on('error', (err) => {\n clearTimeout(connectionTimeout);\n reject(err);\n });\n });\n }\n\n private async handleP2PRequest(data: Uint8Array): Promise<void> {\n const startTime = Date.now();\n let request: RelayRequest;\n\n try {\n request = JSON.parse(data.toString()) as RelayRequest;\n } catch {\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 p2pResponse: 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.peer!.send(JSON.stringify(p2pResponse));\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 type: 'p2p',\n };\n\n this.emit('request', log);\n this.requestCount++;\n } catch (error) {\n const err = error as Error;\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.peer?.send(JSON.stringify(errorResponse));\n }\n }\n\n private cleanup(): void {\n this.peer?.destroy();\n this.ws?.close();\n this.peer = null;\n this.ws = null;\n this.connected = false;\n }\n\n disconnect(): void {\n this.cleanup();\n }\n\n isConnected(): boolean {\n return this.connected;\n }\n\n getRequestCount(): number {\n return this.requestCount;\n }\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' | 'p2p_success' | 'p2p_failed' | 'request_handled';\n\ninterface EventProperties {\n tunnel_start: {\n mode: string;\n has_auth: boolean;\n node_version: string;\n platform: string;\n };\n tunnel_end: {\n duration_seconds: number;\n requests_relay: number;\n requests_p2p: number;\n p2p_success: boolean;\n };\n p2p_success: {\n mode: string;\n connection_time_ms?: number;\n };\n p2p_failed: {\n mode: string;\n reason: string;\n };\n request_handled: {\n transport: 'p2p' | 'relay';\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","Box","Text","jsx","jsxs","jsx","jsxs","useState","useEffect","Box","Text","WebSocket","EventEmitter","EventEmitter","WebSocket","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/tunnel/p2p.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\n .name('untunneled')\n .description('Privacy-first localhost tunneling - P2P by default')\n .version('0.1.0');\n\nprogram\n .argument('<port>', 'Local port to tunnel')\n .option('-r, --relay-fallback', 'Enable HTTP relay fallback if P2P fails')\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 { P2PTunnel } from '../tunnel/p2p.js';\nimport { Analytics } from '../analytics.js';\nimport {\n generateTunnelId,\n getProjectName,\n getTunnelUrl,\n validatePort,\n parseAllowList,\n} from '../config.js';\nimport type { StartOptions, TunnelMode } 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 const mode: TunnelMode = options.relayFallback ? 'p2p-with-fallback' : 'p2p-only';\n\n const analytics = new Analytics({\n enabled: options.telemetry !== false,\n tunnelId,\n });\n\n await analytics.track('tunnel_start', {\n mode,\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 mode,\n });\n\n let p2pTunnel: P2PTunnel | null = null;\n let p2pError: Error | null = null;\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 p2pTunnel = new P2PTunnel({\n localPort,\n tunnelId,\n verbose: options.verbose,\n });\n\n try {\n await p2pTunnel.connect();\n await analytics.track('p2p_success', { mode });\n } catch (error) {\n p2pError = error as Error;\n await analytics.track('p2p_failed', { mode, reason: p2pError.message });\n\n if (!options.relayFallback) {\n console.error('');\n console.error('P2P connection failed:', p2pError.message);\n console.error('');\n console.error('Subsequent requests from the browser will fail.');\n console.error('');\n console.error('Options:');\n console.error(' 1. Use --relay-fallback for automatic fallback');\n console.error(' 2. Try a different network (mobile hotspot, home WiFi)');\n console.error('');\n }\n }\n\n const { waitUntilExit } = render(\n React.createElement(TunnelUI, {\n tunnelUrl,\n localPort,\n showQR: options.qr,\n mode,\n relayTunnel,\n p2pTunnel: p2pTunnel?.isConnected() ? p2pTunnel : null,\n p2pError,\n })\n );\n\n const cleanup = async () => {\n const duration = relayTunnel.getDuration();\n const relayRequests = relayTunnel.getRequestCount();\n const p2pRequests = p2pTunnel?.getRequestCount() ?? 0;\n\n await analytics.track('tunnel_end', {\n duration_seconds: duration,\n requests_relay: relayRequests,\n requests_p2p: p2pRequests,\n p2p_success: p2pTunnel?.isConnected() ?? false,\n });\n\n await relayTunnel.disconnect();\n p2pTunnel?.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, Newline, 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 { P2PTunnel } from '../tunnel/p2p.js';\nimport type { RequestLog as RequestLogType, TunnelMode, TunnelStats } from '../types.js';\n\ninterface TunnelUIProps {\n tunnelUrl: string;\n localPort: number;\n showQR?: boolean;\n mode: TunnelMode;\n relayTunnel: RelayTunnel;\n p2pTunnel?: P2PTunnel | null;\n p2pError?: Error | null;\n}\n\nconst MAX_LOGS = 10;\n\nexport const TunnelUI: React.FC<TunnelUIProps> = ({\n tunnelUrl,\n localPort,\n showQR,\n mode,\n relayTunnel,\n p2pTunnel,\n p2pError,\n}) => {\n useApp();\n const [requests, setRequests] = useState<RequestLogType[]>([]);\n const [stats, setStats] = useState<TunnelStats>({\n total: 0,\n p2p: 0,\n relay: 0,\n startTime: Date.now(),\n });\n const [relayConnected, setRelayConnected] = useState(relayTunnel.isConnected());\n const [p2pConnected, setP2pConnected] = useState(p2pTunnel?.isConnected() ?? false);\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 p2p: prev.p2p + (req.type === 'p2p' ? 1 : 0),\n relay: prev.relay + (req.type === 'relay' ? 1 : 0),\n }));\n };\n\n const handleRelayConnected = () => setRelayConnected(true);\n const handleRelayDisconnected = () => setRelayConnected(false);\n const handleP2PConnected = () => setP2pConnected(true);\n const handleP2PDisconnected = () => setP2pConnected(false);\n\n relayTunnel.on('request', handleRequest);\n relayTunnel.on('connected', handleRelayConnected);\n relayTunnel.on('disconnected', handleRelayDisconnected);\n\n if (p2pTunnel) {\n p2pTunnel.on('request', handleRequest);\n p2pTunnel.on('connected', handleP2PConnected);\n p2pTunnel.on('disconnected', handleP2PDisconnected);\n }\n\n return () => {\n relayTunnel.off('request', handleRequest);\n relayTunnel.off('connected', handleRelayConnected);\n relayTunnel.off('disconnected', handleRelayDisconnected);\n\n if (p2pTunnel) {\n p2pTunnel.off('request', handleRequest);\n p2pTunnel.off('connected', handleP2PConnected);\n p2pTunnel.off('disconnected', handleP2PDisconnected);\n }\n };\n }, [relayTunnel, p2pTunnel]);\n\n const connectionStatus = relayConnected || p2pConnected ? 'connected' : 'connecting';\n const borderColor = p2pConnected ? 'green' : relayConnected ? 'yellow' : 'gray';\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box borderStyle=\"round\" borderColor={borderColor} padding={1} flexDirection=\"column\">\n <Text bold color={borderColor}>\n {p2pConnected ? '🔒' : '⚡'} untunneled.dev\n </Text>\n <Newline />\n <ConnectionStatus\n status={connectionStatus}\n mode={mode}\n p2pConnected={p2pConnected}\n relayConnected={relayConnected}\n />\n <Newline />\n <Box>\n <Text>URL: </Text>\n <Text bold color=\"cyan\">\n {tunnelUrl}\n </Text>\n </Box>\n <Box>\n <Text dimColor>→ localhost:{localPort}</Text>\n </Box>\n </Box>\n\n {p2pError && mode === 'p2p-only' && (\n <Box\n marginTop={1}\n borderStyle=\"single\"\n borderColor=\"red\"\n padding={1}\n flexDirection=\"column\"\n >\n <Text color=\"red\" bold>\n P2P Connection Failed\n </Text>\n <Text dimColor>{p2pError.message}</Text>\n <Newline />\n <Text dimColor>Use --relay-fallback for guaranteed compatibility</Text>\n </Box>\n )}\n\n {showQR && (\n <Box marginTop={1}>\n <QRCode url={tunnelUrl} />\n </Box>\n )}\n\n <Box marginTop={1} gap={2}>\n <Text>\n Requests: <Text color=\"cyan\">{stats.total}</Text>\n </Text>\n {mode !== 'relay' && stats.total > 0 && (\n <>\n <Text>\n P2P: <Text color=\"green\">{stats.p2p}</Text>\n </Text>\n <Text>\n Relay: <Text color=\"yellow\">{stats.relay}</Text>\n </Text>\n <Text dimColor>({Math.round((stats.p2p / stats.total) * 100)}% private)</Text>\n </>\n )}\n </Box>\n\n <Box marginTop={1} flexDirection=\"column\">\n <Text dimColor>Recent requests:</Text>\n <RequestLog requests={requests} />\n </Box>\n\n <Box marginTop={1}>\n <Text dimColor>Press Ctrl+C to stop</Text>\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 'blue';\n return 'green';\n};\n\nconst getMethodColor = (method: string): string => {\n switch (method) {\n case 'GET':\n return 'green';\n case 'POST':\n return 'yellow';\n case 'PUT':\n return 'blue';\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 <Text dimColor> Waiting for requests...</Text>;\n }\n\n return (\n <Box flexDirection=\"column\">\n {requests.map((req) => (\n <Box key={req.id} gap={1}>\n <Text dimColor>{req.timestamp}</Text>\n <Box width={7}>\n <Text color={getMethodColor(req.method)}>{req.method.padEnd(6)}</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={5}>\n <Text color={getStatusColor(req.status)}>{req.status}</Text>\n </Box>\n <Box width={8}>\n <Text dimColor>{req.duration}ms</Text>\n </Box>\n <Box width={7}>\n <Text color={req.type === 'p2p' ? 'green' : 'yellow'}>[{req.type.toUpperCase()}]</Text>\n </Box>\n </Box>\n ))}\n </Box>\n );\n};\n","import React from 'react';\nimport { Box, Text } from 'ink';\nimport type { ConnectionStatus as ConnectionStatusType, TunnelMode } from '../types.js';\n\ninterface ConnectionStatusProps {\n status: ConnectionStatusType;\n mode: TunnelMode;\n p2pConnected: boolean;\n relayConnected: boolean;\n}\n\nconst getStatusIndicator = (status: ConnectionStatusType): { color: string; symbol: string } => {\n switch (status) {\n case 'connected':\n return { color: 'green', symbol: '●' };\n case 'connecting':\n return { color: 'yellow', symbol: '◐' };\n case 'disconnected':\n return { color: 'red', symbol: '○' };\n case 'error':\n return { color: 'red', symbol: '✗' };\n }\n};\n\nconst getModeLabel = (mode: TunnelMode, p2pConnected: boolean): string => {\n if (mode === 'relay') return 'HTTP Relay';\n if (mode === 'p2p-only') return p2pConnected ? 'P2P Direct' : 'P2P (connecting...)';\n return p2pConnected ? 'P2P + Relay Fallback' : 'Relay (P2P pending...)';\n};\n\nexport const ConnectionStatus: React.FC<ConnectionStatusProps> = ({\n status,\n mode,\n p2pConnected,\n relayConnected,\n}) => {\n const indicator = getStatusIndicator(status);\n const modeLabel = getModeLabel(mode, p2pConnected);\n\n return (\n <Box gap={2}>\n <Box>\n <Text color={indicator.color}>{indicator.symbol}</Text>\n <Text> </Text>\n <Text color={indicator.color}>{status.toUpperCase()}</Text>\n </Box>\n <Box>\n <Text dimColor>Mode: </Text>\n <Text color={p2pConnected ? 'green' : 'yellow'}>{modeLabel}</Text>\n </Box>\n {mode !== 'relay' && (\n <Box>\n <Text dimColor>P2P: </Text>\n <Text color={p2pConnected ? 'green' : 'yellow'}>\n {p2pConnected ? 'Active' : 'Pending'}\n </Text>\n </Box>\n )}\n <Box>\n <Text dimColor>Relay: </Text>\n <Text color={relayConnected ? 'green' : 'red'}>\n {relayConnected ? 'Connected' : 'Disconnected'}\n </Text>\n </Box>\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, TunnelMode } 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 mode?: TunnelMode;\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 'X-Tunnel-Mode': this.options.mode || 'relay',\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.P2P_CONNECTION);\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 type: 'relay',\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 SIGNALING_DOMAIN = 'signal.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 getSignalingUrl = (): string => {\n const host = process.env['UNTUNNELED_SIGNALING_HOST'] || SIGNALING_DOMAIN;\n const protocol = host.includes('localhost') ? 'ws' : 'wss';\n return `${protocol}://${host}`;\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 ICE_SERVERS = [\n { urls: 'stun:stun.l.google.com:19302' },\n { urls: 'stun:stun1.l.google.com:19302' },\n { urls: 'stun:stun2.l.google.com:19302' },\n { urls: 'stun:global.stun.twilio.com:3478' },\n];\n\nexport const TIMEOUTS = {\n P2P_CONNECTION: 10000,\n RELAY_REQUEST: 30000,\n WEBSOCKET_PING: 30000,\n RECONNECT_BASE: 1000,\n RECONNECT_MAX: 30000,\n};\n","import SimplePeer from 'simple-peer';\nimport wrtc from '@roamhq/wrtc';\nimport { WebSocket } from 'ws';\nimport { EventEmitter } from 'node:events';\nimport { getSignalingUrl, ICE_SERVERS, TIMEOUTS } from '../config.js';\nimport type { RelayRequest, RelayResponse, RequestLog, SignalingMessage } from '../types.js';\n\ninterface P2PTunnelOptions {\n localPort: number;\n tunnelId: string;\n verbose?: boolean;\n}\n\nexport class P2PTunnel extends EventEmitter {\n private peer: SimplePeer.Instance | null = null;\n private ws: WebSocket | null = null;\n private localPort: number;\n private tunnelId: string;\n private options: P2PTunnelOptions;\n private connected: boolean = false;\n private requestCount: number = 0;\n\n constructor(options: P2PTunnelOptions) {\n super();\n this.options = options;\n this.localPort = options.localPort;\n this.tunnelId = options.tunnelId;\n }\n\n async connect(): Promise<void> {\n if (this.options.verbose) {\n console.log('[P2P] Attempting connection...');\n }\n\n return new Promise((resolve, reject) => {\n const signalingUrl = getSignalingUrl();\n this.ws = new WebSocket(signalingUrl);\n\n const connectionTimeout = setTimeout(() => {\n if (!this.connected) {\n this.cleanup();\n reject(new Error('P2P connection timeout'));\n }\n }, TIMEOUTS.P2P_CONNECTION);\n\n this.ws.on('open', () => {\n if (this.options.verbose) {\n console.log('[P2P] Signaling connected');\n }\n\n const registerMsg: SignalingMessage = {\n type: 'register',\n tunnelId: this.tunnelId,\n role: 'cli',\n };\n this.ws!.send(JSON.stringify(registerMsg));\n\n this.peer = new SimplePeer({\n initiator: false,\n trickle: false,\n config: { iceServers: ICE_SERVERS },\n wrtc: wrtc as unknown as SimplePeer.Options['wrtc'],\n });\n\n this.peer.on('signal', (signal) => {\n if (this.options.verbose) {\n console.log('[P2P] Sending signal to browser');\n }\n\n const signalMsg: SignalingMessage = {\n type: 'signal',\n from: 'cli',\n to: 'browser',\n tunnelId: this.tunnelId,\n signal,\n };\n this.ws!.send(JSON.stringify(signalMsg));\n });\n\n this.peer.on('connect', () => {\n clearTimeout(connectionTimeout);\n this.connected = true;\n this.emit('connected');\n\n if (this.options.verbose) {\n console.log('[P2P] Connection established');\n }\n\n resolve();\n });\n\n this.peer.on('data', async (data) => {\n await this.handleP2PRequest(data);\n });\n\n this.peer.on('error', (err) => {\n clearTimeout(connectionTimeout);\n this.connected = false;\n this.emit('error', err);\n\n if (this.options.verbose) {\n console.error('[P2P] Error:', err.message);\n }\n\n reject(err);\n });\n\n this.peer.on('close', () => {\n this.connected = false;\n this.emit('disconnected');\n });\n });\n\n this.ws.on('message', (data) => {\n const msg = JSON.parse(data.toString()) as SignalingMessage;\n\n if (msg.type === 'signal' && msg.from === 'browser' && this.peer) {\n if (this.options.verbose) {\n console.log('[P2P] Received signal from browser');\n }\n this.peer.signal(msg.signal as SimplePeer.SignalData);\n }\n });\n\n this.ws.on('error', (err) => {\n clearTimeout(connectionTimeout);\n reject(err);\n });\n });\n }\n\n private async handleP2PRequest(data: Uint8Array): Promise<void> {\n const startTime = Date.now();\n let request: RelayRequest;\n\n try {\n request = JSON.parse(data.toString()) as RelayRequest;\n } catch {\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 p2pResponse: 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.peer!.send(JSON.stringify(p2pResponse));\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 type: 'p2p',\n };\n\n this.emit('request', log);\n this.requestCount++;\n } catch (error) {\n const err = error as Error;\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.peer?.send(JSON.stringify(errorResponse));\n }\n }\n\n private cleanup(): void {\n this.peer?.destroy();\n this.ws?.close();\n this.peer = null;\n this.ws = null;\n this.connected = false;\n }\n\n disconnect(): void {\n this.cleanup();\n }\n\n isConnected(): boolean {\n return this.connected;\n }\n\n getRequestCount(): number {\n return this.requestCount;\n }\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' | 'p2p_success' | 'p2p_failed' | 'request_handled';\n\ninterface EventProperties {\n tunnel_start: {\n mode: string;\n has_auth: boolean;\n node_version: string;\n platform: string;\n };\n tunnel_end: {\n duration_seconds: number;\n requests_relay: number;\n requests_p2p: number;\n p2p_success: boolean;\n };\n p2p_success: {\n mode: string;\n connection_time_ms?: number;\n };\n p2p_failed: {\n mode: string;\n reason: string;\n };\n request_handled: {\n transport: 'p2p' | 'relay';\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":";;;AAAA,SAAS,eAAe;;;ACAxB,SAAS,cAAc;AACvB,OAAOA,YAAW;;;ACDlB,SAAgB,YAAAC,WAAU,aAAAC,kBAAiB;AAC3C,SAAS,OAAAC,MAAK,QAAAC,OAAM,SAAS,cAAc;;;ACD3C,SAAgB,UAAU,iBAAiB;AAC3C,SAAS,KAAK,YAAY;AAC1B,OAAO,YAAY;AAkBX,cAMJ,YANI;AAZD,IAAM,SAAgC,CAAC,EAAE,IAAI,MAAM;AACxD,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,EAAE;AAE3C,YAAU,MAAM;AACd,WAAO,SAAS,KAAK,EAAE,OAAO,KAAK,GAAG,CAAC,OAAe;AACpD,kBAAY,EAAE;AAAA,IAChB,CAAC;AAAA,EACH,GAAG,CAAC,GAAG,CAAC;AAER,MAAI,CAAC,UAAU;AACb,WACE,oBAAC,OACC,8BAAC,QAAK,UAAQ,MAAC,mCAAqB,GACtC;AAAA,EAEJ;AAEA,SACE,qBAAC,OAAI,eAAc,UAAS,aAAY,UAAS,UAAU,GACzD;AAAA,wBAAC,QAAK,UAAQ,MAAC,+BAAiB;AAAA,IAChC,oBAAC,QAAM,oBAAS;AAAA,KAClB;AAEJ;;;AC/BA,OAAkB;AAClB,SAAS,OAAAC,MAAK,QAAAC,aAAY;AAiCf,gBAAAC,MAkBC,QAAAC,aAlBD;AA1BX,IAAM,iBAAiB,CAAC,WAA2B;AACjD,MAAI,UAAU,IAAK,QAAO;AAC1B,MAAI,UAAU,IAAK,QAAO;AAC1B,MAAI,UAAU,IAAK,QAAO;AAC1B,SAAO;AACT;AAEA,IAAM,iBAAiB,CAAC,WAA2B;AACjD,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAEO,IAAM,aAAwC,CAAC,EAAE,SAAS,MAAM;AACrE,MAAI,SAAS,WAAW,GAAG;AACzB,WAAO,gBAAAD,KAACD,OAAA,EAAK,UAAQ,MAAC,sCAAwB;AAAA,EAChD;AAEA,SACE,gBAAAC,KAACF,MAAA,EAAI,eAAc,UAChB,mBAAS,IAAI,CAAC,QACb,gBAAAG,MAACH,MAAA,EAAiB,KAAK,GACrB;AAAA,oBAAAE,KAACD,OAAA,EAAK,UAAQ,MAAE,cAAI,WAAU;AAAA,IAC9B,gBAAAC,KAACF,MAAA,EAAI,OAAO,GACV,0BAAAE,KAACD,OAAA,EAAK,OAAO,eAAe,IAAI,MAAM,GAAI,cAAI,OAAO,OAAO,CAAC,GAAE,GACjE;AAAA,IACA,gBAAAC,KAACF,MAAA,EAAI,UAAU,GACb,0BAAAE,KAACD,OAAA,EAAM,cAAI,KAAK,SAAS,KAAK,IAAI,KAAK,UAAU,GAAG,EAAE,IAAI,QAAQ,IAAI,MAAK,GAC7E;AAAA,IACA,gBAAAC,KAACF,MAAA,EAAI,OAAO,GACV,0BAAAE,KAACD,OAAA,EAAK,OAAO,eAAe,IAAI,MAAM,GAAI,cAAI,QAAO,GACvD;AAAA,IACA,gBAAAC,KAACF,MAAA,EAAI,OAAO,GACV,0BAAAG,MAACF,OAAA,EAAK,UAAQ,MAAE;AAAA,UAAI;AAAA,MAAS;AAAA,OAAE,GACjC;AAAA,IACA,gBAAAC,KAACF,MAAA,EAAI,OAAO,GACV,0BAAAG,MAACF,OAAA,EAAK,OAAO,IAAI,SAAS,QAAQ,UAAU,UAAU;AAAA;AAAA,MAAE,IAAI,KAAK,YAAY;AAAA,MAAE;AAAA,OAAC,GAClF;AAAA,OAhBQ,IAAI,EAiBd,CACD,GACH;AAEJ;;;AC7DA,OAAkB;AAClB,SAAS,OAAAG,MAAK,QAAAC,aAAY;AAwCpB,SACE,OAAAC,MADF,QAAAC,aAAA;AA9BN,IAAM,qBAAqB,CAAC,WAAoE;AAC9F,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO,EAAE,OAAO,SAAS,QAAQ,SAAI;AAAA,IACvC,KAAK;AACH,aAAO,EAAE,OAAO,UAAU,QAAQ,SAAI;AAAA,IACxC,KAAK;AACH,aAAO,EAAE,OAAO,OAAO,QAAQ,SAAI;AAAA,IACrC,KAAK;AACH,aAAO,EAAE,OAAO,OAAO,QAAQ,SAAI;AAAA,EACvC;AACF;AAEA,IAAM,eAAe,CAAC,MAAkB,iBAAkC;AACxE,MAAI,SAAS,QAAS,QAAO;AAC7B,MAAI,SAAS,WAAY,QAAO,eAAe,eAAe;AAC9D,SAAO,eAAe,yBAAyB;AACjD;AAEO,IAAM,mBAAoD,CAAC;AAAA,EAChE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAAM;AACJ,QAAM,YAAY,mBAAmB,MAAM;AAC3C,QAAM,YAAY,aAAa,MAAM,YAAY;AAEjD,SACE,gBAAAA,MAACH,MAAA,EAAI,KAAK,GACR;AAAA,oBAAAG,MAACH,MAAA,EACC;AAAA,sBAAAE,KAACD,OAAA,EAAK,OAAO,UAAU,OAAQ,oBAAU,QAAO;AAAA,MAChD,gBAAAC,KAACD,OAAA,EAAK,eAAC;AAAA,MACP,gBAAAC,KAACD,OAAA,EAAK,OAAO,UAAU,OAAQ,iBAAO,YAAY,GAAE;AAAA,OACtD;AAAA,IACA,gBAAAE,MAACH,MAAA,EACC;AAAA,sBAAAE,KAACD,OAAA,EAAK,UAAQ,MAAC,oBAAM;AAAA,MACrB,gBAAAC,KAACD,OAAA,EAAK,OAAO,eAAe,UAAU,UAAW,qBAAU;AAAA,OAC7D;AAAA,IACC,SAAS,WACR,gBAAAE,MAACH,MAAA,EACC;AAAA,sBAAAE,KAACD,OAAA,EAAK,UAAQ,MAAC,mBAAK;AAAA,MACpB,gBAAAC,KAACD,OAAA,EAAK,OAAO,eAAe,UAAU,UACnC,yBAAe,WAAW,WAC7B;AAAA,OACF;AAAA,IAEF,gBAAAE,MAACH,MAAA,EACC;AAAA,sBAAAE,KAACD,OAAA,EAAK,UAAQ,MAAC,qBAAO;AAAA,MACtB,gBAAAC,KAACD,OAAA,EAAK,OAAO,iBAAiB,UAAU,OACrC,2BAAiB,cAAc,gBAClC;AAAA,OACF;AAAA,KACF;AAEJ;;;AHoBQ,SAkDE,UA/CF,OAAAG,MAHA,QAAAC,aAAA;AAnER,IAAM,WAAW;AAEV,IAAM,WAAoC,CAAC;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAAM;AACJ,SAAO;AACP,QAAM,CAAC,UAAU,WAAW,IAAIC,UAA2B,CAAC,CAAC;AAC7D,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAsB;AAAA,IAC9C,OAAO;AAAA,IACP,KAAK;AAAA,IACL,OAAO;AAAA,IACP,WAAW,KAAK,IAAI;AAAA,EACtB,CAAC;AACD,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,UAAS,YAAY,YAAY,CAAC;AAC9E,QAAM,CAAC,cAAc,eAAe,IAAIA,UAAS,WAAW,YAAY,KAAK,KAAK;AAElF,EAAAC,WAAU,MAAM;AACd,UAAM,gBAAgB,CAAC,QAAwB;AAC7C,kBAAY,CAAC,SAAS,CAAC,GAAG,KAAK,MAAM,EAAE,WAAW,EAAE,GAAG,GAAG,CAAC;AAC3D,eAAS,CAAC,UAAU;AAAA,QAClB,GAAG;AAAA,QACH,OAAO,KAAK,QAAQ;AAAA,QACpB,KAAK,KAAK,OAAO,IAAI,SAAS,QAAQ,IAAI;AAAA,QAC1C,OAAO,KAAK,SAAS,IAAI,SAAS,UAAU,IAAI;AAAA,MAClD,EAAE;AAAA,IACJ;AAEA,UAAM,uBAAuB,MAAM,kBAAkB,IAAI;AACzD,UAAM,0BAA0B,MAAM,kBAAkB,KAAK;AAC7D,UAAM,qBAAqB,MAAM,gBAAgB,IAAI;AACrD,UAAM,wBAAwB,MAAM,gBAAgB,KAAK;AAEzD,gBAAY,GAAG,WAAW,aAAa;AACvC,gBAAY,GAAG,aAAa,oBAAoB;AAChD,gBAAY,GAAG,gBAAgB,uBAAuB;AAEtD,QAAI,WAAW;AACb,gBAAU,GAAG,WAAW,aAAa;AACrC,gBAAU,GAAG,aAAa,kBAAkB;AAC5C,gBAAU,GAAG,gBAAgB,qBAAqB;AAAA,IACpD;AAEA,WAAO,MAAM;AACX,kBAAY,IAAI,WAAW,aAAa;AACxC,kBAAY,IAAI,aAAa,oBAAoB;AACjD,kBAAY,IAAI,gBAAgB,uBAAuB;AAEvD,UAAI,WAAW;AACb,kBAAU,IAAI,WAAW,aAAa;AACtC,kBAAU,IAAI,aAAa,kBAAkB;AAC7C,kBAAU,IAAI,gBAAgB,qBAAqB;AAAA,MACrD;AAAA,IACF;AAAA,EACF,GAAG,CAAC,aAAa,SAAS,CAAC;AAE3B,QAAM,mBAAmB,kBAAkB,eAAe,cAAc;AACxE,QAAM,cAAc,eAAe,UAAU,iBAAiB,WAAW;AAEzE,SACE,gBAAAF,MAACG,MAAA,EAAI,eAAc,UAAS,SAAS,GACnC;AAAA,oBAAAH,MAACG,MAAA,EAAI,aAAY,SAAQ,aAA0B,SAAS,GAAG,eAAc,UAC3E;AAAA,sBAAAH,MAACI,OAAA,EAAK,MAAI,MAAC,OAAO,aACf;AAAA,uBAAe,cAAO;AAAA,QAAI;AAAA,SAC7B;AAAA,MACA,gBAAAL,KAAC,WAAQ;AAAA,MACT,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,QAAQ;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA;AAAA,MACF;AAAA,MACA,gBAAAA,KAAC,WAAQ;AAAA,MACT,gBAAAC,MAACG,MAAA,EACC;AAAA,wBAAAJ,KAACK,OAAA,EAAK,mBAAK;AAAA,QACX,gBAAAL,KAACK,OAAA,EAAK,MAAI,MAAC,OAAM,QACd,qBACH;AAAA,SACF;AAAA,MACA,gBAAAL,KAACI,MAAA,EACC,0BAAAH,MAACI,OAAA,EAAK,UAAQ,MAAC;AAAA;AAAA,QAAa;AAAA,SAAU,GACxC;AAAA,OACF;AAAA,IAEC,YAAY,SAAS,cACpB,gBAAAJ;AAAA,MAACG;AAAA,MAAA;AAAA,QACC,WAAW;AAAA,QACX,aAAY;AAAA,QACZ,aAAY;AAAA,QACZ,SAAS;AAAA,QACT,eAAc;AAAA,QAEd;AAAA,0BAAAJ,KAACK,OAAA,EAAK,OAAM,OAAM,MAAI,MAAC,mCAEvB;AAAA,UACA,gBAAAL,KAACK,OAAA,EAAK,UAAQ,MAAE,mBAAS,SAAQ;AAAA,UACjC,gBAAAL,KAAC,WAAQ;AAAA,UACT,gBAAAA,KAACK,OAAA,EAAK,UAAQ,MAAC,+DAAiD;AAAA;AAAA;AAAA,IAClE;AAAA,IAGD,UACC,gBAAAL,KAACI,MAAA,EAAI,WAAW,GACd,0BAAAJ,KAAC,UAAO,KAAK,WAAW,GAC1B;AAAA,IAGF,gBAAAC,MAACG,MAAA,EAAI,WAAW,GAAG,KAAK,GACtB;AAAA,sBAAAH,MAACI,OAAA,EAAK;AAAA;AAAA,QACM,gBAAAL,KAACK,OAAA,EAAK,OAAM,QAAQ,gBAAM,OAAM;AAAA,SAC5C;AAAA,MACC,SAAS,WAAW,MAAM,QAAQ,KACjC,gBAAAJ,MAAA,YACE;AAAA,wBAAAA,MAACI,OAAA,EAAK;AAAA;AAAA,UACC,gBAAAL,KAACK,OAAA,EAAK,OAAM,SAAS,gBAAM,KAAI;AAAA,WACtC;AAAA,QACA,gBAAAJ,MAACI,OAAA,EAAK;AAAA;AAAA,UACG,gBAAAL,KAACK,OAAA,EAAK,OAAM,UAAU,gBAAM,OAAM;AAAA,WAC3C;AAAA,QACA,gBAAAJ,MAACI,OAAA,EAAK,UAAQ,MAAC;AAAA;AAAA,UAAE,KAAK,MAAO,MAAM,MAAM,MAAM,QAAS,GAAG;AAAA,UAAE;AAAA,WAAU;AAAA,SACzE;AAAA,OAEJ;AAAA,IAEA,gBAAAJ,MAACG,MAAA,EAAI,WAAW,GAAG,eAAc,UAC/B;AAAA,sBAAAJ,KAACK,OAAA,EAAK,UAAQ,MAAC,8BAAgB;AAAA,MAC/B,gBAAAL,KAAC,cAAW,UAAoB;AAAA,OAClC;AAAA,IAEA,gBAAAA,KAACI,MAAA,EAAI,WAAW,GACd,0BAAAJ,KAACK,OAAA,EAAK,UAAQ,MAAC,kCAAoB,GACrC;AAAA,KACF;AAEJ;;;AI9JA,SAAS,iBAAiB;AAC1B,SAAS,oBAAoB;;;ACD7B,SAAS,cAAc;AACvB,OAAO,UAAU;AAEV,IAAM,eAAe;AACrB,IAAM,mBAAmB;AACzB,IAAM,gBAAgB;AAEtB,IAAM,cAAc,CAAC,aAA6B;AACvD,QAAM,OAAO,QAAQ,IAAI,uBAAuB,KAAK;AACrD,QAAM,WAAW,KAAK,SAAS,WAAW,IAAI,OAAO;AACrD,SAAO,GAAG,QAAQ,MAAM,IAAI,IAAI,QAAQ;AAC1C;AAEO,IAAM,kBAAkB,MAAc;AAC3C,QAAM,OAAO,QAAQ,IAAI,2BAA2B,KAAK;AACzD,QAAM,WAAW,KAAK,SAAS,WAAW,IAAI,OAAO;AACrD,SAAO,GAAG,QAAQ,MAAM,IAAI;AAC9B;AAEO,IAAM,eAAe,CAAC,aAA6B;AACxD,QAAM,SAAS,QAAQ,IAAI,0BAA0B,KAAK;AAC1D,SAAO,WAAW,QAAQ,IAAI,MAAM;AACtC;AAEO,SAAS,iBAAyB;AACvC,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,aAAa,KAAK,SAAS,GAAG;AAEpC,SACE,WACG,YAAY,EACZ,QAAQ,eAAe,GAAG,EAC1B,QAAQ,OAAO,GAAG,EAClB,QAAQ,UAAU,EAAE,EACpB,MAAM,GAAG,EAAE,KAAK;AAEvB;AAEO,SAAS,iBAAiB,aAA8B;AAC7D,QAAM,OAAO,eAAe,eAAe;AAC3C,QAAM,SAAS,OAAO,CAAC,EAAE,YAAY;AACrC,SAAO,GAAG,IAAI,IAAI,MAAM;AAC1B;AAEO,SAAS,aAAa,MAA+B;AAC1D,QAAM,UAAU,OAAO,SAAS,WAAW,SAAS,MAAM,EAAE,IAAI;AAEhE,MAAI,MAAM,OAAO,KAAK,UAAU,KAAK,UAAU,OAAO;AACpD,UAAM,IAAI,MAAM,wBAAwB,IAAI,gCAAgC;AAAA,EAC9E;AAEA,SAAO;AACT;AAEO,SAAS,eAAe,OAAsC;AACnE,MAAI,CAAC,MAAO,QAAO;AAEnB,SAAO,MACJ,MAAM,GAAG,EACT,IAAI,CAAC,UAAU,MAAM,KAAK,EAAE,YAAY,CAAC,EACzC,OAAO,CAAC,UAAU,MAAM,SAAS,CAAC;AACvC;AAEO,IAAM,cAAc;AAAA,EACzB,EAAE,MAAM,+BAA+B;AAAA,EACvC,EAAE,MAAM,gCAAgC;AAAA,EACxC,EAAE,MAAM,gCAAgC;AAAA,EACxC,EAAE,MAAM,mCAAmC;AAC7C;AAEO,IAAM,WAAW;AAAA,EACtB,gBAAgB;AAAA,EAChB,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,eAAe;AACjB;;;AD7DO,IAAM,cAAN,cAA0B,aAAa;AAAA,EACpC,KAAuB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAoB;AAAA,EACpB,eAAuB;AAAA,EACvB,oBAA4B;AAAA,EAC5B,YAAqB;AAAA,EACrB,eAAsC;AAAA,EAE9C,YAAY,SAA6B;AACvC,UAAM;AACN,SAAK,UAAU;AACf,SAAK,YAAY,QAAQ;AACzB,SAAK,WAAW,QAAQ;AACxB,SAAK,YAAY,QAAQ;AAAA,EAC3B;AAAA,EAEA,MAAM,UAAyB;AAC7B,SAAK,YAAY,KAAK,IAAI;AAC1B,SAAK,YAAY;AAEjB,UAAM,QAAQ,YAAY,KAAK,QAAQ;AAEvC,QAAI,KAAK,QAAQ,SAAS;AACxB,cAAQ,IAAI,yBAAyB,KAAK,EAAE;AAAA,IAC9C;AAEA,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI;AACF,aAAK,KAAK,IAAI,UAAU,OAAO;AAAA,UAC7B,SAAS;AAAA,YACP,iBAAiB,KAAK,QAAQ,YAAY;AAAA,YAC1C,kBAAkB,KAAK,QAAQ,WAAW,KAAK,GAAG,KAAK;AAAA,YACvD,iBAAiB,KAAK,QAAQ,QAAQ;AAAA,UACxC;AAAA,QACF,CAAC;AAED,cAAM,oBAAoB,WAAW,MAAM;AACzC,cAAI,KAAK,MAAM,KAAK,GAAG,eAAe,UAAU,MAAM;AACpD,iBAAK,GAAG,UAAU;AAClB,mBAAO,IAAI,MAAM,oBAAoB,CAAC;AAAA,UACxC;AAAA,QACF,GAAG,SAAS,cAAc;AAE1B,aAAK,GAAG,GAAG,QAAQ,MAAM;AACvB,uBAAa,iBAAiB;AAC9B,eAAK,oBAAoB;AAEzB,cAAI,KAAK,QAAQ,SAAS;AACxB,oBAAQ,IAAI,mBAAmB;AAAA,UACjC;AAEA,eAAK,UAAU;AACf,eAAK,KAAK,WAAW;AACrB,kBAAQ;AAAA,QACV,CAAC;AAED,aAAK,GAAG,GAAG,SAAS,CAAC,QAAQ;AAC3B,uBAAa,iBAAiB;AAE9B,cAAI,KAAK,QAAQ,SAAS;AACxB,oBAAQ,MAAM,4BAA4B,IAAI,OAAO;AAAA,UACvD;AAEA,eAAK,KAAK,SAAS,GAAG;AACtB,iBAAO,GAAG;AAAA,QACZ,CAAC;AAED,aAAK,GAAG,GAAG,SAAS,MAAM;AACxB,eAAK,UAAU;AAEf,cAAI,CAAC,KAAK,WAAW;AACnB,iBAAK,KAAK,cAAc;AACxB,iBAAK,iBAAiB;AAAA,UACxB;AAAA,QACF,CAAC;AAED,aAAK,GAAG,GAAG,WAAW,OAAO,SAAS;AACpC,gBAAM,KAAK,mBAAmB,IAAI;AAAA,QACpC,CAAC;AAAA,MACH,SAAS,OAAO;AACd,eAAO,KAAK;AAAA,MACd;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,mBAAmB,MAAsD;AACrF,UAAM,YAAY,KAAK,IAAI;AAC3B,QAAI;AAEJ,QAAI;AACF,gBAAU,KAAK,MAAM,OAAO,IAAI,CAAC;AAAA,IACnC,QAAQ;AACN,UAAI,KAAK,QAAQ,SAAS;AACxB,gBAAQ,MAAM,8BAA8B;AAAA,MAC9C;AACA;AAAA,IACF;AAEA,QAAI;AACF,YAAM,MAAM,oBAAoB,KAAK,SAAS,GAAG,QAAQ,IAAI;AAE7D,YAAM,eAA4B;AAAA,QAChC,QAAQ,QAAQ;AAAA,QAChB,SAAS,QAAQ;AAAA,MACnB;AAEA,UAAI,QAAQ,QAAQ,CAAC,CAAC,OAAO,MAAM,EAAE,SAAS,QAAQ,MAAM,GAAG;AAC7D,qBAAa,OAAO,QAAQ;AAAA,MAC9B;AAEA,YAAM,WAAW,MAAM,MAAM,KAAK,YAAY;AAC9C,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,WAAW,KAAK,IAAI,IAAI;AAE9B,YAAM,gBAA+B;AAAA,QACnC,IAAI,QAAQ;AAAA,QACZ,QAAQ,SAAS;AAAA,QACjB,YAAY,SAAS;AAAA,QACrB,SAAS,OAAO,YAAY,SAAS,QAAQ,QAAQ,CAAC;AAAA,QACtD;AAAA,MACF;AAEA,WAAK,IAAI,KAAK,KAAK,UAAU,aAAa,CAAC;AAE3C,YAAM,MAAkB;AAAA,QACtB,IAAI,QAAQ;AAAA,QACZ,YAAW,oBAAI,KAAK,GAAE,YAAY,EAAE,UAAU,IAAI,EAAE;AAAA,QACpD,QAAQ,QAAQ;AAAA,QAChB,MAAM,QAAQ;AAAA,QACd,QAAQ,SAAS;AAAA,QACjB;AAAA,QACA,MAAM;AAAA,MACR;AAEA,WAAK,KAAK,WAAW,GAAG;AACxB,WAAK;AAAA,IACP,SAAS,OAAO;AACd,YAAM,MAAM;AAEZ,UAAI,KAAK,QAAQ,SAAS;AACxB,gBAAQ,MAAM,wBAAwB,IAAI,OAAO,EAAE;AAAA,MACrD;AAEA,YAAM,gBAA+B;AAAA,QACnC,IAAI,QAAQ;AAAA,QACZ,QAAQ;AAAA,QACR,YAAY;AAAA,QACZ,SAAS,CAAC;AAAA,QACV,MAAM,UAAU,IAAI,OAAO;AAAA,MAC7B;AAEA,WAAK,IAAI,KAAK,KAAK,UAAU,aAAa,CAAC;AAAA,IAC7C;AAAA,EACF;AAAA,EAEQ,YAAkB;AACxB,SAAK,eAAe,YAAY,MAAM;AACpC,UAAI,KAAK,MAAM,KAAK,GAAG,eAAe,UAAU,MAAM;AACpD,aAAK,GAAG,KAAK;AAAA,MACf;AAAA,IACF,GAAG,SAAS,cAAc;AAAA,EAC5B;AAAA,EAEQ,YAAkB;AACxB,QAAI,KAAK,cAAc;AACrB,oBAAc,KAAK,YAAY;AAC/B,WAAK,eAAe;AAAA,IACtB;AAAA,EACF;AAAA,EAEA,MAAc,mBAAkC;AAC9C,QAAI,KAAK,UAAW;AAEpB,SAAK;AACL,UAAM,QAAQ,KAAK;AAAA,MACjB,SAAS,iBAAiB,KAAK,IAAI,GAAG,KAAK,oBAAoB,CAAC;AAAA,MAChE,SAAS;AAAA,IACX;AAEA,QAAI,KAAK,QAAQ,SAAS;AACxB,cAAQ,IAAI,2BAA2B,KAAK,eAAe,KAAK,iBAAiB,GAAG;AAAA,IACtF;AAEA,eAAW,YAAY;AACrB,UAAI,KAAK,UAAW;AAEpB,UAAI;AACF,cAAM,KAAK,QAAQ;AAAA,MACrB,SAAS,GAAG;AAAA,MAAC;AAAA,IACf,GAAG,KAAK;AAAA,EACV;AAAA,EAEA,MAAM,aAA4B;AAChC,SAAK,YAAY;AACjB,SAAK,UAAU;AAEf,QAAI,KAAK,IAAI;AACX,WAAK,GAAG,MAAM,KAAM,gBAAgB;AACpC,WAAK,KAAK;AAAA,IACZ;AAAA,EACF;AAAA,EAEA,cAAsB;AACpB,QAAI,KAAK,cAAc,EAAG,QAAO;AACjC,WAAO,KAAK,OAAO,KAAK,IAAI,IAAI,KAAK,aAAa,GAAI;AAAA,EACxD;AAAA,EAEA,kBAA0B;AACxB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,cAAuB;AACrB,WAAO,KAAK,OAAO,QAAQ,KAAK,GAAG,eAAe,UAAU;AAAA,EAC9D;AAAA,EAEA,eAAuB;AACrB,WAAO,KAAK;AAAA,EACd;AACF;;;AE7OA,OAAO,gBAAgB;AACvB,OAAO,UAAU;AACjB,SAAS,aAAAC,kBAAiB;AAC1B,SAAS,gBAAAC,qBAAoB;AAUtB,IAAM,YAAN,cAAwBC,cAAa;AAAA,EAClC,OAAmC;AAAA,EACnC,KAAuB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAqB;AAAA,EACrB,eAAuB;AAAA,EAE/B,YAAY,SAA2B;AACrC,UAAM;AACN,SAAK,UAAU;AACf,SAAK,YAAY,QAAQ;AACzB,SAAK,WAAW,QAAQ;AAAA,EAC1B;AAAA,EAEA,MAAM,UAAyB;AAC7B,QAAI,KAAK,QAAQ,SAAS;AACxB,cAAQ,IAAI,gCAAgC;AAAA,IAC9C;AAEA,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,eAAe,gBAAgB;AACrC,WAAK,KAAK,IAAIC,WAAU,YAAY;AAEpC,YAAM,oBAAoB,WAAW,MAAM;AACzC,YAAI,CAAC,KAAK,WAAW;AACnB,eAAK,QAAQ;AACb,iBAAO,IAAI,MAAM,wBAAwB,CAAC;AAAA,QAC5C;AAAA,MACF,GAAG,SAAS,cAAc;AAE1B,WAAK,GAAG,GAAG,QAAQ,MAAM;AACvB,YAAI,KAAK,QAAQ,SAAS;AACxB,kBAAQ,IAAI,2BAA2B;AAAA,QACzC;AAEA,cAAM,cAAgC;AAAA,UACpC,MAAM;AAAA,UACN,UAAU,KAAK;AAAA,UACf,MAAM;AAAA,QACR;AACA,aAAK,GAAI,KAAK,KAAK,UAAU,WAAW,CAAC;AAEzC,aAAK,OAAO,IAAI,WAAW;AAAA,UACzB,WAAW;AAAA,UACX,SAAS;AAAA,UACT,QAAQ,EAAE,YAAY,YAAY;AAAA,UAClC;AAAA,QACF,CAAC;AAED,aAAK,KAAK,GAAG,UAAU,CAAC,WAAW;AACjC,cAAI,KAAK,QAAQ,SAAS;AACxB,oBAAQ,IAAI,iCAAiC;AAAA,UAC/C;AAEA,gBAAM,YAA8B;AAAA,YAClC,MAAM;AAAA,YACN,MAAM;AAAA,YACN,IAAI;AAAA,YACJ,UAAU,KAAK;AAAA,YACf;AAAA,UACF;AACA,eAAK,GAAI,KAAK,KAAK,UAAU,SAAS,CAAC;AAAA,QACzC,CAAC;AAED,aAAK,KAAK,GAAG,WAAW,MAAM;AAC5B,uBAAa,iBAAiB;AAC9B,eAAK,YAAY;AACjB,eAAK,KAAK,WAAW;AAErB,cAAI,KAAK,QAAQ,SAAS;AACxB,oBAAQ,IAAI,8BAA8B;AAAA,UAC5C;AAEA,kBAAQ;AAAA,QACV,CAAC;AAED,aAAK,KAAK,GAAG,QAAQ,OAAO,SAAS;AACnC,gBAAM,KAAK,iBAAiB,IAAI;AAAA,QAClC,CAAC;AAED,aAAK,KAAK,GAAG,SAAS,CAAC,QAAQ;AAC7B,uBAAa,iBAAiB;AAC9B,eAAK,YAAY;AACjB,eAAK,KAAK,SAAS,GAAG;AAEtB,cAAI,KAAK,QAAQ,SAAS;AACxB,oBAAQ,MAAM,gBAAgB,IAAI,OAAO;AAAA,UAC3C;AAEA,iBAAO,GAAG;AAAA,QACZ,CAAC;AAED,aAAK,KAAK,GAAG,SAAS,MAAM;AAC1B,eAAK,YAAY;AACjB,eAAK,KAAK,cAAc;AAAA,QAC1B,CAAC;AAAA,MACH,CAAC;AAED,WAAK,GAAG,GAAG,WAAW,CAAC,SAAS;AAC9B,cAAM,MAAM,KAAK,MAAM,KAAK,SAAS,CAAC;AAEtC,YAAI,IAAI,SAAS,YAAY,IAAI,SAAS,aAAa,KAAK,MAAM;AAChE,cAAI,KAAK,QAAQ,SAAS;AACxB,oBAAQ,IAAI,oCAAoC;AAAA,UAClD;AACA,eAAK,KAAK,OAAO,IAAI,MAA+B;AAAA,QACtD;AAAA,MACF,CAAC;AAED,WAAK,GAAG,GAAG,SAAS,CAAC,QAAQ;AAC3B,qBAAa,iBAAiB;AAC9B,eAAO,GAAG;AAAA,MACZ,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,iBAAiB,MAAiC;AAC9D,UAAM,YAAY,KAAK,IAAI;AAC3B,QAAI;AAEJ,QAAI;AACF,gBAAU,KAAK,MAAM,KAAK,SAAS,CAAC;AAAA,IACtC,QAAQ;AACN;AAAA,IACF;AAEA,QAAI;AACF,YAAM,MAAM,oBAAoB,KAAK,SAAS,GAAG,QAAQ,IAAI;AAE7D,YAAM,eAA4B;AAAA,QAChC,QAAQ,QAAQ;AAAA,QAChB,SAAS,QAAQ;AAAA,MACnB;AAEA,UAAI,QAAQ,QAAQ,CAAC,CAAC,OAAO,MAAM,EAAE,SAAS,QAAQ,MAAM,GAAG;AAC7D,qBAAa,OAAO,QAAQ;AAAA,MAC9B;AAEA,YAAM,WAAW,MAAM,MAAM,KAAK,YAAY;AAC9C,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,WAAW,KAAK,IAAI,IAAI;AAE9B,YAAM,cAA6B;AAAA,QACjC,IAAI,QAAQ;AAAA,QACZ,QAAQ,SAAS;AAAA,QACjB,YAAY,SAAS;AAAA,QACrB,SAAS,OAAO,YAAY,SAAS,QAAQ,QAAQ,CAAC;AAAA,QACtD;AAAA,MACF;AAEA,WAAK,KAAM,KAAK,KAAK,UAAU,WAAW,CAAC;AAE3C,YAAM,MAAkB;AAAA,QACtB,IAAI,QAAQ;AAAA,QACZ,YAAW,oBAAI,KAAK,GAAE,YAAY,EAAE,UAAU,IAAI,EAAE;AAAA,QACpD,QAAQ,QAAQ;AAAA,QAChB,MAAM,QAAQ;AAAA,QACd,QAAQ,SAAS;AAAA,QACjB;AAAA,QACA,MAAM;AAAA,MACR;AAEA,WAAK,KAAK,WAAW,GAAG;AACxB,WAAK;AAAA,IACP,SAAS,OAAO;AACd,YAAM,MAAM;AAEZ,YAAM,gBAA+B;AAAA,QACnC,IAAI,QAAQ;AAAA,QACZ,QAAQ;AAAA,QACR,YAAY;AAAA,QACZ,SAAS,CAAC;AAAA,QACV,MAAM,UAAU,IAAI,OAAO;AAAA,MAC7B;AAEA,WAAK,MAAM,KAAK,KAAK,UAAU,aAAa,CAAC;AAAA,IAC/C;AAAA,EACF;AAAA,EAEQ,UAAgB;AACtB,SAAK,MAAM,QAAQ;AACnB,SAAK,IAAI,MAAM;AACf,SAAK,OAAO;AACZ,SAAK,KAAK;AACV,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,aAAmB;AACjB,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,cAAuB;AACrB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,kBAA0B;AACxB,WAAO,KAAK;AAAA,EACd;AACF;;;ACrNA,SAAS,eAAe;AAExB,IAAM,kBAAkB,QAAQ,IAAI,iBAAiB,KAAK;AAC1D,IAAM,eAAe;AAsCd,IAAM,YAAN,MAAgB;AAAA,EACb,UAA0B;AAAA,EAC1B;AAAA,EACA;AAAA,EAER,YAAY,SAA2B;AACrC,SAAK,WAAW,QAAQ;AACxB,SAAK,UAAU,QAAQ;AAEvB,QAAI,KAAK,WAAW,oBAAoB,mBAAmB;AACzD,WAAK,UAAU,IAAI,QAAQ,iBAAiB;AAAA,QAC1C,MAAM;AAAA,QACN,SAAS;AAAA,QACT,eAAe;AAAA,MACjB,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAM,MAA2B,OAAU,YAAgD;AACzF,QAAI,CAAC,KAAK,WAAW,CAAC,KAAK,QAAS;AAEpC,QAAI;AACF,WAAK,QAAQ,QAAQ;AAAA,QACnB,YAAY,KAAK;AAAA,QACjB;AAAA,QACA,YAAY;AAAA,UACV,GAAG;AAAA,UACH,SAAS;AAAA,UACT,KAAK;AAAA,QACP;AAAA,MACF,CAAC;AAAA,IACH,SAAS,GAAG;AAAA,IAAC;AAAA,EACf;AAAA,EAEA,MAAM,WAA0B;AAC9B,QAAI,CAAC,KAAK,QAAS;AAEnB,QAAI;AACF,YAAM,KAAK,QAAQ,SAAS;AAAA,IAC9B,SAAS,GAAG;AAAA,IAAC;AAAA,EACf;AAAA,EAEA,YAAqB;AACnB,WAAO,KAAK,WAAW,KAAK,YAAY;AAAA,EAC1C;AACF;;;ARvEA,eAAsB,aAAa,MAAc,SAAsC;AACrF,MAAI;AAEJ,MAAI;AACF,gBAAY,aAAa,IAAI;AAAA,EAC/B,SAAS,OAAO;AACd,YAAQ,MAAO,MAAgB,OAAO;AACtC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,cAAc,eAAe;AACnC,QAAM,WAAW,iBAAiB,WAAW;AAC7C,QAAM,YAAY,aAAa,QAAQ;AACvC,QAAM,OAAmB,QAAQ,gBAAgB,sBAAsB;AAEvE,QAAM,YAAY,IAAI,UAAU;AAAA,IAC9B,SAAS,QAAQ,cAAc;AAAA,IAC/B;AAAA,EACF,CAAC;AAED,QAAM,UAAU,MAAM,gBAAgB;AAAA,IACpC;AAAA,IACA,UAAU,CAAC,CAAC,QAAQ,YAAY,CAAC,CAAC,QAAQ;AAAA,IAC1C,cAAc,QAAQ;AAAA,IACtB,UAAU,QAAQ;AAAA,EACpB,CAAC;AAED,QAAM,cAAc,IAAI,YAAY;AAAA,IAClC;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU,QAAQ;AAAA,IAClB,WAAW,eAAe,QAAQ,KAAK;AAAA,IACvC,SAAS,QAAQ;AAAA,IACjB;AAAA,EACF,CAAC;AAED,MAAI,YAA8B;AAClC,MAAI,WAAyB;AAE7B,MAAI;AACF,UAAM,YAAY,QAAQ;AAE1B,QAAI,QAAQ,SAAS;AACnB,cAAQ,IAAI,eAAe,SAAS,EAAE;AACtC,cAAQ,IAAI,2BAA2B,SAAS,EAAE;AAAA,IACpD;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,sCAAuC,MAAgB,OAAO;AAC5E,UAAM,UAAU,SAAS;AACzB,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,cAAY,IAAI,UAAU;AAAA,IACxB;AAAA,IACA;AAAA,IACA,SAAS,QAAQ;AAAA,EACnB,CAAC;AAED,MAAI;AACF,UAAM,UAAU,QAAQ;AACxB,UAAM,UAAU,MAAM,eAAe,EAAE,KAAK,CAAC;AAAA,EAC/C,SAAS,OAAO;AACd,eAAW;AACX,UAAM,UAAU,MAAM,cAAc,EAAE,MAAM,QAAQ,SAAS,QAAQ,CAAC;AAEtE,QAAI,CAAC,QAAQ,eAAe;AAC1B,cAAQ,MAAM,EAAE;AAChB,cAAQ,MAAM,0BAA0B,SAAS,OAAO;AACxD,cAAQ,MAAM,EAAE;AAChB,cAAQ,MAAM,iDAAiD;AAC/D,cAAQ,MAAM,EAAE;AAChB,cAAQ,MAAM,UAAU;AACxB,cAAQ,MAAM,kDAAkD;AAChE,cAAQ,MAAM,0DAA0D;AACxE,cAAQ,MAAM,EAAE;AAAA,IAClB;AAAA,EACF;AAEA,QAAM,EAAE,cAAc,IAAI;AAAA,IACxBC,OAAM,cAAc,UAAU;AAAA,MAC5B;AAAA,MACA;AAAA,MACA,QAAQ,QAAQ;AAAA,MAChB;AAAA,MACA;AAAA,MACA,WAAW,WAAW,YAAY,IAAI,YAAY;AAAA,MAClD;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,UAAU,YAAY;AAC1B,UAAM,WAAW,YAAY,YAAY;AACzC,UAAM,gBAAgB,YAAY,gBAAgB;AAClD,UAAM,cAAc,WAAW,gBAAgB,KAAK;AAEpD,UAAM,UAAU,MAAM,cAAc;AAAA,MAClC,kBAAkB;AAAA,MAClB,gBAAgB;AAAA,MAChB,cAAc;AAAA,MACd,aAAa,WAAW,YAAY,KAAK;AAAA,IAC3C,CAAC;AAED,UAAM,YAAY,WAAW;AAC7B,eAAW,WAAW;AACtB,UAAM,UAAU,SAAS;AAAA,EAC3B;AAEA,UAAQ,GAAG,UAAU,YAAY;AAC/B,UAAM,QAAQ;AACd,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AAED,UAAQ,GAAG,WAAW,YAAY;AAChC,UAAM,QAAQ;AACd,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AAED,QAAM,cAAc;AACtB;;;AStIA,eAAsB,cAA6B;AACjD,UAAQ,IAAI,iBAAiB;AAC7B,UAAQ,IAAI,uBAAuB;AACnC,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,wCAAwC;AACtD;;;ACLA,eAAsB,YAAY,UAAiC;AACjE,UAAQ,IAAI,oBAAoB,QAAQ,EAAE;AAC1C,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,wCAAwC;AACtD;;;AXDA,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,YAAY,EACjB,YAAY,oDAAoD,EAChE,QAAQ,OAAO;AAElB,QACG,SAAS,UAAU,sBAAsB,EACzC,OAAO,wBAAwB,yCAAyC,EACxE,OAAO,QAAQ,gCAAgC,EAC/C,OAAO,yBAAyB,6BAA6B,EAC7D,OAAO,oBAAoB,mCAAmC,EAC9D,OAAO,iBAAiB,kBAAkB,EAC1C,OAAO,aAAa,oBAAoB,EACxC,OAAO,kBAAkB,kCAAkC,EAC3D,OAAO,YAAY;AAEtB,QAAQ,QAAQ,MAAM,EAAE,YAAY,qBAAqB,EAAE,OAAO,WAAW;AAE7E,QACG,QAAQ,MAAM,EACd,SAAS,eAAe,mBAAmB,EAC3C,YAAY,uBAAuB,EACnC,OAAO,WAAW;AAErB,QAAQ,MAAM;","names":["React","useState","useEffect","Box","Text","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","jsx","jsxs","useState","useEffect","Box","Text","WebSocket","EventEmitter","EventEmitter","WebSocket","React"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "untunneled.dev",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Privacy-first localhost tunneling - P2P by default",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -21,6 +21,7 @@
21
21
  "author": "",
22
22
  "license": "MIT",
23
23
  "dependencies": {
24
+ "@roamhq/wrtc": "^0.8.0",
24
25
  "chalk": "^5.3.0",
25
26
  "commander": "^14.0.2",
26
27
  "ink": "^6.6.0",