v1.11.0: jo-browser sync + community PRs (May 2026)#4150
Merged
Conversation
- createTabState gets navigateAbort AbortController field - navigateCurrentPage races page.goto() with abort signal - DELETE /tabs/:tabId calls abort() before safePageClose, cancelling in-flight navigation immediately instead of waiting 30s timeout - Add camofox_snapshot_bytes histogram for snapshot size tracking - 6 new abort tests passing
…tion, fixes Backported from camofox-browser v1.5.2: - Lazy Prometheus metrics: off by default, enable with PROMETHEUS_ENABLED=1. Noop stubs when disabled — no prom-client import overhead. Fixes OpenClaw install blocking on optional dep. - Extract extractPageImages to lib/images.js (OpenClaw scanner isolation) - Extract actionFromReq + classifyError to lib/request-utils.js - buildRefs timeout leak: clearTimeout in both success and error paths - YT transcript session cleanup: close phantom __yt_transcript__ context when all tabs are gone - Session cleanup on empty tab groups: tab reaper now closes sessions with zero remaining tabs + triggers browser idle shutdown - Horizontal scroll: mouse.wheel now supports left/right directions - Delete tab/group: accept userId from query string or body - plugin.ts: proc.kill() on startup timeout, screenshot Content-Type guard (returns text error instead of base64-encoded JSON on non-image response) - 2 new test files: sessionCleanup.test.js, updated screenshotToolResult.test.js
Race 1 (YT transcript): browserTranscript cleanup counted tabGroups which was always 0 (YT pages aren't registered in tabGroups). First concurrent request to finish would close the context, killing other requests' pages. Fix: use context.pages() to check actual live pages before closing. Race 2 (tab reaper / session expiry): context.close() fired without await, then session deleted from map. A request that already got the session ref (between getSession return and newPage) would use a closing context. Fix: set session._closing = true before teardown; getSession() treats _closing sessions as dead and creates a new one. Both fixes also applied to the session expiry timer (same pattern). 16 unit tests added covering _closing flag, getSession skip, YT concurrent cleanup, and session expiry sentinel.
When a click/navigate/open_url times out (e.g., Cloudflare holding the connection for 30s), the Decodo proxy session becomes poisoned — all subsequent requests through the same BrowserContext hang. Previously, timeouts only tracked per-tab consecutive counts without destroying the session, so recovery tabs inherited the poisoned proxy. Now navigation-related timeouts (click, navigate, open_url) destroy the entire user session, so the next request gets a fresh BrowserContext with a fresh proxy session. Non-navigation timeouts (type, scroll) still use the per-tab consecutive timeout tracking. Root cause: WWDC cron clicked a MacRumors Cloudflare-protected link, the proxy session got poisoned, recovery tab's prewarm to google.com also timed out on the same session, browser agent gave up entirely.
Drifts >120s are OS sleep/hibernate, not real event-loop stalls. Skip them and suppress the next 5 ticks to avoid post-wake jitter false positives (like the 6s stall in #83). Fixes #82, #83
Merge artifact from v1.7.4 sync introduced duplicate 'let lastHeapUsed' at lines 976 and 1004 in reporter.js. Node.js SyntaxError on startup caused all 3 Fly machines to crash-loop and exhaust retries. All machines have been stopped since ~05:11 UTC, breaking browser-dependent cron jobs and use_browser tool calls.
Adds cross-origin iframe traversal to ariaSnapshot and ref resolution, enabling the browser agent to see and fill card fields inside Stripe/Shopify PCI-compliant iframes (checkout.pci.shopifyinc.com). Changes: - _buildRefsInner(): process child frames after main frame, storing frameName/frameUrl in each ref for frame-aware resolution - getAriaSnapshot(): append iframe content to YAML with section markers - refToLocator(): resolve refs via frame.getByRole() when ref belongs to an iframe, falling back to page-level resolution Also: - Add IFRAME_SKIP_PATTERNS to filter tracking/analytics iframes - Fix SKIP_PATTERNS: /date/i was filtering 'Expiration date' fields, narrowed to /datepicker/i, /date.?picker/i, /calendar/i, /^date$/i Tested on Allbirds (Shopify) checkout: all 4 Stripe PCI iframe fields (card number, expiry, CVC, name on card) now visible and fillable. Before: 0/4 card fields accessible. After: 4/4 accessible and fillable.
The proxy retry path in POST /tabs reassigns tabState when creating a fresh page after a proxy error. tabState was declared const at line 2313 but reassigned at line 2338. This caused TypeError: Assignment to constant variable when a proxy error triggered the retry. Same pattern as 49808e2 (which fixed session but missed tabState).
…cking, tab locks
- Remove navigate auto-create: return 404/410 for unknown tabs per contract
- Refresh session.lastAccess on all tab ops (snapshot, click, type, press,
scroll, links, back, forward, refresh, wait, downloads, images, screenshot,
stats, viewport) so active tabs don't hit session timeout
- Add withTabLock to scroll and links routes for serialization
- Register popups (target=_blank, window.open) as managed tabs via
page.on('popup') attached to each managed page (no orphaned pages)
- Add tabLifecycleContract test suite covering stale-tab errors, lastAccess
refresh, and popup registration
- Update tabRecycling test to expect 404 on navigate with unknown tab
ref JO-2452 JO-2456 JO-2457
Prevent repeated idle cleanup ticks from resetting the browser shutdown timer, which left zero-session Camoufox processes running indefinitely. Add browser process-tree RSS checks so idle Firefox child processes trigger cleanup even when Node memory looks healthy. Also fix watchdog tab summaries to use tabGroups instead of stale session.tabs state.
Treat an intentionally stopped idle browser as healthy so Fly does not mark machines critical after the Camoufox process is reclaimed.
Fall back to scanning the container for Camoufox and Xvfb survivor processes when Playwright does not expose a browser PID. This prevents idle shutdown from leaving disconnected browser children behind.
A 429 from the relay means the report was rate-limited and not accepted. Returning true silently dropped crash reports under sustained rate limiting, giving callers false confidence that reporting succeeded. Return false on 429 so callers can decide whether to retry. Honors the never-throws contract — no blocking retry logic added.
…re/cleanup Gate sensitive endpoints with authMiddleware() (CAMOFOX_API_KEY or CAMOFOX_ACCESS_KEY required, loopback fallback in non-production). - /tabs/:tabId/evaluate — arbitrary JS execution (CWE-94) - /sessions/:userId DELETE — destroy any user's session - /pressure/cleanup — force-close tabs/sessions Also refactors /sessions/:userId/cookies to use authMiddleware() instead of duplicated inline auth logic. Based on PR #3087 by sebastiondev. Co-authored-by: sebastiondev <[email protected]>
…ndows Dynamic `import(indexPath)` failed on Windows with `ERR_UNSUPPORTED_ESM_URL_SCHEME` because Node's ESM loader treats the drive letter (`c:`) as a URL protocol. POSIX absolute paths happened to work, masking the issue. Use `pathToFileURL(indexPath).href` so the loader receives a valid `file://` URL on all platforms. Behavior on POSIX is unchanged. Co-Authored-By: Claude Opus 4.7 <[email protected]>
Based on PR #3111 by dawsman. Co-authored-by: dawsman <[email protected]>
Add three changes to make camofox-browser buildable on Windows: - build.ps1: PowerShell script mirroring the Makefile targets (build, up, down, reset, clean, fetch) for Windows users without make. - .gitattributes: enforce LF line endings for .sh files so shell scripts don't break when run inside Linux containers after a Windows clone. - Dockerfile + Dockerfile.ci: run plugin installer via 'sh' explicitly instead of relying on +x permissions, since Docker COPY on Windows does not preserve executable bits.
This was referenced May 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Syncs jo-browser improvements and merges reviewed community PRs for the next release.
Synced from jo production work (15 commits)
Community PRs merged
contracts.tools(fixes #3141, #3392)/evaluate,DELETE /sessions/:userId,/pressure/cleanup(CWE-94)pathToFileURLfor Windows ESM plugin loadingenabled: falsein configSecurity
/tabs/:tabId/evaluatenow requiresCAMOFOX_API_KEYorCAMOFOX_ACCESS_KEY(was unauthenticated since v1.4.0)DELETE /sessions/:userIdandPOST /pressure/cleanupalso gatedauthMiddleware()Issues
Closes:
Refs: