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
+23 -4
View File
@@ -30,9 +30,12 @@ const { resolveDashboardPort } = require("../server/lib/server-info");
const PLUGIN_ROOT = path.resolve(__dirname, "..");
const NPM = "npm";
const NODE = "node";
/** `cmd` is always NPM: run through `boot.npmCommand`, never a PATH lookup. */
function run(_cmd, args, cwd, stdio) {
/** NPM runs through `boot.npmCommand`, NODE is the running node itself —
* neither is ever a PATH lookup. */
function run(cmd, args, cwd, stdio) {
if (cmd === NODE) return spawnSync(process.execPath, args, { cwd, stdio }).status === 0;
const npm = boot.npmCommand(args);
const res = spawnSync(npm.file, npm.args, { cwd, stdio, env: npm.env, shell: npm.shell });
return res.status === 0;
@@ -89,14 +92,30 @@ function buildClient({
// `npm run build` failed, and the PREVIOUS bundle stayed in place: the
// dashboard kept serving the old version's UI while the API was already the
// new one. An explicit CLI flag outranks the env, which is the point.
const npmFlags = ["--no-audit", "--no-fund", "--include=dev"];
//
// `--ignore-scripts`, and the build run by node itself below rather than
// `npm run build`: a lifecycle script and a `.bin/*.cmd` shim both go
// through cmd.exe and look `node` up on PATH, and on Windows the server's
// PATH did not have it — esbuild's postinstall died with "'node' is not
// recognized", both installs failed, and the update kept the old UI.
// esbuild is the only dependency with an install script, and it finds its
// platform binary without it.
const npmFlags = ["--no-audit", "--no-fund", "--include=dev", "--ignore-scripts"];
if (
!exec(NPM, ["ci", ...npmFlags], src, stdio) &&
!exec(NPM, ["install", ...npmFlags], src, stdio)
) {
return "failed";
}
if (!exec(NPM, ["run", "build"], src, stdio)) return "failed";
// = client/package.json's "build" script (`tsc -b && vite build`); a test
// keeps the two in step.
const bin = (...p) => path.join(src, "node_modules", ...p);
if (
!exec(NODE, [bin("typescript", "bin", "tsc"), "-b"], src, stdio) ||
!exec(NODE, [bin("vite", "bin", "vite.js"), "build"], src, stdio)
) {
return "failed";
}
fs.rmSync(dist, { recursive: true, force: true });
fs.cpSync(path.join(src, "dist"), dist, { recursive: true });