nport 2.1.2 → 2.1.3

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.
Files changed (2) hide show
  1. package/dist/index.js +24 -23
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- var he=Object.defineProperty;var i=(o,e)=>he(o,"name",{value:e,configurable:!0});import on from"ora";import b from"chalk";import P from"path";import{fileURLToPath as Ie}from"url";import{createRequire as we}from"module";var Q="https://api.nport.link";var X="user-";var $={THRESHOLD:5,COOLDOWN:3e4},Z={SUCCESS:["Registered tunnel connection"],ERROR:["ERR","error"],NETWORK_WARNING:["failed to accept QUIC stream","failed to dial to edge with quic","failed to accept incoming stream requests","Failed to dial a quic connection","timeout: no recent network activity","failed to dial to edge","quic:"],IGNORE:["Cannot determine default origin certificate path","No file cert.pem","origincert option","TUNNEL_ORIGIN_CERT","context canceled","failed to run the datagram handler","failed to serve tunnel connection","Connection terminated","no more connections active and exiting","Serve tunnel error","accept stream listener encountered a failure","Retrying connection","icmp router terminated","use of closed network connection","Application error 0x0"]},ee=["en","vi"],ne="https://github.com/tuanngocptn/nport",B="https://nport.link",te="https://buymeacoffee.com/tuanngocptn";var Ee=Ie(import.meta.url),oe=P.dirname(P.dirname(Ee)),be=we(import.meta.url),re=be("../package.json");function Ce(){return process.env.NPORT_BACKEND_URL?process.env.NPORT_BACKEND_URL:Q}i(Ce,"getBackendUrl");var u={PACKAGE_NAME:re.name,CURRENT_VERSION:re.version,BACKEND_URL:Ce(),DEFAULT_PORT:8080,SUBDOMAIN_PREFIX:X,TUNNEL_TIMEOUT_HOURS:4,UPDATE_CHECK_TIMEOUT:3e3},ke={IS_WINDOWS:process.platform==="win32",BIN_NAME:process.platform==="win32"?"cloudflared.exe":"cloudflared"},x={BIN_DIR:P.join(oe,"bin"),BIN_PATH:P.join(oe,"bin",ke.BIN_NAME)},U=Z,W={WARNING_THRESHOLD:$.THRESHOLD,WARNING_COOLDOWN:$.COOLDOWN},se=u.TUNNEL_TIMEOUT_HOURS*60*60*1e3;var O=class{static{i(this,"ArgumentParser")}static parse(e){let n=this.parsePort(e),t=this.parseSubdomain(e),r=this.parseLanguage(e),c=this.parseBackendUrl(e),g=this.parseSetBackend(e);return{port:n,subdomain:t,language:r,backendUrl:c,setBackend:g}}static parsePort(e){return parseInt(e[0],10)||u.DEFAULT_PORT}static parseSubdomain(e){let n=[()=>this.findFlagWithEquals(e,"--subdomain="),()=>this.findFlagWithEquals(e,"-s="),()=>this.findFlagWithValue(e,"--subdomain"),()=>this.findFlagWithValue(e,"-s")];for(let t of n){let r=t();if(r)return r}return this.generateRandomSubdomain()}static parseLanguage(e){if(e.includes("--language")||e.includes("--lang")||e.includes("-l")){let t=e.indexOf("--language")!==-1?e.indexOf("--language"):e.indexOf("--lang")!==-1?e.indexOf("--lang"):e.indexOf("-l"),r=e[t+1];if(!r||r.startsWith("-"))return"prompt"}let n=[()=>this.findFlagWithEquals(e,"--language="),()=>this.findFlagWithEquals(e,"--lang="),()=>this.findFlagWithEquals(e,"-l="),()=>this.findFlagWithValue(e,"--language"),()=>this.findFlagWithValue(e,"--lang"),()=>this.findFlagWithValue(e,"-l")];for(let t of n){let r=t();if(r)return r}return null}static parseBackendUrl(e){let n=[()=>this.findFlagWithEquals(e,"--backend="),()=>this.findFlagWithEquals(e,"-b="),()=>this.findFlagWithValue(e,"--backend"),()=>this.findFlagWithValue(e,"-b")];for(let t of n){let r=t();if(r)return r}return null}static parseSetBackend(e){let n=[()=>this.findFlagWithEquals(e,"--set-backend="),()=>this.findFlagWithValue(e,"--set-backend")];for(let t of n){let r=t();if(r)return r}return e.includes("--set-backend")?"clear":null}static findFlagWithEquals(e,n){let t=e.find(r=>r.startsWith(n));return t?t.split("=")[1]:null}static findFlagWithValue(e,n){let t=e.indexOf(n);return t!==-1&&e[t+1]?e[t+1]:null}static generateRandomSubdomain(){return`${u.SUBDOMAIN_PREFIX}${Math.floor(Math.random()*1e4)}`}};import nn from"ora";import J from"chalk";import tn from"fs";var M=class{static{i(this,"TunnelState")}tunnelId=null;subdomain=null;port=null;backendUrl=null;tunnelProcess=null;timeoutId=null;connectionCount=0;startTime=null;updateInfo=null;networkIssueCount=0;lastNetworkWarningTime=0;networkWarningShown=!1;setTunnel(e,n,t,r=null){this.tunnelId=e,this.subdomain=n,this.port=t,this.backendUrl=r,this.startTime||(this.startTime=Date.now())}setUpdateInfo(e){this.updateInfo=e}setProcess(e){this.tunnelProcess=e}setTimeout(e){this.timeoutId=e}clearTimeout(){this.timeoutId&&(clearTimeout(this.timeoutId),this.timeoutId=null)}incrementConnection(){return this.connectionCount++,this.connectionCount}hasTunnel(){return this.tunnelId!==null}hasProcess(){return this.tunnelProcess!==null&&!this.tunnelProcess.killed}getDurationSeconds(){return this.startTime?(Date.now()-this.startTime)/1e3:0}incrementNetworkIssue(){return this.networkIssueCount++,this.networkIssueCount}resetNetworkIssues(){this.networkIssueCount=0}shouldShowNetworkWarning(e,n){let t=Date.now();return this.networkIssueCount>=e&&t-this.lastNetworkWarningTime>n?(this.lastNetworkWarningTime=t,!0):!1}setNetworkWarningShown(e){this.networkWarningShown=e}reset(){this.clearTimeout(),this.tunnelId=null,this.subdomain=null,this.port=null,this.backendUrl=null,this.tunnelProcess=null,this.connectionCount=0,this.startTime=null,this.updateInfo=null,this.networkIssueCount=0,this.lastNetworkWarningTime=0,this.networkWarningShown=!1}},l=new M;import{spawn as Se}from"child_process";import p from"chalk";import Ue from"fs";import a from"chalk";import Ne from"readline";import T from"fs";import G from"path";import ve from"os";var V=class{static{i(this,"ConfigManager")}configDir;configFile;oldLangFile;config;constructor(){this.configDir=G.join(ve.homedir(),".nport"),this.configFile=G.join(this.configDir,"config.json"),this.oldLangFile=G.join(this.configDir,"lang"),this.config=this.loadConfig(),this.migrateOldConfig()}loadConfig(){try{if(T.existsSync(this.configFile)){let e=T.readFileSync(this.configFile,"utf8");return JSON.parse(e)}}catch{console.warn("Warning: Could not load config file, using defaults")}return{}}migrateOldConfig(){try{if(!this.config.language&&T.existsSync(this.oldLangFile)){let e=T.readFileSync(this.oldLangFile,"utf8").trim();if(e&&["en","vi"].includes(e)){this.config.language=e,this.saveConfig();try{T.unlinkSync(this.oldLangFile)}catch{}}}}catch{}}saveConfig(){try{return T.existsSync(this.configDir)||T.mkdirSync(this.configDir,{recursive:!0}),T.writeFileSync(this.configFile,JSON.stringify(this.config,null,2),"utf8"),!0}catch{return console.warn("Warning: Could not save configuration"),!1}}getBackendUrl(){return this.config.backendUrl??null}setBackendUrl(e){return e?this.config.backendUrl=e:delete this.config.backendUrl,this.saveConfig()}getLanguage(){return this.config.language??null}setLanguage(e){return e?this.config.language=e:delete this.config.language,this.saveConfig()}getAll(){return{...this.config}}clear(){return this.config={},this.saveConfig()}},I=new V;var H={en:{header:"N P O R T \u26A1\uFE0F Free & Open Source from Vietnam \u2764\uFE0F",creatingTunnel:"Creating tunnel for port {port}...",checkingUpdates:"Checking for updates...",tunnelLive:"\u{1F680} WE LIVE BABY!",connection1:" \u2714 [1/2] Connection established...",connection2:" \u2714 [2/2] Compression enabled...",timeRemaining:"\u23F1\uFE0F Time: {hours}h remaining",footerTitle:"\u{1F525} KEEP THE VIBE ALIVE?",footerSubtitle:"(Made with \u2764\uFE0F in Vietnam)",dropStar:"\u2B50\uFE0F Drop a Star: ",sendCoffee:"\u2615\uFE0F Buy Coffee: ",newVersion:"\u{1F6A8} NEW VERSION (v{version}) detected!",updateCommand:"> npm install -g nport@latest",tunnelShutdown:"\u{1F6D1} TUNNEL SHUTDOWN.",cleaningUp:"Cleaning up... ",cleanupDone:"Done.",cleanupFailed:"Failed.",subdomainReleased:"Subdomain... Released. \u{1F5D1}\uFE0F",serverBusy:"(Server might be down or busy)",goodbyeTitle:"\u{1F44B} BEFORE YOU GO...",goodbyeMessage:"Thanks for using NPort!",website:"\u{1F310} Website: ",author:"\u{1F464} Author: ",changeLanguage:"\u{1F30D} Language: ",changeLanguageHint:"nport --language",versionTitle:"NPort v{version}",versionSubtitle:"Free & open source ngrok alternative",versionLatest:"\u2714 You're running the latest version!",versionAvailable:"\u{1F6A8} New version available: v{version}",versionUpdate:"Update now: ",learnMore:"Learn more: ",languagePrompt:`
2
+ var he=Object.defineProperty;var i=(o,e)=>he(o,"name",{value:e,configurable:!0});import on from"ora";import b from"chalk";import P from"path";import{fileURLToPath as we}from"url";import{createRequire as Ie}from"module";var Q="https://api.nport.link";var X="user-";var $={THRESHOLD:5,COOLDOWN:3e4},Z={SUCCESS:["Registered tunnel connection"],ERROR:["ERR","error"],NETWORK_WARNING:["failed to accept QUIC stream","failed to dial to edge with quic","failed to accept incoming stream requests","Failed to dial a quic connection","timeout: no recent network activity","failed to dial to edge","quic:"],IGNORE:["Cannot determine default origin certificate path","No file cert.pem","origincert option","TUNNEL_ORIGIN_CERT","context canceled","failed to run the datagram handler","failed to serve tunnel connection","Connection terminated","no more connections active and exiting","Serve tunnel error","accept stream listener encountered a failure","Retrying connection","icmp router terminated","use of closed network connection","Application error 0x0"]},ee=["en","vi"],ne="https://github.com/tuanngocptn/nport",W="https://nport.link",te="https://buymeacoffee.com/tuanngocptn";var Ee=we(import.meta.url),oe=P.dirname(P.dirname(Ee)),be=Ie(import.meta.url),re=be("../package.json");function ke(){return process.env.NPORT_BACKEND_URL?process.env.NPORT_BACKEND_URL:Q}i(ke,"getBackendUrl");var u={PACKAGE_NAME:re.name,CURRENT_VERSION:re.version,BACKEND_URL:ke(),DEFAULT_PORT:8080,SUBDOMAIN_PREFIX:X,TUNNEL_TIMEOUT_HOURS:4,UPDATE_CHECK_TIMEOUT:3e3},Ce={IS_WINDOWS:process.platform==="win32",BIN_NAME:process.platform==="win32"?"cloudflared.exe":"cloudflared"},x={BIN_DIR:P.join(oe,"bin"),BIN_PATH:P.join(oe,"bin",Ce.BIN_NAME)},U=Z,B={WARNING_THRESHOLD:$.THRESHOLD,WARNING_COOLDOWN:$.COOLDOWN},se=u.TUNNEL_TIMEOUT_HOURS*60*60*1e3;var O=class{static{i(this,"ArgumentParser")}static parse(e){let n=this.parsePort(e),t=this.parseSubdomain(e),r=this.parseLanguage(e),c=this.parseBackendUrl(e),d=this.parseSetBackend(e);return{port:n,subdomain:t,language:r,backendUrl:c,setBackend:d}}static parsePort(e){return parseInt(e[0],10)||u.DEFAULT_PORT}static parseSubdomain(e){let n=[()=>this.findFlagWithEquals(e,"--subdomain="),()=>this.findFlagWithEquals(e,"-s="),()=>this.findFlagWithValue(e,"--subdomain"),()=>this.findFlagWithValue(e,"-s")];for(let t of n){let r=t();if(r)return r}return this.generateRandomSubdomain()}static parseLanguage(e){if(e.includes("--language")||e.includes("--lang")||e.includes("-l")){let t=e.indexOf("--language")!==-1?e.indexOf("--language"):e.indexOf("--lang")!==-1?e.indexOf("--lang"):e.indexOf("-l"),r=e[t+1];if(!r||r.startsWith("-"))return"prompt"}let n=[()=>this.findFlagWithEquals(e,"--language="),()=>this.findFlagWithEquals(e,"--lang="),()=>this.findFlagWithEquals(e,"-l="),()=>this.findFlagWithValue(e,"--language"),()=>this.findFlagWithValue(e,"--lang"),()=>this.findFlagWithValue(e,"-l")];for(let t of n){let r=t();if(r)return r}return null}static parseBackendUrl(e){let n=[()=>this.findFlagWithEquals(e,"--backend="),()=>this.findFlagWithEquals(e,"-b="),()=>this.findFlagWithValue(e,"--backend"),()=>this.findFlagWithValue(e,"-b")];for(let t of n){let r=t();if(r)return r}return null}static parseSetBackend(e){let n=[()=>this.findFlagWithEquals(e,"--set-backend="),()=>this.findFlagWithValue(e,"--set-backend")];for(let t of n){let r=t();if(r)return r}return e.includes("--set-backend")?"clear":null}static findFlagWithEquals(e,n){let t=e.find(r=>r.startsWith(n));return t?t.split("=")[1]:null}static findFlagWithValue(e,n){let t=e.indexOf(n);return t!==-1&&e[t+1]?e[t+1]:null}static generateRandomSubdomain(){return`${u.SUBDOMAIN_PREFIX}${Math.floor(Math.random()*1e4)}`}};import nn from"ora";import J from"chalk";import tn from"fs";var M=class{static{i(this,"TunnelState")}tunnelId=null;subdomain=null;port=null;backendUrl=null;tunnelProcess=null;timeoutId=null;connectionCount=0;startTime=null;updateInfo=null;networkIssueCount=0;lastNetworkWarningTime=0;networkWarningShown=!1;setTunnel(e,n,t,r=null){this.tunnelId=e,this.subdomain=n,this.port=t,this.backendUrl=r,this.startTime||(this.startTime=Date.now())}setUpdateInfo(e){this.updateInfo=e}setProcess(e){this.tunnelProcess=e}setTimeout(e){this.timeoutId=e}clearTimeout(){this.timeoutId&&(clearTimeout(this.timeoutId),this.timeoutId=null)}incrementConnection(){return this.connectionCount++,this.connectionCount}hasTunnel(){return this.tunnelId!==null}hasProcess(){return this.tunnelProcess!==null&&!this.tunnelProcess.killed}getDurationSeconds(){return this.startTime?(Date.now()-this.startTime)/1e3:0}incrementNetworkIssue(){return this.networkIssueCount++,this.networkIssueCount}resetNetworkIssues(){this.networkIssueCount=0}shouldShowNetworkWarning(e,n){let t=Date.now();return this.networkIssueCount>=e&&t-this.lastNetworkWarningTime>n?(this.lastNetworkWarningTime=t,!0):!1}setNetworkWarningShown(e){this.networkWarningShown=e}reset(){this.clearTimeout(),this.tunnelId=null,this.subdomain=null,this.port=null,this.backendUrl=null,this.tunnelProcess=null,this.connectionCount=0,this.startTime=null,this.updateInfo=null,this.networkIssueCount=0,this.lastNetworkWarningTime=0,this.networkWarningShown=!1}},l=new M;import{spawn as Se}from"child_process";import g from"chalk";import Ue from"fs";import a from"chalk";import Ne from"readline";import T from"fs";import G from"path";import ve from"os";var V=class{static{i(this,"ConfigManager")}configDir;configFile;oldLangFile;config;constructor(){this.configDir=G.join(ve.homedir(),".nport"),this.configFile=G.join(this.configDir,"config.json"),this.oldLangFile=G.join(this.configDir,"lang"),this.config=this.loadConfig(),this.migrateOldConfig()}loadConfig(){try{if(T.existsSync(this.configFile)){let e=T.readFileSync(this.configFile,"utf8");return JSON.parse(e)}}catch{console.warn("Warning: Could not load config file, using defaults")}return{}}migrateOldConfig(){try{if(!this.config.language&&T.existsSync(this.oldLangFile)){let e=T.readFileSync(this.oldLangFile,"utf8").trim();if(e&&["en","vi"].includes(e)){this.config.language=e,this.saveConfig();try{T.unlinkSync(this.oldLangFile)}catch{}}}}catch{}}saveConfig(){try{return T.existsSync(this.configDir)||T.mkdirSync(this.configDir,{recursive:!0}),T.writeFileSync(this.configFile,JSON.stringify(this.config,null,2),"utf8"),!0}catch{return console.warn("Warning: Could not save configuration"),!1}}getBackendUrl(){return this.config.backendUrl??null}setBackendUrl(e){return e?this.config.backendUrl=e:delete this.config.backendUrl,this.saveConfig()}getLanguage(){return this.config.language??null}setLanguage(e){return e?this.config.language=e:delete this.config.language,this.saveConfig()}getAll(){return{...this.config}}clear(){return this.config={},this.saveConfig()}},w=new V;var H={en:{header:"N P O R T \u26A1\uFE0F Free & Open Source from Vietnam \u2764\uFE0F",creatingTunnel:"Creating tunnel for port {port}...",checkingUpdates:"Checking for updates...",tunnelLive:"\u{1F680} WE LIVE BABY!",connection1:" \u2714 [1/2] Connection established...",connection2:" \u2714 [2/2] Compression enabled...",timeRemaining:"\u23F1\uFE0F Time: {hours}h remaining",footerTitle:"\u{1F525} KEEP THE VIBE ALIVE?",footerSubtitle:"(Made with \u2764\uFE0F in Vietnam)",dropStar:"\u2B50\uFE0F Drop a Star: ",sendCoffee:"\u2615\uFE0F Buy Coffee: ",newVersion:"\u{1F6A8} NEW VERSION (v{version}) detected!",updateCommand:"> npm install -g nport@latest",tunnelShutdown:"\u{1F6D1} TUNNEL SHUTDOWN.",cleaningUp:"Cleaning up... ",cleanupDone:"Done.",cleanupFailed:"Failed.",subdomainReleased:"Subdomain... Released. \u{1F5D1}\uFE0F",serverBusy:"(Server might be down or busy)",goodbyeTitle:"\u{1F44B} BEFORE YOU GO...",goodbyeMessage:"Thanks for using NPort!",website:"\u{1F310} Website: ",author:"\u{1F464} Author: ",changeLanguage:"\u{1F30D} Language: ",changeLanguageHint:"nport --language",versionTitle:"NPort v{version}",versionSubtitle:"Free & open source ngrok alternative",versionLatest:"\u2714 You're running the latest version!",versionAvailable:"\u{1F6A8} New version available: v{version}",versionUpdate:"Update now: ",learnMore:"Learn more: ",languagePrompt:`
3
3
  \u{1F30D} Language Selection / Ch\u1ECDn ng\xF4n ng\u1EEF
4
4
  `,languageQuestion:"Choose your language (1-2): ",languageEnglish:"1. English",languageVietnamese:"2. Ti\u1EBFng Vi\u1EC7t (Vietnamese)",languageInvalid:"Invalid choice. Using English by default.",languageSaved:"\u2714 Language preference saved!",networkIssueTitle:`
5
5
  \u26A0\uFE0F NETWORK CONNECTIVITY ISSUE DETECTED`,networkIssueDesc:" Cloudflared is having trouble maintaining a stable connection to Cloudflare's edge servers.",networkIssueTunnel:" \u{1F4E1} Your tunnel is still working, but connection quality may be affected.",networkIssueReasons:`
@@ -13,7 +13,7 @@ var he=Object.defineProperty;var i=(o,e)=>he(o,"name",{value:e,configurable:!0})
13
13
  \u{1F4A1} C\xF3 th\u1EC3 do:`,networkIssueReason1:" \u2022 M\u1EA1ng internet kh\xF4ng \u1ED5n \u0111\u1ECBnh ho\u1EB7c m\u1EA5t g\xF3i tin",networkIssueReason2:" \u2022 Firewall/Router ch\u1EB7n UDP traffic (giao th\u1EE9c QUIC)",networkIssueReason3:" \u2022 Nh\xE0 m\u1EA1ng throttle ho\u1EB7c t\u1EAFc ngh\u1EBDn m\u1EA1ng",networkIssueFix:`
14
14
  \u{1F527} Th\u1EED c\xE1c c\xE1ch sau:`,networkIssueFix1:" \u2022 Ki\u1EC3m tra k\u1EBFt n\u1ED1i internet c\u1EE7a b\u1EA1n",networkIssueFix2:" \u2022 Th\u1EED \u0111\u1ED5i sang m\u1EA1ng kh\xE1c (v\xED d\u1EE5: 4G/5G)",networkIssueFix3:" \u2022 T\u1EAFt VPN/Proxy n\u1EBFu \u0111ang b\u1EADt",networkIssueFix4:" \u2022 Tunnel s\u1EBD t\u1EF1 \u0111\u1ED9ng chuy\u1EC3n sang HTTP/2 n\u1EBFu QUIC fail",networkIssueIgnore:`
15
15
  \u2139\uFE0F L\u1ED7i n\xE0y th\u01B0\u1EDDng kh\xF4ng nghi\xEAm tr\u1ECDng - tunnel v\u1EABn ho\u1EA1t \u0111\u1ED9ng b\xECnh th\u01B0\u1EDDng.
16
- `}},j=class{static{i(this,"LanguageManager")}currentLanguage="en";availableLanguages=ee;t(e,n={}){let r=(H[this.currentLanguage]||H.en)[e]||H.en[e]||e;return Object.keys(n).forEach(c=>{let g=n[c];g!==void 0&&(r=r.replace(`{${c}}`,String(g)))}),r}loadLanguagePreference(){let e=I.getLanguage();return e&&this.availableLanguages.includes(e)?e:null}saveLanguagePreference(e){I.setLanguage(e)}setLanguage(e){return this.availableLanguages.includes(e)?(this.currentLanguage=e,!0):!1}getLanguage(){return this.currentLanguage}async promptLanguageSelection(){return new Promise(e=>{let n=Ne.createInterface({input:process.stdin,output:process.stdout});console.log(this.t("languagePrompt")),console.log(` ${this.t("languageEnglish")}`),console.log(` ${this.t("languageVietnamese")}
16
+ `}},j=class{static{i(this,"LanguageManager")}currentLanguage="en";availableLanguages=ee;t(e,n={}){let r=(H[this.currentLanguage]||H.en)[e]||H.en[e]||e;return Object.keys(n).forEach(c=>{let d=n[c];d!==void 0&&(r=r.replace(`{${c}}`,String(d)))}),r}loadLanguagePreference(){let e=w.getLanguage();return e&&this.availableLanguages.includes(e)?e:null}saveLanguagePreference(e){w.setLanguage(e)}setLanguage(e){return this.availableLanguages.includes(e)?(this.currentLanguage=e,!0):!1}getLanguage(){return this.currentLanguage}async promptLanguageSelection(){return new Promise(e=>{let n=Ne.createInterface({input:process.stdin,output:process.stdout});console.log(this.t("languagePrompt")),console.log(` ${this.t("languageEnglish")}`),console.log(` ${this.t("languageVietnamese")}
17
17
  `),n.question(`${this.t("languageQuestion")}`,t=>{n.close();let r=t.trim(),c="en";r==="1"?c="en":r==="2"?c="vi":console.log(`
18
18
  ${this.t("languageInvalid")}
19
19
  `),this.setLanguage(c),this.saveLanguagePreference(c),console.log(`${this.t("languageSaved")}
@@ -35,35 +35,36 @@ ${this.t("languageInvalid")}
35
35
  `)),this.displayGoodbye()}static displayGoodbye(){console.log(a.gray(" "+"\u2500".repeat(54)+`
36
36
  `)),console.log(a.cyan.bold(` ${s.t("goodbyeTitle")}
37
37
  `)),console.log(a.gray(` ${s.t("goodbyeMessage")}
38
- `)),console.log(a.cyan(` ${s.t("website")}`)+a.white(B)),console.log(a.cyan(` ${s.t("author")}`)+a.white("Nick Pham (https://github.com/tuanngocptn)")),console.log(a.cyan(` ${s.t("changeLanguage")}`)+a.yellow(s.t("changeLanguageHint"))),console.log()}static displayVersion(e,n){console.log(a.cyan.bold(`
38
+ `)),console.log(a.cyan(` ${s.t("website")}`)+a.white(W)),console.log(a.cyan(` ${s.t("author")}`)+a.white("Nick Pham (https://github.com/tuanngocptn)")),console.log(a.cyan(` ${s.t("changeLanguage")}`)+a.yellow(s.t("changeLanguageHint"))),console.log()}static displayVersion(e,n){console.log(a.cyan.bold(`
39
39
  ${s.t("versionTitle",{version:e})}`)),console.log(a.gray(`${s.t("versionSubtitle")}
40
40
  `)),n?.shouldUpdate?(console.log(a.yellow(s.t("versionAvailable",{version:n.latest}))),console.log(a.cyan(s.t("versionUpdate"))+a.white(`npm install -g nport@latest
41
41
  `))):console.log(a.green(`${s.t("versionLatest")}
42
- `)),console.log(a.gray(s.t("learnMore"))+a.cyan(`${B}
43
- `))}};var v=class{static{i(this,"BinaryManager")}static validate(e){return Ue.existsSync(e)?!0:(console.error(p.red(`
44
- \u274C Error: Cloudflared binary not found at: ${e}`)),console.error(p.yellow(`\u{1F449} Please run 'npm install' again to download the binary.
45
- `)),!1)}static spawn(e,n,t){return Se(e,["tunnel","run","--token",n,"--url",`http://localhost:${t}`])}static attachHandlers(e,n=null){e.stderr?.on("data",t=>this.handleStderr(t)),e.on("error",t=>this.handleError(t,n)),e.on("close",t=>this.handleClose(t))}static handleStderr(e){let n=e.toString();if(!U.IGNORE.some(t=>n.includes(t))){if(U.NETWORK_WARNING.some(t=>n.includes(t))){this.handleNetworkWarning();return}if(U.SUCCESS.some(t=>n.includes(t))){let t=l.incrementConnection();t===1?(l.resetNetworkIssues(),console.log(p.green(s.t("connection1")))):t===4&&(console.log(p.green(s.t("connection2"))),m.displayFooter(l.updateInfo));return}U.ERROR.some(t=>n.includes(t))&&console.error(p.red(`[Cloudflared] ${n.trim()}`))}}static handleNetworkWarning(){l.incrementNetworkIssue(),l.shouldShowNetworkWarning(W.WARNING_THRESHOLD,W.WARNING_COOLDOWN)&&this.displayNetworkWarning()}static displayNetworkWarning(){console.log(p.yellow(s.t("networkIssueTitle"))),console.log(p.gray(s.t("networkIssueDesc"))),console.log(p.cyan(s.t("networkIssueTunnel"))),console.log(p.yellow(s.t("networkIssueReasons"))),console.log(p.gray(s.t("networkIssueReason1"))),console.log(p.gray(s.t("networkIssueReason2"))),console.log(p.gray(s.t("networkIssueReason3"))),console.log(p.yellow(s.t("networkIssueFix"))),console.log(p.gray(s.t("networkIssueFix1"))),console.log(p.gray(s.t("networkIssueFix2"))),console.log(p.gray(s.t("networkIssueFix3"))),console.log(p.gray(s.t("networkIssueFix4"))),console.log(p.blue(s.t("networkIssueIgnore")))}static handleError(e,n){n&&n.fail("Failed to spawn cloudflared process."),console.error(p.red(`Process Error: ${e.message}`))}static handleClose(e){e!==0&&e!==null&&console.log(p.red(`Tunnel process exited with code ${e}`))}};import E from"fs";import R from"path";import Re from"https";import le from"os";import{execSync as _e}from"child_process";import{fileURLToPath as ce}from"url";var Le=ce(import.meta.url),Ae=R.dirname(R.dirname(Le)),D=R.join(Ae,"bin"),ie="cloudflared",Pe=".tgz",xe="cloudflared.tgz",F=le.platform(),ae=le.arch(),ue=F==="win32",Oe=ue?`${ie}.exe`:ie,w=R.join(D,Oe),Fe="https://github.com/cloudflare/cloudflared/releases/latest/download",De=[301,302],$e=200,Be="755",We={darwin:{x64:"cloudflared-darwin-amd64.tgz",arm64:"cloudflared-darwin-arm64.tgz"},win32:{x64:"cloudflared-windows-amd64.exe",ia32:"cloudflared-windows-386.exe"},linux:{x64:"cloudflared-linux-amd64",arm64:"cloudflared-linux-arm64",arm:"cloudflared-linux-arm"}};function Me(o){return{x64:"x64",amd64:"amd64",arm64:"arm64",ia32:"ia32",arm:"arm"}[o]||o}i(Me,"normalizeArch");function Ge(){let o=Me(ae),e=We[F];if(!e)throw new Error(`Unsupported platform: ${F}. Supported platforms: darwin, win32, linux`);let n=e[o];if(!n)throw new Error(`Unsupported architecture: ${ae} for platform ${F}. Supported architectures: ${Object.keys(e).join(", ")}`);return`${Fe}/${n}`}i(Ge,"getDownloadUrl");function Ve(o){return o.endsWith(Pe)}i(Ve,"isCompressedArchive");function He(o){E.existsSync(o)||E.mkdirSync(o,{recursive:!0})}i(He,"ensureDirectory");function C(o){try{E.existsSync(o)&&E.unlinkSync(o)}catch{}}i(C,"safeUnlink");function ge(o,e=Be){ue||E.chmodSync(o,e)}i(ge,"setExecutablePermissions");function je(o,e){if(!E.existsSync(o))throw new Error(e||`File not found: ${o}`)}i(je,"validateFileExists");async function de(o,e){return new Promise((n,t)=>{let r=E.createWriteStream(e);Re.get(o,c=>{if(De.includes(c.statusCode)){r.close(),C(e),de(c.headers.location,e).then(n).catch(t);return}if(c.statusCode!==$e){r.close(),C(e),t(new Error(`Download failed with status code ${c.statusCode} from ${o}`));return}c.pipe(r),r.on("finish",()=>{r.close(()=>n(e))}),r.on("error",g=>{r.close(),C(e),t(g)})}).on("error",c=>{r.close(),C(e),t(new Error(`Network error: ${c.message}`))})})}i(de,"downloadFile");function Ke(o,e){try{_e(`tar -xzf "${o}" -C "${e}"`,{stdio:"pipe"})}catch(n){throw new Error(`Extraction failed: ${n.message}`)}}i(Ke,"extractTarGz");var k={info:i(o=>console.log(`\u2139\uFE0F ${o}`),"info"),success:i(o=>console.log(`\u2705 ${o}`),"success"),warn:i(o=>console.warn(`\u26A0\uFE0F ${o}`),"warn"),error:i(o=>console.error(`\u274C ${o}`),"error"),progress:i(o=>console.log(`\u{1F6A7} ${o}`),"progress"),extract:i(o=>console.log(`\u{1F4E6} ${o}`),"extract")};async function qe(){k.progress("Cloudflared binary not found. Downloading... (This happens only once)");let o=Ge(),e=Ve(o),n=e?R.join(D,xe):w;try{return await de(o,n),e&&(k.extract("Extracting binary..."),Ke(n,D),C(n),je(w,"Extraction failed: Binary not found after extraction")),ge(w),k.success("Download complete."),w}catch(t){throw C(n),C(w),t}}i(qe,"installBinary");async function K(){if(He(D),E.existsSync(w))return ge(w),w;try{return await qe()}catch(o){k.error(`Installation failed: ${o.message}`),process.exit(1)}}i(K,"ensureCloudflared");function ze(){return!!(process.env.CI||process.env.GITHUB_ACTIONS||process.env.GITLAB_CI||process.env.CIRCLECI||process.env.TRAVIS||process.env.JENKINS_URL||process.env.BUILDKITE)}i(ze,"isCI");async function Je(){if(ze()){k.info("Running in CI environment - skipping binary download");return}try{let o=await K();k.success(`Cloudflared binary is ready at: ${o}`)}catch(o){k.error(o.message),process.exit(1)}}i(Je,"main");var Ye=ce(import.meta.url);(process.argv[1]===Ye||process.argv[1]?.endsWith("bin-manager.js"))&&Je();import pe from"axios";import d from"chalk";var _=class{static{i(this,"APIClient")}static async createTunnel(e,n=null){let t=n||u.BACKEND_URL;try{let{data:r}=await pe.post(t,{subdomain:e});if(!r.success)throw new Error(r.error||"Unknown error from backend");return{tunnelId:r.tunnelId,tunnelToken:r.tunnelToken,url:r.url}}catch(r){throw this.handleError(r,e)}}static async deleteTunnel(e,n,t=null){let r=t||u.BACKEND_URL;await pe.delete(r,{data:{subdomain:e,tunnelId:n}})}static handleError(e,n){let t=e.response?.data?.error;return t?t.includes("SUBDOMAIN_PROTECTED:")?new Error(`Subdomain "${n}" is already taken or in use.
42
+ `)),console.log(a.gray(s.t("learnMore"))+a.cyan(`${W}
43
+ `))}};var v=class{static{i(this,"BinaryManager")}static validate(e){return Ue.existsSync(e)?!0:(console.error(g.red(`
44
+ \u274C Error: Cloudflared binary not found at: ${e}`)),console.error(g.yellow(`\u{1F449} Please run 'npm install' again to download the binary.
45
+ `)),!1)}static spawn(e,n,t){let r=process.platform==="win32";return Se(e,["tunnel","run","--token",n,"--url",`http://localhost:${t}`],{windowsHide:!0,shell:r,stdio:["ignore","pipe","pipe"]})}static attachHandlers(e,n=null){e.stderr?.on("data",t=>this.handleStderr(t)),e.on("error",t=>this.handleError(t,n)),e.on("close",t=>this.handleClose(t))}static handleStderr(e){let n=e.toString();if(!U.IGNORE.some(t=>n.includes(t))){if(U.NETWORK_WARNING.some(t=>n.includes(t))){this.handleNetworkWarning();return}if(U.SUCCESS.some(t=>n.includes(t))){let t=l.incrementConnection();t===1?(l.resetNetworkIssues(),console.log(g.green(s.t("connection1")))):t===4&&(console.log(g.green(s.t("connection2"))),m.displayFooter(l.updateInfo));return}U.ERROR.some(t=>n.includes(t))&&console.error(g.red(`[Cloudflared] ${n.trim()}`))}}static handleNetworkWarning(){l.incrementNetworkIssue(),l.shouldShowNetworkWarning(B.WARNING_THRESHOLD,B.WARNING_COOLDOWN)&&this.displayNetworkWarning()}static displayNetworkWarning(){console.log(g.yellow(s.t("networkIssueTitle"))),console.log(g.gray(s.t("networkIssueDesc"))),console.log(g.cyan(s.t("networkIssueTunnel"))),console.log(g.yellow(s.t("networkIssueReasons"))),console.log(g.gray(s.t("networkIssueReason1"))),console.log(g.gray(s.t("networkIssueReason2"))),console.log(g.gray(s.t("networkIssueReason3"))),console.log(g.yellow(s.t("networkIssueFix"))),console.log(g.gray(s.t("networkIssueFix1"))),console.log(g.gray(s.t("networkIssueFix2"))),console.log(g.gray(s.t("networkIssueFix3"))),console.log(g.gray(s.t("networkIssueFix4"))),console.log(g.blue(s.t("networkIssueIgnore")))}static handleError(e,n){n&&n.fail("Failed to spawn cloudflared process."),console.error(g.red(`Process Error: ${e.message}`)),process.platform==="win32"&&(e.message.includes("UNKNOWN")||e.message.includes("ENOENT"))&&(console.error(g.yellow(`
46
+ \u{1F4A1} Windows troubleshooting tips:`)),console.error(g.gray(" 1. Check if Windows Defender/antivirus is blocking cloudflared.exe")),console.error(g.gray(" 2. Try running the terminal as Administrator")),console.error(g.gray(" 3. Reinstall nport: npm uninstall -g nport && npm install -g nport")))}static handleClose(e){e!==0&&e!==null&&console.log(g.red(`Tunnel process exited with code ${e}`))}};import E from"fs";import R from"path";import Re from"https";import le from"os";import{execSync as _e}from"child_process";import{fileURLToPath as ce}from"url";var Ae=ce(import.meta.url),Le=R.dirname(R.dirname(Ae)),D=R.join(Le,"bin"),ie="cloudflared",Pe=".tgz",xe="cloudflared.tgz",F=le.platform(),ae=le.arch(),ue=F==="win32",Oe=ue?`${ie}.exe`:ie,I=R.join(D,Oe),Fe="https://github.com/cloudflare/cloudflared/releases/latest/download",De=[301,302],$e=200,We="755",Be={darwin:{x64:"cloudflared-darwin-amd64.tgz",arm64:"cloudflared-darwin-arm64.tgz"},win32:{x64:"cloudflared-windows-amd64.exe",ia32:"cloudflared-windows-386.exe"},linux:{x64:"cloudflared-linux-amd64",arm64:"cloudflared-linux-arm64",arm:"cloudflared-linux-arm"}};function Me(o){return{x64:"x64",amd64:"amd64",arm64:"arm64",ia32:"ia32",arm:"arm"}[o]||o}i(Me,"normalizeArch");function Ge(){let o=Me(ae),e=Be[F];if(!e)throw new Error(`Unsupported platform: ${F}. Supported platforms: darwin, win32, linux`);let n=e[o];if(!n)throw new Error(`Unsupported architecture: ${ae} for platform ${F}. Supported architectures: ${Object.keys(e).join(", ")}`);return`${Fe}/${n}`}i(Ge,"getDownloadUrl");function Ve(o){return o.endsWith(Pe)}i(Ve,"isCompressedArchive");function He(o){E.existsSync(o)||E.mkdirSync(o,{recursive:!0})}i(He,"ensureDirectory");function k(o){try{E.existsSync(o)&&E.unlinkSync(o)}catch{}}i(k,"safeUnlink");function ge(o,e=We){ue||E.chmodSync(o,e)}i(ge,"setExecutablePermissions");function je(o,e){if(!E.existsSync(o))throw new Error(e||`File not found: ${o}`)}i(je,"validateFileExists");async function de(o,e){return new Promise((n,t)=>{let r=E.createWriteStream(e);Re.get(o,c=>{if(De.includes(c.statusCode)){r.close(),k(e),de(c.headers.location,e).then(n).catch(t);return}if(c.statusCode!==$e){r.close(),k(e),t(new Error(`Download failed with status code ${c.statusCode} from ${o}`));return}c.pipe(r),r.on("finish",()=>{r.close(()=>n(e))}),r.on("error",d=>{r.close(),k(e),t(d)})}).on("error",c=>{r.close(),k(e),t(new Error(`Network error: ${c.message}`))})})}i(de,"downloadFile");function Ke(o,e){try{_e(`tar -xzf "${o}" -C "${e}"`,{stdio:"pipe"})}catch(n){throw new Error(`Extraction failed: ${n.message}`)}}i(Ke,"extractTarGz");var C={info:i(o=>console.log(`\u2139\uFE0F ${o}`),"info"),success:i(o=>console.log(`\u2705 ${o}`),"success"),warn:i(o=>console.warn(`\u26A0\uFE0F ${o}`),"warn"),error:i(o=>console.error(`\u274C ${o}`),"error"),progress:i(o=>console.log(`\u{1F6A7} ${o}`),"progress"),extract:i(o=>console.log(`\u{1F4E6} ${o}`),"extract")};async function qe(){C.progress("Cloudflared binary not found. Downloading... (This happens only once)");let o=Ge(),e=Ve(o),n=e?R.join(D,xe):I;try{return await de(o,n),e&&(C.extract("Extracting binary..."),Ke(n,D),k(n),je(I,"Extraction failed: Binary not found after extraction")),ge(I),C.success("Download complete."),I}catch(t){throw k(n),k(I),t}}i(qe,"installBinary");async function K(){if(He(D),E.existsSync(I))return ge(I),I;try{return await qe()}catch(o){C.error(`Installation failed: ${o.message}`),process.exit(1)}}i(K,"ensureCloudflared");function ze(){return!!(process.env.CI||process.env.GITHUB_ACTIONS||process.env.GITLAB_CI||process.env.CIRCLECI||process.env.TRAVIS||process.env.JENKINS_URL||process.env.BUILDKITE)}i(ze,"isCI");async function Je(){if(ze()){C.info("Running in CI environment - skipping binary download");return}try{let o=await K();C.success(`Cloudflared binary is ready at: ${o}`)}catch(o){C.error(o.message),process.exit(1)}}i(Je,"main");var Ye=ce(import.meta.url);(process.argv[1]===Ye||process.argv[1]?.endsWith("bin-manager.js"))&&Je();import pe from"axios";import p from"chalk";var _=class{static{i(this,"APIClient")}static async createTunnel(e,n=null){let t=n||u.BACKEND_URL;try{let{data:r}=await pe.post(t,{subdomain:e});if(!r.success)throw new Error(r.error||"Unknown error from backend");return{tunnelId:r.tunnelId,tunnelToken:r.tunnelToken,url:r.url}}catch(r){throw this.handleError(r,e)}}static async deleteTunnel(e,n,t=null){let r=t||u.BACKEND_URL;await pe.delete(r,{data:{subdomain:e,tunnelId:n}})}static handleError(e,n){let t=e.response?.data?.error;return t?t.includes("SUBDOMAIN_PROTECTED:")?new Error(`Subdomain "${n}" is already taken or in use.
46
47
 
47
- `+d.yellow(`\u{1F4A1} Try one of these options:
48
- `)+d.gray(" 1. Choose a different subdomain: ")+d.cyan(`nport ${l.port||u.DEFAULT_PORT} -s ${n}-v2
49
- `)+d.gray(" 2. Use a random subdomain: ")+d.cyan(`nport ${l.port||u.DEFAULT_PORT}
50
- `)+d.gray(" 3. Wait a few minutes and retry if you just stopped a tunnel with this name")):t.includes("SUBDOMAIN_IN_USE:")||t.includes("currently in use")||t.includes("already exists and is currently active")?new Error(d.red(`\u2717 Subdomain "${n}" is already in use!
48
+ `+p.yellow(`\u{1F4A1} Try one of these options:
49
+ `)+p.gray(" 1. Choose a different subdomain: ")+p.cyan(`nport ${l.port||u.DEFAULT_PORT} -s ${n}-v2
50
+ `)+p.gray(" 2. Use a random subdomain: ")+p.cyan(`nport ${l.port||u.DEFAULT_PORT}
51
+ `)+p.gray(" 3. Wait a few minutes and retry if you just stopped a tunnel with this name")):t.includes("SUBDOMAIN_IN_USE:")||t.includes("currently in use")||t.includes("already exists and is currently active")?new Error(p.red(`\u2717 Subdomain "${n}" is already in use!
51
52
 
52
- `)+d.yellow(`\u{1F4A1} This subdomain is currently being used by another active tunnel.
53
+ `)+p.yellow(`\u{1F4A1} This subdomain is currently being used by another active tunnel.
53
54
 
54
- `)+d.white(`Choose a different subdomain:
55
- `)+d.gray(" 1. Add a suffix: ")+d.cyan(`nport ${l.port||u.DEFAULT_PORT} -s ${n}-2
56
- `)+d.gray(" 2. Try a variation: ")+d.cyan(`nport ${l.port||u.DEFAULT_PORT} -s my-${n}
57
- `)+d.gray(" 3. Use random name: ")+d.cyan(`nport ${l.port||u.DEFAULT_PORT}
55
+ `)+p.white(`Choose a different subdomain:
56
+ `)+p.gray(" 1. Add a suffix: ")+p.cyan(`nport ${l.port||u.DEFAULT_PORT} -s ${n}-2
57
+ `)+p.gray(" 2. Try a variation: ")+p.cyan(`nport ${l.port||u.DEFAULT_PORT} -s my-${n}
58
+ `)+p.gray(" 3. Use random name: ")+p.cyan(`nport ${l.port||u.DEFAULT_PORT}
58
59
  `)):t.includes("already have a tunnel")||t.includes("[1013]")?new Error(`Subdomain "${n}" is already taken or in use.
59
60
 
60
- `+d.yellow(`\u{1F4A1} Try one of these options:
61
- `)+d.gray(" 1. Choose a different subdomain: ")+d.cyan(`nport ${l.port||u.DEFAULT_PORT} -s ${n}-v2
62
- `)+d.gray(" 2. Use a random subdomain: ")+d.cyan(`nport ${l.port||u.DEFAULT_PORT}
63
- `)+d.gray(" 3. Wait a few minutes and retry if you just stopped a tunnel with this name")):new Error(`Backend Error: ${t}`):e.response?new Error(`Backend Error: ${JSON.stringify(e.response.data,null,2)}`):e}};import en from"axios";import Xe from"axios";import{createHash as Ze}from"crypto";import y from"os";import L from"fs";import me from"path";var q={measurementId:"G-JJHG4DP1K9",apiSecret:"NjNID8jtRJe9s8uSBz2jfw"},h={enabled:!0,debug:process.env.NPORT_DEBUG==="true",timeout:2e3,userIdFile:me.join(y.homedir(),".nport","analytics-id")},z=class{static{i(this,"AnalyticsManager")}userId=null;sessionId=null;sessionStartTime=null;disabled=!1;constructor(){process.env.NPORT_ANALYTICS==="false"&&(this.disabled=!0)}async initialize(){if(!this.disabled){if(!q.apiSecret){h.debug&&console.warn("[Analytics] API secret not configured. Analytics disabled."),this.disabled=!0;return}try{this.userId=await this.getUserId(),this.sessionId=this.generateSessionId(),this.sessionStartTime=Date.now(),h.debug&&(console.log("[Analytics] Initialized successfully"),console.log(`[Analytics] User ID: ${this.userId}`),console.log(`[Analytics] Session ID: ${this.sessionId}`))}catch(e){h.debug&&console.warn("[Analytics] Failed to initialize:",e),this.disabled=!0}}}async getUserId(){try{let e=me.join(y.homedir(),".nport");if(L.existsSync(e)||L.mkdirSync(e,{recursive:!0}),L.existsSync(h.userIdFile)){let t=L.readFileSync(h.userIdFile,"utf8").trim();if(t)return t}let n=this.generateAnonymousId();return L.writeFileSync(h.userIdFile,n,"utf8"),n}catch{return this.generateAnonymousId()}}generateAnonymousId(){let e=[y.hostname(),y.platform(),y.arch(),y.homedir()].join("-");return Ze("sha256").update(e).digest("hex").substring(0,32)}generateSessionId(){return Math.floor(Date.now()/1e3)}async trackEvent(e,n={}){if(!(this.disabled||!h.enabled||!this.userId))try{let t=this.buildPayload(e,n),c=`https://www.google-analytics.com/mp/collect?measurement_id=${q.measurementId}&api_secret=${q.apiSecret}`;h.debug&&(console.log(`[Analytics] Sending event: ${e}`),console.log("[Analytics] Payload:",JSON.stringify(t,null,2))),Xe.post(c,t,{timeout:h.timeout,headers:{"Content-Type":"application/json"}}).then(g=>{h.debug&&(console.log(`[Analytics] Response status: ${g.status}`),g.data&&console.log("[Analytics] Response:",JSON.stringify(g.data,null,2)))}).catch(g=>{h.debug&&console.warn("[Analytics] Request failed:",g.message)})}catch(t){h.debug&&console.warn("[Analytics] Error tracking event:",t)}}getEngagementTime(){return this.sessionStartTime?Math.max(100,Date.now()-this.sessionStartTime):100}buildPayload(e,n){return{client_id:this.userId,timestamp_micros:Date.now()*1e3,events:[{name:e,params:{session_id:String(this.sessionId),engagement_time_msec:this.getEngagementTime(),...this.getSystemInfo(),...n}}]}}getSystemInfo(){return{os_platform:y.platform(),os_version:y.release(),os_arch:y.arch(),node_version:process.version}}trackCliStart(e,n,t){this.trackEvent("cli_start",{port:String(e),has_custom_subdomain:n&&!n.startsWith("user-"),cli_version:t})}trackTunnelCreated(e,n){this.trackEvent("tunnel_created",{subdomain_type:e.startsWith("user-")?"random":"custom",port:String(n)})}trackTunnelError(e,n){this.trackEvent("tunnel_error",{error_type:e,error_message:n.substring(0,100)})}trackTunnelShutdown(e,n){this.trackEvent("tunnel_shutdown",{shutdown_reason:e,duration_seconds:String(Math.floor(n))})}trackUpdateAvailable(e,n){this.trackEvent("update_available",{current_version:e,latest_version:n})}},f=new z;var N=class{static{i(this,"VersionManager")}static async checkForUpdates(){try{let n=(await en.get(`https://registry.npmjs.org/${u.PACKAGE_NAME}/latest`,{timeout:u.UPDATE_CHECK_TIMEOUT})).data.version,t=this.compareVersions(n,u.CURRENT_VERSION)>0;return t&&f.trackUpdateAvailable(u.CURRENT_VERSION,n),{current:u.CURRENT_VERSION,latest:n,shouldUpdate:t}}catch{return null}}static compareVersions(e,n){let t=e.split(".").map(Number),r=n.split(".").map(Number),c=Math.max(t.length,r.length);for(let g=0;g<c;g++){let A=t[g]||0,Y=r[g]||0;if(A>Y)return 1;if(A<Y)return-1}return 0}};var S=class{static{i(this,"TunnelOrchestrator")}static async start(e){l.setTunnel(null,e.subdomain,e.port,e.backendUrl),await f.initialize(),f.trackCliStart(e.port,e.subdomain,u.CURRENT_VERSION),m.displayStartupBanner(e.port);let n=await N.checkForUpdates();if(l.setUpdateInfo(n),!tn.existsSync(x.BIN_PATH)){console.log(J.yellow(`
61
+ `+p.yellow(`\u{1F4A1} Try one of these options:
62
+ `)+p.gray(" 1. Choose a different subdomain: ")+p.cyan(`nport ${l.port||u.DEFAULT_PORT} -s ${n}-v2
63
+ `)+p.gray(" 2. Use a random subdomain: ")+p.cyan(`nport ${l.port||u.DEFAULT_PORT}
64
+ `)+p.gray(" 3. Wait a few minutes and retry if you just stopped a tunnel with this name")):new Error(`Backend Error: ${t}`):e.response?new Error(`Backend Error: ${JSON.stringify(e.response.data,null,2)}`):e}};import en from"axios";import Xe from"axios";import{createHash as Ze}from"crypto";import y from"os";import A from"fs";import me from"path";var q={measurementId:"G-JJHG4DP1K9",apiSecret:"NjNID8jtRJe9s8uSBz2jfw"},h={enabled:!0,debug:process.env.NPORT_DEBUG==="true",timeout:2e3,userIdFile:me.join(y.homedir(),".nport","analytics-id")},z=class{static{i(this,"AnalyticsManager")}userId=null;sessionId=null;sessionStartTime=null;disabled=!1;constructor(){process.env.NPORT_ANALYTICS==="false"&&(this.disabled=!0)}async initialize(){if(!this.disabled){if(!q.apiSecret){h.debug&&console.warn("[Analytics] API secret not configured. Analytics disabled."),this.disabled=!0;return}try{this.userId=await this.getUserId(),this.sessionId=this.generateSessionId(),this.sessionStartTime=Date.now(),h.debug&&(console.log("[Analytics] Initialized successfully"),console.log(`[Analytics] User ID: ${this.userId}`),console.log(`[Analytics] Session ID: ${this.sessionId}`))}catch(e){h.debug&&console.warn("[Analytics] Failed to initialize:",e),this.disabled=!0}}}async getUserId(){try{let e=me.join(y.homedir(),".nport");if(A.existsSync(e)||A.mkdirSync(e,{recursive:!0}),A.existsSync(h.userIdFile)){let t=A.readFileSync(h.userIdFile,"utf8").trim();if(t)return t}let n=this.generateAnonymousId();return A.writeFileSync(h.userIdFile,n,"utf8"),n}catch{return this.generateAnonymousId()}}generateAnonymousId(){let e=[y.hostname(),y.platform(),y.arch(),y.homedir()].join("-");return Ze("sha256").update(e).digest("hex").substring(0,32)}generateSessionId(){return Math.floor(Date.now()/1e3)}async trackEvent(e,n={}){if(!(this.disabled||!h.enabled||!this.userId))try{let t=this.buildPayload(e,n),c=`https://www.google-analytics.com/mp/collect?measurement_id=${q.measurementId}&api_secret=${q.apiSecret}`;h.debug&&(console.log(`[Analytics] Sending event: ${e}`),console.log("[Analytics] Payload:",JSON.stringify(t,null,2))),Xe.post(c,t,{timeout:h.timeout,headers:{"Content-Type":"application/json"}}).then(d=>{h.debug&&(console.log(`[Analytics] Response status: ${d.status}`),d.data&&console.log("[Analytics] Response:",JSON.stringify(d.data,null,2)))}).catch(d=>{h.debug&&console.warn("[Analytics] Request failed:",d.message)})}catch(t){h.debug&&console.warn("[Analytics] Error tracking event:",t)}}getEngagementTime(){return this.sessionStartTime?Math.max(100,Date.now()-this.sessionStartTime):100}buildPayload(e,n){return{client_id:this.userId,timestamp_micros:Date.now()*1e3,events:[{name:e,params:{session_id:String(this.sessionId),engagement_time_msec:this.getEngagementTime(),...this.getSystemInfo(),...n}}]}}getSystemInfo(){return{os_platform:y.platform(),os_version:y.release(),os_arch:y.arch(),node_version:process.version}}trackCliStart(e,n,t){this.trackEvent("cli_start",{port:String(e),has_custom_subdomain:n&&!n.startsWith("user-"),cli_version:t})}trackTunnelCreated(e,n){this.trackEvent("tunnel_created",{subdomain_type:e.startsWith("user-")?"random":"custom",port:String(n)})}trackTunnelError(e,n){this.trackEvent("tunnel_error",{error_type:e,error_message:n.substring(0,100)})}trackTunnelShutdown(e,n){this.trackEvent("tunnel_shutdown",{shutdown_reason:e,duration_seconds:String(Math.floor(n))})}trackUpdateAvailable(e,n){this.trackEvent("update_available",{current_version:e,latest_version:n})}},f=new z;var N=class{static{i(this,"VersionManager")}static async checkForUpdates(){try{let n=(await en.get(`https://registry.npmjs.org/${u.PACKAGE_NAME}/latest`,{timeout:u.UPDATE_CHECK_TIMEOUT})).data.version,t=this.compareVersions(n,u.CURRENT_VERSION)>0;return t&&f.trackUpdateAvailable(u.CURRENT_VERSION,n),{current:u.CURRENT_VERSION,latest:n,shouldUpdate:t}}catch{return null}}static compareVersions(e,n){let t=e.split(".").map(Number),r=n.split(".").map(Number),c=Math.max(t.length,r.length);for(let d=0;d<c;d++){let L=t[d]||0,Y=r[d]||0;if(L>Y)return 1;if(L<Y)return-1}return 0}};var S=class{static{i(this,"TunnelOrchestrator")}static async start(e){l.setTunnel(null,e.subdomain,e.port,e.backendUrl),await f.initialize(),f.trackCliStart(e.port,e.subdomain,u.CURRENT_VERSION),m.displayStartupBanner(e.port);let n=await N.checkForUpdates();if(l.setUpdateInfo(n),!tn.existsSync(x.BIN_PATH)){console.log(J.yellow(`
64
65
  \u{1F4E6} Cloudflared binary not found. Downloading...
65
66
  `));try{await K()}catch(r){f.trackTunnelError("binary_download_failed",r.message),console.error(J.red(`
66
- \u274C Failed to download cloudflared: ${r.message}`)),process.exit(1)}}v.validate(x.BIN_PATH)||(f.trackTunnelError("binary_missing","Cloudflared binary not found"),await new Promise(r=>setTimeout(r,100)),process.exit(1));let t=nn(s.t("creatingTunnel",{port:e.port})).start();try{let r=await _.createTunnel(e.subdomain,e.backendUrl);l.setTunnel(r.tunnelId,e.subdomain,e.port,e.backendUrl),f.trackTunnelCreated(e.subdomain,e.port),t.stop(),console.log(J.green(` ${s.t("tunnelLive")}`)),m.displayTunnelSuccess(r.url,e.port,n);let c=v.spawn(x.BIN_PATH,r.tunnelToken,e.port);l.setProcess(c),v.attachHandlers(c,t);let g=setTimeout(()=>{m.displayTimeoutWarning(),this.cleanup("timeout")},se);l.setTimeout(g)}catch(r){let c=r,g=c.message.includes("already taken")?"subdomain_taken":"tunnel_creation_failed";f.trackTunnelError(g,c.message),m.displayError(c,t),await new Promise(A=>setTimeout(A,100)),process.exit(1)}}static async cleanup(e="manual"){l.clearTimeout(),l.hasTunnel()||process.exit(0),m.displayCleanupStart();let n=l.getDurationSeconds();f.trackTunnelShutdown(e,n);try{l.hasProcess()&&l.tunnelProcess&&l.tunnelProcess.kill(),l.subdomain&&l.tunnelId&&await _.deleteTunnel(l.subdomain,l.tunnelId,l.backendUrl),m.displayCleanupSuccess()}catch{m.displayCleanupError()}await new Promise(t=>setTimeout(t,100)),process.exit(0)}};async function rn(){let o=on(s.t("checkingUpdates")).start(),e=await N.checkForUpdates();o.stop(),m.displayVersion(u.CURRENT_VERSION,e)}i(rn,"displayVersion");function sn(o){o==="clear"?(I.setBackendUrl(null),console.log(b.green("\u2714 Backend URL cleared. Using default backend.")),console.log(b.gray(` Default: https://api.nport.link
67
- `))):(I.setBackendUrl(o),console.log(b.green("\u2714 Backend URL saved successfully!")),console.log(b.cyan(` Backend: ${o}`)),console.log(b.gray(`
67
+ \u274C Failed to download cloudflared: ${r.message}`)),process.exit(1)}}v.validate(x.BIN_PATH)||(f.trackTunnelError("binary_missing","Cloudflared binary not found"),await new Promise(r=>setTimeout(r,100)),process.exit(1));let t=nn(s.t("creatingTunnel",{port:e.port})).start();try{let r=await _.createTunnel(e.subdomain,e.backendUrl);l.setTunnel(r.tunnelId,e.subdomain,e.port,e.backendUrl),f.trackTunnelCreated(e.subdomain,e.port),t.stop(),console.log(J.green(` ${s.t("tunnelLive")}`)),m.displayTunnelSuccess(r.url,e.port,n);let c=v.spawn(x.BIN_PATH,r.tunnelToken,e.port);l.setProcess(c),v.attachHandlers(c,t);let d=setTimeout(()=>{m.displayTimeoutWarning(),this.cleanup("timeout")},se);l.setTimeout(d)}catch(r){let c=r,d=c.message.includes("already taken")?"subdomain_taken":"tunnel_creation_failed";f.trackTunnelError(d,c.message),m.displayError(c,t),await new Promise(L=>setTimeout(L,100)),process.exit(1)}}static async cleanup(e="manual"){l.clearTimeout(),l.hasTunnel()||process.exit(0),m.displayCleanupStart();let n=l.getDurationSeconds();f.trackTunnelShutdown(e,n);try{l.hasProcess()&&l.tunnelProcess&&l.tunnelProcess.kill(),l.subdomain&&l.tunnelId&&await _.deleteTunnel(l.subdomain,l.tunnelId,l.backendUrl),m.displayCleanupSuccess()}catch{m.displayCleanupError()}await new Promise(t=>setTimeout(t,100)),process.exit(0)}};async function rn(){let o=on(s.t("checkingUpdates")).start(),e=await N.checkForUpdates();o.stop(),m.displayVersion(u.CURRENT_VERSION,e)}i(rn,"displayVersion");function sn(o){o==="clear"?(w.setBackendUrl(null),console.log(b.green("\u2714 Backend URL cleared. Using default backend.")),console.log(b.gray(` Default: https://api.nport.link
68
+ `))):(w.setBackendUrl(o),console.log(b.green("\u2714 Backend URL saved successfully!")),console.log(b.cyan(` Backend: ${o}`)),console.log(b.gray(`
68
69
  This backend will be used for all future sessions.`)),console.log(b.gray(` To clear: nport --set-backend
69
- `)));let e=I.getBackendUrl();e&&(console.log(b.white("Current configuration:")),console.log(b.cyan(` Saved backend: ${e}`)))}i(sn,"handleSetBackend");async function an(){try{let o=process.argv.slice(2),e=O.parse(o);await s.initialize(e.language),(o.includes("-v")||o.includes("--version"))&&(await rn(),process.exit(0)),e.setBackend&&(sn(e.setBackend),process.exit(0)),e.language==="prompt"&&(o.includes("--language")||o.includes("--lang")||o.includes("-l"))&&process.exit(0);let n=e.backendUrl;if(!n){let r=I.getBackendUrl();r&&(n=r)}let t={port:e.port,subdomain:e.subdomain,backendUrl:n,language:e.language};await S.start(t)}catch(o){console.error(`Fatal Error: ${o.message}`),process.exit(1)}}i(an,"main");process.on("SIGINT",()=>S.cleanup());process.on("SIGTERM",()=>S.cleanup());an();
70
+ `)));let e=w.getBackendUrl();e&&(console.log(b.white("Current configuration:")),console.log(b.cyan(` Saved backend: ${e}`)))}i(sn,"handleSetBackend");async function an(){try{let o=process.argv.slice(2),e=O.parse(o);await s.initialize(e.language),(o.includes("-v")||o.includes("--version"))&&(await rn(),process.exit(0)),e.setBackend&&(sn(e.setBackend),process.exit(0)),e.language==="prompt"&&(o.includes("--language")||o.includes("--lang")||o.includes("-l"))&&process.exit(0);let n=e.backendUrl;if(!n){let r=w.getBackendUrl();r&&(n=r)}let t={port:e.port,subdomain:e.subdomain,backendUrl:n,language:e.language};await S.start(t)}catch(o){console.error(`Fatal Error: ${o.message}`),process.exit(1)}}i(an,"main");process.on("SIGINT",()=>S.cleanup());process.on("SIGTERM",()=>S.cleanup());an();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nport",
3
- "version": "2.1.2",
3
+ "version": "2.1.3",
4
4
  "description": "Free & open source ngrok alternative - Tunnel HTTP/HTTPS connections via Cloudflare Edge with custom subdomains",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",