Skip to content

Commit 5ceb97f

Browse files
committed
fix(plugin-terminals): make node-pty an optional dependency
The prebuilt PTY binary isn't published for every Node ABI on every OS (e.g. Node 26 on Windows 404s and the source-build fallback crashes), which failed `pnpm install`. node-pty is already lazily imported with a piped-child fallback, so move it to optionalDependencies — a missing prebuild is now non-fatal. Gate the PTY tests on actual backend availability: the real-TTY check runs wherever a PTY exists (incl. Windows conpty), while the POSIX-only semantics (SIGWINCH, foreground process name, stdin echo) stay POSIX-gated.
1 parent b43d19b commit 5ceb97f

3 files changed

Lines changed: 60 additions & 25 deletions

File tree

plugins/terminals/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,15 @@
5757
}
5858
},
5959
"dependencies": {
60-
"@homebridge/node-pty-prebuilt-multiarch": "catalog:deps",
6160
"@xterm/addon-fit": "catalog:frontend",
6261
"@xterm/xterm": "catalog:frontend",
6362
"nostics": "catalog:deps",
6463
"pathe": "catalog:deps",
6564
"valibot": "catalog:deps"
6665
},
66+
"optionalDependencies": {
67+
"@homebridge/node-pty-prebuilt-multiarch": "catalog:deps"
68+
},
6769
"devDependencies": {
6870
"@types/node": "catalog:types",
6971
"devframe": "workspace:*",

plugins/terminals/test/terminals.test.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,22 @@ import process from 'node:process'
44
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
55
import { WebSocket } from 'ws'
66
import { SESSIONS_STATE_KEY, TERMINAL_STREAM_CHANNEL } from '../src/constants'
7+
import { isPtyAvailable } from '../src/node/index'
78
import { bootClient, call, collectUntil, startTerminalsServer } from './_utils'
89

910
vi.stubGlobal('WebSocket', WebSocket)
1011

1112
const NODE = process.execPath
12-
// PTY semantics differ on Windows (conpty): no SIGWINCH, the foreground
13-
// process name resolves to the TERM name, and stdin round-trips are slow to
14-
// render. These behaviours are exercised on POSIX; Windows keeps the
15-
// `isTTY` interactive coverage below.
16-
const itPosix = process.platform === 'win32' ? it.skip : it
13+
const ptyAvailable = await isPtyAvailable()
14+
const isWindows = process.platform === 'win32'
15+
16+
// A real pseudo-terminal works wherever the PTY backend is installed
17+
// (including Windows conpty); skip when the optional native module is absent.
18+
const itPty = ptyAvailable ? it : it.skip
19+
20+
// These rely on POSIX PTY semantics that conpty doesn't provide: SIGWINCH,
21+
// foreground-process-name resolution, and prompt stdin echo timing.
22+
const itPosixPty = (!isWindows && ptyAvailable) ? it : it.skip
1723

1824
function subscribe(client: TestClient, id: string) {
1925
return client.streaming.subscribe<string>(TERMINAL_STREAM_CHANNEL, id)
@@ -71,7 +77,7 @@ describe('@devframes/plugin-terminals', () => {
7177
).rejects.toThrow(/read-only/i)
7278
})
7379

74-
itPosix('runs an interactive PTY session that accepts input', async () => {
80+
itPosixPty('runs an interactive PTY session that accepts input', async () => {
7581
const client = bootClient(server.port)
7682
await new Promise(r => setTimeout(r, 50))
7783

@@ -90,7 +96,7 @@ describe('@devframes/plugin-terminals', () => {
9096
expect(output).toContain('echo:ping')
9197
})
9298

93-
it('gives interactive sessions a real TTY (TUI support)', async () => {
99+
itPty('gives interactive sessions a real TTY (TUI support)', async () => {
94100
const client = bootClient(server.port)
95101
await new Promise(r => setTimeout(r, 50))
96102

@@ -105,7 +111,7 @@ describe('@devframes/plugin-terminals', () => {
105111
expect(output).toContain('isTTY=true')
106112
})
107113

108-
itPosix('propagates resize to the PTY (SIGWINCH) for TUI layout', async () => {
114+
itPosixPty('propagates resize to the PTY (SIGWINCH) for TUI layout', async () => {
109115
const client = bootClient(server.port)
110116
await new Promise(r => setTimeout(r, 50))
111117

@@ -140,7 +146,7 @@ describe('@devframes/plugin-terminals', () => {
140146
expect(restarted.status).toBe('running')
141147
})
142148

143-
itPosix('tracks the foreground process name for PTY sessions', async () => {
149+
itPosixPty('tracks the foreground process name for PTY sessions', async () => {
144150
const client = bootClient(server.port)
145151
await new Promise(r => setTimeout(r, 50))
146152

pnpm-lock.yaml

Lines changed: 42 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)