untunneled.dev 0.1.4-alpha3 → 0.1.4-alpha5
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 +112 -72
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -311,7 +311,7 @@ var ICE_SERVERS = [
|
|
|
311
311
|
{ urls: "stun:global.stun.twilio.com:3478" }
|
|
312
312
|
];
|
|
313
313
|
var TIMEOUTS = {
|
|
314
|
-
P2P_CONNECTION:
|
|
314
|
+
P2P_CONNECTION: 3e4,
|
|
315
315
|
RELAY_REQUEST: 3e4,
|
|
316
316
|
WEBSOCKET_PING: 3e4,
|
|
317
317
|
RECONNECT_BASE: 1e3,
|
|
@@ -517,6 +517,8 @@ var P2PTunnel = class extends EventEmitter2 {
|
|
|
517
517
|
options;
|
|
518
518
|
connected = false;
|
|
519
519
|
requestCount = 0;
|
|
520
|
+
signalingConnected = false;
|
|
521
|
+
pingInterval = null;
|
|
520
522
|
constructor(options) {
|
|
521
523
|
super();
|
|
522
524
|
this.options = options;
|
|
@@ -532,116 +534,136 @@ var P2PTunnel = class extends EventEmitter2 {
|
|
|
532
534
|
this.ws = new WebSocket2(signalingUrl);
|
|
533
535
|
const connectionTimeout = setTimeout(() => {
|
|
534
536
|
if (!this.connected) {
|
|
535
|
-
this.cleanup();
|
|
536
537
|
reject(new Error("P2P connection timeout"));
|
|
537
538
|
}
|
|
538
539
|
}, TIMEOUTS.P2P_CONNECTION);
|
|
539
540
|
this.ws.on("open", () => {
|
|
541
|
+
this.signalingConnected = true;
|
|
540
542
|
if (this.options.verbose) {
|
|
541
543
|
console.log("[P2P] Signaling connected");
|
|
542
544
|
}
|
|
545
|
+
this.setupPing();
|
|
543
546
|
const registerMsg = {
|
|
544
547
|
type: "register",
|
|
545
548
|
tunnelId: this.tunnelId,
|
|
546
549
|
role: "cli"
|
|
547
550
|
};
|
|
548
551
|
this.ws.send(JSON.stringify(registerMsg));
|
|
549
|
-
this.
|
|
550
|
-
iceServers: ICE_SERVERS
|
|
551
|
-
});
|
|
552
|
-
this.pc.onicecandidate = (event) => {
|
|
553
|
-
if (event.candidate) {
|
|
554
|
-
if (this.options.verbose) {
|
|
555
|
-
console.log("[P2P] Sending ICE candidate");
|
|
556
|
-
}
|
|
557
|
-
const signalMsg = {
|
|
558
|
-
type: "signal",
|
|
559
|
-
from: "cli",
|
|
560
|
-
to: "browser",
|
|
561
|
-
tunnelId: this.tunnelId,
|
|
562
|
-
signal: { candidate: event.candidate }
|
|
563
|
-
};
|
|
564
|
-
this.ws.send(JSON.stringify(signalMsg));
|
|
565
|
-
}
|
|
566
|
-
};
|
|
567
|
-
this.pc.oniceconnectionstatechange = () => {
|
|
568
|
-
if (this.options.verbose) {
|
|
569
|
-
console.log("[P2P] ICE state:", this.pc?.iceConnectionState);
|
|
570
|
-
}
|
|
571
|
-
};
|
|
572
|
-
this.pc.ondatachannel = (event) => {
|
|
573
|
-
if (this.options.verbose) {
|
|
574
|
-
console.log("[P2P] Received data channel");
|
|
575
|
-
}
|
|
576
|
-
this.dataChannel = event.channel;
|
|
577
|
-
this.setupDataChannel(connectionTimeout, resolve);
|
|
578
|
-
};
|
|
552
|
+
this.initPeerConnection();
|
|
579
553
|
});
|
|
580
554
|
this.ws.on("message", async (data) => {
|
|
581
555
|
const msg = JSON.parse(data.toString());
|
|
582
556
|
if (this.options.verbose) {
|
|
583
557
|
console.log("[P2P] Received:", msg.type, msg.from || "");
|
|
584
558
|
}
|
|
585
|
-
if (msg.type === "signal" && msg.from === "browser"
|
|
586
|
-
|
|
587
|
-
if (signal.type === "offer" && signal.sdp) {
|
|
588
|
-
if (this.options.verbose) {
|
|
589
|
-
console.log("[P2P] Received offer, creating answer");
|
|
590
|
-
}
|
|
591
|
-
try {
|
|
592
|
-
await this.pc.setRemoteDescription(
|
|
593
|
-
new RTCSessionDescription({ type: "offer", sdp: signal.sdp })
|
|
594
|
-
);
|
|
595
|
-
const answer = await this.pc.createAnswer();
|
|
596
|
-
await this.pc.setLocalDescription(answer);
|
|
597
|
-
const signalMsg = {
|
|
598
|
-
type: "signal",
|
|
599
|
-
from: "cli",
|
|
600
|
-
to: "browser",
|
|
601
|
-
tunnelId: this.tunnelId,
|
|
602
|
-
signal: { type: "answer", sdp: this.pc.localDescription?.sdp }
|
|
603
|
-
};
|
|
604
|
-
this.ws.send(JSON.stringify(signalMsg));
|
|
605
|
-
if (this.options.verbose) {
|
|
606
|
-
console.log("[P2P] Sent answer");
|
|
607
|
-
}
|
|
608
|
-
} catch (err) {
|
|
609
|
-
console.error("[P2P] Error handling offer:", err);
|
|
610
|
-
reject(err);
|
|
611
|
-
}
|
|
612
|
-
} else if (signal.candidate) {
|
|
613
|
-
if (this.options.verbose) {
|
|
614
|
-
console.log("[P2P] Adding ICE candidate");
|
|
615
|
-
}
|
|
616
|
-
try {
|
|
617
|
-
await this.pc.addIceCandidate(new RTCIceCandidate(signal.candidate));
|
|
618
|
-
} catch (err) {
|
|
619
|
-
console.error("[P2P] Error adding ICE candidate:", err);
|
|
620
|
-
}
|
|
621
|
-
}
|
|
559
|
+
if (msg.type === "signal" && msg.from === "browser") {
|
|
560
|
+
await this.handleBrowserSignal(msg, connectionTimeout, resolve);
|
|
622
561
|
}
|
|
623
562
|
});
|
|
624
563
|
this.ws.on("error", (err) => {
|
|
625
564
|
clearTimeout(connectionTimeout);
|
|
565
|
+
this.signalingConnected = false;
|
|
626
566
|
reject(err);
|
|
627
567
|
});
|
|
628
568
|
this.ws.on("close", () => {
|
|
569
|
+
this.signalingConnected = false;
|
|
570
|
+
this.clearPing();
|
|
629
571
|
if (this.options.verbose) {
|
|
630
572
|
console.log("[P2P] Signaling closed");
|
|
631
573
|
}
|
|
632
574
|
});
|
|
633
575
|
});
|
|
634
576
|
}
|
|
635
|
-
|
|
577
|
+
initPeerConnection() {
|
|
578
|
+
this.pc = new RTCPeerConnection({
|
|
579
|
+
iceServers: ICE_SERVERS
|
|
580
|
+
});
|
|
581
|
+
this.pc.onicecandidate = (event) => {
|
|
582
|
+
if (event.candidate && this.ws && this.signalingConnected) {
|
|
583
|
+
if (this.options.verbose) {
|
|
584
|
+
console.log("[P2P] Sending ICE candidate");
|
|
585
|
+
}
|
|
586
|
+
const signalMsg = {
|
|
587
|
+
type: "signal",
|
|
588
|
+
from: "cli",
|
|
589
|
+
to: "browser",
|
|
590
|
+
tunnelId: this.tunnelId,
|
|
591
|
+
signal: { candidate: event.candidate }
|
|
592
|
+
};
|
|
593
|
+
this.ws.send(JSON.stringify(signalMsg));
|
|
594
|
+
}
|
|
595
|
+
};
|
|
596
|
+
this.pc.oniceconnectionstatechange = () => {
|
|
597
|
+
if (this.options.verbose) {
|
|
598
|
+
console.log("[P2P] ICE state:", this.pc?.iceConnectionState);
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
this.pc.ondatachannel = (event) => {
|
|
602
|
+
if (this.options.verbose) {
|
|
603
|
+
console.log("[P2P] Received data channel");
|
|
604
|
+
}
|
|
605
|
+
this.dataChannel = event.channel;
|
|
606
|
+
this.setupDataChannel();
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
async handleBrowserSignal(msg, connectionTimeout, resolve) {
|
|
610
|
+
if (!this.pc) {
|
|
611
|
+
this.initPeerConnection();
|
|
612
|
+
}
|
|
613
|
+
const signal = msg.signal;
|
|
614
|
+
if (signal.type === "offer" && signal.sdp) {
|
|
615
|
+
if (this.options.verbose) {
|
|
616
|
+
console.log("[P2P] Received offer, creating answer");
|
|
617
|
+
}
|
|
618
|
+
try {
|
|
619
|
+
await this.pc.setRemoteDescription(
|
|
620
|
+
new RTCSessionDescription({ type: "offer", sdp: signal.sdp })
|
|
621
|
+
);
|
|
622
|
+
const answer = await this.pc.createAnswer();
|
|
623
|
+
await this.pc.setLocalDescription(answer);
|
|
624
|
+
const signalMsg = {
|
|
625
|
+
type: "signal",
|
|
626
|
+
from: "cli",
|
|
627
|
+
to: "browser",
|
|
628
|
+
tunnelId: this.tunnelId,
|
|
629
|
+
signal: { type: "answer", sdp: this.pc.localDescription?.sdp }
|
|
630
|
+
};
|
|
631
|
+
this.ws.send(JSON.stringify(signalMsg));
|
|
632
|
+
if (this.options.verbose) {
|
|
633
|
+
console.log("[P2P] Sent answer");
|
|
634
|
+
}
|
|
635
|
+
this.setupDataChannelResolver(connectionTimeout, resolve);
|
|
636
|
+
} catch (err) {
|
|
637
|
+
console.error("[P2P] Error handling offer:", err);
|
|
638
|
+
}
|
|
639
|
+
} else if (signal.candidate) {
|
|
640
|
+
if (this.options.verbose) {
|
|
641
|
+
console.log("[P2P] Adding ICE candidate");
|
|
642
|
+
}
|
|
643
|
+
try {
|
|
644
|
+
await this.pc.addIceCandidate(new RTCIceCandidate(signal.candidate));
|
|
645
|
+
} catch (err) {
|
|
646
|
+
console.error("[P2P] Error adding ICE candidate:", err);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
setupDataChannelResolver(connectionTimeout, resolve) {
|
|
651
|
+
const checkConnection = () => {
|
|
652
|
+
if (this.connected) {
|
|
653
|
+
clearTimeout(connectionTimeout);
|
|
654
|
+
resolve();
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
this.once("connected", checkConnection);
|
|
658
|
+
}
|
|
659
|
+
setupDataChannel() {
|
|
636
660
|
if (!this.dataChannel) return;
|
|
637
661
|
this.dataChannel.onopen = () => {
|
|
638
|
-
clearTimeout(connectionTimeout);
|
|
639
662
|
this.connected = true;
|
|
640
663
|
this.emit("connected");
|
|
641
664
|
if (this.options.verbose) {
|
|
642
665
|
console.log("[P2P] Data channel open");
|
|
643
666
|
}
|
|
644
|
-
resolve();
|
|
645
667
|
};
|
|
646
668
|
this.dataChannel.onmessage = async (event) => {
|
|
647
669
|
await this.handleP2PRequest(event.data);
|
|
@@ -657,6 +679,19 @@ var P2PTunnel = class extends EventEmitter2 {
|
|
|
657
679
|
}
|
|
658
680
|
};
|
|
659
681
|
}
|
|
682
|
+
setupPing() {
|
|
683
|
+
this.pingInterval = setInterval(() => {
|
|
684
|
+
if (this.ws && this.ws.readyState === WebSocket2.OPEN) {
|
|
685
|
+
this.ws.ping();
|
|
686
|
+
}
|
|
687
|
+
}, TIMEOUTS.WEBSOCKET_PING);
|
|
688
|
+
}
|
|
689
|
+
clearPing() {
|
|
690
|
+
if (this.pingInterval) {
|
|
691
|
+
clearInterval(this.pingInterval);
|
|
692
|
+
this.pingInterval = null;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
660
695
|
async handleP2PRequest(data) {
|
|
661
696
|
const startTime = Date.now();
|
|
662
697
|
let request;
|
|
@@ -709,6 +744,7 @@ var P2PTunnel = class extends EventEmitter2 {
|
|
|
709
744
|
}
|
|
710
745
|
}
|
|
711
746
|
cleanup() {
|
|
747
|
+
this.clearPing();
|
|
712
748
|
this.dataChannel?.close();
|
|
713
749
|
this.pc?.close();
|
|
714
750
|
this.ws?.close();
|
|
@@ -716,6 +752,7 @@ var P2PTunnel = class extends EventEmitter2 {
|
|
|
716
752
|
this.pc = null;
|
|
717
753
|
this.ws = null;
|
|
718
754
|
this.connected = false;
|
|
755
|
+
this.signalingConnected = false;
|
|
719
756
|
}
|
|
720
757
|
disconnect() {
|
|
721
758
|
this.cleanup();
|
|
@@ -723,6 +760,9 @@ var P2PTunnel = class extends EventEmitter2 {
|
|
|
723
760
|
isConnected() {
|
|
724
761
|
return this.connected;
|
|
725
762
|
}
|
|
763
|
+
isSignalingConnected() {
|
|
764
|
+
return this.signalingConnected;
|
|
765
|
+
}
|
|
726
766
|
getRequestCount() {
|
|
727
767
|
return this.requestCount;
|
|
728
768
|
}
|
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, this.options.mode);\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, mode?: string): string => {\n const host = process.env['UNTUNNELED_RELAY_HOST'] || RELAY_DOMAIN;\n const protocol = host.includes('localhost') ? 'ws' : 'wss';\n const modeParam = mode ? `?mode=${mode}` : '';\n return `${protocol}://${host}/${tunnelId}${modeParam}`;\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 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\nconst { RTCPeerConnection, RTCSessionDescription, RTCIceCandidate } = wrtc;\n\ntype WrtcPeerConnection = InstanceType<typeof RTCPeerConnection>;\ntype WrtcDataChannel = ReturnType<WrtcPeerConnection['createDataChannel']>;\n\ninterface P2PTunnelOptions {\n localPort: number;\n tunnelId: string;\n verbose?: boolean;\n}\n\nexport class P2PTunnel extends EventEmitter {\n private pc: WrtcPeerConnection | null = null;\n private dataChannel: WrtcDataChannel | 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.pc = new RTCPeerConnection({\n iceServers: ICE_SERVERS,\n });\n\n this.pc.onicecandidate = (event: {\n candidate: InstanceType<typeof RTCIceCandidate> | null;\n }) => {\n if (event.candidate) {\n if (this.options.verbose) {\n console.log('[P2P] Sending ICE candidate');\n }\n const signalMsg: SignalingMessage = {\n type: 'signal',\n from: 'cli',\n to: 'browser',\n tunnelId: this.tunnelId,\n signal: { candidate: event.candidate },\n };\n this.ws!.send(JSON.stringify(signalMsg));\n }\n };\n\n this.pc.oniceconnectionstatechange = () => {\n if (this.options.verbose) {\n console.log('[P2P] ICE state:', this.pc?.iceConnectionState);\n }\n };\n\n this.pc.ondatachannel = (event: { channel: WrtcDataChannel }) => {\n if (this.options.verbose) {\n console.log('[P2P] Received data channel');\n }\n this.dataChannel = event.channel;\n this.setupDataChannel(connectionTimeout, resolve);\n };\n });\n\n this.ws.on('message', async (data) => {\n const msg = JSON.parse(data.toString()) as SignalingMessage;\n\n if (this.options.verbose) {\n console.log('[P2P] Received:', msg.type, msg.from || '');\n }\n\n if (msg.type === 'signal' && msg.from === 'browser' && this.pc) {\n const signal = msg.signal as {\n type?: string;\n sdp?: string;\n candidate?: Record<string, unknown>;\n };\n\n if (signal.type === 'offer' && signal.sdp) {\n if (this.options.verbose) {\n console.log('[P2P] Received offer, creating answer');\n }\n try {\n await this.pc.setRemoteDescription(\n new RTCSessionDescription({ type: 'offer', sdp: signal.sdp })\n );\n const answer = await this.pc.createAnswer();\n await this.pc.setLocalDescription(answer);\n\n const signalMsg: SignalingMessage = {\n type: 'signal',\n from: 'cli',\n to: 'browser',\n tunnelId: this.tunnelId,\n signal: { type: 'answer', sdp: this.pc.localDescription?.sdp },\n };\n this.ws!.send(JSON.stringify(signalMsg));\n if (this.options.verbose) {\n console.log('[P2P] Sent answer');\n }\n } catch (err) {\n console.error('[P2P] Error handling offer:', err);\n reject(err);\n }\n } else if (signal.candidate) {\n if (this.options.verbose) {\n console.log('[P2P] Adding ICE candidate');\n }\n try {\n await this.pc.addIceCandidate(new RTCIceCandidate(signal.candidate));\n } catch (err) {\n console.error('[P2P] Error adding ICE candidate:', err);\n }\n }\n }\n });\n\n this.ws.on('error', (err) => {\n clearTimeout(connectionTimeout);\n reject(err);\n });\n\n this.ws.on('close', () => {\n if (this.options.verbose) {\n console.log('[P2P] Signaling closed');\n }\n });\n });\n }\n\n private setupDataChannel(connectionTimeout: NodeJS.Timeout, resolve: () => void): void {\n if (!this.dataChannel) return;\n\n this.dataChannel.onopen = () => {\n clearTimeout(connectionTimeout);\n this.connected = true;\n this.emit('connected');\n\n if (this.options.verbose) {\n console.log('[P2P] Data channel open');\n }\n\n resolve();\n };\n\n this.dataChannel.onmessage = async (event: MessageEvent) => {\n await this.handleP2PRequest(event.data);\n };\n\n this.dataChannel.onerror = (event: unknown) => {\n console.error('[P2P] Data channel error:', event);\n };\n\n this.dataChannel.onclose = () => {\n this.connected = false;\n this.emit('disconnected');\n if (this.options.verbose) {\n console.log('[P2P] Data channel closed');\n }\n };\n }\n\n private async handleP2PRequest(data: string): Promise<void> {\n const startTime = Date.now();\n let request: RelayRequest;\n\n try {\n request = JSON.parse(data) 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.dataChannel?.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.dataChannel?.send(JSON.stringify(errorResponse));\n }\n }\n\n private cleanup(): void {\n this.dataChannel?.close();\n this.pc?.close();\n this.ws?.close();\n this.dataChannel = null;\n this.pc = 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, this.options.mode);\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, mode?: string): string => {\n const host = process.env['UNTUNNELED_RELAY_HOST'] || RELAY_DOMAIN;\n const protocol = host.includes('localhost') ? 'ws' : 'wss';\n const modeParam = mode ? `?mode=${mode}` : '';\n return `${protocol}://${host}/${tunnelId}${modeParam}`;\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: 30000,\n RELAY_REQUEST: 30000,\n WEBSOCKET_PING: 30000,\n RECONNECT_BASE: 1000,\n RECONNECT_MAX: 30000,\n};\n","import 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\nconst { RTCPeerConnection, RTCSessionDescription, RTCIceCandidate } = wrtc;\n\ntype WrtcPeerConnection = InstanceType<typeof RTCPeerConnection>;\ntype WrtcDataChannel = ReturnType<WrtcPeerConnection['createDataChannel']>;\n\ninterface P2PTunnelOptions {\n localPort: number;\n tunnelId: string;\n verbose?: boolean;\n}\n\nexport class P2PTunnel extends EventEmitter {\n private pc: WrtcPeerConnection | null = null;\n private dataChannel: WrtcDataChannel | 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 private signalingConnected: boolean = false;\n private pingInterval: NodeJS.Timeout | null = null;\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 reject(new Error('P2P connection timeout'));\n }\n }, TIMEOUTS.P2P_CONNECTION);\n\n this.ws.on('open', () => {\n this.signalingConnected = true;\n if (this.options.verbose) {\n console.log('[P2P] Signaling connected');\n }\n\n this.setupPing();\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.initPeerConnection();\n });\n\n this.ws.on('message', async (data) => {\n const msg = JSON.parse(data.toString()) as SignalingMessage;\n\n if (this.options.verbose) {\n console.log('[P2P] Received:', msg.type, msg.from || '');\n }\n\n if (msg.type === 'signal' && msg.from === 'browser') {\n await this.handleBrowserSignal(msg, connectionTimeout, resolve);\n }\n });\n\n this.ws.on('error', (err) => {\n clearTimeout(connectionTimeout);\n this.signalingConnected = false;\n reject(err);\n });\n\n this.ws.on('close', () => {\n this.signalingConnected = false;\n this.clearPing();\n if (this.options.verbose) {\n console.log('[P2P] Signaling closed');\n }\n });\n });\n }\n\n private initPeerConnection(): void {\n this.pc = new RTCPeerConnection({\n iceServers: ICE_SERVERS,\n });\n\n this.pc.onicecandidate = (event: {\n candidate: InstanceType<typeof RTCIceCandidate> | null;\n }) => {\n if (event.candidate && this.ws && this.signalingConnected) {\n if (this.options.verbose) {\n console.log('[P2P] Sending ICE candidate');\n }\n const signalMsg: SignalingMessage = {\n type: 'signal',\n from: 'cli',\n to: 'browser',\n tunnelId: this.tunnelId,\n signal: { candidate: event.candidate },\n };\n this.ws.send(JSON.stringify(signalMsg));\n }\n };\n\n this.pc.oniceconnectionstatechange = () => {\n if (this.options.verbose) {\n console.log('[P2P] ICE state:', this.pc?.iceConnectionState);\n }\n };\n\n this.pc.ondatachannel = (event: { channel: WrtcDataChannel }) => {\n if (this.options.verbose) {\n console.log('[P2P] Received data channel');\n }\n this.dataChannel = event.channel;\n this.setupDataChannel();\n };\n }\n\n private async handleBrowserSignal(\n msg: SignalingMessage,\n connectionTimeout: NodeJS.Timeout,\n resolve: () => void\n ): Promise<void> {\n if (!this.pc) {\n this.initPeerConnection();\n }\n\n const signal = msg.signal as {\n type?: string;\n sdp?: string;\n candidate?: Record<string, unknown>;\n };\n\n if (signal.type === 'offer' && signal.sdp) {\n if (this.options.verbose) {\n console.log('[P2P] Received offer, creating answer');\n }\n try {\n await this.pc!.setRemoteDescription(\n new RTCSessionDescription({ type: 'offer', sdp: signal.sdp })\n );\n const answer = await this.pc!.createAnswer();\n await this.pc!.setLocalDescription(answer);\n\n const signalMsg: SignalingMessage = {\n type: 'signal',\n from: 'cli',\n to: 'browser',\n tunnelId: this.tunnelId,\n signal: { type: 'answer', sdp: this.pc!.localDescription?.sdp },\n };\n this.ws!.send(JSON.stringify(signalMsg));\n if (this.options.verbose) {\n console.log('[P2P] Sent answer');\n }\n\n this.setupDataChannelResolver(connectionTimeout, resolve);\n } catch (err) {\n console.error('[P2P] Error handling offer:', err);\n }\n } else if (signal.candidate) {\n if (this.options.verbose) {\n console.log('[P2P] Adding ICE candidate');\n }\n try {\n await this.pc!.addIceCandidate(new RTCIceCandidate(signal.candidate));\n } catch (err) {\n console.error('[P2P] Error adding ICE candidate:', err);\n }\n }\n }\n\n private setupDataChannelResolver(connectionTimeout: NodeJS.Timeout, resolve: () => void): void {\n const checkConnection = () => {\n if (this.connected) {\n clearTimeout(connectionTimeout);\n resolve();\n }\n };\n\n this.once('connected', checkConnection);\n }\n\n private setupDataChannel(): void {\n if (!this.dataChannel) return;\n\n this.dataChannel.onopen = () => {\n this.connected = true;\n this.emit('connected');\n\n if (this.options.verbose) {\n console.log('[P2P] Data channel open');\n }\n };\n\n this.dataChannel.onmessage = async (event: MessageEvent) => {\n await this.handleP2PRequest(event.data);\n };\n\n this.dataChannel.onerror = (event: unknown) => {\n console.error('[P2P] Data channel error:', event);\n };\n\n this.dataChannel.onclose = () => {\n this.connected = false;\n this.emit('disconnected');\n if (this.options.verbose) {\n console.log('[P2P] Data channel closed');\n }\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 handleP2PRequest(data: string): Promise<void> {\n const startTime = Date.now();\n let request: RelayRequest;\n\n try {\n request = JSON.parse(data) 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.dataChannel?.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.dataChannel?.send(JSON.stringify(errorResponse));\n }\n }\n\n private cleanup(): void {\n this.clearPing();\n this.dataChannel?.close();\n this.pc?.close();\n this.ws?.close();\n this.dataChannel = null;\n this.pc = null;\n this.ws = null;\n this.connected = false;\n this.signalingConnected = false;\n }\n\n disconnect(): void {\n this.cleanup();\n }\n\n isConnected(): boolean {\n return this.connected;\n }\n\n isSignalingConnected(): boolean {\n return this.signalingConnected;\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"]}
|