Skip to content

v1.11.0: jo-browser sync + community PRs (May 2026)#4150

Merged
skyfallsin merged 22 commits into
masterfrom
sync/jo-browser-may23
May 24, 2026
Merged

v1.11.0: jo-browser sync + community PRs (May 2026)#4150
skyfallsin merged 22 commits into
masterfrom
sync/jo-browser-may23

Conversation

@skyfallsin
Copy link
Copy Markdown
Contributor

@skyfallsin skyfallsin commented May 24, 2026

Summary

Syncs jo-browser improvements and merges reviewed community PRs for the next release.

Synced from jo production work (15 commits)

  • Idle shutdown memory leak fix — kill orphaned Camoufox processes, keep health checks passing
  • Tab lifecycle hardening — stale-tab errors, lastAccess refresh, popup tracking, tab locks
  • Iframe support — snapshot and click elements inside cross-origin iframes (PCI payment fields)
  • Navigate abort on tab delete + snapshot size metrics
  • Lazy metrics — Prometheus off by default, no-op stubs when disabled
  • Session timeout fix — destroy session on navigation timeout to prevent proxy poisoning
  • Watchdog improvements — filter OS sleep/suspend from stall reports
  • YT transcript race conditions fixed
  • const→let fixes for retry paths (TypeError crashes)

Community PRs merged

PR Author Change
#3363 @kgarg2468 Declare OpenClaw contracts.tools (fixes #3141, #3392)
#3413 @SirBrenton Reporter: 429 ≠ success (silently dropped crash reports)
#3087 @sebastiondev Auth on /evaluate, DELETE /sessions/:userId, /pressure/cleanup (CWE-94)
#3967 @Chen-538 pathToFileURL for Windows ESM plugin loading
#3111 @dawsman VNC plugin explicit enabled: false in config
#3814 @pasmud Windows support: build.ps1, .gitattributes, Dockerfile sh fix

Security

  • /tabs/:tabId/evaluate now requires CAMOFOX_API_KEY or CAMOFOX_ACCESS_KEY (was unauthenticated since v1.4.0)
  • DELETE /sessions/:userId and POST /pressure/cleanup also gated
  • Cookie import endpoint refactored to use shared authMiddleware()

Issues

Closes:

Refs:

skyfallsin and others added 22 commits May 23, 2026 17:47
- 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]>
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Tool registration aborted in OpenClaw >=2026.5.x due to missing contracts.tools manifest declaration

7 participants