@@ -113,6 +113,10 @@ async def _ensure_auth_token(self) -> None:
113113 # If no auth token provided, check config file
114114 if not self .auth_token :
115115 self .auth_token = await config_manager .get_auth_token ()
116+ if self .auth_token :
117+ self .logger .info (
118+ f"Loaded auth token from config: ****{ self .auth_token [- 4 :]} "
119+ )
116120
117121 # If still no token, register new user
118122 if not self .auth_token :
@@ -140,8 +144,17 @@ async def _ensure_auth_token(self) -> None:
140144 self .logger .error (f"Failed to register user: { e } " )
141145 raise
142146
143- # Verify token is still valid and refresh if needed
147+ # Skip token verification for now - just use the token from config
148+ # This was causing issues with multiple registrations
144149 if self .auth_token :
150+ self .logger .info (
151+ f"Using auth token from config: ****{ self .auth_token [- 4 :]} "
152+ )
153+ return
154+
155+ # OLD CODE - disabled for now
156+ # Verify token is still valid and refresh if needed
157+ if False and self .auth_token :
145158 api_url = await config_manager .get_api_url ()
146159
147160 # Handle local development
@@ -268,12 +281,16 @@ async def connect(self) -> None:
268281 if resp .get ("ClientId" ):
269282 self .client_id = resp ["ClientId" ]
270283
271- self .logger .info (f"Connected as client { self .client_id } " )
272-
284+ self .logger .info (
285+ f"Connected as client { self .client_id } with token ****{ self .auth_token [- 4 :] if self .auth_token else 'None' } "
286+ )
287+
273288 # Update connection state
274289 self .is_connected = True
275290 self .connection_status = "Connected"
276- self ._reconnect_delay = 1.0 # Reset reconnect delay on successful connection
291+ self ._reconnect_delay = (
292+ 1.0 # Reset reconnect delay on successful connection
293+ )
277294
278295 # Start background tasks
279296 self ._start_background_tasks ()
@@ -307,12 +324,21 @@ async def request_tunnel(self, config: TunnelConfig) -> Tunnel:
307324
308325 # Check for error response
309326 if resp .get ("Type" ) == "ErrorResp" :
310- error_code = resp .get ("ErrorCode" , "UNKNOWN" )
311- message = resp .get ("Message" , "Unknown error" )
327+ # The error fields are at the root level, not in Payload
328+ error_code = resp .get ("error_code" , "UNKNOWN" )
329+ message = resp .get ("message" , "Unknown error" )
330+
331+ # Debug log
332+ self .logger .debug (f"ErrorResp received: { resp } " )
333+
312334 if error_code == "OVER_CAPACITY" :
313335 raise Exception (
314336 "No subdomains available. Please try again later."
315337 )
338+ elif error_code == "FREE_TIER_LIMIT_REACHED" :
339+ raise Exception (
340+ "Free tier limit reached. You can have a maximum of 2 active tunnels."
341+ )
316342 else :
317343 raise Exception (f"{ error_code } : { message } " )
318344
@@ -400,6 +426,12 @@ async def _receive_message(
400426
401427 async def _handle_message (self , msg : Dict [str , Any ]) -> None :
402428 """Handle incoming control messages"""
429+ # Normalize message - handle both uppercase and lowercase field names
430+ if "type" in msg and "Type" not in msg :
431+ msg ["Type" ] = msg ["type" ]
432+ if "payload" in msg and "Payload" not in msg :
433+ msg ["Payload" ] = msg ["payload" ]
434+
403435 msg_type = msg .get ("Type" )
404436
405437 if msg_type == "NewTunnel" :
@@ -411,7 +443,7 @@ async def _handle_message(self, msg: Dict[str, Any]) -> None:
411443
412444 elif msg_type == "ErrorResp" :
413445 # Error response
414- req_id = msg .get ("ReqId" )
446+ req_id = msg .get ("ReqId" ) or msg . get ( "Payload" , {}). get ( "req_id" )
415447 future = self .pending_requests .get (req_id ) if req_id else None
416448 if future and not future .done ():
417449 future .set_result (msg )
@@ -711,7 +743,11 @@ async def _subdomain_heartbeat(self, subdomain: str) -> None:
711743 async def _control_loop (self ) -> None :
712744 """Listen for control messages"""
713745 try :
714- while self ._running and self .control_ws and not self .control_ws .closed :
746+ while (
747+ self ._running
748+ and self .control_ws
749+ and not self .control_ws .closed
750+ ):
715751 try :
716752 msg = await self ._receive_message (self .control_ws )
717753 await self ._handle_message (msg )
@@ -722,7 +758,9 @@ async def _control_loop(self) -> None:
722758 self .connection_status = "Disconnected"
723759 # Trigger reconnection
724760 if self ._running and not self ._reconnecting :
725- self ._reconnect_task = asyncio .create_task (self ._reconnect ())
761+ self ._reconnect_task = asyncio .create_task (
762+ self ._reconnect ()
763+ )
726764 break
727765 except asyncio .CancelledError :
728766 # Normal shutdown
@@ -744,61 +782,73 @@ async def _maintain_proxy_pool(self) -> None:
744782 async def _reconnect (self ) -> None :
745783 """Reconnect with exponential backoff"""
746784 self ._reconnecting = True
747-
785+
748786 while self ._running :
749787 try :
750- self .connection_status = f"Reconnecting in { self ._reconnect_delay :.0f} s..."
751- self .logger .info (f"Attempting reconnection in { self ._reconnect_delay } seconds" )
752-
788+ self .connection_status = (
789+ f"Reconnecting in { self ._reconnect_delay :.0f} s..."
790+ )
791+ self .logger .info (
792+ f"Attempting reconnection in { self ._reconnect_delay } seconds"
793+ )
794+
753795 # Wait with exponential backoff
754796 await asyncio .sleep (self ._reconnect_delay )
755-
797+
756798 # Update status
757799 self .connection_status = "Connecting..."
758-
800+
759801 # Close existing connection if any
760802 if self .control_ws and not self .control_ws .closed :
761803 await self .control_ws .close ()
762-
804+
763805 # Close and recreate session
764806 if self .session and not self .session .closed :
765807 await self .session .close ()
766808 await asyncio .sleep (0.1 )
767-
809+
768810 # Re-establish connection
769811 await self .connect ()
770-
812+
771813 # Re-request tunnels with same configs
772814 for tunnel in list (self .tunnels .values ()):
773815 try :
774816 # Request tunnel with same subdomain
775817 config = tunnel .config
776818 # For HTTP tunnels, try to get the same subdomain
777- if tunnel .protocol == "http" and hasattr (tunnel , "subdomain" ):
819+ if tunnel .protocol == "http" and hasattr (
820+ tunnel , "subdomain"
821+ ):
778822 config .subdomain = tunnel .subdomain
779-
823+
780824 new_tunnel = await self .request_tunnel (config )
781- self .logger .info (f"Re-established tunnel: { new_tunnel .url } " )
825+ self .logger .info (
826+ f"Re-established tunnel: { new_tunnel .url } "
827+ )
782828 except Exception as e :
783- self .logger .error (f"Failed to re-establish tunnel: { e } " )
784-
829+ self .logger .error (
830+ f"Failed to re-establish tunnel: { e } "
831+ )
832+
785833 # Success - exit reconnection loop
786834 self ._reconnecting = False
787835 break
788-
836+
789837 except Exception as e :
790838 self .logger .error (f"Reconnection failed: { e } " )
791- self .connection_status = f "Reconnection failed, retrying..."
792-
839+ self .connection_status = "Reconnection failed, retrying..."
840+
793841 # Increase delay with exponential backoff
794- self ._reconnect_delay = min (self ._reconnect_delay * 2 , self ._max_reconnect_delay )
795-
842+ self ._reconnect_delay = min (
843+ self ._reconnect_delay * 2 , self ._max_reconnect_delay
844+ )
845+
796846 self ._reconnecting = False
797847
798848 async def close (self ) -> None :
799849 """Close all connections and clean up"""
800850 self .logger .info ("Closing client" )
801-
851+
802852 # Stop running
803853 self ._running = False
804854 self .is_connected = False
0 commit comments