@@ -1126,34 +1126,63 @@ <h3>Start Instance</h3>
11261126 const ws = new WebSocket ( url ) ;
11271127 ws . binaryType = "arraybuffer" ;
11281128
1129- let isFirstMessage = true ;
1129+ let isReady = false ;
1130+ let isFirstData = true ;
1131+ let handshakeTimeout = null ;
11301132
11311133 ws . onopen = ( ) => {
1132- if ( ttySocket === ws ) {
1133- updateStatus ( "websocket live" ) ;
1134- focusTerminalIfPossible ( ) ;
1135- }
1136- // Send initial resize on connect
1137- if ( term && term . cols > 0 && term . rows > 0 ) {
1138- sendResize ( term . cols , term . rows ) ;
1139- }
1134+ // Set handshake timeout (5 seconds)
1135+ handshakeTimeout = setTimeout ( ( ) => {
1136+ if ( ! isReady && ws === ttySocket ) {
1137+ console . error ( 'WebSocket handshake timeout: no "ready" message received' ) ;
1138+ updateStatus ( "handshake timeout" ) ;
1139+ ws . close ( ) ;
1140+ // Fallback to SSE after timeout
1141+ setTimeout ( ( ) => startSSE ( ) , 500 ) ;
1142+ }
1143+ } , 5000 ) ;
11401144 } ;
11411145 ws . onmessage = ( ev ) => {
1146+ // Handle handshake: wait for "ready" message first
1147+ if ( ! isReady ) {
1148+ try {
1149+ const msg = JSON . parse ( ev . data ) ;
1150+ if ( msg . type === 'ready' ) {
1151+ isReady = true ;
1152+ // Clear handshake timeout
1153+ if ( handshakeTimeout ) {
1154+ clearTimeout ( handshakeTimeout ) ;
1155+ handshakeTimeout = null ;
1156+ }
1157+ if ( ttySocket === ws ) {
1158+ updateStatus ( "websocket live" ) ;
1159+ focusTerminalIfPossible ( ) ;
1160+ }
1161+ // Send initial resize to backend
1162+ if ( term && term . cols > 0 && term . rows > 0 ) {
1163+ sendResize ( term . cols , term . rows ) ;
1164+ }
1165+ return ;
1166+ }
1167+ } catch ( e ) {
1168+ // Not JSON, treat as data
1169+ }
1170+ }
1171+
1172+ // Handle binary/text data
11421173 if ( ev . data instanceof ArrayBuffer ) {
11431174 const u8 = new Uint8Array ( ev . data ) ;
11441175 if ( u8 . length && term ) term . write ( u8 ) ;
11451176 } else if ( term ) term . write ( ev . data ) ;
11461177
1147- // After receiving first message (initial log), trigger resize
1148- // This helps TUI programs redraw their interface
1149- if ( isFirstMessage && term ) {
1150- isFirstMessage = false ;
1178+ // After receiving first data (after ready), trigger resize for TUI redraw
1179+ if ( isFirstData && isReady && term ) {
1180+ isFirstData = false ;
11511181 setTimeout ( ( ) => {
11521182 if ( term && ws === ttySocket && ws . readyState === WebSocket . OPEN ) {
11531183 const cols = term . cols ;
11541184 const rows = term . rows ;
11551185 if ( cols > 0 && rows > 0 ) {
1156- // Send resize to backend and trigger local redraw
11571186 sendResize ( cols , rows ) ;
11581187 term . resize ( cols , rows ) ;
11591188 }
@@ -1163,6 +1192,11 @@ <h3>Start Instance</h3>
11631192 } ;
11641193
11651194 ws . onclose = ( ) => {
1195+ // Clear handshake timeout on close
1196+ if ( handshakeTimeout ) {
1197+ clearTimeout ( handshakeTimeout ) ;
1198+ handshakeTimeout = null ;
1199+ }
11661200 if ( ttySocket === ws ) {
11671201 updateStatus ( "ws closed, retrying..." ) ;
11681202 ttyReconnectTimer = setTimeout ( connectTTY , 1000 ) ;
0 commit comments