app
Pup - the AI's own browser
Public Made by Adomby adom
pup gives your AI a real browser on your desktop that it drives completely, in the background, signed in as you: windows that never jump in front of your work, one shared profile that learns your logins, and every window labeled on the taskbar with its AI thread's name.
← Commit history
v2.0.570 (insiders): Queued extension channel; Trusted-Types-safe annotate overlay; annotate on the extension lane; worker self-reload.
2 files changed
+21−9
src/extension/annotate-overlay.js+5−1@@ -98,7 +98,11 @@ window.__pupStartAnnotate = function (initTool, initMode) { function setCursor() { if (S.tool === 'pen' || S.tool === 'arrow') { cv.style.cursor = 'none';- spr.innerHTML = S.tool === 'pen' ? penSvg() : arrowSvg();+ // No innerHTML: pages that enforce Trusted Types (Google Docs) refuse it. An <img> src is not a sink.+ while (spr.firstChild) spr.removeChild(spr.firstChild);+ const sprImg = document.createElement('img'); sprImg.width = 24; sprImg.height = 24; sprImg.draggable = false; sprImg.alt = '';+ sprImg.src = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(S.tool === 'pen' ? penSvg() : arrowSvg());+ spr.appendChild(sprImg); sprHot = S.tool === 'pen' ? [5, 19] : [2, 2]; // shown on the next pointer move, so it never appears at a stale position } else {
src/extension/worker.js+16−8@@ -148,8 +148,7 @@ function pupExtWaitComplete(tabId, ms) { } let pupExtDiag = { tries: 0, pkg: null, lastErr: '' };-async function pupExtPoll() {- if (pupExtBusy) return;+async function pupExtPollOnce() { pupExtDiag.tries++; pupExtDiag.pkg = await packagedPort(); const found = await findPort();@@ -169,20 +168,20 @@ async function pupExtPoll() { } // Only the extension-driven browser serves this channel. A normal pup window loads the same extension // and must NEVER answer another thread's ext commands.- if (!_pkgLane) return;+ if (!_pkgLane) return false; if (!found) { // No pup reachable. Surface WHY on the action tooltip so a human (or a screenshot) can read it without // needing the very channel that is broken. try { chrome.action.setTitle({ title: `Adom Pup — CANNOT REACH BRIDGE (tries ${pupExtDiag.tries}, port.json ${pupExtDiag.pkg})` }); } catch (e) {}- return;+ return false; } let cmd = null; try { const r = await fetch(`http://127.0.0.1:${found.port}/extcmd?lane=${encodeURIComponent(_pkgLane)}`, { signal: AbortSignal.timeout(2500) }); cmd = await r.json();- } catch (e) { return; }- if (!cmd || !cmd.id) return;- pupExtBusy = true;+ } catch (e) { return false; }+ if (!cmd || !cmd.id) return false;+ let out = { id: cmd.id, ok: false, error: `unsupported action ${cmd.action}` }; try { if (cmd.action === 'open') {@@ -399,7 +398,16 @@ async function pupExtPoll() { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(out), }); } catch (e) {}- pupExtBusy = false;+ return true;+}++// 2.0.570: the bridge queues commands; drain the queue on every poll (up to 12 per tick) instead of+// serving one command per 1.2 s, so a watcher polling every 500 ms and a verb do not starve each other.+async function pupExtPoll() {+ if (pupExtBusy) return;+ pupExtBusy = true;+ try { for (let k = 0; k < 12; k++) { if (!(await pupExtPollOnce())) break; } }+ finally { pupExtBusy = false; } } // A tight poll keeps the MV3 worker alive while pup is driving; the alarm revives it if it ever