build(pty): restore bundled ConPTY after rebuild#2752
Conversation
Greptile SummaryThis PR fixes a long-standing Windows 10 regression where fullscreen TUIs (opencode, claude, amp, etc.) received no mouse input or scroll events because the OS in-box ConPTY silently swallowed mouse-mode DECSETs and SGR mouse reports. The fix opts node-pty into its bundled, up-to-date ConPTY dll (
Confidence Score: 5/5Safe to merge; the fallback to in-box ConPTY is well-guarded and the dll restore runs at all the right callsites. The retry logic in spawnLocalPty is sound — markConptyDllUnavailable is only called when the in-box fallback succeeds, so command-level failures (bad cwd, missing binary) don't permanently disable the bundled dll. The copy script validates all required files before picking a version folder, handles the no-rebuilt-output case cleanly, and is tested against the key edge cases. The change is Windows-only at runtime and no-ops silently on all other platforms. No files require special attention; the most scrutiny-worthy path is the spawnLocalPty retry loop in local-pty.ts, which is covered by the new unit tests.
|
| Filename | Overview |
|---|---|
| .github/workflows/windows-beta-build.yml | Fixes sqlite3→better-sqlite3 module name in electron-rebuild args and adds copy-conpty-dll step after native rebuild; straightforward CI plumbing. |
| apps/emdash-desktop/scripts/copy-conpty-dll.ts | New utility that restores the bundled ConPTY dll after electron-rebuild wipes build/Release; validates arch, checks all required files exist before picking a version folder, and handles platform/arch guards cleanly. |
| apps/emdash-desktop/scripts/copy-conpty-dll.test.ts | Comprehensive tests covering no-op on non-Windows, correct arch selection, missing output/source, incomplete version folders, and unsupported archs. |
| apps/emdash-desktop/scripts/postinstall.ts | Correctly gates the copyConptyDll call on !disablePty, mirroring the existing PTY rebuild guard; the call is a no-op off Windows. |
| apps/emdash-desktop/scripts/release/rebuild-native.ts | Correctly chains copyConptyDll after rebuild completes for the target arch; arch is already validated earlier in the script. |
| apps/emdash-desktop/src/main/core/pty/conpty-dll.ts | Well-designed module with proper memoization, escape hatch env var, test seams, and correct mirroring of node-pty's native module search order. |
| apps/emdash-desktop/src/main/core/pty/conpty-dll.test.ts | Tests cover platform guard, env-var override, rebuilt-vs-prebuilt dll path resolution, and the electron-rebuild wipe scenario. |
| apps/emdash-desktop/src/main/core/pty/local-pty.ts | Spawn logic cleanly delegates ConPTY selection and adds a resilient fallback; markConptyDllUnavailable is only called when the fallback succeeds. |
| apps/emdash-desktop/src/main/core/pty/local-pty.test.ts | New unit tests verify ConPTY flag forwarding, dll-failure retry, dual-failure error propagation, and no-retry when dll was not requested. |
| apps/emdash-desktop/src/main/core/pty/local-pty.integration.test.ts | New Windows-only integration test verifies mouse-mode and alt-screen DECSETs pass through the bundled ConPTY; correctly uses it.runIf to skip on non-Windows. |
| apps/emdash-desktop/package.json | Appends the copy-conpty-dll step to the rebuild script so the dll is restored after every manual pnpm run rebuild invocation. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[spawnLocalPty called] --> B[resolveUseConptyDll]
B --> C{win32?}
C -- No --> D[return false]
C -- Yes --> E{EMDASH_DISABLE_CONPTY_DLL=1?}
E -- Yes --> D
E -- No --> F[conptyDllIsPresent - cached]
F --> G{cachedPresence}
G -- false --> D
G -- true --> H[return true]
D --> I[spawn with useConptyDll=false]
H --> J[spawn with useConptyDll=true]
J --> K{spawn throws?}
K -- No --> L[return LocalPtySession]
K -- Yes --> M[log warn]
M --> N[retry spawn useConptyDll=false]
N --> O{retry throws?}
O -- No --> P[markConptyDllUnavailable]
P --> Q[return LocalPtySession]
O -- Yes --> R[throw spawnError]
I --> S{spawn throws?}
S -- No --> L
S -- Yes --> T[throw spawnError]
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A[spawnLocalPty called] --> B[resolveUseConptyDll]
B --> C{win32?}
C -- No --> D[return false]
C -- Yes --> E{EMDASH_DISABLE_CONPTY_DLL=1?}
E -- Yes --> D
E -- No --> F[conptyDllIsPresent - cached]
F --> G{cachedPresence}
G -- false --> D
G -- true --> H[return true]
D --> I[spawn with useConptyDll=false]
H --> J[spawn with useConptyDll=true]
J --> K{spawn throws?}
K -- No --> L[return LocalPtySession]
K -- Yes --> M[log warn]
M --> N[retry spawn useConptyDll=false]
N --> O{retry throws?}
O -- No --> P[markConptyDllUnavailable]
P --> Q[return LocalPtySession]
O -- Yes --> R[throw spawnError]
I --> S{spawn throws?}
S -- No --> L
S -- Yes --> T[throw spawnError]
Reviews (3): Last reviewed commit: "fix(pty): select complete bundled ConPTY..." | Re-trigger Greptile
Description
useConptyDll:true, loading the bundled upt odate conptyScreenshot/Recording (if applicable)
on windows:
https://cap.link/p11pxnh1aefg92x
Checklist
messages and, when possible, the PR title