diff --git a/src/main/ports/advertised-url-watcher.test.ts b/src/main/ports/advertised-url-watcher.test.ts index 8ec67d0b2..3d9078b16 100644 --- a/src/main/ports/advertised-url-watcher.test.ts +++ b/src/main/ports/advertised-url-watcher.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it } from 'vitest' +import { describe, expect, it, vi } from 'vitest' import { AdvertisedUrlWatcher, classifyHost, @@ -186,6 +186,25 @@ describe('AdvertisedUrlWatcher.ingest', () => { expect(watcher.lookup(WORKTREE, 3001)?.host).toBe('app.example.com') }) + it('keeps repeated PTY binds as no-ops once pending data is drained', () => { + const watcher = new AdvertisedUrlWatcher({ now: () => 1_000 }) + watcher.ingest(PTY, 'early https://app.example.com:3001/\n') + watcher.bindPty(PTY, WORKTREE) + + const internals = watcher as unknown as { ptyToWorktree: Map } + const originalSet = internals.ptyToWorktree.set + const setSpy = vi.fn(originalSet.bind(internals.ptyToWorktree)) + internals.ptyToWorktree.set = setSpy + try { + watcher.bindPty(PTY, WORKTREE) + } finally { + internals.ptyToWorktree.set = originalSet + } + + expect(setSpy).not.toHaveBeenCalled() + expect(watcher.lookup(WORKTREE, 3001)?.host).toBe('app.example.com') + }) + it('unbindPty drops buffered state for the PTY', () => { const watcher = bindFresh() watcher.ingest(PTY, 'cached https://cached.example.com:3002/\n') diff --git a/src/main/ports/advertised-url-watcher.ts b/src/main/ports/advertised-url-watcher.ts index 6d338e8fa..3eb4d6f05 100644 --- a/src/main/ports/advertised-url-watcher.ts +++ b/src/main/ports/advertised-url-watcher.ts @@ -281,8 +281,11 @@ export class AdvertisedUrlWatcher { } bindPty(ptyId: string, worktreeId: string): void { - this.ptyToWorktree.set(ptyId, worktreeId) const pending = this.pending.get(ptyId) + if (this.ptyToWorktree.get(ptyId) === worktreeId && pending === undefined) { + return + } + this.ptyToWorktree.set(ptyId, worktreeId) if (pending !== undefined) { this.pending.delete(ptyId) this.ingest(ptyId, pending)