fix(terminal): prevent double session creation, restore sessions on reload
Three fixes for multi-session terminal bugs: 1. Race-condition double creation: The auto-create effect fired twice because loadSessions returned 0 while an earlier createSession was still in flight. Added hasAutoCreated guard ref to ensure only one auto-create happens. 2. Page reload spawns new sessions: After server restart, list_terminal_sessions filtered out DB-only sessions (no in-memory counterpart), so the frontend thought no sessions existed and auto-created new ones. Reverted the filter so DB rows are always returned. The WebSocket handler now restores the in-memory session from the DB row on demand when connecting. 3. No input after connection: The backend _read_loop would break on any send error, causing asyncio.wait to cancel the _write_loop. Made _read_loop retry up to 3 times before giving up, preventing transient send errors from killing input handling. Quality gates: pytest (15/15 passed), tsc clean
This commit is contained in:
@@ -26,6 +26,7 @@ export const TerminalPage: React.FC = () => {
|
||||
const [isFullscreen, setIsFullscreen] = useState(false);
|
||||
const terminalRefs = useRef<Record<string, React.RefObject<TerminalRef>>>({});
|
||||
const headerAutoHide = useAutoHide({ timeout: 3000, enabled: isMobile });
|
||||
const hasAutoCreated = useRef(false);
|
||||
|
||||
const {
|
||||
sessions,
|
||||
@@ -39,9 +40,16 @@ export const TerminalPage: React.FC = () => {
|
||||
error,
|
||||
} = useTerminalSessions(instanceId ?? "");
|
||||
|
||||
// Auto-create default session if none exist
|
||||
// Auto-create default session if none exist (guard against double-fire)
|
||||
useEffect(() => {
|
||||
if (!loading && sessions.length === 0 && !error && instanceId) {
|
||||
if (
|
||||
!loading &&
|
||||
sessions.length === 0 &&
|
||||
!error &&
|
||||
instanceId &&
|
||||
!hasAutoCreated.current
|
||||
) {
|
||||
hasAutoCreated.current = true;
|
||||
void createSession("Session 1");
|
||||
}
|
||||
}, [loading, sessions.length, error, instanceId, createSession]);
|
||||
@@ -232,16 +240,13 @@ export const TerminalPage: React.FC = () => {
|
||||
{sessions
|
||||
.filter((session) => session.id === activeSessionId)
|
||||
.map((session) => (
|
||||
<div
|
||||
key={session.id}
|
||||
className="terminal-instance active"
|
||||
>
|
||||
<div key={session.id} className="terminal-instance active">
|
||||
<TerminalComponent
|
||||
ref={terminalRefs.current[session.id]}
|
||||
instanceId={instanceId}
|
||||
sessionId={session.id}
|
||||
onClose={() => handleClose(session.id)}
|
||||
isMobile={true}
|
||||
ref={terminalRefs.current[session.id]}
|
||||
instanceId={instanceId}
|
||||
sessionId={session.id}
|
||||
onClose={() => handleClose(session.id)}
|
||||
isMobile={true}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
@@ -291,10 +296,7 @@ export const TerminalPage: React.FC = () => {
|
||||
{sessions
|
||||
.filter((session) => session.id === activeSessionId)
|
||||
.map((session) => (
|
||||
<div
|
||||
key={session.id}
|
||||
className="terminal-instance active"
|
||||
>
|
||||
<div key={session.id} className="terminal-instance active">
|
||||
<TerminalComponent
|
||||
ref={terminalRefs.current[session.id]}
|
||||
instanceId={instanceId}
|
||||
|
||||
Reference in New Issue
Block a user