fix(terminal): contain transient resize failures
This commit is contained in:
committed by
Menghuan1918
parent
2088ef6891
commit
cdd5706f5b
@@ -40,6 +40,28 @@ export function clampDims(cols: number, rows: number): { cols: number; rows: num
|
||||
return { cols: clamp(cols), rows: clamp(rows) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Best-effort resize for WebSocket-driven terminal views. Layout animation
|
||||
* can briefly produce unusable dimensions, and node-pty can reject a resize
|
||||
* after the socket setup's outer try/catch has returned. Ignore that one
|
||||
* frame so the host stays alive and a later valid measurement can retry.
|
||||
* Returns whether node-pty accepted the resize.
|
||||
*/
|
||||
export function tryResizePty(
|
||||
pty: Pick<IPty, 'resize'>,
|
||||
cols: number,
|
||||
rows: number,
|
||||
): boolean {
|
||||
if (!Number.isFinite(cols) || !Number.isFinite(rows)) return false
|
||||
const dims = clampDims(cols, rows)
|
||||
try {
|
||||
pty.resize(dims.cols, dims.rows)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializable snapshot of one agent terminal — the shape the model sees
|
||||
* through `terminal_list` and the sidebar sees through the push endpoint.
|
||||
|
||||
+3
-5
@@ -41,7 +41,7 @@ import { launchExternal } from './open-external.ts'
|
||||
import * as git from './git.ts'
|
||||
import { SettingsConflictError, settingsNamespace, type SettingsNamespace } from '@deepseek-ai/dsh-settings'
|
||||
import { defaultShell, ensureSpawnHelper, PtyManager, shellDisplayName } from './pty-manager.ts'
|
||||
import { AgentPtyRegistry, clampDims, type AgentTerminalHandle } from './agent-pty.ts'
|
||||
import { AgentPtyRegistry, tryResizePty, type AgentTerminalHandle } from './agent-pty.ts'
|
||||
import {
|
||||
DSH_NODE_PTY_RANGE,
|
||||
depsStatus,
|
||||
@@ -1199,8 +1199,7 @@ async function attachTerminal(
|
||||
&& control.type === 'resize'
|
||||
&& typeof control.cols === 'number' && typeof control.rows === 'number'
|
||||
) {
|
||||
const dims = clampDims(control.cols, control.rows)
|
||||
handle.pty.resize(dims.cols, dims.rows)
|
||||
tryResizePty(handle.pty, control.cols, control.rows)
|
||||
} else {
|
||||
handle.pty.write(text)
|
||||
}
|
||||
@@ -1268,8 +1267,7 @@ function pumpAgentTerminal(
|
||||
&& control.type === 'resize'
|
||||
&& typeof control.cols === 'number' && typeof control.rows === 'number'
|
||||
) {
|
||||
const dims = clampDims(control.cols, control.rows)
|
||||
handle.pty.resize(dims.cols, dims.rows)
|
||||
tryResizePty(handle.pty, control.cols, control.rows)
|
||||
} else if (control === null) {
|
||||
// Raw text input (a JSON-looking string the pty would have received
|
||||
// verbatim is reachable in theory but is exotic for an agent terminal;
|
||||
|
||||
+37
-2
@@ -5,8 +5,14 @@
|
||||
* console noise from node-pty's ConPTY module on Windows is expected and
|
||||
* does not affect the assertions.
|
||||
*/
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { AgentPtyRegistry, ALLOWED_SIGNALS, snapshotOf, type AgentTerminalSnapshot } from '../src/agent-pty.ts'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import {
|
||||
AgentPtyRegistry,
|
||||
ALLOWED_SIGNALS,
|
||||
snapshotOf,
|
||||
tryResizePty,
|
||||
type AgentTerminalSnapshot,
|
||||
} from '../src/agent-pty.ts'
|
||||
|
||||
/**
|
||||
* Resolve a shell binary for tests: on Windows use PowerShell (available on
|
||||
@@ -33,6 +39,35 @@ async function waitForTranscript(
|
||||
return handle?.transcript ?? ''
|
||||
}
|
||||
|
||||
describe('tryResizePty', () => {
|
||||
it('clamps valid dimensions before resizing', () => {
|
||||
const resize = vi.fn()
|
||||
|
||||
expect(tryResizePty({ resize }, 5000, 80.9)).toBe(true)
|
||||
expect(resize).toHaveBeenCalledWith(1024, 80)
|
||||
})
|
||||
|
||||
it('contains a node-pty resize failure', () => {
|
||||
const resize = vi.fn(() => {
|
||||
throw new Error('Usage: pty.resize(fd, cols, rows, xPixel, yPixel)')
|
||||
})
|
||||
|
||||
expect(tryResizePty({ resize }, 80, 24)).toBe(false)
|
||||
expect(resize).toHaveBeenCalledWith(80, 24)
|
||||
})
|
||||
|
||||
it.each([
|
||||
[Number.NaN, 24],
|
||||
[Number.POSITIVE_INFINITY, 24],
|
||||
[80, Number.NEGATIVE_INFINITY],
|
||||
])('rejects non-finite dimensions without calling node-pty (%s, %s)', (cols, rows) => {
|
||||
const resize = vi.fn()
|
||||
|
||||
expect(tryResizePty({ resize }, cols, rows)).toBe(false)
|
||||
expect(resize).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('AgentPtyRegistry', () => {
|
||||
it('creates a terminal with a uuid, writes the command to stdin, and lists it', async () => {
|
||||
const registry = new AgentPtyRegistry(testShell())
|
||||
|
||||
Reference in New Issue
Block a user