diff --git a/packages/pty/pty-local/src/session.ts b/packages/pty/pty-local/src/session.ts index ebd7e076cf..faea1ef84d 100644 --- a/packages/pty/pty-local/src/session.ts +++ b/packages/pty/pty-local/src/session.ts @@ -167,7 +167,7 @@ export class LocalPtySession implements PtyBackendSession { private activeDeadlineTimer: NodeJS.Timeout | undefined private activeAbort: (() => void) | undefined private interrupting: LocalSendOperation | undefined - private activeWrite: { operation: LocalSendOperation; settled: Promise } | undefined + private activeWrite: Promise | undefined private pollingReady: LocalSendOperation | undefined private polling = false private promptSeen = false @@ -238,7 +238,7 @@ export class LocalPtySession implements PtyBackendSession { } this.activeDeadlineTimer = setTimeout(() => { if (this.active === operation) { - this.settleActive('timeout', this.activeWrite?.operation === operation || this.interrupting === operation) + this.settleActive('timeout', this.activeWrite !== undefined || this.interrupting === operation) } }, this.config.timeoutMs) void this.beginSend(operation, request) @@ -254,11 +254,7 @@ export class LocalPtySession implements PtyBackendSession { if (input.length > 0 && !operation.cancelRequested) { this.resetReadinessEvidence() const write = this.terminal.write(input) - const activeWrite = { - operation, - settled: write.then(() => true, () => false), - } - this.activeWrite = activeWrite + this.activeWrite = write.then(() => true, () => false) try { await write } finally { @@ -272,7 +268,7 @@ export class LocalPtySession implements PtyBackendSession { return } // Closing can race the awaited provider write even though static analysis sees only local assignments. - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + // oxlint-disable-next-line typescript/no-unnecessary-condition -- awaited provider writes can close the session. if (this.active === operation && !this.closing) { this.pollingReady = operation this.schedulePoll(operation) @@ -448,7 +444,7 @@ export class LocalPtySession implements PtyBackendSession { this.polling = false const active = this.active // Awaited provider inspection can clear or replace the active send despite static analysis. - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + // oxlint-disable-next-line typescript/no-unnecessary-condition -- awaited inspection can replace the active send. if (active !== undefined && this.pollingReady === active) this.schedulePoll(active) } } @@ -506,7 +502,7 @@ export class LocalPtySession implements PtyBackendSession { private async interruptOnce(operation: LocalSendOperation): Promise { try { const activeWrite = this.activeWrite - if (activeWrite?.operation === operation && !await activeWrite.settled) return + if (activeWrite !== undefined && !await activeWrite) return await this.terminal.signalForeground('SIGINT') } catch (error: unknown) { if (this.active === operation && !this.closing) this.onTransportFailure(error) diff --git a/packages/subprocess/subprocess-local/src/terminal.ts b/packages/subprocess/subprocess-local/src/terminal.ts index 93b7a8fc2e..53c531675f 100644 --- a/packages/subprocess/subprocess-local/src/terminal.ts +++ b/packages/subprocess/subprocess-local/src/terminal.ts @@ -58,19 +58,18 @@ export class LocalTerminalHandle implements SubprocessTerminalHandle { exitCode: exitSignal === undefined || exitSignal === 0 ? exitCode : null, signal: signalName(exitSignal), }) - void this.terminate().catch(() => {}) }) } // node-pty writes synchronously; the seam returns a promise for remote transports. - // eslint-disable-next-line @typescript-eslint/require-await + // oxlint-disable-next-line typescript/require-await -- Preserve promise rejection semantics at the async provider seam. async write(data: string): Promise { if (this.exited) throw new Error('terminal process has exited') this.terminal.write(data) } // Local inspection is synchronous; the seam returns a promise for remote transports. - // eslint-disable-next-line @typescript-eslint/require-await + // oxlint-disable-next-line typescript/require-await -- Preserve promise rejection semantics at the async provider seam. async inspectForeground(): Promise { this.descendants() const processGroupId = this.inspector.foregroundPgid(this.pid) diff --git a/packages/subprocess/subprocess-local/tests/terminal.spec.ts b/packages/subprocess/subprocess-local/tests/terminal.spec.ts index 2eeb7c6a27..8a6270883f 100644 --- a/packages/subprocess/subprocess-local/tests/terminal.spec.ts +++ b/packages/subprocess/subprocess-local/tests/terminal.spec.ts @@ -128,6 +128,7 @@ describe('LocalTerminalHandle', () => { const handle = new LocalTerminalHandle(pty.asPty(), inspector, 20) const quiescent = handle.terminate() + expect(handle.terminate()).toBe(quiescent) await vi.advanceTimersByTimeAsync(20) expect(inspector.processes).toContainEqual([124, 'SIGKILL']) expect(pty.kills).toEqual([])