Skip to content

Fix/viewer stability#4

Open
PaoloC68 wants to merge 5 commits into
a0-community-plugins:mainfrom
PaoloC68:fix/viewer-stability
Open

Fix/viewer stability#4
PaoloC68 wants to merge 5 commits into
a0-community-plugins:mainfrom
PaoloC68:fix/viewer-stability

Conversation

@PaoloC68
Copy link
Copy Markdown

This PR fixes five distinct bugs that together cause the CamoFox VNC viewer iframe to disappear, reload every few seconds, and steal keyboard focus from the Agent Zero chat input — eventually making the WebUI unusable until the plugin is disabled. All five are reproducible on a fresh install of Agent Zero + this plugin.

Debugged collaboratively with a user on a long session; root-causes traced end-to-end with server logs, state file inspection, and live DevTools console probing. Each commit is self-contained and explains its own fix.

Bugs fixed

1. execute.py — silent CLI helper fallback (commit d87f44a)

When execute.py runs outside /[a0](https://helpa0.com/#) as cwd, from usr.plugins.camofox_browser.helpers.cli import resolve_camofox_command fails silently and falls back to a stub that always raises RuntimeError("CamoFox CLI helper unavailable"). Setup step 7 fails with no useful diagnostic. Fix: prepend /[a0](https://helpa0.com/#) to sys.path before the import.

2. viewer_url.pyvnc_session_key derived from timestamp (commit 92aa7b3)

vnc_session_key = int(vnc_ts * 1000) changes on every set_vnc() write. The WebUI store uses session_key to detect real session changes — a timestamp-based key defeats this and causes the iframe to be reloaded on every 3-second poll, generating a new signed JWT each time. Fix: hash the raw upstream URL — session_key now represents session identity.

3. state.py_normalize_entry clearing live vnc_url after idle TTL (commit 815a6b4, part 1)

The worst of the bunch. _normalize_entry() wipes vnc_url when vnc_age > _VNC_IDLE_TTL_SECONDS (10 s by default!) even though the camofox server's VNC stack is still running. The URL should reflect server reality, not a wall-clock heuristic. After 10 s of "idle" the iframe vanishes mid-session. Fix: remove the time-based clearing. URL changes only via explicit set_vnc() / clear_vnc().

4. state.pyget(user_id) strict per-user lookup (commit 815a6b4, part 2)

The WebUI has no agent context so resolve_user_id() returns "a0-default", while agent tools resolve to "a0-agent-N". Once the store caches _userId="a0-default", it keeps asking for that user even after browsing has moved to a0-agent-0. Result: empty vnc_url, no iframe. Fix: get() falls back to any active user when the requested one is idle.

5. camofox-store.jsshowBrowser() toggles even when server already visible (commit d269c23)

if (this.vncUrl) return; is the only early-out. If the WebUI's vncUrl is null but the server is already in virtual mode, clicking the browser button triggers a full toggle cycle (stop VNC → close persistent context → relaunch → start VNC = ~3 s downtime) for no reason. Fix: if displayMode is virtual/headed or _rawVncUrl is set, just reveal the panel and force a poll.

6. camofox-browser-btn.html — iframe wrapped in x-if (commit a3111ce)

x-if destroys and recreates DOM on every state flip. During the inevitable null-URL gap on any toggle, the iframe is torn out of the DOM and the placeholder briefly flashes. When the URL returns, a fresh iframe is built, noVNC autoconnects, and the new iframe steals keyboard focus — making chat input impossible. Fix: use x-show for the iframe (DOM persists, only CSS toggles). Placeholder also gates on _rawVncUrl to suppress flashes.

Test plan

  1. Fresh Agent Zero instance with this plugin enabled
  2. Trigger any camofox_browse action → iframe should appear
  3. Wait > 10 minutes idle → iframe should still be there
  4. Send chat messages that trigger more browser actions → no focus loss, no "reload every 10 s" loop
  5. Click browser icon while iframe is hidden → no toggle round-trip, panel reveals current session immediately

Notes for the maintainer

CC: full debug session preserved if you want any specific scenario walked through.

PaoloC68 added 5 commits May 11, 2026 22:19
When execute.py runs outside /a0 cwd, the 'from usr.plugins...' import
fails silently and falls back to a dummy resolve_camofox_command that
always raises 'CamoFox CLI helper unavailable'. Prepending /a0 to
sys.path makes the import succeed regardless of cwd.
vnc_session_key was set to int(vnc_ts * 1000), so every set_vnc() write
produced a new key even when the upstream URL was unchanged. The WebUI
store uses session_key to detect 'real' session changes — a timestamp-
based key defeated that and caused the iframe to reload on every poll.

Hashing the raw URL makes session_key represent session identity: it
changes only when the actual upstream token changes.
Two bugs fixed:

1. _normalize_entry() cleared vnc_url after _VNC_IDLE_TTL_SECONDS even
   though the camofox server's VNC stack was still running. The URL
   should reflect server reality (set_vnc/clear_vnc), not a wall-clock
   heuristic. After ~1h idle the iframe vanished mid-session.

2. get(user_id) returned a strict per-user lookup. The WebUI resolves
   to 'a0-default' (no agent context) while agent tools resolve to
   'a0-agent-N'. Once the store cached _userId='a0-default', it kept
   asking for that user even after browsing moved to 'a0-agent-0'.
   Now get() falls back to any active user when the requested one is
   idle, so the viewer always sees the live session.

Also bumps _VNC_IDLE_TTL_SECONDS comment to reflect that the value is
now effectively unused for clearing (kept for browsing staleness only).
showBrowser() called toggle-display whenever this.vncUrl was null,
even if the server was already running in virtual/headed mode and the
WebUI just hadn't received the URL via poll yet. Each toggle cycle
stops VNC, closes the persistent context, relaunches, and starts a
fresh VNC — ~3 seconds of darkness during which the iframe loses its
source and on reconnect grabs keyboard focus.

Now if displayMode is virtual/headed or _rawVncUrl is set, just
reveal the panel and force a poll instead of toggling.
x-if destroys and recreates the DOM element when its condition
flips. During the inevitable ~3s gap between 'vnc stopped' and 'vnc
started' on a toggle (or any momentary null vnc_url), the iframe was
torn out of the DOM. When the URL returned, a fresh iframe was built,
noVNC autoconnected, and the new iframe stole focus.

x-show only toggles CSS display, so the iframe element persists
across gaps. The placeholder now also gates on _rawVncUrl so it only
shows for sessions that have never received a URL.
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.

1 participant