From d3cb38832bbe8502617ffa78d8d5a01a7410a1ed Mon Sep 17 00:00:00 2001 From: Max <112043822+Maxaubert@users.noreply.github.com> Date: Sat, 10 Oct 2026 08:04:30 +0200 Subject: [PATCH 1/4] fix: pin node-pty 1.2.0-beta.15 again, and test the pin (#159) #130's merge put node-pty ^1.1.0 back, undoing #128, and the owner saw node-pty 1.1.0's 'Assertion failed! remove_pty_baton' dialog again. Re-pin, and a unit test fails on any build without the pin. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01FHHaWKR4M5QtW7Wecyuk4t --- docs/regression-rules.md | 2 + package-lock.json | 78 ++++++++++++++++++++++++++++++++++--- package.json | 6 +-- src/main/nodePtyPin.test.ts | 24 ++++++++++++ 4 files changed, 101 insertions(+), 9 deletions(-) create mode 100644 src/main/nodePtyPin.test.ts diff --git a/docs/regression-rules.md b/docs/regression-rules.md index ca44b6b..78836ae 100644 --- a/docs/regression-rules.md +++ b/docs/regression-rules.md @@ -49,6 +49,8 @@ one-liner in `CLAUDE.md` only if it is among the most important. `ThreadSafeFunction::CallJS` -> abort). So every kill goes through `killPty` and `will-quit` holds the quit on `shellsGone` (3 s cap). It waits for the agent's `exitCode`, set by the native callback, not the exit EVENT, which lags 1-2.7 s for a pwsh killed mid-start (a warm shell). `quitManyShells`. + The pin was LOST once (#159, 2026-10-10): an icon PR's merge put `^1.1.0` back and the dialog + returned for the owner. `src/main/nodePtyPin.test.ts` now fails on any build without the pin. - **`titleBarStyle: 'hidden'`, never `frame: false`**: DWM will not composite acrylic behind a frameless window. - **Material before colour** (`material.ts`, measured on Electron 43): `setBackgroundMaterial('none')` diff --git a/package-lock.json b/package-lock.json index ec04151..f2ecb57 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "prism-terminal", - "version": "0.35.1", + "version": "0.35.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "prism-terminal", - "version": "0.35.1", + "version": "0.35.2", "license": "MIT", "dependencies": { "@xterm/addon-fit": "^0.11.0", @@ -14,7 +14,7 @@ "@xterm/addon-unicode11": "^0.9.0", "@xterm/addon-web-links": "^0.12.0", "@xterm/xterm": "^6.0.0", - "node-pty": "^1.1.0", + "node-pty": "1.2.0-beta.15", "react": "^19.2.7", "react-dom": "^19.2.7" }, @@ -2204,6 +2204,72 @@ "node": ">=14.0.0" } }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { + "version": "2.8.1", + "dev": true, + "inBundle": true, + "license": "0BSD", + "optional": true + }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", @@ -6107,9 +6173,9 @@ "license": "MIT" }, "node_modules/node-pty": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0.tgz", - "integrity": "sha512-20JqtutY6JPXTUnL0ij1uad7Qe1baT46lyolh2sSENDd4sTzKZ4nmAFkeAARDKwmlLjPx6XKRlwRUxwjOy+lUg==", + "version": "1.2.0-beta.15", + "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.2.0-beta.15.tgz", + "integrity": "sha512-vORSzHXi4Ofl7HemVWpuudLqCPdaQb4LfpRCUpE5HPxhp4JYscl8zZwxh11p26v2wvW24WMwnMfLjhRLixrfxA==", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 1dcd21a..f93f5f0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "prism-terminal", "productName": "Prism Terminal", - "version": "0.35.1", + "version": "0.35.2", "description": "A tabbed Windows terminal for AI CLIs.", "main": "./out/main/index.js", "author": "Max", @@ -29,7 +29,7 @@ "@xterm/addon-unicode11": "^0.9.0", "@xterm/addon-web-links": "^0.12.0", "@xterm/xterm": "^6.0.0", - "node-pty": "^1.1.0", + "node-pty": "1.2.0-beta.15", "react": "^19.2.7", "react-dom": "^19.2.7" }, @@ -57,6 +57,6 @@ "vitest": "^3.2.7" }, "allowScripts": { - "node-pty@1.1.0": true + "node-pty@1.2.0-beta.15": true } } diff --git a/src/main/nodePtyPin.test.ts b/src/main/nodePtyPin.test.ts new file mode 100644 index 0000000..13660f7 --- /dev/null +++ b/src/main/nodePtyPin.test.ts @@ -0,0 +1,24 @@ +import { readFileSync } from 'node:fs' +import { join } from 'node:path' +import { describe, expect, it } from 'vitest' + +// #127/#128 pinned node-pty to 1.2.0-beta.15: 1.1.0's exit threads race on an +// unlocked vector and its prebuild asserts ("Assertion failed! remove_pty_baton"), +// a modal dialog that holds the app open. #130 (an icon PR) put ^1.1.0 back in a +// merge and nothing noticed until the owner saw the dialog again (#159). The +// quit e2e only catches the race sometimes; this catches the pin every run. +const PIN = '1.2.0-beta.15' +const root = join(__dirname, '..', '..') +const json = (f: string): Record => JSON.parse(readFileSync(join(root, f), 'utf8')) + +describe('node-pty pin (#159)', () => { + it('package.json asks for exactly the pinned build', () => { + const pkg = json('package.json') + expect(pkg.dependencies['node-pty']).toBe(PIN) + expect(JSON.stringify(pkg)).not.toContain('node-pty@1.1.0') + }) + + it('the lockfile resolves it', () => { + expect(json('package-lock.json').packages['node_modules/node-pty'].version).toBe(PIN) + }) +}) From 67109cfda12a1195c73a555e93875ba633314e25 Mon Sep 17 00:00:00 2001 From: Max <112043822+Maxaubert@users.noreply.github.com> Date: Sat, 10 Oct 2026 08:37:52 +0200 Subject: [PATCH 2/4] fix(core): the core asks its hosts for node-pty 1.2.0-beta.15 (#159) Core's peer was ^1.1.0, which no prerelease matches, so Prism's npm refused the pin (ERESOLVE). Core 0.28.2. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01FHHaWKR4M5QtW7Wecyuk4t --- core/README.md | 4 +++- core/package.json | 4 ++-- src/main/nodePtyPin.test.ts | 5 +++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/core/README.md b/core/README.md index a4a7387..340d44d 100644 --- a/core/README.md +++ b/core/README.md @@ -277,7 +277,9 @@ resolve: { dedupe: ['react', 'react-dom'] }, optimizeDeps: { exclude: ['prism-te `react`, `node-pty` and `@xterm/*` must stay at compatible versions in both apps so npm keeps ONE copy of each; the core is verified against Prism Terminal's -lockfile but ships against Prism's. +lockfile but ships against Prism's. `node-pty` is EXACTLY `1.2.0-beta.15` in the +core's peer and in both apps (1.1.0's quit crash, #127/#159); a `^1.1.0` peer +does not match a prerelease, so npm refuses the pin in the host. ## Releasing the core diff --git a/core/package.json b/core/package.json index 1e67f97..d63f334 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "prism-term-core", - "version": "0.28.1", + "version": "0.28.2", "description": "What Prism Terminal and Prism share: the terminal (pty, shells, agent detection and indicator, themes, links, the panel, dictation) and the update chip with its window. TypeScript source, compiled by the host.", "license": "MIT", "private": true, @@ -40,7 +40,7 @@ "@xterm/addon-unicode11": "^0.9.0", "@xterm/addon-web-links": "^0.12.0", "@xterm/xterm": "^6.0.0", - "node-pty": "^1.1.0", + "node-pty": "1.2.0-beta.15", "react": "^19.2.7", "react-dom": "^19.2.7" } diff --git a/src/main/nodePtyPin.test.ts b/src/main/nodePtyPin.test.ts index 13660f7..144953b 100644 --- a/src/main/nodePtyPin.test.ts +++ b/src/main/nodePtyPin.test.ts @@ -18,6 +18,11 @@ describe('node-pty pin (#159)', () => { expect(JSON.stringify(pkg)).not.toContain('node-pty@1.1.0') }) + // A `^1.1.0` peer does not match a prerelease, so Prism's npm refused the pin. + it('the core asks its hosts for the same build', () => { + expect(json('core/package.json').peerDependencies['node-pty']).toBe(PIN) + }) + it('the lockfile resolves it', () => { expect(json('package-lock.json').packages['node_modules/node-pty'].version).toBe(PIN) }) From 1738f5497103cd86242955e9573a5faaaa7c2585 Mon Sep 17 00:00:00 2001 From: Max <112043822+Maxaubert@users.noreply.github.com> Date: Sat, 10 Oct 2026 08:38:33 +0200 Subject: [PATCH 3/4] fix(core): the peer allows 1.1.0 or the pinned beta (#159) Prism still asks for 1.1.0 until its own pin lands; an exact peer would make its core bump fail to install. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01FHHaWKR4M5QtW7Wecyuk4t --- core/README.md | 7 ++++--- core/package.json | 2 +- src/main/nodePtyPin.test.ts | 7 ++++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/core/README.md b/core/README.md index 340d44d..a881270 100644 --- a/core/README.md +++ b/core/README.md @@ -277,9 +277,10 @@ resolve: { dedupe: ['react', 'react-dom'] }, optimizeDeps: { exclude: ['prism-te `react`, `node-pty` and `@xterm/*` must stay at compatible versions in both apps so npm keeps ONE copy of each; the core is verified against Prism Terminal's -lockfile but ships against Prism's. `node-pty` is EXACTLY `1.2.0-beta.15` in the -core's peer and in both apps (1.1.0's quit crash, #127/#159); a `^1.1.0` peer -does not match a prerelease, so npm refuses the pin in the host. +lockfile but ships against Prism's. Both apps pin `node-pty` to EXACTLY +`1.2.0-beta.15` (1.1.0's quit crash, #127/#159). The core's peer names that +build (`^1.1.0 || 1.2.0-beta.15`): a bare `^1.1.0` matches no prerelease, so npm +refused the pin in Prism. ## Releasing the core diff --git a/core/package.json b/core/package.json index d63f334..092d71d 100644 --- a/core/package.json +++ b/core/package.json @@ -40,7 +40,7 @@ "@xterm/addon-unicode11": "^0.9.0", "@xterm/addon-web-links": "^0.12.0", "@xterm/xterm": "^6.0.0", - "node-pty": "1.2.0-beta.15", + "node-pty": "^1.1.0 || 1.2.0-beta.15", "react": "^19.2.7", "react-dom": "^19.2.7" } diff --git a/src/main/nodePtyPin.test.ts b/src/main/nodePtyPin.test.ts index 144953b..485442e 100644 --- a/src/main/nodePtyPin.test.ts +++ b/src/main/nodePtyPin.test.ts @@ -18,9 +18,10 @@ describe('node-pty pin (#159)', () => { expect(JSON.stringify(pkg)).not.toContain('node-pty@1.1.0') }) - // A `^1.1.0` peer does not match a prerelease, so Prism's npm refused the pin. - it('the core asks its hosts for the same build', () => { - expect(json('core/package.json').peerDependencies['node-pty']).toBe(PIN) + // A `^1.1.0` peer matches no prerelease, so Prism's npm refused the pin. It + // still allows 1.1.0 so Prism's core bump installs before Prism's own pin lands. + it('the core lets its hosts take the pinned build', () => { + expect(json('core/package.json').peerDependencies['node-pty'].split(' || ')).toContain(PIN) }) it('the lockfile resolves it', () => { From 7c791103f65bbad63f80d6d388830432d65e18f6 Mon Sep 17 00:00:00 2001 From: Max <112043822+Maxaubert@users.noreply.github.com> Date: Sat, 10 Oct 2026 08:41:00 +0200 Subject: [PATCH 4/4] fix: type the pin test's package files, no any (#159) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01FHHaWKR4M5QtW7Wecyuk4t --- src/main/nodePtyPin.test.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/nodePtyPin.test.ts b/src/main/nodePtyPin.test.ts index 485442e..167b63d 100644 --- a/src/main/nodePtyPin.test.ts +++ b/src/main/nodePtyPin.test.ts @@ -9,7 +9,12 @@ import { describe, expect, it } from 'vitest' // quit e2e only catches the race sometimes; this catches the pin every run. const PIN = '1.2.0-beta.15' const root = join(__dirname, '..', '..') -const json = (f: string): Record => JSON.parse(readFileSync(join(root, f), 'utf8')) +interface Pkg { + dependencies: Record + peerDependencies: Record + packages: Record +} +const json = (f: string): Pkg => JSON.parse(readFileSync(join(root, f), 'utf8')) as Pkg describe('node-pty pin (#159)', () => { it('package.json asks for exactly the pinned build', () => {