fix: v2.5.6 — Windows panes without sh, UI build without node on PATH

- Windows panes: a WinGet tmux runs native programs and there is no `sh`, so
  every pane's `sh -c` wrapper died at once ("Command failed: tmux set-option
  … mouse on"). paneArgv runs the CLI itself on win32, and a Terminal gets
  tmux's own default shell (newSession drops the `--` for an empty argv).
- Plugin UI build: client deps install with --ignore-scripts and tsc/vite run
  by the running node instead of `npm run build`. On Windows esbuild's
  postinstall went through cmd.exe with no `node` on the server's PATH, both
  installs failed, and Update now kept the previous UI. A test keeps the two
  build steps equal to client/package.json's build script.
- Update all: Atomics.wait instead of a `sleep` binary Windows lacks.
This commit is contained in:
2026-10-06 23:17:42 +07:00
parent 5fe5d51b1b
commit fcf8d8010a
13 changed files with 112 additions and 39 deletions
+20 -13
View File
@@ -77,6 +77,23 @@ const WAIT_FOR_CLIENT =
'i=0; while [ "$(tmux display -p -t "$TMUX_PANE" \'#{session_attached}\' 2>/dev/null)" = 0 ]' +
" && [ $i -lt 20 ]; do sleep 0.1; i=$((i+1)); done;";
const CLIENT_WAIT_MS = 2000;
/**
* A pane's tmux command: `argv` behind {@link WAIT_FOR_CLIENT}, or — with no
* `argv` — the login shell behind it. Windows has no `sh` (a WinGet tmux runs
* native programs), and an `sh -c` there died at once and took the session
* with it, so it gets the command itself, or nothing for tmux's own default
* shell.
* ponytail: no wait for the attach on Windows, so a pane may first draw at
* 80×24 (issue #55); port the wait to PowerShell if that shows.
*/
function paneArgv(argv, platform = process.platform) {
if (platform === "win32") return argv || [];
// argv rides as sh's positional parameters, never inside the script text.
return argv
? ["sh", "-c", `${WAIT_FOR_CLIENT} exec "$@"`, "sh", ...argv]
: ["sh", "-c", `${WAIT_FOR_CLIENT} exec "$SHELL" -l`];
}
const CODEX_SANDBOXES = new Set(["read-only", "workspace-write", "danger-full-access"]);
const CODEX_APPROVALS = new Set(["on-request", "never"]);
// Codex has no `--effort` flag; reasoning effort is a config override.
@@ -493,13 +510,7 @@ function spawnRun(args) {
};
if (!tmux.hasSession(id)) {
// argv rides as sh's positional parameters, never inside the script text.
tmux.newSession({
name: id,
cwd,
argv: ["sh", "-c", `${WAIT_FOR_CLIENT} exec "$@"`, "sh", ...argv],
env,
});
tmux.newSession({ name: id, cwd, argv: paneArgv(argv), env });
record();
schedulePaste();
} else if (SHELL_COMMANDS.has(tmux.paneCommand(id) || "")) {
@@ -554,12 +565,7 @@ function spawnShell({ laneId, slot = 0, cwd }) {
};
// Started once the pane has its real size (WAIT_FOR_CLIENT, issue #55), as
// the login shell tmux would have run (tmux sets SHELL in the pane).
tmux.newSession({
name: id,
cwd,
argv: ["sh", "-c", `${WAIT_FOR_CLIENT} exec "$SHELL" -l`],
env,
});
tmux.newSession({ name: id, cwd, argv: paneArgv(null), env });
dashboardRuns?.recordRun({
id,
sessionId: null,
@@ -636,4 +642,5 @@ module.exports = {
runIdForLane,
MAX_PANE_SLOT,
SHELL_COMMANDS,
paneArgv,
};