app
Pup - Puppeteer Bridge
Public Made by Adomby adom
pup is the AI's own browser: a real, full Chrome on the user's desktop that the AI fully controls (a sandbox, not the user's signed-in browser). Rides Bridge; pup_* verbs open windows and tabs, navigate, screenshot, and eval JS.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
// Adom Pup service worker — per-tab owner badge + the snip keyboard command.
// Polls pup /health (cheap, cached port) and badges each tab whose URL matches a pup session
// with the owning thread's initials + a stable per-thread color. Settings-aware: pup's
// toolbarExtension/tbOwnerBadge prefs ride /health; OFF clears badges and stands down.
const CANDIDATE_PORTS = [64230, 8851];
async function findPort() {
const { pupPort } = await chrome.storage.local.get('pupPort');
const ports = pupPort ? [pupPort, ...CANDIDATE_PORTS.filter(p => p !== pupPort)] : CANDIDATE_PORTS;
for (const port of ports) {
try {
const r = await fetch(`http://127.0.0.1:${port}/health`, { signal: AbortSignal.timeout(1200) });
const j = await r.json();
if (j && j.bridge === 'puppeteer') { chrome.storage.local.set({ pupPort: port }); return { port, health: j }; }
} catch (e) {}
}
return null;
}
function threadColor(name) {
let h = 0;
for (let i = 0; i < (name || '').length; i++) h = (h * 31 + name.charCodeAt(i)) >>> 0;
const palette = ['#f59e0b', '#38bdf8', '#39d98a', '#c084fc', '#fb7185', '#5eead4'];
return palette[h % palette.length];
}
function initials(name) {
const parts = String(name || '').split(/[\s-_]+/).filter(Boolean);
return (parts.length >= 2 ? parts[0][0] + parts[1][0] : String(name || '??').slice(0, 2)).toUpperCase();
}
async function sweepBadges() {
const found = await findPort();
if (!found) return;
const sessions = found.health.sessions || [];
const tabs = await chrome.tabs.query({});
for (const tab of tabs) {
if (!tab.url) continue;
const hit = sessions.find(s => s.url && (s.url === tab.url || tab.url.startsWith((s.url || '').split('?')[0])));
try {
if (hit && hit.owner) {
chrome.action.setBadgeText({ tabId: tab.id, text: initials(hit.owner) });
chrome.action.setBadgeBackgroundColor({ tabId: tab.id, color: threadColor(hit.owner) });
chrome.action.setTitle({ tabId: tab.id, title: `Adom Pup — ${hit.owner}'s window` });
} else {
chrome.action.setBadgeText({ tabId: tab.id, text: '' });
}
} catch (e) {}
}
}
chrome.alarms.create('pup-badges', { periodInMinutes: 0.5 });
chrome.alarms.onAlarm.addListener(a => { if (a.name === 'pup-badges') sweepBadges(); });
chrome.tabs.onActivated.addListener(() => sweepBadges());
chrome.runtime.onStartup.addListener(() => sweepBadges());
sweepBadges();
// content-script bridge: the floaty pencil's config + click routing (page cannot reach the
// loopback itself; the worker can)
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
(async () => {
if (msg && msg.t === 'pup-floaty-cfg') {
const found = await findPort();
const tb = (found && found.health && found.health.toolbar) || {};
sendResponse({ enabled: !!found && tb.enabled !== false && tb.floaty !== false });
return;
}
if (msg && msg.t === 'pup-annotate') {
const found = await findPort();
if (!found || !sender.tab || !sender.tab.url) { sendResponse({ ok: false }); return; }
await fetch(`http://127.0.0.1:${found.port}/command`, {
method: 'POST',
headers: { 'content-type': 'application/json', 'x-adom-caller-thread': 'user-toolbar', 'x-adom-caller-reason': 'floaty pencil click' },
body: JSON.stringify({ command: 'pup_annotate', args: { _tabUrl: sender.tab.url, tool: msg.tool || 'pen', caller: { aiThread: 'user-toolbar' } } }),
}).catch(() => {});
sendResponse({ ok: true });
}
})();
return true;
});
// v2.0.64: programmatic content-script injection. Declarative content_scripts do not fire under
// this --load-extension path (verified live: zero injection on any site while the worker, popup,
// and action all work), so the worker injects the floaty itself when a tab finishes loading.
chrome.tabs.onUpdated.addListener((tabId, info, tab) => {
if (info.status !== 'complete' || !tab || !tab.url || !/^https?:/.test(tab.url)) return;
chrome.scripting.executeScript({ target: { tabId }, files: ['content.js'] }).catch(() => {});
});
chrome.commands.onCommand.addListener(async (command) => {
if (command !== 'pup-snip') return;
const found = await findPort();
if (!found) return;
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
if (!tab || !tab.url) return;
// v2.0.60: resolve server-side by ANY tab URL (the 2.0.58 popup fix; the hotkey had the same
// main-page-only matching bug — the reason snip "never worked" from the keyboard)
await fetch(`http://127.0.0.1:${found.port}/command`, {
method: 'POST',
headers: { 'content-type': 'application/json', 'x-adom-caller-thread': 'user-toolbar', 'x-adom-caller-reason': 'Ctrl+Shift+A annotate hotkey' },
body: JSON.stringify({ command: 'pup_annotate', args: { _tabUrl: tab.url, tool: 'pen', caller: { aiThread: 'user-toolbar' } } }),
}).catch(() => {});
});