untunneled.dev 0.3.6 → 0.3.11

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/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # untunneled.dev CLI
2
+
3
+ Fast, free localhost tunneling for developers.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ npx untunneled.dev 3000
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```bash
14
+ untunneled <port> [--qr] [--name <name>] [--password <password>] [--verbose] [--no-telemetry]
15
+ ```
16
+
17
+ ### Examples
18
+
19
+ ```bash
20
+ # Start a tunnel
21
+ npx untunneled.dev 3000
22
+
23
+ # Request a custom subdomain
24
+ npx untunneled.dev 3000 --name demo-api
25
+
26
+ # Password protect a tunnel
27
+ npx untunneled.dev 3000 --password "demo123"
28
+
29
+ # Show QR code on start
30
+ npx untunneled.dev 3000 --qr
31
+ ```
32
+
33
+ ## Docs
34
+
35
+ Full documentation: https://untunneled.dev/docs
package/dist/index.js CHANGED
@@ -171,6 +171,10 @@ var TunnelUI = ({
171
171
  const [uptime, setUptime] = useState3(0);
172
172
  const [isModalOpen, setIsModalOpen] = useState3(Boolean(showQR));
173
173
  useInput((input, key) => {
174
+ if (key.ctrl && input === "c") {
175
+ process.kill(process.pid, "SIGINT");
176
+ return;
177
+ }
174
178
  if (input === "q" || input === "c" || input === "Q" || input === "C") {
175
179
  setIsModalOpen((prev) => !prev);
176
180
  }
@@ -330,6 +334,8 @@ import { EventEmitter } from "events";
330
334
 
331
335
  // src/config.ts
332
336
  import { nanoid } from "nanoid";
337
+ import { execSync } from "child_process";
338
+ import os from "os";
333
339
  import path from "path";
334
340
  var RELAY_DOMAIN = "relay.untunneled.dev";
335
341
  var TUNNEL_DOMAIN = "untunneled.dev";
@@ -342,15 +348,84 @@ var getTunnelUrl = (tunnelId) => {
342
348
  const domain = process.env["UNTUNNELED_TUNNEL_DOMAIN"] || TUNNEL_DOMAIN;
343
349
  return `https://${tunnelId}.${domain}`;
344
350
  };
351
+ var slugifyName = (value, maxLength = 20) => {
352
+ return value.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, maxLength);
353
+ };
354
+ var getGitRepoName = () => {
355
+ try {
356
+ const root = execSync("git rev-parse --show-toplevel", {
357
+ cwd: process.cwd(),
358
+ stdio: ["ignore", "pipe", "ignore"]
359
+ }).toString().trim();
360
+ return root ? path.basename(root) : null;
361
+ } catch {
362
+ return null;
363
+ }
364
+ };
365
+ var getGitUserName = () => {
366
+ try {
367
+ const name = execSync("git config user.name", {
368
+ cwd: process.cwd(),
369
+ stdio: ["ignore", "pipe", "ignore"]
370
+ }).toString().trim();
371
+ if (name) return name;
372
+ const email = execSync("git config user.email", {
373
+ cwd: process.cwd(),
374
+ stdio: ["ignore", "pipe", "ignore"]
375
+ }).toString().trim();
376
+ if (!email) return null;
377
+ const [user] = email.split("@");
378
+ return user || null;
379
+ } catch {
380
+ return null;
381
+ }
382
+ };
383
+ var getLocalUsername = () => {
384
+ try {
385
+ return os.userInfo().username || null;
386
+ } catch {
387
+ return null;
388
+ }
389
+ };
390
+ var addCandidate = (candidates, value) => {
391
+ if (!value) return;
392
+ const slug = slugifyName(value);
393
+ if (!slug || candidates.includes(slug)) return;
394
+ candidates.push(slug);
395
+ };
345
396
  function getProjectName() {
346
397
  const cwd = process.cwd();
347
398
  const folderName = path.basename(cwd);
348
- return folderName.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 20) || "tunnel";
399
+ return slugifyName(folderName) || "tunnel";
349
400
  }
350
- function generateTunnelId(projectName) {
351
- const name = projectName || getProjectName();
352
- const suffix = nanoid(6).toLowerCase();
353
- return `${name}-${suffix}`;
401
+ function getTunnelIdCandidates(options) {
402
+ const candidates = [];
403
+ if (options?.name) {
404
+ addCandidate(candidates, options.name);
405
+ const base = candidates[0];
406
+ if (base) {
407
+ candidates.push(`${base}-${nanoid(6).toLowerCase()}`);
408
+ return candidates;
409
+ }
410
+ }
411
+ const gitRepoName = getGitRepoName();
412
+ const directoryName = getProjectName();
413
+ const gitUserName = getGitUserName();
414
+ const localUserName = getLocalUsername();
415
+ addCandidate(candidates, gitRepoName);
416
+ addCandidate(candidates, directoryName);
417
+ if (gitUserName && gitRepoName) {
418
+ addCandidate(candidates, `${gitUserName}-${gitRepoName}`);
419
+ }
420
+ if (gitUserName && directoryName) {
421
+ addCandidate(candidates, `${gitUserName}-${directoryName}`);
422
+ }
423
+ if (localUserName && directoryName) {
424
+ addCandidate(candidates, `${localUserName}-${directoryName}`);
425
+ }
426
+ const fallbackBase = directoryName || gitRepoName || "tunnel";
427
+ candidates.push(`${fallbackBase}-${nanoid(6).toLowerCase()}`);
428
+ return candidates;
354
429
  }
355
430
  function validatePort(port) {
356
431
  const portNum = typeof port === "string" ? parseInt(port, 10) : port;
@@ -398,6 +473,17 @@ var RelayTunnel = class extends EventEmitter {
398
473
  console.log(`[Relay] Connecting to ${wsUrl}`);
399
474
  }
400
475
  return new Promise((resolve, reject) => {
476
+ let didOpen = false;
477
+ let settled = false;
478
+ const finish = (error) => {
479
+ if (settled) return;
480
+ settled = true;
481
+ if (error) {
482
+ reject(error);
483
+ } else {
484
+ resolve();
485
+ }
486
+ };
401
487
  try {
402
488
  this.ws = new WebSocket(wsUrl, {
403
489
  headers: {
@@ -406,31 +492,53 @@ var RelayTunnel = class extends EventEmitter {
406
492
  });
407
493
  const connectionTimeout = setTimeout(() => {
408
494
  if (this.ws && this.ws.readyState !== WebSocket.OPEN) {
495
+ this.isClosing = true;
409
496
  this.ws.terminate();
410
- reject(new Error("Connection timeout"));
497
+ finish(new Error("Connection timeout"));
411
498
  }
412
499
  }, TIMEOUTS.RELAY_REQUEST);
413
500
  this.ws.on("open", () => {
414
501
  clearTimeout(connectionTimeout);
415
502
  this.reconnectAttempts = 0;
503
+ didOpen = true;
416
504
  if (this.options.verbose) {
417
505
  console.log("[Relay] Connected");
418
506
  }
419
507
  this.setupPing();
420
508
  this.emit("connected");
421
- resolve();
509
+ finish();
422
510
  });
423
511
  this.ws.on("error", (err) => {
424
512
  clearTimeout(connectionTimeout);
425
513
  if (this.options.verbose) {
426
514
  console.error("[Relay] WebSocket error:", err.message);
427
515
  }
428
- this.emit("error", err);
429
- reject(err);
516
+ if (didOpen || this.listenerCount("error") > 0) {
517
+ this.emit("error", err);
518
+ }
519
+ if (!didOpen) {
520
+ finish(err);
521
+ }
522
+ });
523
+ this.ws.on("unexpected-response", (_request, response) => {
524
+ clearTimeout(connectionTimeout);
525
+ const error = new Error(
526
+ response.statusCode === 409 ? "Tunnel ID already in use" : `Unexpected response: ${response.statusCode || "unknown"}`
527
+ );
528
+ if (response.statusCode === 409) {
529
+ error.code = "TUNNEL_ID_IN_USE";
530
+ }
531
+ this.isClosing = true;
532
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
533
+ this.ws.close();
534
+ } else {
535
+ this.ws?.terminate();
536
+ }
537
+ finish(error);
430
538
  });
431
539
  this.ws.on("close", () => {
432
540
  this.clearPing();
433
- if (!this.isClosing) {
541
+ if (!this.isClosing && didOpen) {
434
542
  this.emit("disconnected");
435
543
  this.attemptReconnect();
436
544
  }
@@ -510,13 +618,14 @@ var RelayTunnel = class extends EventEmitter {
510
618
  if (this.options.verbose || err.code === "ECONNREFUSED") {
511
619
  console.error(`[Relay] Proxy error: ${err.message}`);
512
620
  }
621
+ const friendlyMessage = err.message === "fetch failed" || err.code === "ECONNREFUSED" ? "Tunnel is offline. Restart the CLI to reconnect." : `Error: ${err.message}`;
513
622
  const errorResponse = {
514
623
  type: "http_response",
515
624
  id: request.id,
516
625
  status: 502,
517
626
  statusText: "Bad Gateway",
518
627
  headers: {},
519
- body: `Error: ${err.message}`
628
+ body: friendlyMessage
520
629
  };
521
630
  this.recordMetrics(duration, 502, requestBytes, 0);
522
631
  this.ws?.send(JSON.stringify(errorResponse));
@@ -681,7 +790,7 @@ var RelayTunnel = class extends EventEmitter {
681
790
  import { PostHog } from "posthog-node";
682
791
  import crypto from "crypto";
683
792
  import fs from "fs";
684
- import os from "os";
793
+ import os2 from "os";
685
794
  import path2 from "path";
686
795
  var POSTHOG_API_KEY = "phc_oB9sTloW28uJQ7HGYDRZRQ23JaQApZ57LrrPFVd796S";
687
796
  var POSTHOG_HOST = "https://us.i.posthog.com";
@@ -720,7 +829,7 @@ var Analytics = class {
720
829
  event,
721
830
  properties: {
722
831
  ...properties,
723
- version: "0.3.6",
832
+ version: "0.3.11",
724
833
  cli: true
725
834
  }
726
835
  });
@@ -751,7 +860,7 @@ var Analytics = class {
751
860
  return crypto.createHash("sha256").update(rotationKey).digest("hex").slice(0, 32);
752
861
  }
753
862
  getAnonymousId() {
754
- const baseDir = process.env["XDG_CONFIG_HOME"] || process.env["APPDATA"] || path2.join(os.homedir(), ".config");
863
+ const baseDir = process.env["XDG_CONFIG_HOME"] || process.env["APPDATA"] || path2.join(os2.homedir(), ".config");
755
864
  const configDir = path2.join(baseDir, "untunneled.dev");
756
865
  const idPath = path2.join(configDir, "anon_id");
757
866
  try {
@@ -780,9 +889,7 @@ async function startCommand(port, options) {
780
889
  console.error(error.message);
781
890
  process.exit(1);
782
891
  }
783
- const projectName = getProjectName();
784
- const tunnelId = generateTunnelId(projectName);
785
- const tunnelUrl = getTunnelUrl(tunnelId);
892
+ const tunnelIdCandidates = getTunnelIdCandidates({ name: options.name });
786
893
  const analytics = new Analytics({
787
894
  enabled: options.telemetry !== false
788
895
  });
@@ -791,52 +898,80 @@ async function startCommand(port, options) {
791
898
  node_version: process.version,
792
899
  platform: process.platform
793
900
  });
794
- const relayTunnel = new RelayTunnel({
795
- localPort,
796
- tunnelId,
797
- tunnelUrl,
798
- password: options.password,
799
- verbose: options.verbose
800
- });
801
- try {
802
- await relayTunnel.connect();
803
- if (options.verbose) {
804
- console.log(`Tunnel URL: ${tunnelUrl}`);
805
- console.log(`Forwarding to localhost:${localPort}`);
901
+ let relayTunnel = null;
902
+ let tunnelId = "";
903
+ let tunnelUrl = "";
904
+ for (const candidate of tunnelIdCandidates) {
905
+ tunnelId = candidate;
906
+ tunnelUrl = getTunnelUrl(tunnelId);
907
+ relayTunnel = new RelayTunnel({
908
+ localPort,
909
+ tunnelId,
910
+ tunnelUrl,
911
+ password: options.password,
912
+ verbose: options.verbose
913
+ });
914
+ try {
915
+ await relayTunnel.connect();
916
+ break;
917
+ } catch (error) {
918
+ const err = error;
919
+ if (err.code === "TUNNEL_ID_IN_USE") {
920
+ await relayTunnel.disconnect();
921
+ relayTunnel = null;
922
+ continue;
923
+ }
924
+ console.error("Failed to connect to relay server:", err.message);
925
+ await analytics.shutdown();
926
+ process.exit(1);
806
927
  }
807
- } catch (error) {
808
- console.error("Failed to connect to relay server:", error.message);
928
+ }
929
+ if (!relayTunnel) {
930
+ console.error("All candidate tunnel URLs are currently in use. Try again or pass --name.");
809
931
  await analytics.shutdown();
810
932
  process.exit(1);
811
933
  }
934
+ const activeTunnel = relayTunnel;
935
+ if (options.verbose) {
936
+ console.log(`Tunnel URL: ${tunnelUrl}`);
937
+ console.log(`Forwarding to localhost:${localPort}`);
938
+ }
812
939
  const { waitUntilExit } = render(
813
940
  React5.createElement(TunnelUI, {
814
941
  tunnelUrl,
815
942
  localPort,
816
943
  showQR: options.qr,
817
- relayTunnel
944
+ relayTunnel: activeTunnel
818
945
  })
819
946
  );
820
947
  const cleanup = async () => {
821
- const duration = relayTunnel.getDuration();
822
- const requests = relayTunnel.getRequestCount();
948
+ const duration = activeTunnel.getDuration();
949
+ const requests = activeTunnel.getRequestCount();
823
950
  await analytics.track("tunnel_end", {
824
951
  duration_seconds: duration,
825
952
  requests
826
953
  });
827
- const summary = relayTunnel.getMetricsSummary();
954
+ const summary = activeTunnel.getMetricsSummary();
828
955
  await analytics.track("session_summary", summary);
829
- await relayTunnel.disconnect();
956
+ await activeTunnel.disconnect();
830
957
  await analytics.shutdown();
831
958
  };
832
- process.on("SIGINT", async () => {
833
- await cleanup();
834
- process.exit(0);
835
- });
836
- process.on("SIGTERM", async () => {
837
- await cleanup();
838
- process.exit(0);
839
- });
959
+ let isShuttingDown = false;
960
+ const handleShutdown = async () => {
961
+ if (isShuttingDown) return;
962
+ isShuttingDown = true;
963
+ const timeout = setTimeout(() => {
964
+ process.exit(0);
965
+ }, 2e3);
966
+ try {
967
+ await cleanup();
968
+ } finally {
969
+ clearTimeout(timeout);
970
+ process.exit(0);
971
+ }
972
+ };
973
+ process.once("SIGINT", handleShutdown);
974
+ process.once("SIGTERM", handleShutdown);
840
975
  await waitUntilExit();
841
976
  }
842
977
 
@@ -857,8 +992,8 @@ async function stopCommand(tunnelId) {
857
992
 
858
993
  // src/index.ts
859
994
  var program = new Command();
860
- program.name("untunneled").description("Fast, free localhost tunneling").version("0.3.6");
861
- program.argument("<port>", "Local port to tunnel").option("--qr", "Open the QR code modal on start").option("--password <password>", "Password protect the tunnel").option("--verbose", "Show detailed logs").option("--no-telemetry", "Disable anonymous usage tracking").action(startCommand);
995
+ program.name("untunneled").description("Fast, free localhost tunneling").version("0.3.11");
996
+ program.argument("<port>", "Local port to tunnel").option("--qr", "Open the QR code modal on start").option("--name <name>", "Set a custom tunnel subdomain").option("--password <password>", "Password protect the tunnel").option("--verbose", "Show detailed logs").option("--no-telemetry", "Disable anonymous usage tracking").action(startCommand);
862
997
  program.command("list").description("List active tunnels").action(listCommand);
863
998
  program.command("stop").argument("<tunnel-id>", "Tunnel ID to stop").description("Stop a running tunnel").action(stopCommand);
864
999
  program.parse();
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/commands/start.ts","../src/ui/TunnelUI.tsx","../src/ui/QRCode.tsx","../src/ui/RequestLog.tsx","../src/ui/ConnectionStatus.tsx","../src/tunnel/relay.ts","../src/config.ts","../src/analytics.ts","../src/commands/list.ts","../src/commands/stop.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { startCommand, listCommand, stopCommand } from './commands/index.js';\n\nconst program = new Command();\n\nprogram\n .name('untunneled')\n .description('Fast, free localhost tunneling')\n .version(process.env.UNTUNNELED_VERSION || '0.3.6');\n\nprogram\n .argument('<port>', 'Local port to tunnel')\n .option('--qr', 'Open the QR code modal on start')\n .option('--password <password>', 'Password protect the tunnel')\n .option('--verbose', 'Show detailed logs')\n .option('--no-telemetry', 'Disable anonymous usage tracking')\n .action(startCommand);\n\nprogram.command('list').description('List active tunnels').action(listCommand);\n\nprogram\n .command('stop')\n .argument('<tunnel-id>', 'Tunnel ID to stop')\n .description('Stop a running tunnel')\n .action(stopCommand);\n\nprogram.parse();\n","import { render } from 'ink';\nimport React from 'react';\nimport { TunnelUI } from '../ui/TunnelUI.js';\nimport { RelayTunnel } from '../tunnel/relay.js';\nimport { Analytics } from '../analytics.js';\nimport { generateTunnelId, getProjectName, getTunnelUrl, validatePort } from '../config.js';\nimport type { StartOptions } from '../types.js';\n\nexport async function startCommand(port: string, options: StartOptions): Promise<void> {\n let localPort: number;\n\n try {\n localPort = validatePort(port);\n } catch (error) {\n console.error((error as Error).message);\n process.exit(1);\n }\n\n const projectName = getProjectName();\n const tunnelId = generateTunnelId(projectName);\n const tunnelUrl = getTunnelUrl(tunnelId);\n\n const analytics = new Analytics({\n enabled: options.telemetry !== false,\n });\n\n await analytics.track('tunnel_start', {\n has_auth: !!options.password,\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 verbose: options.verbose,\n });\n\n try {\n await relayTunnel.connect();\n\n if (options.verbose) {\n console.log(`Tunnel URL: ${tunnelUrl}`);\n console.log(`Forwarding to localhost:${localPort}`);\n }\n } catch (error) {\n console.error('Failed to connect to relay server:', (error as Error).message);\n await analytics.shutdown();\n process.exit(1);\n }\n\n const { waitUntilExit } = render(\n React.createElement(TunnelUI, {\n tunnelUrl,\n localPort,\n showQR: options.qr,\n relayTunnel,\n })\n );\n\n const cleanup = async () => {\n const duration = relayTunnel.getDuration();\n const requests = relayTunnel.getRequestCount();\n\n await analytics.track('tunnel_end', {\n duration_seconds: duration,\n requests: requests,\n });\n\n const summary = relayTunnel.getMetricsSummary();\n await analytics.track('session_summary', summary);\n\n await relayTunnel.disconnect();\n await analytics.shutdown();\n };\n\n process.on('SIGINT', async () => {\n await cleanup();\n process.exit(0);\n });\n\n process.on('SIGTERM', async () => {\n await cleanup();\n process.exit(0);\n });\n\n await waitUntilExit();\n}\n","import React, { useState, useEffect } from 'react';\nimport { Box, Text, useApp, useInput } from 'ink';\nimport { QRCode } from './QRCode.js';\nimport { RequestLog } from './RequestLog.js';\nimport { ConnectionStatus } from './ConnectionStatus.js';\nimport type { RelayTunnel } from '../tunnel/relay.js';\nimport type { RequestLog as RequestLogType, TunnelStats } from '../types.js';\n\ninterface TunnelUIProps {\n tunnelUrl: string;\n localPort: number;\n showQR?: boolean;\n relayTunnel: RelayTunnel;\n}\n\nconst MAX_LOGS = 10;\n\nconst LOGO = `\n ╦ ╦╔╗╔╔╦╗╦ ╦╔╗╔╔╗╔╔═╗╦ ╔═╗╔╦╗ ╔╦╗╔═╗╦ ╦\n ║ ║║║║ ║ ║ ║║║║║║║║╣ ║ ║╣ ║║ ║║║╣ ╚╗╔╝\n ╚═╝╝╚╝ ╩ ╚═╝╝╚╝╝╚╝╚═╝╩═╝╚═╝═╩╝o═╩╝╚═╝ ╚╝ \n`;\n\nconst formatUptime = (ms: number) => {\n const seconds = Math.floor(ms / 1000);\n const minutes = Math.floor(seconds / 60);\n const hours = Math.floor(minutes / 60);\n return `${hours}h ${minutes % 60}m ${seconds % 60}s`;\n};\n\nexport const TunnelUI: React.FC<TunnelUIProps> = ({\n tunnelUrl,\n localPort,\n showQR,\n relayTunnel,\n}) => {\n useApp();\n const [requests, setRequests] = useState<RequestLogType[]>([]);\n const [stats, setStats] = useState<TunnelStats>({\n total: 0,\n startTime: Date.now(),\n });\n const [relayConnected, setRelayConnected] = useState(relayTunnel.isConnected());\n const [uptime, setUptime] = useState(0);\n const [isModalOpen, setIsModalOpen] = useState(Boolean(showQR));\n\n useInput((input, key) => {\n if (input === 'q' || input === 'c' || input === 'Q' || input === 'C') {\n setIsModalOpen((prev) => !prev);\n }\n if (key.escape) {\n setIsModalOpen(false);\n }\n });\n\n useEffect(() => {\n const handleRequest = (req: RequestLogType) => {\n setRequests((prev) => [...prev.slice(-(MAX_LOGS - 1)), req]);\n setStats((prev) => ({\n ...prev,\n total: prev.total + 1,\n }));\n };\n\n const handleRelayConnected = () => setRelayConnected(true);\n const handleRelayDisconnected = () => setRelayConnected(false);\n\n relayTunnel.on('request', handleRequest);\n relayTunnel.on('connected', handleRelayConnected);\n relayTunnel.on('disconnected', handleRelayDisconnected);\n\n const uptimeTimer = setInterval(() => {\n setUptime(Date.now() - stats.startTime);\n }, 1000);\n\n return () => {\n relayTunnel.off('request', handleRequest);\n relayTunnel.off('connected', handleRelayConnected);\n relayTunnel.off('disconnected', handleRelayDisconnected);\n clearInterval(uptimeTimer);\n };\n }, [relayTunnel, stats.startTime]);\n\n const connectionStatus = relayConnected ? 'connected' : 'connecting';\n const accentColor = relayConnected ? 'cyan' : 'yellow';\n\n return (\n <Box flexDirection=\"column\" paddingX={2} paddingY={1}>\n {/* Header */}\n <Box flexDirection=\"column\" marginBottom={1}>\n <Text color=\"cyan\" bold>\n {LOGO}\n </Text>\n <Box justifyContent=\"space-between\" paddingX={1}>\n <ConnectionStatus status={connectionStatus} />\n <Text dimColor>v0.3.6</Text>\n </Box>\n </Box>\n\n {/* Main Connection Info */}\n <Box\n borderStyle=\"round\"\n borderColor={accentColor}\n paddingX={2}\n paddingY={1}\n flexDirection=\"column\"\n >\n <Box>\n <Text bold>Public URL: </Text>\n <Text bold color=\"black\" backgroundColor=\"cyan\">\n {' '}\n {tunnelUrl}{' '}\n </Text>\n </Box>\n <Box marginTop={1}>\n <Text dimColor>Forwarding: </Text>\n <Text color=\"yellow\">http://localhost:{localPort}</Text>\n <Text dimColor> → </Text>\n <Text color=\"cyan\">{tunnelUrl}</Text>\n </Box>\n </Box>\n\n {/* Stats Bar */}\n <Box marginTop={1} paddingX={1} gap={4}>\n <Box>\n <Text dimColor>Requests: </Text>\n <Text color=\"white\" bold>\n {stats.total}\n </Text>\n </Box>\n <Box>\n <Text dimColor>Uptime: </Text>\n <Text color=\"white\" bold>\n {formatUptime(uptime)}\n </Text>\n </Box>\n <Box>\n <Text dimColor>Latency: </Text>\n <Text color=\"white\" bold>\n {requests.length > 0 ? `${requests[requests.length - 1]?.duration}ms` : '-'}\n </Text>\n </Box>\n </Box>\n\n {/* Request Log */}\n <Box marginTop={1} flexDirection=\"column\">\n <Box marginBottom={1}>\n <Text bold color=\"cyan\">\n ─── Recent Requests ───\n </Text>\n </Box>\n <RequestLog requests={requests} />\n </Box>\n\n {/* Footer */}\n <Box\n marginTop={1}\n borderStyle=\"single\"\n borderTop={true}\n borderBottom={false}\n borderLeft={false}\n borderRight={false}\n borderColor=\"gray\"\n paddingTop={1}\n >\n <Box flexGrow={1} gap={2}>\n <Box>\n <Text dimColor>Press </Text>\n <Text color=\"yellow\" bold>\n Ctrl+C\n </Text>\n <Text dimColor> to stop</Text>\n </Box>\n <Box>\n <Text dimColor>Press </Text>\n <Text color=\"yellow\" bold>\n Q\n </Text>\n <Text dimColor> for QR</Text>\n </Box>\n </Box>\n <Box>\n <Text dimColor>Sponsor: </Text>\n <Text color=\"magenta\" bold>\n untunneled.dev/sponsor\n </Text>\n </Box>\n </Box>\n\n {isModalOpen && (\n <Box\n position=\"absolute\"\n width=\"100%\"\n height=\"100%\"\n alignItems=\"center\"\n justifyContent=\"center\"\n >\n <Box\n flexDirection=\"column\"\n alignItems=\"center\"\n borderStyle=\"round\"\n borderColor=\"cyan\"\n padding={1}\n backgroundColor=\"black\"\n >\n <Box marginBottom={1}>\n <Text bold color=\"cyan\">\n Scan to Open URL\n </Text>\n </Box>\n <QRCode url={tunnelUrl} />\n <Box marginTop={1}>\n <Text dimColor>Press </Text>\n <Text color=\"yellow\" bold>\n ESC\n </Text>\n <Text dimColor> or </Text>\n <Text color=\"yellow\" bold>\n Q\n </Text>\n <Text dimColor> to close</Text>\n </Box>\n </Box>\n </Box>\n )}\n </Box>\n );\n};\n","import React, { useState, useEffect } from 'react';\nimport { Box, Text } from 'ink';\nimport qrcode from 'qrcode-terminal';\n\ninterface QRCodeProps {\n url: string;\n}\n\nexport const QRCode: React.FC<QRCodeProps> = ({ url }) => {\n const [qrString, setQrString] = useState('');\n\n useEffect(() => {\n qrcode.generate(url, { small: true }, (qr: string) => {\n setQrString(qr);\n });\n }, [url]);\n\n if (!qrString) {\n return (\n <Box>\n <Text dimColor>Generating QR code...</Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" borderStyle=\"single\" paddingX={1}>\n <Text dimColor>Scan with mobile:</Text>\n <Text>{qrString}</Text>\n </Box>\n );\n};\n","import React from 'react';\nimport { Box, Text } from 'ink';\nimport type { RequestLog as RequestLogType } from '../types.js';\n\ninterface RequestLogProps {\n requests: RequestLogType[];\n}\n\nconst getStatusColor = (status: number): string => {\n if (status >= 500) return 'red';\n if (status >= 400) return 'yellow';\n if (status >= 300) return 'cyan';\n if (status >= 200) return 'green';\n return 'gray';\n};\n\nconst getMethodColor = (method: string): string => {\n switch (method.toUpperCase()) {\n case 'GET':\n return 'cyan';\n case 'POST':\n return 'green';\n case 'PUT':\n return 'yellow';\n case 'DELETE':\n return 'red';\n case 'PATCH':\n return 'magenta';\n default:\n return 'white';\n }\n};\n\nexport const RequestLog: React.FC<RequestLogProps> = ({ requests }) => {\n if (requests.length === 0) {\n return (\n <Box\n borderStyle=\"single\"\n borderColor=\"gray\"\n flexDirection=\"column\"\n alignItems=\"center\"\n paddingX={3}\n paddingY={1}\n width={40}\n alignSelf=\"center\"\n >\n <Text color=\"cyan\">⏳ Waiting for requests...</Text>\n <Box height={1} />\n <Text dimColor>Open the URL in your browser</Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\">\n {requests.map((req) => {\n const time = new Date(req.timestamp).toLocaleTimeString([], {\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false,\n });\n\n return (\n <Box key={req.id} gap={1}>\n <Text dimColor>[{time}]</Text>\n <Box width={8}>\n <Text color={getMethodColor(req.method)} bold>\n {req.method.padEnd(7)}\n </Text>\n </Box>\n <Box flexGrow={1}>\n <Text>{req.path.length > 40 ? req.path.substring(0, 37) + '...' : req.path}</Text>\n </Box>\n <Box width={6}>\n <Text color={getStatusColor(req.status)} bold>\n {req.status}\n </Text>\n </Box>\n <Box width={10} justifyContent=\"flex-end\">\n <Text color={req.duration > 500 ? 'yellow' : 'gray'}>\n {req.duration.toString().padStart(4)}ms\n </Text>\n </Box>\n </Box>\n );\n })}\n </Box>\n );\n};\n","import React, { useState, useEffect } from 'react';\nimport { Box, Text } from 'ink';\nimport type { ConnectionStatus as ConnectionStatusType } from '../types.js';\n\ninterface ConnectionStatusProps {\n status: ConnectionStatusType;\n}\n\nconst SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];\n\nconst getStatusIndicator = (\n status: ConnectionStatusType,\n frame: string\n): { color: string; symbol: string; label: string } => {\n switch (status) {\n case 'connected':\n return { color: 'green', symbol: '✔', label: 'CONNECTED' };\n case 'connecting':\n return { color: 'yellow', symbol: frame, label: 'CONNECTING' };\n case 'disconnected':\n return { color: 'gray', symbol: '○', label: 'DISCONNECTED' };\n case 'error':\n return { color: 'red', symbol: '✘', label: 'ERROR' };\n default:\n return { color: 'white', symbol: '?', label: String(status).toUpperCase() };\n }\n};\n\nexport const ConnectionStatus: React.FC<ConnectionStatusProps> = ({ status }) => {\n const [frameIndex, setFrameIndex] = useState(0);\n\n useEffect(() => {\n if (status !== 'connecting') return;\n\n const timer = setInterval(() => {\n setFrameIndex((prev) => (prev + 1) % SPINNER_FRAMES.length);\n }, 80);\n\n return () => clearInterval(timer);\n }, [status]);\n\n const frame = SPINNER_FRAMES[frameIndex] ?? ' ';\n const indicator = getStatusIndicator(status, frame);\n\n return (\n <Box>\n <Text color={indicator.color} bold>\n {indicator.symbol} {indicator.label}\n </Text>\n </Box>\n );\n};\n","import { WebSocket } from 'ws';\nimport { EventEmitter } from 'node:events';\nimport { getRelayUrl, TIMEOUTS } from '../config.js';\nimport type {\n RelayFrame,\n WsOpen,\n WsMessage,\n WsClose,\n RelayResponse,\n RequestLog,\n} from '../types.js';\n\ninterface RelayTunnelOptions {\n localPort: number;\n tunnelId: string;\n tunnelUrl: string;\n password?: string;\n verbose?: boolean;\n}\n\nexport class RelayTunnel extends EventEmitter {\n private ws: WebSocket | null = null;\n private localPort: number;\n private tunnelId: string;\n private tunnelUrl: string;\n private options: RelayTunnelOptions;\n private startTime: number = 0;\n private requestCount: number = 0;\n private bytesIn: number = 0;\n private bytesOut: number = 0;\n private statusCounts = { ok: 0, clientError: 0, serverError: 0, other: 0 };\n private latencySamples: number[] = [];\n private reconnectAttempts: number = 0;\n private isClosing: boolean = false;\n private pingInterval: NodeJS.Timeout | null = null;\n private activeWebSockets: Map<string, WebSocket> = new Map();\n\n constructor(options: RelayTunnelOptions) {\n super();\n this.options = options;\n this.localPort = options.localPort;\n this.tunnelId = options.tunnelId;\n this.tunnelUrl = options.tunnelUrl;\n }\n\n async connect(): Promise<void> {\n this.startTime = Date.now();\n this.isClosing = false;\n\n const wsUrl = getRelayUrl(this.tunnelId);\n\n if (this.options.verbose) {\n console.log(`[Relay] Connecting to ${wsUrl}`);\n }\n\n return new Promise((resolve, reject) => {\n try {\n this.ws = new WebSocket(wsUrl, {\n headers: {\n 'X-Tunnel-Auth': this.options.password || '',\n },\n });\n\n const connectionTimeout = setTimeout(() => {\n if (this.ws && this.ws.readyState !== WebSocket.OPEN) {\n this.ws.terminate();\n reject(new Error('Connection timeout'));\n }\n }, TIMEOUTS.RELAY_REQUEST);\n\n this.ws.on('open', () => {\n clearTimeout(connectionTimeout);\n this.reconnectAttempts = 0;\n\n if (this.options.verbose) {\n console.log('[Relay] Connected');\n }\n\n this.setupPing();\n this.emit('connected');\n resolve();\n });\n\n this.ws.on('error', (err) => {\n clearTimeout(connectionTimeout);\n\n if (this.options.verbose) {\n console.error('[Relay] WebSocket error:', err.message);\n }\n\n this.emit('error', err);\n reject(err);\n });\n\n this.ws.on('close', () => {\n this.clearPing();\n\n if (!this.isClosing) {\n this.emit('disconnected');\n this.attemptReconnect();\n }\n });\n\n this.ws.on('message', async (data) => {\n await this.handleRelayFrame(data);\n });\n } catch (error) {\n reject(error);\n }\n });\n }\n\n private async handleRelayFrame(data: Buffer | ArrayBuffer | Buffer[]): Promise<void> {\n let frame: RelayFrame;\n\n try {\n frame = JSON.parse(String(data)) as RelayFrame;\n } catch {\n if (this.options.verbose) {\n console.error('[Relay] Invalid frame data');\n }\n return;\n }\n\n switch (frame.type) {\n case 'http_request':\n await this.handleHttpRequest(frame);\n break;\n case 'ws_open':\n await this.handleWsOpen(frame);\n break;\n case 'ws_message':\n await this.handleWsMessage(frame);\n break;\n case 'ws_close':\n await this.handleWsClose(frame);\n break;\n }\n }\n\n private async handleHttpRequest(request: RelayFrame & { type: 'http_request' }): Promise<void> {\n const startTime = Date.now();\n const requestBytes = request.body ? Buffer.byteLength(request.body, 'utf8') : 0;\n\n try {\n const url = `http://127.0.0.1:${this.localPort}${request.path}`;\n\n const fetchOptions: RequestInit = {\n method: request.method,\n headers: request.headers,\n };\n\n if (request.body && !['GET', 'HEAD'].includes(request.method)) {\n fetchOptions.body = request.body;\n }\n\n const response = await fetch(url, fetchOptions);\n const body = await response.text();\n const duration = Date.now() - startTime;\n const responseBytes = Buffer.byteLength(body, 'utf8');\n\n const relayResponse: RelayResponse = {\n type: 'http_response',\n id: request.id,\n status: response.status,\n statusText: response.statusText,\n headers: Object.fromEntries(response.headers.entries()),\n body,\n };\n\n this.ws?.send(JSON.stringify(relayResponse));\n\n const log: RequestLog = {\n id: request.id,\n timestamp: Date.now(),\n method: request.method,\n path: request.path,\n status: response.status,\n duration,\n };\n\n this.recordMetrics(duration, response.status, requestBytes, responseBytes);\n\n this.emit('request', log);\n this.requestCount++;\n } catch (error) {\n const err = error as any;\n const duration = Date.now() - startTime;\n\n if (this.options.verbose || err.code === 'ECONNREFUSED') {\n console.error(`[Relay] Proxy error: ${err.message}`);\n }\n\n const errorResponse: RelayResponse = {\n type: 'http_response',\n id: request.id,\n status: 502,\n statusText: 'Bad Gateway',\n headers: {},\n body: `Error: ${err.message}`,\n };\n\n this.recordMetrics(duration, 502, requestBytes, 0);\n\n this.ws?.send(JSON.stringify(errorResponse));\n }\n }\n\n private recordMetrics(duration: number, status: number, bytesIn: number, bytesOut: number): void {\n this.bytesIn += bytesIn;\n this.bytesOut += bytesOut;\n\n if (status >= 200 && status < 300) this.statusCounts.ok += 1;\n else if (status >= 400 && status < 500) this.statusCounts.clientError += 1;\n else if (status >= 500 && status < 600) this.statusCounts.serverError += 1;\n else this.statusCounts.other += 1;\n\n if (this.latencySamples.length < 500) {\n this.latencySamples.push(duration);\n } else {\n const total = this.requestCount + 1;\n const index = Math.floor(Math.random() * total);\n if (index < this.latencySamples.length) {\n this.latencySamples[index] = duration;\n }\n }\n }\n\n private async handleWsOpen(frame: WsOpen): Promise<void> {\n const localUrl = `ws://127.0.0.1:${this.localPort}${frame.url}`;\n\n if (this.options.verbose) {\n console.log(`[Relay] Opening WebSocket to ${localUrl}`);\n }\n\n const protocolHeader =\n frame.headers['sec-websocket-protocol'] || frame.headers['Sec-WebSocket-Protocol'];\n const filteredHeaders = Object.fromEntries(\n Object.entries(frame.headers).filter(([key]) => {\n const header = key.toLowerCase();\n return (\n header !== 'connection' &&\n header !== 'upgrade' &&\n header !== 'host' &&\n !header.startsWith('sec-websocket')\n );\n })\n );\n\n const localOrigin = `http://127.0.0.1:${this.localPort}`;\n filteredHeaders['origin'] = localOrigin;\n if (filteredHeaders['referer']) {\n filteredHeaders['referer'] = `${localOrigin}${frame.url}`;\n }\n\n const protocols = protocolHeader\n ? protocolHeader\n .split(',')\n .map((value) => value.trim())\n .filter(Boolean)\n : undefined;\n\n const localWs = protocols?.length\n ? new WebSocket(localUrl, protocols, { headers: filteredHeaders })\n : new WebSocket(localUrl, { headers: filteredHeaders });\n\n this.activeWebSockets.set(frame.id, localWs);\n\n localWs.on('message', (data, isBinary) => {\n const message: WsMessage = {\n type: 'ws_message',\n id: frame.id,\n data: isBinary ? (data as Buffer).toString('base64') : data.toString(),\n isBinary,\n };\n this.ws?.send(JSON.stringify(message));\n });\n\n localWs.on('close', () => {\n const closeFrame: WsClose = {\n type: 'ws_close',\n id: frame.id,\n };\n this.ws?.send(JSON.stringify(closeFrame));\n this.activeWebSockets.delete(frame.id);\n });\n\n localWs.on('error', (err) => {\n if (this.options.verbose) {\n console.error(`[Relay] Local WebSocket error (${frame.id}):`, err.message);\n }\n localWs.close();\n });\n }\n\n private async handleWsMessage(frame: WsMessage): Promise<void> {\n const localWs = this.activeWebSockets.get(frame.id);\n if (localWs && localWs.readyState === WebSocket.OPEN) {\n const data = frame.isBinary ? Buffer.from(frame.data, 'base64') : frame.data;\n localWs.send(data);\n }\n }\n\n private async handleWsClose(frame: WsClose): Promise<void> {\n const localWs = this.activeWebSockets.get(frame.id);\n if (localWs) {\n localWs.close();\n this.activeWebSockets.delete(frame.id);\n }\n }\n\n private setupPing(): void {\n this.pingInterval = setInterval(() => {\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n this.ws.ping();\n }\n }, TIMEOUTS.WEBSOCKET_PING);\n }\n\n private clearPing(): void {\n if (this.pingInterval) {\n clearInterval(this.pingInterval);\n this.pingInterval = null;\n }\n }\n\n private async attemptReconnect(): Promise<void> {\n if (this.isClosing) return;\n\n this.reconnectAttempts++;\n const delay = Math.min(\n TIMEOUTS.RECONNECT_BASE * Math.pow(2, this.reconnectAttempts - 1),\n TIMEOUTS.RECONNECT_MAX\n );\n\n if (this.options.verbose) {\n console.log(`[Relay] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);\n }\n\n setTimeout(async () => {\n if (this.isClosing) return;\n\n try {\n await this.connect();\n } catch (_) {}\n }, delay);\n }\n\n async disconnect(): Promise<void> {\n this.isClosing = true;\n this.clearPing();\n\n for (const ws of this.activeWebSockets.values()) {\n ws.close();\n }\n this.activeWebSockets.clear();\n\n if (this.ws) {\n this.ws.close(1000, 'Client closing');\n this.ws = null;\n }\n }\n\n getDuration(): number {\n if (this.startTime === 0) return 0;\n return Math.floor((Date.now() - this.startTime) / 1000);\n }\n\n getRequestCount(): number {\n return this.requestCount;\n }\n\n getMetricsSummary(): {\n total_requests: number;\n total_bytes_in: number;\n total_bytes_out: number;\n latency_avg_ms: number;\n latency_p50_ms: number;\n latency_p90_ms: number;\n latency_p99_ms: number;\n status_2xx: number;\n status_4xx: number;\n status_5xx: number;\n status_other: number;\n } {\n const samples = [...this.latencySamples].sort((a, b) => a - b);\n const avg = samples.length\n ? Math.round(samples.reduce((sum, value) => sum + value, 0) / samples.length)\n : 0;\n\n const percentile = (p: number) => {\n if (samples.length === 0) return 0;\n const index = Math.min(samples.length - 1, Math.floor(p * (samples.length - 1)));\n return samples[index] ?? 0;\n };\n\n return {\n total_requests: this.requestCount,\n total_bytes_in: this.bytesIn,\n total_bytes_out: this.bytesOut,\n latency_avg_ms: avg,\n latency_p50_ms: percentile(0.5),\n latency_p90_ms: percentile(0.9),\n latency_p99_ms: percentile(0.99),\n status_2xx: this.statusCounts.ok,\n status_4xx: this.statusCounts.clientError,\n status_5xx: this.statusCounts.serverError,\n status_other: this.statusCounts.other,\n };\n }\n\n isConnected(): boolean {\n return this.ws !== null && this.ws.readyState === WebSocket.OPEN;\n }\n\n getTunnelUrl(): string {\n return this.tunnelUrl;\n }\n}\n","import { nanoid } from 'nanoid';\nimport path from 'node:path';\n\nexport const RELAY_DOMAIN = 'relay.untunneled.dev';\nexport const TUNNEL_DOMAIN = 'untunneled.dev';\n\nexport const getRelayUrl = (tunnelId: string): string => {\n const host = process.env['UNTUNNELED_RELAY_HOST'] || RELAY_DOMAIN;\n const protocol = host.includes('localhost') ? 'ws' : 'wss';\n return `${protocol}://${host}/${tunnelId}`;\n};\n\nexport const getTunnelUrl = (tunnelId: string): string => {\n const domain = process.env['UNTUNNELED_TUNNEL_DOMAIN'] || TUNNEL_DOMAIN;\n return `https://${tunnelId}.${domain}`;\n};\n\nexport function getProjectName(): string {\n const cwd = process.cwd();\n const folderName = path.basename(cwd);\n\n return (\n folderName\n .toLowerCase()\n .replace(/[^a-z0-9-]/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '')\n .slice(0, 20) || 'tunnel'\n );\n}\n\nexport function generateTunnelId(projectName?: string): string {\n const name = projectName || getProjectName();\n const suffix = nanoid(6).toLowerCase();\n return `${name}-${suffix}`;\n}\n\nexport function validatePort(port: string | number): number {\n const portNum = typeof port === 'string' ? parseInt(port, 10) : port;\n\n if (isNaN(portNum) || portNum < 1 || portNum > 65535) {\n throw new Error(`Invalid port number: ${port}. Must be between 1 and 65535.`);\n }\n\n return portNum;\n}\n\nexport const TIMEOUTS = {\n RELAY_REQUEST: 30000,\n WEBSOCKET_PING: 30000,\n RECONNECT_BASE: 1000,\n RECONNECT_MAX: 30000,\n};\n","import { PostHog } from 'posthog-node';\nimport crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nconst POSTHOG_API_KEY = process.env.POSTHOG_API_KEY || 'phc_placeholder';\nconst POSTHOG_HOST = process.env.POSTHOG_HOST || 'https://app.posthog.com';\nconst TELEMETRY_DEBUG =\n process.env['UNTUNNELED_TELEMETRY_DEBUG'] === '1' ||\n process.env['UNTUNNELED_TELEMETRY_DEBUG'] === 'true';\n\ninterface AnalyticsOptions {\n enabled: boolean;\n}\n\ntype EventName = 'tunnel_start' | 'tunnel_end' | 'session_summary';\n\ninterface EventProperties {\n tunnel_start: {\n has_auth: boolean;\n node_version: string;\n platform: string;\n };\n tunnel_end: {\n duration_seconds: number;\n requests: number;\n };\n session_summary: {\n total_requests: number;\n total_bytes_in: number;\n total_bytes_out: number;\n latency_avg_ms: number;\n latency_p50_ms: number;\n latency_p90_ms: number;\n latency_p99_ms: number;\n status_2xx: number;\n status_4xx: number;\n status_5xx: number;\n status_other: number;\n };\n}\n\nexport class Analytics {\n private posthog: PostHog | null = null;\n private enabled: boolean;\n private distinctId: string;\n\n constructor(options: AnalyticsOptions) {\n this.enabled = options.enabled;\n this.distinctId = this.getRotatingDistinctId();\n\n if (this.enabled && POSTHOG_API_KEY && 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 if (TELEMETRY_DEBUG) {\n console.log('[Telemetry] Enabled', {\n host: POSTHOG_HOST,\n keyPrefix: POSTHOG_API_KEY.slice(0, 6),\n });\n }\n } else if (TELEMETRY_DEBUG) {\n console.log('[Telemetry] Disabled', {\n enabled: this.enabled,\n hasKey: POSTHOG_API_KEY !== 'phc_placeholder',\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.distinctId,\n event,\n properties: {\n ...properties,\n version: process.env.UNTUNNELED_VERSION || '0.3.6',\n cli: true,\n },\n });\n if (TELEMETRY_DEBUG) {\n console.log('[Telemetry] Captured', { event });\n }\n } catch (_) {}\n }\n\n async shutdown(): Promise<void> {\n if (!this.posthog) return;\n\n try {\n if (TELEMETRY_DEBUG) {\n console.log('[Telemetry] Flushing');\n }\n await this.posthog.flush();\n await this.posthog.shutdown();\n } catch (_) {}\n }\n\n isEnabled(): boolean {\n return this.enabled && this.posthog !== null;\n }\n\n private getRotatingDistinctId(): string {\n const anonId = this.getAnonymousId();\n const now = new Date();\n const rotationKey = `${anonId}:${now.getUTCFullYear()}-${now.getUTCMonth() + 1}`;\n return crypto.createHash('sha256').update(rotationKey).digest('hex').slice(0, 32);\n }\n\n private getAnonymousId(): string {\n const baseDir =\n process.env['XDG_CONFIG_HOME'] ||\n process.env['APPDATA'] ||\n path.join(os.homedir(), '.config');\n const configDir = path.join(baseDir, 'untunneled.dev');\n const idPath = path.join(configDir, 'anon_id');\n\n try {\n if (!fs.existsSync(configDir)) {\n fs.mkdirSync(configDir, { recursive: true });\n }\n\n if (fs.existsSync(idPath)) {\n const existing = fs.readFileSync(idPath, 'utf8').trim();\n if (existing) return existing;\n }\n\n const newId = crypto.randomUUID();\n fs.writeFileSync(idPath, newId, 'utf8');\n return newId;\n } catch {\n return crypto.randomUUID();\n }\n }\n}\n","export async function listCommand(): Promise<void> {\n console.log('Active tunnels:');\n console.log(' (No active tunnels)');\n console.log('');\n console.log('Note: Tunnel list feature coming soon.');\n}\n","export async function stopCommand(tunnelId: string): Promise<void> {\n console.log(`Stopping tunnel: ${tunnelId}`);\n console.log('');\n console.log('Note: Remote stop feature coming soon.');\n}\n"],"mappings":"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","names":["React","useState","useEffect","Box","Text","Box","Text","jsx","jsxs","useState","useEffect","Box","Text","jsx","jsxs","jsx","jsxs","useState","useEffect","Box","Text","path","React"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/commands/start.ts","../src/ui/TunnelUI.tsx","../src/ui/QRCode.tsx","../src/ui/RequestLog.tsx","../src/ui/ConnectionStatus.tsx","../src/tunnel/relay.ts","../src/config.ts","../src/analytics.ts","../src/commands/list.ts","../src/commands/stop.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { startCommand, listCommand, stopCommand } from './commands/index.js';\n\nconst program = new Command();\n\nprogram\n .name('untunneled')\n .description('Fast, free localhost tunneling')\n .version(process.env.UNTUNNELED_VERSION || '0.3.11');\n\nprogram\n .argument('<port>', 'Local port to tunnel')\n .option('--qr', 'Open the QR code modal on start')\n .option('--name <name>', 'Set a custom tunnel subdomain')\n .option('--password <password>', 'Password protect the tunnel')\n .option('--verbose', 'Show detailed logs')\n .option('--no-telemetry', 'Disable anonymous usage tracking')\n .action(startCommand);\n\nprogram.command('list').description('List active tunnels').action(listCommand);\n\nprogram\n .command('stop')\n .argument('<tunnel-id>', 'Tunnel ID to stop')\n .description('Stop a running tunnel')\n .action(stopCommand);\n\nprogram.parse();\n","import { render } from 'ink';\nimport React from 'react';\nimport { TunnelUI } from '../ui/TunnelUI.js';\nimport { RelayTunnel } from '../tunnel/relay.js';\nimport { Analytics } from '../analytics.js';\nimport { getTunnelIdCandidates, getTunnelUrl, validatePort } from '../config.js';\nimport type { StartOptions } from '../types.js';\n\nexport async function startCommand(port: string, options: StartOptions): Promise<void> {\n let localPort: number;\n\n try {\n localPort = validatePort(port);\n } catch (error) {\n console.error((error as Error).message);\n process.exit(1);\n }\n\n const tunnelIdCandidates = getTunnelIdCandidates({ name: options.name });\n\n const analytics = new Analytics({\n enabled: options.telemetry !== false,\n });\n\n await analytics.track('tunnel_start', {\n has_auth: !!options.password,\n node_version: process.version,\n platform: process.platform,\n });\n\n let relayTunnel: RelayTunnel | null = null;\n let tunnelId = '';\n let tunnelUrl = '';\n\n for (const candidate of tunnelIdCandidates) {\n tunnelId = candidate;\n tunnelUrl = getTunnelUrl(tunnelId);\n relayTunnel = new RelayTunnel({\n localPort,\n tunnelId,\n tunnelUrl,\n password: options.password,\n verbose: options.verbose,\n });\n\n try {\n await relayTunnel.connect();\n break;\n } catch (error) {\n const err = error as NodeJS.ErrnoException;\n if (err.code === 'TUNNEL_ID_IN_USE') {\n await relayTunnel.disconnect();\n relayTunnel = null;\n continue;\n }\n\n console.error('Failed to connect to relay server:', err.message);\n await analytics.shutdown();\n process.exit(1);\n }\n }\n\n if (!relayTunnel) {\n console.error('All candidate tunnel URLs are currently in use. Try again or pass --name.');\n await analytics.shutdown();\n process.exit(1);\n }\n\n const activeTunnel = relayTunnel;\n\n if (options.verbose) {\n console.log(`Tunnel URL: ${tunnelUrl}`);\n console.log(`Forwarding to localhost:${localPort}`);\n }\n\n const { waitUntilExit } = render(\n React.createElement(TunnelUI, {\n tunnelUrl,\n localPort,\n showQR: options.qr,\n relayTunnel: activeTunnel,\n })\n );\n\n const cleanup = async () => {\n const duration = activeTunnel.getDuration();\n const requests = activeTunnel.getRequestCount();\n\n await analytics.track('tunnel_end', {\n duration_seconds: duration,\n requests: requests,\n });\n\n const summary = activeTunnel.getMetricsSummary();\n await analytics.track('session_summary', summary);\n\n await activeTunnel.disconnect();\n await analytics.shutdown();\n };\n\n let isShuttingDown = false;\n const handleShutdown = async () => {\n if (isShuttingDown) return;\n isShuttingDown = true;\n\n const timeout = setTimeout(() => {\n process.exit(0);\n }, 2000);\n\n try {\n await cleanup();\n } finally {\n clearTimeout(timeout);\n process.exit(0);\n }\n };\n\n process.once('SIGINT', handleShutdown);\n process.once('SIGTERM', handleShutdown);\n\n await waitUntilExit();\n}\n","import React, { useState, useEffect } from 'react';\nimport { Box, Text, useApp, useInput } from 'ink';\nimport { QRCode } from './QRCode.js';\nimport { RequestLog } from './RequestLog.js';\nimport { ConnectionStatus } from './ConnectionStatus.js';\nimport type { RelayTunnel } from '../tunnel/relay.js';\nimport type { RequestLog as RequestLogType, TunnelStats } from '../types.js';\n\ninterface TunnelUIProps {\n tunnelUrl: string;\n localPort: number;\n showQR?: boolean;\n relayTunnel: RelayTunnel;\n}\n\nconst MAX_LOGS = 10;\n\nconst LOGO = `\n ╦ ╦╔╗╔╔╦╗╦ ╦╔╗╔╔╗╔╔═╗╦ ╔═╗╔╦╗ ╔╦╗╔═╗╦ ╦\n ║ ║║║║ ║ ║ ║║║║║║║║╣ ║ ║╣ ║║ ║║║╣ ╚╗╔╝\n ╚═╝╝╚╝ ╩ ╚═╝╝╚╝╝╚╝╚═╝╩═╝╚═╝═╩╝o═╩╝╚═╝ ╚╝ \n`;\n\nconst formatUptime = (ms: number) => {\n const seconds = Math.floor(ms / 1000);\n const minutes = Math.floor(seconds / 60);\n const hours = Math.floor(minutes / 60);\n return `${hours}h ${minutes % 60}m ${seconds % 60}s`;\n};\n\nexport const TunnelUI: React.FC<TunnelUIProps> = ({\n tunnelUrl,\n localPort,\n showQR,\n relayTunnel,\n}) => {\n useApp();\n const [requests, setRequests] = useState<RequestLogType[]>([]);\n const [stats, setStats] = useState<TunnelStats>({\n total: 0,\n startTime: Date.now(),\n });\n const [relayConnected, setRelayConnected] = useState(relayTunnel.isConnected());\n const [uptime, setUptime] = useState(0);\n const [isModalOpen, setIsModalOpen] = useState(Boolean(showQR));\n\n useInput((input, key) => {\n if (key.ctrl && input === 'c') {\n process.kill(process.pid, 'SIGINT');\n return;\n }\n if (input === 'q' || input === 'c' || input === 'Q' || input === 'C') {\n setIsModalOpen((prev) => !prev);\n }\n if (key.escape) {\n setIsModalOpen(false);\n }\n });\n\n useEffect(() => {\n const handleRequest = (req: RequestLogType) => {\n setRequests((prev) => [...prev.slice(-(MAX_LOGS - 1)), req]);\n setStats((prev) => ({\n ...prev,\n total: prev.total + 1,\n }));\n };\n\n const handleRelayConnected = () => setRelayConnected(true);\n const handleRelayDisconnected = () => setRelayConnected(false);\n\n relayTunnel.on('request', handleRequest);\n relayTunnel.on('connected', handleRelayConnected);\n relayTunnel.on('disconnected', handleRelayDisconnected);\n\n const uptimeTimer = setInterval(() => {\n setUptime(Date.now() - stats.startTime);\n }, 1000);\n\n return () => {\n relayTunnel.off('request', handleRequest);\n relayTunnel.off('connected', handleRelayConnected);\n relayTunnel.off('disconnected', handleRelayDisconnected);\n clearInterval(uptimeTimer);\n };\n }, [relayTunnel, stats.startTime]);\n\n const connectionStatus = relayConnected ? 'connected' : 'connecting';\n const accentColor = relayConnected ? 'cyan' : 'yellow';\n\n return (\n <Box flexDirection=\"column\" paddingX={2} paddingY={1}>\n {/* Header */}\n <Box flexDirection=\"column\" marginBottom={1}>\n <Text color=\"cyan\" bold>\n {LOGO}\n </Text>\n <Box justifyContent=\"space-between\" paddingX={1}>\n <ConnectionStatus status={connectionStatus} />\n <Text dimColor>v0.3.6</Text>\n </Box>\n </Box>\n\n {/* Main Connection Info */}\n <Box\n borderStyle=\"round\"\n borderColor={accentColor}\n paddingX={2}\n paddingY={1}\n flexDirection=\"column\"\n >\n <Box>\n <Text bold>Public URL: </Text>\n <Text bold color=\"black\" backgroundColor=\"cyan\">\n {' '}\n {tunnelUrl}{' '}\n </Text>\n </Box>\n <Box marginTop={1}>\n <Text dimColor>Forwarding: </Text>\n <Text color=\"yellow\">http://localhost:{localPort}</Text>\n <Text dimColor> → </Text>\n <Text color=\"cyan\">{tunnelUrl}</Text>\n </Box>\n </Box>\n\n {/* Stats Bar */}\n <Box marginTop={1} paddingX={1} gap={4}>\n <Box>\n <Text dimColor>Requests: </Text>\n <Text color=\"white\" bold>\n {stats.total}\n </Text>\n </Box>\n <Box>\n <Text dimColor>Uptime: </Text>\n <Text color=\"white\" bold>\n {formatUptime(uptime)}\n </Text>\n </Box>\n <Box>\n <Text dimColor>Latency: </Text>\n <Text color=\"white\" bold>\n {requests.length > 0 ? `${requests[requests.length - 1]?.duration}ms` : '-'}\n </Text>\n </Box>\n </Box>\n\n {/* Request Log */}\n <Box marginTop={1} flexDirection=\"column\">\n <Box marginBottom={1}>\n <Text bold color=\"cyan\">\n ─── Recent Requests ───\n </Text>\n </Box>\n <RequestLog requests={requests} />\n </Box>\n\n {/* Footer */}\n <Box\n marginTop={1}\n borderStyle=\"single\"\n borderTop={true}\n borderBottom={false}\n borderLeft={false}\n borderRight={false}\n borderColor=\"gray\"\n paddingTop={1}\n >\n <Box flexGrow={1} gap={2}>\n <Box>\n <Text dimColor>Press </Text>\n <Text color=\"yellow\" bold>\n Ctrl+C\n </Text>\n <Text dimColor> to stop</Text>\n </Box>\n <Box>\n <Text dimColor>Press </Text>\n <Text color=\"yellow\" bold>\n Q\n </Text>\n <Text dimColor> for QR</Text>\n </Box>\n </Box>\n <Box>\n <Text dimColor>Sponsor: </Text>\n <Text color=\"magenta\" bold>\n untunneled.dev/sponsor\n </Text>\n </Box>\n </Box>\n\n {isModalOpen && (\n <Box\n position=\"absolute\"\n width=\"100%\"\n height=\"100%\"\n alignItems=\"center\"\n justifyContent=\"center\"\n >\n <Box\n flexDirection=\"column\"\n alignItems=\"center\"\n borderStyle=\"round\"\n borderColor=\"cyan\"\n padding={1}\n backgroundColor=\"black\"\n >\n <Box marginBottom={1}>\n <Text bold color=\"cyan\">\n Scan to Open URL\n </Text>\n </Box>\n <QRCode url={tunnelUrl} />\n <Box marginTop={1}>\n <Text dimColor>Press </Text>\n <Text color=\"yellow\" bold>\n ESC\n </Text>\n <Text dimColor> or </Text>\n <Text color=\"yellow\" bold>\n Q\n </Text>\n <Text dimColor> to close</Text>\n </Box>\n </Box>\n </Box>\n )}\n </Box>\n );\n};\n","import React, { useState, useEffect } from 'react';\nimport { Box, Text } from 'ink';\nimport qrcode from 'qrcode-terminal';\n\ninterface QRCodeProps {\n url: string;\n}\n\nexport const QRCode: React.FC<QRCodeProps> = ({ url }) => {\n const [qrString, setQrString] = useState('');\n\n useEffect(() => {\n qrcode.generate(url, { small: true }, (qr: string) => {\n setQrString(qr);\n });\n }, [url]);\n\n if (!qrString) {\n return (\n <Box>\n <Text dimColor>Generating QR code...</Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" borderStyle=\"single\" paddingX={1}>\n <Text dimColor>Scan with mobile:</Text>\n <Text>{qrString}</Text>\n </Box>\n );\n};\n","import React from 'react';\nimport { Box, Text } from 'ink';\nimport type { RequestLog as RequestLogType } from '../types.js';\n\ninterface RequestLogProps {\n requests: RequestLogType[];\n}\n\nconst getStatusColor = (status: number): string => {\n if (status >= 500) return 'red';\n if (status >= 400) return 'yellow';\n if (status >= 300) return 'cyan';\n if (status >= 200) return 'green';\n return 'gray';\n};\n\nconst getMethodColor = (method: string): string => {\n switch (method.toUpperCase()) {\n case 'GET':\n return 'cyan';\n case 'POST':\n return 'green';\n case 'PUT':\n return 'yellow';\n case 'DELETE':\n return 'red';\n case 'PATCH':\n return 'magenta';\n default:\n return 'white';\n }\n};\n\nexport const RequestLog: React.FC<RequestLogProps> = ({ requests }) => {\n if (requests.length === 0) {\n return (\n <Box\n borderStyle=\"single\"\n borderColor=\"gray\"\n flexDirection=\"column\"\n alignItems=\"center\"\n paddingX={3}\n paddingY={1}\n width={40}\n alignSelf=\"center\"\n >\n <Text color=\"cyan\">⏳ Waiting for requests...</Text>\n <Box height={1} />\n <Text dimColor>Open the URL in your browser</Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\">\n {requests.map((req) => {\n const time = new Date(req.timestamp).toLocaleTimeString([], {\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false,\n });\n\n return (\n <Box key={req.id} gap={1}>\n <Text dimColor>[{time}]</Text>\n <Box width={8}>\n <Text color={getMethodColor(req.method)} bold>\n {req.method.padEnd(7)}\n </Text>\n </Box>\n <Box flexGrow={1}>\n <Text>{req.path.length > 40 ? req.path.substring(0, 37) + '...' : req.path}</Text>\n </Box>\n <Box width={6}>\n <Text color={getStatusColor(req.status)} bold>\n {req.status}\n </Text>\n </Box>\n <Box width={10} justifyContent=\"flex-end\">\n <Text color={req.duration > 500 ? 'yellow' : 'gray'}>\n {req.duration.toString().padStart(4)}ms\n </Text>\n </Box>\n </Box>\n );\n })}\n </Box>\n );\n};\n","import React, { useState, useEffect } from 'react';\nimport { Box, Text } from 'ink';\nimport type { ConnectionStatus as ConnectionStatusType } from '../types.js';\n\ninterface ConnectionStatusProps {\n status: ConnectionStatusType;\n}\n\nconst SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];\n\nconst getStatusIndicator = (\n status: ConnectionStatusType,\n frame: string\n): { color: string; symbol: string; label: string } => {\n switch (status) {\n case 'connected':\n return { color: 'green', symbol: '✔', label: 'CONNECTED' };\n case 'connecting':\n return { color: 'yellow', symbol: frame, label: 'CONNECTING' };\n case 'disconnected':\n return { color: 'gray', symbol: '○', label: 'DISCONNECTED' };\n case 'error':\n return { color: 'red', symbol: '✘', label: 'ERROR' };\n default:\n return { color: 'white', symbol: '?', label: String(status).toUpperCase() };\n }\n};\n\nexport const ConnectionStatus: React.FC<ConnectionStatusProps> = ({ status }) => {\n const [frameIndex, setFrameIndex] = useState(0);\n\n useEffect(() => {\n if (status !== 'connecting') return;\n\n const timer = setInterval(() => {\n setFrameIndex((prev) => (prev + 1) % SPINNER_FRAMES.length);\n }, 80);\n\n return () => clearInterval(timer);\n }, [status]);\n\n const frame = SPINNER_FRAMES[frameIndex] ?? ' ';\n const indicator = getStatusIndicator(status, frame);\n\n return (\n <Box>\n <Text color={indicator.color} bold>\n {indicator.symbol} {indicator.label}\n </Text>\n </Box>\n );\n};\n","import { WebSocket } from 'ws';\nimport { EventEmitter } from 'node:events';\nimport { getRelayUrl, TIMEOUTS } from '../config.js';\nimport type {\n RelayFrame,\n WsOpen,\n WsMessage,\n WsClose,\n RelayResponse,\n RequestLog,\n} from '../types.js';\n\ninterface RelayTunnelOptions {\n localPort: number;\n tunnelId: string;\n tunnelUrl: string;\n password?: string;\n verbose?: boolean;\n}\n\nexport class RelayTunnel extends EventEmitter {\n private ws: WebSocket | null = null;\n private localPort: number;\n private tunnelId: string;\n private tunnelUrl: string;\n private options: RelayTunnelOptions;\n private startTime: number = 0;\n private requestCount: number = 0;\n private bytesIn: number = 0;\n private bytesOut: number = 0;\n private statusCounts = { ok: 0, clientError: 0, serverError: 0, other: 0 };\n private latencySamples: number[] = [];\n private reconnectAttempts: number = 0;\n private isClosing: boolean = false;\n private pingInterval: NodeJS.Timeout | null = null;\n private activeWebSockets: Map<string, WebSocket> = new Map();\n\n constructor(options: RelayTunnelOptions) {\n super();\n this.options = options;\n this.localPort = options.localPort;\n this.tunnelId = options.tunnelId;\n this.tunnelUrl = options.tunnelUrl;\n }\n\n async connect(): Promise<void> {\n this.startTime = Date.now();\n this.isClosing = false;\n\n const wsUrl = getRelayUrl(this.tunnelId);\n\n if (this.options.verbose) {\n console.log(`[Relay] Connecting to ${wsUrl}`);\n }\n\n return new Promise((resolve, reject) => {\n let didOpen = false;\n let settled = false;\n const finish = (error?: Error) => {\n if (settled) return;\n settled = true;\n if (error) {\n reject(error);\n } else {\n resolve();\n }\n };\n\n try {\n this.ws = new WebSocket(wsUrl, {\n headers: {\n 'X-Tunnel-Auth': this.options.password || '',\n },\n });\n\n const connectionTimeout = setTimeout(() => {\n if (this.ws && this.ws.readyState !== WebSocket.OPEN) {\n this.isClosing = true;\n this.ws.terminate();\n finish(new Error('Connection timeout'));\n }\n }, TIMEOUTS.RELAY_REQUEST);\n\n this.ws.on('open', () => {\n clearTimeout(connectionTimeout);\n this.reconnectAttempts = 0;\n didOpen = true;\n\n if (this.options.verbose) {\n console.log('[Relay] Connected');\n }\n\n this.setupPing();\n this.emit('connected');\n finish();\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 if (didOpen || this.listenerCount('error') > 0) {\n this.emit('error', err);\n }\n if (!didOpen) {\n finish(err);\n }\n });\n\n this.ws.on('unexpected-response', (_request, response) => {\n clearTimeout(connectionTimeout);\n\n const error = new Error(\n response.statusCode === 409\n ? 'Tunnel ID already in use'\n : `Unexpected response: ${response.statusCode || 'unknown'}`\n ) as NodeJS.ErrnoException;\n\n if (response.statusCode === 409) {\n error.code = 'TUNNEL_ID_IN_USE';\n }\n\n this.isClosing = true;\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n this.ws.close();\n } else {\n this.ws?.terminate();\n }\n finish(error);\n });\n\n this.ws.on('close', () => {\n this.clearPing();\n\n if (!this.isClosing && didOpen) {\n this.emit('disconnected');\n this.attemptReconnect();\n }\n });\n\n this.ws.on('message', async (data) => {\n await this.handleRelayFrame(data);\n });\n } catch (error) {\n reject(error);\n }\n });\n }\n\n private async handleRelayFrame(data: Buffer | ArrayBuffer | Buffer[]): Promise<void> {\n let frame: RelayFrame;\n\n try {\n frame = JSON.parse(String(data)) as RelayFrame;\n } catch {\n if (this.options.verbose) {\n console.error('[Relay] Invalid frame data');\n }\n return;\n }\n\n switch (frame.type) {\n case 'http_request':\n await this.handleHttpRequest(frame);\n break;\n case 'ws_open':\n await this.handleWsOpen(frame);\n break;\n case 'ws_message':\n await this.handleWsMessage(frame);\n break;\n case 'ws_close':\n await this.handleWsClose(frame);\n break;\n }\n }\n\n private async handleHttpRequest(request: RelayFrame & { type: 'http_request' }): Promise<void> {\n const startTime = Date.now();\n const requestBytes = request.body ? Buffer.byteLength(request.body, 'utf8') : 0;\n\n try {\n const url = `http://127.0.0.1:${this.localPort}${request.path}`;\n\n const fetchOptions: RequestInit = {\n method: request.method,\n headers: request.headers,\n };\n\n if (request.body && !['GET', 'HEAD'].includes(request.method)) {\n fetchOptions.body = request.body;\n }\n\n const response = await fetch(url, fetchOptions);\n const body = await response.text();\n const duration = Date.now() - startTime;\n const responseBytes = Buffer.byteLength(body, 'utf8');\n\n const relayResponse: RelayResponse = {\n type: 'http_response',\n id: request.id,\n status: response.status,\n statusText: response.statusText,\n headers: Object.fromEntries(response.headers.entries()),\n body,\n };\n\n this.ws?.send(JSON.stringify(relayResponse));\n\n const log: RequestLog = {\n id: request.id,\n timestamp: Date.now(),\n method: request.method,\n path: request.path,\n status: response.status,\n duration,\n };\n\n this.recordMetrics(duration, response.status, requestBytes, responseBytes);\n\n this.emit('request', log);\n this.requestCount++;\n } catch (error) {\n const err = error as NodeJS.ErrnoException;\n const duration = Date.now() - startTime;\n\n if (this.options.verbose || err.code === 'ECONNREFUSED') {\n console.error(`[Relay] Proxy error: ${err.message}`);\n }\n\n const friendlyMessage =\n err.message === 'fetch failed' || err.code === 'ECONNREFUSED'\n ? 'Tunnel is offline. Restart the CLI to reconnect.'\n : `Error: ${err.message}`;\n\n const errorResponse: RelayResponse = {\n type: 'http_response',\n id: request.id,\n status: 502,\n statusText: 'Bad Gateway',\n headers: {},\n body: friendlyMessage,\n };\n\n this.recordMetrics(duration, 502, requestBytes, 0);\n\n this.ws?.send(JSON.stringify(errorResponse));\n }\n }\n\n private recordMetrics(duration: number, status: number, bytesIn: number, bytesOut: number): void {\n this.bytesIn += bytesIn;\n this.bytesOut += bytesOut;\n\n if (status >= 200 && status < 300) this.statusCounts.ok += 1;\n else if (status >= 400 && status < 500) this.statusCounts.clientError += 1;\n else if (status >= 500 && status < 600) this.statusCounts.serverError += 1;\n else this.statusCounts.other += 1;\n\n if (this.latencySamples.length < 500) {\n this.latencySamples.push(duration);\n } else {\n const total = this.requestCount + 1;\n const index = Math.floor(Math.random() * total);\n if (index < this.latencySamples.length) {\n this.latencySamples[index] = duration;\n }\n }\n }\n\n private async handleWsOpen(frame: WsOpen): Promise<void> {\n const localUrl = `ws://127.0.0.1:${this.localPort}${frame.url}`;\n\n if (this.options.verbose) {\n console.log(`[Relay] Opening WebSocket to ${localUrl}`);\n }\n\n const protocolHeader =\n frame.headers['sec-websocket-protocol'] || frame.headers['Sec-WebSocket-Protocol'];\n const filteredHeaders = Object.fromEntries(\n Object.entries(frame.headers).filter(([key]) => {\n const header = key.toLowerCase();\n return (\n header !== 'connection' &&\n header !== 'upgrade' &&\n header !== 'host' &&\n !header.startsWith('sec-websocket')\n );\n })\n );\n\n const localOrigin = `http://127.0.0.1:${this.localPort}`;\n filteredHeaders['origin'] = localOrigin;\n if (filteredHeaders['referer']) {\n filteredHeaders['referer'] = `${localOrigin}${frame.url}`;\n }\n\n const protocols = protocolHeader\n ? protocolHeader\n .split(',')\n .map((value) => value.trim())\n .filter(Boolean)\n : undefined;\n\n const localWs = protocols?.length\n ? new WebSocket(localUrl, protocols, { headers: filteredHeaders })\n : new WebSocket(localUrl, { headers: filteredHeaders });\n\n this.activeWebSockets.set(frame.id, localWs);\n\n localWs.on('message', (data, isBinary) => {\n const message: WsMessage = {\n type: 'ws_message',\n id: frame.id,\n data: isBinary ? (data as Buffer).toString('base64') : data.toString(),\n isBinary,\n };\n this.ws?.send(JSON.stringify(message));\n });\n\n localWs.on('close', () => {\n const closeFrame: WsClose = {\n type: 'ws_close',\n id: frame.id,\n };\n this.ws?.send(JSON.stringify(closeFrame));\n this.activeWebSockets.delete(frame.id);\n });\n\n localWs.on('error', (err) => {\n if (this.options.verbose) {\n console.error(`[Relay] Local WebSocket error (${frame.id}):`, err.message);\n }\n localWs.close();\n });\n }\n\n private async handleWsMessage(frame: WsMessage): Promise<void> {\n const localWs = this.activeWebSockets.get(frame.id);\n if (localWs && localWs.readyState === WebSocket.OPEN) {\n const data = frame.isBinary ? Buffer.from(frame.data, 'base64') : frame.data;\n localWs.send(data);\n }\n }\n\n private async handleWsClose(frame: WsClose): Promise<void> {\n const localWs = this.activeWebSockets.get(frame.id);\n if (localWs) {\n localWs.close();\n this.activeWebSockets.delete(frame.id);\n }\n }\n\n private setupPing(): void {\n this.pingInterval = setInterval(() => {\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n this.ws.ping();\n }\n }, TIMEOUTS.WEBSOCKET_PING);\n }\n\n private clearPing(): void {\n if (this.pingInterval) {\n clearInterval(this.pingInterval);\n this.pingInterval = null;\n }\n }\n\n private async attemptReconnect(): Promise<void> {\n if (this.isClosing) return;\n\n this.reconnectAttempts++;\n const delay = Math.min(\n TIMEOUTS.RECONNECT_BASE * Math.pow(2, this.reconnectAttempts - 1),\n TIMEOUTS.RECONNECT_MAX\n );\n\n if (this.options.verbose) {\n console.log(`[Relay] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);\n }\n\n setTimeout(async () => {\n if (this.isClosing) return;\n\n try {\n await this.connect();\n } catch (_) {}\n }, delay);\n }\n\n async disconnect(): Promise<void> {\n this.isClosing = true;\n this.clearPing();\n\n for (const ws of this.activeWebSockets.values()) {\n ws.close();\n }\n this.activeWebSockets.clear();\n\n if (this.ws) {\n this.ws.close(1000, 'Client closing');\n this.ws = null;\n }\n }\n\n getDuration(): number {\n if (this.startTime === 0) return 0;\n return Math.floor((Date.now() - this.startTime) / 1000);\n }\n\n getRequestCount(): number {\n return this.requestCount;\n }\n\n getMetricsSummary(): {\n total_requests: number;\n total_bytes_in: number;\n total_bytes_out: number;\n latency_avg_ms: number;\n latency_p50_ms: number;\n latency_p90_ms: number;\n latency_p99_ms: number;\n status_2xx: number;\n status_4xx: number;\n status_5xx: number;\n status_other: number;\n } {\n const samples = [...this.latencySamples].sort((a, b) => a - b);\n const avg = samples.length\n ? Math.round(samples.reduce((sum, value) => sum + value, 0) / samples.length)\n : 0;\n\n const percentile = (p: number) => {\n if (samples.length === 0) return 0;\n const index = Math.min(samples.length - 1, Math.floor(p * (samples.length - 1)));\n return samples[index] ?? 0;\n };\n\n return {\n total_requests: this.requestCount,\n total_bytes_in: this.bytesIn,\n total_bytes_out: this.bytesOut,\n latency_avg_ms: avg,\n latency_p50_ms: percentile(0.5),\n latency_p90_ms: percentile(0.9),\n latency_p99_ms: percentile(0.99),\n status_2xx: this.statusCounts.ok,\n status_4xx: this.statusCounts.clientError,\n status_5xx: this.statusCounts.serverError,\n status_other: this.statusCounts.other,\n };\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 { execSync } from 'node:child_process';\nimport os from 'node:os';\nimport path from 'node:path';\n\nexport const RELAY_DOMAIN = 'relay.untunneled.dev';\nexport const TUNNEL_DOMAIN = 'untunneled.dev';\n\nexport const getRelayUrl = (tunnelId: string): string => {\n const host = process.env['UNTUNNELED_RELAY_HOST'] || RELAY_DOMAIN;\n const protocol = host.includes('localhost') ? 'ws' : 'wss';\n return `${protocol}://${host}/${tunnelId}`;\n};\n\nexport const getTunnelUrl = (tunnelId: string): string => {\n const domain = process.env['UNTUNNELED_TUNNEL_DOMAIN'] || TUNNEL_DOMAIN;\n return `https://${tunnelId}.${domain}`;\n};\n\nconst slugifyName = (value: string, maxLength = 20): string => {\n return value\n .toLowerCase()\n .replace(/[^a-z0-9-]/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '')\n .slice(0, maxLength);\n};\n\nconst getGitRepoName = (): string | null => {\n try {\n const root = execSync('git rev-parse --show-toplevel', {\n cwd: process.cwd(),\n stdio: ['ignore', 'pipe', 'ignore'],\n })\n .toString()\n .trim();\n return root ? path.basename(root) : null;\n } catch {\n return null;\n }\n};\n\nconst getGitUserName = (): string | null => {\n try {\n const name = execSync('git config user.name', {\n cwd: process.cwd(),\n stdio: ['ignore', 'pipe', 'ignore'],\n })\n .toString()\n .trim();\n if (name) return name;\n\n const email = execSync('git config user.email', {\n cwd: process.cwd(),\n stdio: ['ignore', 'pipe', 'ignore'],\n })\n .toString()\n .trim();\n if (!email) return null;\n const [user] = email.split('@');\n return user || null;\n } catch {\n return null;\n }\n};\n\nconst getLocalUsername = (): string | null => {\n try {\n return os.userInfo().username || null;\n } catch {\n return null;\n }\n};\n\nconst addCandidate = (candidates: string[], value?: string | null): void => {\n if (!value) return;\n const slug = slugifyName(value);\n if (!slug || candidates.includes(slug)) return;\n candidates.push(slug);\n};\n\nexport function getProjectName(): string {\n const cwd = process.cwd();\n const folderName = path.basename(cwd);\n\n return slugifyName(folderName) || 'tunnel';\n}\n\nexport function getTunnelIdCandidates(options?: { name?: string }): string[] {\n const candidates: string[] = [];\n\n if (options?.name) {\n addCandidate(candidates, options.name);\n const base = candidates[0];\n if (base) {\n candidates.push(`${base}-${nanoid(6).toLowerCase()}`);\n return candidates;\n }\n }\n\n const gitRepoName = getGitRepoName();\n const directoryName = getProjectName();\n const gitUserName = getGitUserName();\n const localUserName = getLocalUsername();\n\n addCandidate(candidates, gitRepoName);\n addCandidate(candidates, directoryName);\n\n if (gitUserName && gitRepoName) {\n addCandidate(candidates, `${gitUserName}-${gitRepoName}`);\n }\n\n if (gitUserName && directoryName) {\n addCandidate(candidates, `${gitUserName}-${directoryName}`);\n }\n\n if (localUserName && directoryName) {\n addCandidate(candidates, `${localUserName}-${directoryName}`);\n }\n\n const fallbackBase = directoryName || gitRepoName || 'tunnel';\n candidates.push(`${fallbackBase}-${nanoid(6).toLowerCase()}`);\n return candidates;\n}\n\nexport function generateTunnelId(projectName?: string): string {\n const name = projectName ? slugifyName(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 const TIMEOUTS = {\n RELAY_REQUEST: 30000,\n WEBSOCKET_PING: 30000,\n RECONNECT_BASE: 1000,\n RECONNECT_MAX: 30000,\n};\n","import { PostHog } from 'posthog-node';\nimport crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nconst POSTHOG_API_KEY = process.env.POSTHOG_API_KEY || 'phc_placeholder';\nconst POSTHOG_HOST = process.env.POSTHOG_HOST || 'https://app.posthog.com';\nconst TELEMETRY_DEBUG =\n process.env['UNTUNNELED_TELEMETRY_DEBUG'] === '1' ||\n process.env['UNTUNNELED_TELEMETRY_DEBUG'] === 'true';\n\ninterface AnalyticsOptions {\n enabled: boolean;\n}\n\ntype EventName = 'tunnel_start' | 'tunnel_end' | 'session_summary';\n\ninterface EventProperties {\n tunnel_start: {\n has_auth: boolean;\n node_version: string;\n platform: string;\n };\n tunnel_end: {\n duration_seconds: number;\n requests: number;\n };\n session_summary: {\n total_requests: number;\n total_bytes_in: number;\n total_bytes_out: number;\n latency_avg_ms: number;\n latency_p50_ms: number;\n latency_p90_ms: number;\n latency_p99_ms: number;\n status_2xx: number;\n status_4xx: number;\n status_5xx: number;\n status_other: number;\n };\n}\n\nexport class Analytics {\n private posthog: PostHog | null = null;\n private enabled: boolean;\n private distinctId: string;\n\n constructor(options: AnalyticsOptions) {\n this.enabled = options.enabled;\n this.distinctId = this.getRotatingDistinctId();\n\n if (this.enabled && POSTHOG_API_KEY && 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 if (TELEMETRY_DEBUG) {\n console.log('[Telemetry] Enabled', {\n host: POSTHOG_HOST,\n keyPrefix: POSTHOG_API_KEY.slice(0, 6),\n });\n }\n } else if (TELEMETRY_DEBUG) {\n console.log('[Telemetry] Disabled', {\n enabled: this.enabled,\n hasKey: POSTHOG_API_KEY !== 'phc_placeholder',\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.distinctId,\n event,\n properties: {\n ...properties,\n version: process.env.UNTUNNELED_VERSION || '0.3.11',\n cli: true,\n },\n });\n if (TELEMETRY_DEBUG) {\n console.log('[Telemetry] Captured', { event });\n }\n } catch (_) {}\n }\n\n async shutdown(): Promise<void> {\n if (!this.posthog) return;\n\n try {\n if (TELEMETRY_DEBUG) {\n console.log('[Telemetry] Flushing');\n }\n await this.posthog.flush();\n await this.posthog.shutdown();\n } catch (_) {}\n }\n\n isEnabled(): boolean {\n return this.enabled && this.posthog !== null;\n }\n\n private getRotatingDistinctId(): string {\n const anonId = this.getAnonymousId();\n const now = new Date();\n const rotationKey = `${anonId}:${now.getUTCFullYear()}-${now.getUTCMonth() + 1}`;\n return crypto.createHash('sha256').update(rotationKey).digest('hex').slice(0, 32);\n }\n\n private getAnonymousId(): string {\n const baseDir =\n process.env['XDG_CONFIG_HOME'] ||\n process.env['APPDATA'] ||\n path.join(os.homedir(), '.config');\n const configDir = path.join(baseDir, 'untunneled.dev');\n const idPath = path.join(configDir, 'anon_id');\n\n try {\n if (!fs.existsSync(configDir)) {\n fs.mkdirSync(configDir, { recursive: true });\n }\n\n if (fs.existsSync(idPath)) {\n const existing = fs.readFileSync(idPath, 'utf8').trim();\n if (existing) return existing;\n }\n\n const newId = crypto.randomUUID();\n fs.writeFileSync(idPath, newId, 'utf8');\n return newId;\n } catch {\n return crypto.randomUUID();\n }\n }\n}\n","export async function listCommand(): Promise<void> {\n console.log('Active tunnels:');\n console.log(' (No active tunnels)');\n console.log('');\n console.log('Note: Tunnel list feature coming soon.');\n}\n","export async function stopCommand(tunnelId: string): Promise<void> {\n console.log(`Stopping tunnel: ${tunnelId}`);\n console.log('');\n console.log('Note: Remote stop feature coming soon.');\n}\n"],"mappings":"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","names":["React","useState","useEffect","Box","Text","Box","Text","jsx","jsxs","useState","useEffect","Box","Text","jsx","jsxs","jsx","jsxs","useState","useEffect","Box","Text","os","path","React"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "untunneled.dev",
3
- "version": "0.3.6",
3
+ "version": "0.3.11",
4
4
  "description": "Fast, free localhost tunneling for developers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",