fix: v2.5.8 — the browser terminal attaches on Windows

node-pty on Windows looks the file it starts up on PATH exactly as given and
never adds `.exe`, so `tmux attach-session` started as a bare `tmux` failed
with "File not found" on every attach: the browser's terminal socket opened,
closed, and reconnected every 1.5 s.

- pty-attach.js: attachBinary names tmux by its full file name on Windows
  (ai-tools.which("tmux"), else tmux.exe)
- websocket.js: an attach that throws is logged as `[WS] attach <run> failed`,
  the one place that says why the terminal keeps reconnecting
This commit is contained in:
2026-10-07 00:23:32 +07:00
parent 4163f431bb
commit 7543b8d62a
8 changed files with 31 additions and 9 deletions
+14
View File
@@ -169,3 +169,17 @@ describe("pty-attach", () => {
assert.equal(killed, true);
});
});
describe("attachBinary on Windows", () => {
it("names tmux with its extension — node-pty there never adds `.exe`", () => {
// A bare `tmux` made node-pty answer "File not found" on every attach, so
// the browser's terminal opened, closed and reconnected every 1.5 s.
const saved = process.env.PATH;
process.env.PATH = "";
try {
assert.equal(ptyAttach.attachBinary("win32"), "tmux.exe");
} finally {
process.env.PATH = saved;
}
});
});
+8 -3
View File
@@ -42,9 +42,14 @@ function __setSpawnImpl(fn) {
* file), else `tmux` on PATH. A client must match its server — tmux installed
* in a newer version while the server runs makes PATH's client fail every
* attach with "open terminal failed: not a terminal", though every other tmux
* command still works. Linux only; elsewhere PATH's tmux as before.
* command still works. Linux only; elsewhere PATH's tmux as before — on
* Windows by its full file name: node-pty there looks the file up on PATH as
* given, never adding `.exe`, so a bare `tmux` failed every attach with
* "File not found" (the browser saw a socket that opened and closed, again
* every 1.5 s).
*/
function attachBinary() {
function attachBinary(platform = process.platform) {
if (platform === "win32") return require("./ai-tools").which("tmux") || "tmux.exe";
const pid = tmux.serverPid();
if (!pid) return "tmux";
const exe = `/proc/${pid}/exe`;
@@ -153,4 +158,4 @@ function attach(ws, runId, { cols, rows }) {
// resolves a working native binding with no compiler needed.
__setSpawnImpl((...args) => require("@lydell/node-pty").spawn(...args));
module.exports = { attach, validateRunId, __setSpawnImpl };
module.exports = { attach, attachBinary, validateRunId, __setSpawnImpl };
+3
View File
@@ -121,6 +121,9 @@ function initPtyWebSocket(server) {
try {
attach(ws, runId, size || { cols: 80, rows: 24 });
} catch (err) {
// The browser only sees a socket that closed and retries every 1.5 s;
// this line is the one place that says why.
console.warn(`[WS] attach ${runId} failed:`, err.message);
try {
ws.close(1008, err.message);
} catch {