@@ -47,7 +47,10 @@ function loadPersistedTunnel() {
4747
4848function savePersistedTunnel ( id ) {
4949 fs . mkdirSync ( TUNNEL_CONFIG_DIR , { recursive : true } ) ;
50- fs . writeFileSync ( TUNNEL_CONFIG_PATH , JSON . stringify ( { tunnelId : id , createdAt : new Date ( ) . toISOString ( ) } , null , 2 ) ) ;
50+ fs . writeFileSync (
51+ TUNNEL_CONFIG_PATH ,
52+ JSON . stringify ( { tunnelId : id , createdAt : new Date ( ) . toISOString ( ) } , null , 2 ) ,
53+ ) ;
5154}
5255
5356function deletePersisted ( ) {
@@ -68,7 +71,10 @@ function deletePersisted() {
6871function isTunnelValid ( id ) {
6972 try {
7073 if ( ! SAFE_ID_RE . test ( id ) ) return false ;
71- execFileSync ( devtunnelCmd , [ 'show' , id , '--json' ] , { encoding : 'utf-8' , stdio : [ 'pipe' , 'pipe' , 'pipe' ] } ) ;
74+ execFileSync ( devtunnelCmd , [ 'show' , id , '--json' ] , {
75+ encoding : 'utf-8' ,
76+ stdio : [ 'pipe' , 'pipe' , 'pipe' ] ,
77+ } ) ;
7278 return true ;
7379 } catch {
7480 return false ;
@@ -87,7 +93,9 @@ async function startTunnel(port, options = {}) {
8793 log . error ( '' ) ;
8894 log . error ( ' Install it:' ) ;
8995 log . error ( ' Windows: winget install Microsoft.devtunnel' ) ;
90- log . error ( ' or: Invoke-WebRequest -Uri https://aka.ms/TunnelsCliDownload/win-x64 -OutFile devtunnel.exe' ) ;
96+ log . error (
97+ ' or: Invoke-WebRequest -Uri https://aka.ms/TunnelsCliDownload/win-x64 -OutFile devtunnel.exe' ,
98+ ) ;
9199 log . error ( ' macOS: brew install --cask devtunnel' ) ;
92100 log . error ( ' Linux: curl -sL https://aka.ms/DevTunnelCliInstall | bash' ) ;
93101 log . error ( '' ) ;
@@ -103,15 +111,29 @@ async function startTunnel(port, options = {}) {
103111 // Ensure user is logged in
104112 let loggedIn = false ;
105113 try {
106- const userOut = execFileSync ( devtunnelCmd , [ 'user' , 'show' ] , { encoding : 'utf-8' , stdio : [ 'pipe' , 'pipe' , 'pipe' ] } ) ;
114+ const userOut = execFileSync ( devtunnelCmd , [ 'user' , 'show' ] , {
115+ encoding : 'utf-8' ,
116+ stdio : [ 'pipe' , 'pipe' , 'pipe' ] ,
117+ } ) ;
107118 // user show can succeed but show "not logged in" status
108119 loggedIn = userOut && ! userOut . toLowerCase ( ) . includes ( 'not logged in' ) ;
109120 } catch { }
110121
111122 if ( ! loggedIn ) {
112123 log . info ( 'devtunnel not logged in, launching login...' ) ;
113124 log . info ( 'A browser window will open for authentication.' ) ;
114- execFileSync ( devtunnelCmd , [ 'user' , 'login' ] , { stdio : 'inherit' } ) ;
125+ try {
126+ execFileSync ( devtunnelCmd , [ 'user' , 'login' ] , { stdio : 'inherit' } ) ;
127+ } catch ( loginErr ) {
128+ log . error ( '' ) ;
129+ log . error ( ' DevTunnel login failed. To use tunnels, run:' ) ;
130+ log . error ( ' devtunnel user login' ) ;
131+ log . error ( '' ) ;
132+ log . error ( ' Or start without a tunnel:' ) ;
133+ log . error ( ' termbeam --no-tunnel' ) ;
134+ log . error ( '' ) ;
135+ return null ;
136+ }
115137 }
116138
117139 const persisted = options . persisted ;
@@ -130,7 +152,9 @@ async function startTunnel(port, options = {}) {
130152 if ( saved ) {
131153 log . info ( 'Persisted tunnel expired, creating new one' ) ;
132154 }
133- const createOut = execFileSync ( devtunnelCmd , [ 'create' , '--expiration' , '30d' , '--json' ] , { encoding : 'utf-8' } ) ;
155+ const createOut = execFileSync ( devtunnelCmd , [ 'create' , '--expiration' , '30d' , '--json' ] , {
156+ encoding : 'utf-8' ,
157+ } ) ;
134158 const tunnelData = JSON . parse ( createOut ) ;
135159 tunnelId = tunnelData . tunnel . tunnelId ;
136160 savePersistedTunnel ( tunnelId ) ;
@@ -140,18 +164,28 @@ async function startTunnel(port, options = {}) {
140164 tunnelMode = 'ephemeral' ;
141165 tunnelExpiry = '1 day' ;
142166 // Ephemeral tunnel — create fresh, will be deleted on shutdown
143- const createOut = execFileSync ( devtunnelCmd , [ 'create' , '--expiration' , '1d' , '--json' ] , { encoding : 'utf-8' } ) ;
167+ const createOut = execFileSync ( devtunnelCmd , [ 'create' , '--expiration' , '1d' , '--json' ] , {
168+ encoding : 'utf-8' ,
169+ } ) ;
144170 const tunnelData = JSON . parse ( createOut ) ;
145171 tunnelId = tunnelData . tunnel . tunnelId ;
146172 log . info ( `Created ephemeral tunnel ${ tunnelId } ` ) ;
147173 }
148174
149175 // Idempotent port and access setup
150176 try {
151- execFileSync ( devtunnelCmd , [ 'port' , 'create' , tunnelId , '-p' , String ( port ) , '--protocol' , 'http' ] , { stdio : 'pipe' } ) ;
177+ execFileSync (
178+ devtunnelCmd ,
179+ [ 'port' , 'create' , tunnelId , '-p' , String ( port ) , '--protocol' , 'http' ] ,
180+ { stdio : 'pipe' } ,
181+ ) ;
152182 } catch { }
153183 try {
154- execFileSync ( devtunnelCmd , [ 'access' , 'create' , tunnelId , '-p' , String ( port ) , '--anonymous' ] , { stdio : 'pipe' } ) ;
184+ execFileSync (
185+ devtunnelCmd ,
186+ [ 'access' , 'create' , tunnelId , '-p' , String ( port ) , '--anonymous' ] ,
187+ { stdio : 'pipe' } ,
188+ ) ;
155189 } catch { }
156190
157191 const hostProc = spawn ( devtunnelCmd , [ 'host' , tunnelId ] , {
@@ -193,8 +227,13 @@ function cleanupTunnel() {
193227 // On Windows, kill the process tree to ensure all children die
194228 if ( process . platform === 'win32' && tunnelProc . pid ) {
195229 try {
196- execFileSync ( 'taskkill' , [ '/pid' , String ( tunnelProc . pid ) , '/T' , '/F' ] , { stdio : 'pipe' , timeout : 5000 } ) ;
197- } catch { /* best effort */ }
230+ execFileSync ( 'taskkill' , [ '/pid' , String ( tunnelProc . pid ) , '/T' , '/F' ] , {
231+ stdio : 'pipe' ,
232+ timeout : 5000 ,
233+ } ) ;
234+ } catch {
235+ /* best effort */
236+ }
198237 } else {
199238 tunnelProc . kill ( 'SIGKILL' ) ;
200239 }
0 commit comments