Add API to promote a headless Browser page to InteractiveBrowser mid-session #76
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
When a headless
Browsersession hits a captcha or anti-bot challenge, there's no way to hand the current page to a human for interactive solving. The current workaround in mort is:InteractiveBrowserin the captcha proxy for the humanBrowserwith those cookies and retry from scratchThis works but is wasteful and fragile — the retry navigates from scratch, which may trigger different anti-bot behavior, and any page-local state (JS variables, session storage, service workers) is lost.
Proposed Solution
Add a way to promote an existing headless page to an
InteractiveBrowserso the same page (with its full state) can be handed to a human:The promoted
InteractiveBrowserwould reuse the exact same Playwright process, browser instance, context (cookies, storage), and page. The human interacts with the page (solving a captcha, clicking through a consent wall, etc.), then the caller can either:InteractiveBrowserto extract contentKey design considerations
Browsershould not close the page/context after promotion. Need to handle the lifecycle carefully — either the caller explicitly transfers ownership, or theDocumentis detached from the originalBrowser.Documentwraps aplaywright.Pagebut doesn't expose it. The promotion API would need access to the underlying page, either throughDocumentor by having the caller pass it.InteractiveBrowser.Screenshot()method is already implemented for the existing interactive browser and would work on the promoted page without changes.BrowserContext, so cookies set during interactive use are immediately available if the session is demoted back to scraping.Use Cases
Current Architecture Blockers
All Playwright objects (
pw,browser,ctx,page) are unexported fields in bothplayWrightBrowserandinteractiveBrowser. There's no shared interface or inheritance betweenBrowserandInteractiveBrowser. Implementing this requires either:Starting work on this. Plan:
detachedfield todocumentstruct —Close()becomes no-op when detachedownsInfrastructureanddetachedfields tointeractiveBrowser— promoted browsers only close the page, not the entire pw/browser/ctx stackpromote.gowith package-levelPromoteToInteractive(Document)andDemoteToDocument(InteractiveBrowser)functionspromote_test.goBranch:
feature/76-promote-to-interactiveImplementation complete. PR: #78
Changes:
document.go— addeddetachedfield;Close()is a no-op when detachedinteractive.go— addedownsInfrastructureanddetachedfields; promoted browsers only close the page, not the pw/browser/ctx stackpromote.go(new) —PromoteToInteractive()andDemoteToDocument()package-level functionspromote_test.go(new) — 4 unit tests covering error pathsAll builds, tests, and vet pass.