Platform
Platform-independent. This is a backend defect in src/backend/ssh/terminal.ts; the report is based on source inspection, not on a single client platform.
Server Installation Method
N/A — verified by reading source at Termix-SSH/Termix, branch main.
Version
main @ commit b30ed8eabea35107522b14452e7adc8e29b7ec52. (I have not mapped this to specific released versions — please confirm the affected release range from the code refs below.)
Troubleshooting
(Other boxes intentionally left unchecked because they don't apply — this is a source-level report, not a runtime-log report.)
The Problem
The get_cwd WebSocket handler writes a carriage-return-terminated shell command directly into the live PTY stream, with no check for what application currently holds that PTY. When the File Manager action is used on a terminal tab that has an interactive/foreground program running, that program receives the command text followed by an Enter, so input the user never sent gets submitted.
Verified code path (all links pinned to b30ed8eabea35107522b14452e7adc8e29b7ec52):
- Frontend sends
get_cwd — src/ui/desktop/apps/features/terminal/Terminal.tsx#L715-L720:
openFileManager: () => {
if (webSocketRef.current?.readyState === WebSocket.OPEN) {
webSocketRef.current.send(JSON.stringify({ type: "get_cwd" }));
} else {
- What invokes it — the File Manager icon button on a terminal tab,
src/ui/desktop/navigation/tabs/Tab.tsx#L276-L288 (onClick → onOpenFileManager()), which TopNavbar wires to terminalRef.current.openFileManager() only when the host has File Manager enabled, src/ui/desktop/navigation/TopNavbar.tsx#L505-L509.
- Backend writes the command into the PTY —
src/backend/ssh/terminal.ts#L761-L773:
case "get_cwd": {
const activeStream =
sessionManager.getSession(currentSessionId)?.sshStream ?? sshStream;
...
cwdPending = true;
cwdBuffer = "";
// Split the sentinel across shell variables so the echoed command
// itself never contains "TERMIX_CWD:" — only the output line does.
activeStream.write('a=TERMIX_CWD; echo "$a:$(pwd)"\r');
break;
}
The literal bytes written are a=TERMIX_CWD; echo "$a:$(pwd)" followed by a carriage return (\r). This case "get_cwd" block performs the write unconditionally; there is no check in that handler for whether an interactive or full-screen application currently owns the PTY.
This is a problem because the command + \r is delivered as input to whatever program is in the foreground of that PTY. If that program reads a line of input, the carriage return submits it — together with whatever the user had already typed and not yet sent.
How to Reproduce
- Add/enable an SSH host with Enable File Manager turned on (Host Manager → File Manager tab) and open a terminal tab to it.
- In that terminal, run
cat (no arguments) so a foreground program is reading line-buffered stdin.
- Type
hello but do not press Enter.
- Click the File Manager (folder) icon on that terminal's tab.
- Actual:
terminal.ts:772 writes a=TERMIX_CWD; echo "$a:$(pwd)" + \r into the PTY; cat therefore receives and echoes a completed line containing the user's hello followed by the injected probe text — i.e., a line was submitted that the user never entered.
- Expected: obtaining the working directory for the File Manager must not write keystrokes or an Enter into the foreground program's input. (Originally observed with an interactive line-based CLI running in the terminal: the probe text was appended to an unsent draft and submitted.)
Additional Context
- The injected string is deliberately constructed (comment at
terminal.ts:770-771) so the echoed command never contains the TERMIX_CWD: sentinel — only the result line does — and the backend then scrapes that result line (CWD_SENTINEL = "TERMIX_CWD:", terminal.ts:1503). So the design assumes a shell prompt that echoes and runs the line; it does not account for a foreground application that simply consumes the bytes as input.
- The File Manager icon is rendered on the terminal tab itself (
Tab.tsx:276-288), adjacent to the tab's other affordances.
- Verified workaround: turning off Enable File Manager for the host makes
onOpenFileManager undefined (TopNavbar.tsx:505-509), so the tab icon is not rendered and get_cwd can no longer be triggered from it.
- Suggestion (not verified, offered only as a direction): gate the
get_cwd write on the PTY not being in the alternate screen / not in a raw-input mode, or obtain the cwd without writing into the interactive PTY.
Platform
Platform-independent. This is a backend defect in
src/backend/ssh/terminal.ts; the report is based on source inspection, not on a single client platform.Server Installation Method
N/A — verified by reading source at
Termix-SSH/Termix, branchmain.Version
main@ commitb30ed8eabea35107522b14452e7adc8e29b7ec52. (I have not mapped this to specific released versions — please confirm the affected release range from the code refs below.)Troubleshooting
(Other boxes intentionally left unchecked because they don't apply — this is a source-level report, not a runtime-log report.)
The Problem
The
get_cwdWebSocket handler writes a carriage-return-terminated shell command directly into the live PTY stream, with no check for what application currently holds that PTY. When the File Manager action is used on a terminal tab that has an interactive/foreground program running, that program receives the command text followed by an Enter, so input the user never sent gets submitted.Verified code path (all links pinned to
b30ed8eabea35107522b14452e7adc8e29b7ec52):get_cwd—src/ui/desktop/apps/features/terminal/Terminal.tsx#L715-L720:src/ui/desktop/navigation/tabs/Tab.tsx#L276-L288(onClick→onOpenFileManager()), whichTopNavbarwires toterminalRef.current.openFileManager()only when the host has File Manager enabled,src/ui/desktop/navigation/TopNavbar.tsx#L505-L509.src/backend/ssh/terminal.ts#L761-L773:The literal bytes written are
a=TERMIX_CWD; echo "$a:$(pwd)"followed by a carriage return (\r). Thiscase "get_cwd"block performs the write unconditionally; there is no check in that handler for whether an interactive or full-screen application currently owns the PTY.This is a problem because the command +
\ris delivered as input to whatever program is in the foreground of that PTY. If that program reads a line of input, the carriage return submits it — together with whatever the user had already typed and not yet sent.How to Reproduce
cat(no arguments) so a foreground program is reading line-buffered stdin.hellobut do not press Enter.terminal.ts:772writesa=TERMIX_CWD; echo "$a:$(pwd)"+\rinto the PTY;cattherefore receives and echoes a completed line containing the user'shellofollowed by the injected probe text — i.e., a line was submitted that the user never entered.Additional Context
terminal.ts:770-771) so the echoed command never contains theTERMIX_CWD:sentinel — only the result line does — and the backend then scrapes that result line (CWD_SENTINEL = "TERMIX_CWD:",terminal.ts:1503). So the design assumes a shell prompt that echoes and runs the line; it does not account for a foreground application that simply consumes the bytes as input.Tab.tsx:276-288), adjacent to the tab's other affordances.onOpenFileManagerundefined(TopNavbar.tsx:505-509), so the tab icon is not rendered andget_cwdcan no longer be triggered from it.get_cwdwrite on the PTY not being in the alternate screen / not in a raw-input mode, or obtain the cwd without writing into the interactive PTY.