@@ -22,7 +22,7 @@ import { sessionManager } from "./terminal-session-manager.js";
2222import {
2323 detectTmux ,
2424 attachOrCreateTmuxSession ,
25- queryNewestTmuxSession ,
25+ waitForTmuxSession ,
2626} from "./tmux-helper.js" ;
2727
2828async function performPortKnocking (
@@ -806,8 +806,8 @@ wss.on("connection", async (ws: WebSocket, req) => {
806806 : null ;
807807 if ( session ?. sshStream ) {
808808 const existingName = tmuxData . sessionName || undefined ;
809- attachOrCreateTmuxSession ( session . sshStream , existingName ) ;
810809 if ( existingName ) {
810+ attachOrCreateTmuxSession ( session . sshStream , existingName ) ;
811811 session . tmuxSessionName = existingName ;
812812 sshLogger . info ( "User selected tmux session to attach" , {
813813 operation : "tmux_user_attach" ,
@@ -821,30 +821,49 @@ wss.on("connection", async (ws: WebSocket, req) => {
821821 } ) ,
822822 ) ;
823823 } else {
824- // New session from picker -- query name after startup
824+ const newName = `termix-${ session . hostId } -${ Date . now ( ) . toString ( 36 ) . slice ( - 4 ) } ` ;
825+ attachOrCreateTmuxSession ( session . sshStream , undefined , newName ) ;
825826 const sshConn = session . sshConn ;
826- setTimeout ( async ( ) => {
827- const sessionName = sshConn
828- ? await queryNewestTmuxSession ( sshConn )
829- : null ;
830- session . tmuxSessionName = sessionName ;
831- sshLogger . info ( "User requested new tmux session" , {
832- operation : "tmux_user_create" ,
833- sessionName ,
834- hostId : session . hostId ,
835- } ) ;
836- ws . send (
837- JSON . stringify ( {
838- type : "tmux_session_created" ,
839- sessionName ,
840- } ) ,
841- ) ;
842- } , 500 ) ;
827+ if ( sshConn ) {
828+ ( async ( ) => {
829+ const confirmed = await waitForTmuxSession ( sshConn , newName ) ;
830+ session . tmuxSessionName = confirmed ;
831+ sshLogger . info ( "User requested new tmux session" , {
832+ operation : "tmux_user_create" ,
833+ sessionName : confirmed ,
834+ hostId : session . hostId ,
835+ } ) ;
836+ ws . send (
837+ JSON . stringify ( {
838+ type : "tmux_session_created" ,
839+ sessionName : confirmed ,
840+ } ) ,
841+ ) ;
842+ } ) ( ) ;
843+ }
843844 }
844845 }
845846 break ;
846847 }
847848
849+ case "tmux_detach" : {
850+ const session = currentSessionId
851+ ? sessionManager . getSession ( currentSessionId )
852+ : null ;
853+ if ( session ?. sshConn && session . tmuxSessionName ) {
854+ const tmuxName = session . tmuxSessionName ;
855+ session . sshStream ?. write ( "\x02d" ) ;
856+ session . tmuxSessionName = null ;
857+ sshLogger . info ( "User detached from tmux session" , {
858+ operation : "tmux_user_detach" ,
859+ sessionName : tmuxName ,
860+ hostId : session . hostId ,
861+ } ) ;
862+ ws . send ( JSON . stringify ( { type : "tmux_detached" , sessionName : tmuxName } ) ) ;
863+ }
864+ break ;
865+ }
866+
848867 case "totp_response" : {
849868 const totpData = data as TOTPResponseData ;
850869 if ( keyboardInteractiveFinish && totpData ?. code ) {
@@ -1646,31 +1665,27 @@ wss.on("connection", async (ws: WebSocket, req) => {
16461665 "tmux is not installed on the remote host. Falling back to standard shell." ,
16471666 } ) ,
16481667 ) ;
1649- // tmux unavailable, run commands in plain shell
16501668 runPostShellCommands ( 0 ) ;
16511669 } else if ( detection . sessions . length === 0 ) {
1652- attachOrCreateTmuxSession ( stream ) ;
1653- // Query the name tmux assigned after a short delay
1654- setTimeout ( async ( ) => {
1655- const sessionName = await queryNewestTmuxSession ( conn ) ;
1656- const session = sessionManager . getSession ( boundSessionId ) ;
1657- if ( session ) {
1658- session . tmuxSessionName = sessionName ;
1659- }
1660- sshLogger . info ( "Created new tmux session" , {
1661- operation : "tmux_new_session" ,
1662- sessionName,
1663- hostId : id ,
1664- } ) ;
1665- ws . send (
1666- JSON . stringify ( {
1667- type : "tmux_session_created" ,
1668- sessionName,
1669- } ) ,
1670- ) ;
1671- } , 500 ) ;
1672- // Wait for tmux to start before running commands inside it
1673- runPostShellCommands ( 500 ) ;
1670+ const newName = `termix-${ id } -${ Date . now ( ) . toString ( 36 ) . slice ( - 4 ) } ` ;
1671+ attachOrCreateTmuxSession ( stream , undefined , newName ) ;
1672+ const confirmed = await waitForTmuxSession ( conn , newName ) ;
1673+ const session = sessionManager . getSession ( boundSessionId ) ;
1674+ if ( session ) {
1675+ session . tmuxSessionName = confirmed ;
1676+ }
1677+ sshLogger . info ( "Created new tmux session" , {
1678+ operation : "tmux_new_session" ,
1679+ sessionName : confirmed ,
1680+ hostId : id ,
1681+ } ) ;
1682+ ws . send (
1683+ JSON . stringify ( {
1684+ type : "tmux_session_created" ,
1685+ sessionName : confirmed ,
1686+ } ) ,
1687+ ) ;
1688+ runPostShellCommands ( 0 ) ;
16741689 } else if ( detection . sessions . length === 1 ) {
16751690 attachOrCreateTmuxSession ( stream , detection . sessions [ 0 ] . name ) ;
16761691 const sessionName = detection . sessions [ 0 ] . name ;
0 commit comments