fix: wait for PTY startup readiness

This commit is contained in:
NI0317
2026-07-21 16:12:42 +08:00
parent 58cde5103a
commit 85ac747208
6 changed files with 39 additions and 12 deletions

View File

@@ -148,6 +148,7 @@ export class LocalPtySession implements PtyBackendSession {
private activeTimer: NodeJS.Timeout | undefined
private activeAbort: (() => void) | undefined
private promptSeen = false
private initializing = false
private lastOutputAt = Date.now()
private closePromise: Promise<void> | undefined
@@ -171,13 +172,19 @@ export class LocalPtySession implements PtyBackendSession {
/**
* Capture startup output through the same readiness contract as later sends.
* @param signal - optional cancellation while the shell reaches its first prompt.
* @returns Resolves after startup readiness; rejects if the shell exits.
* @returns Resolves after startup readiness; rejects on exit or readiness timeout.
*/
async initialize(signal?: AbortSignal): Promise<void> {
const operation = this.startSend({ text: '', submit: false, ...signal !== undefined ? { signal } : {} })
const result = await operation.done
if (result.waitReason === 'session_exit') throw new Error('PTY shell exited during startup')
this.motd = result.viewport
this.initializing = true
try {
const operation = this.startSend({ text: '', submit: false, ...signal !== undefined ? { signal } : {} })
const result = await operation.done
if (result.waitReason === 'session_exit') throw new Error('PTY shell exited during startup')
if (result.waitReason === 'timeout') throw new Error('PTY shell did not reach readiness before startup timeout')
this.motd = result.viewport
} finally {
this.initializing = false
}
}
startSend(request: PtySendRequest): PtySendOperation {
@@ -289,14 +296,15 @@ export class LocalPtySession implements PtyBackendSession {
return
}
const elapsed = Date.now() - operation.startedAt
if (elapsed >= this.config.exactProbeAfterMs) {
const startupHasOutput = !this.initializing || this.scrollback.snapshot().text.length > 0
if (startupHasOutput && elapsed >= this.config.exactProbeAfterMs) {
const pgid = this.inspector.foregroundPgid(this.pid)
if (pgid !== undefined && this.inspector.isStdinWaiting(pgid)) {
this.settleActive('stdin_read')
return
}
}
if (Date.now() - this.lastOutputAt >= this.config.idleSilenceMs) {
if (startupHasOutput && Date.now() - this.lastOutputAt >= this.config.idleSilenceMs) {
this.settleActive('inferred_idle')
return
}