app
Adom Desktop - Puppeteer Bridge
Public Made by Adomby adom
The Adom Desktop Puppeteer (pup) bridge — drive a real headful browser on the user's desktop from the cloud: open/screenshot/eval/record. Cold-start drives the browser ALREADY on the machine (installed Chrome, else Microsoft Edge — every Windows PC ships with Edge) with a fresh isolated profile, so the common case needs NO download; Chrome for Testing is fetched only as a last resort. Two artifacts: the Releases zip is the bridge runtime (also bundled in Adom Desktop); the pkg installs the container-side Claude skills.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pup — Adom Desktop</title>
<style>
@font-face {
font-family: 'Familjen Grotesk';
src: url('https://adom.inc/fonts/familjen-grotesk/familjen-grotesk-latin-700-normal.woff2') format('woff2');
font-weight: 700; font-display: swap;
}
@font-face {
font-family: 'Familjen Grotesk';
src: url('https://adom.inc/fonts/familjen-grotesk/familjen-grotesk-latin-500-normal.woff2') format('woff2');
font-weight: 500; font-display: swap;
}
@font-face {
font-family: 'Satoshi';
src: url('https://adom.inc/fonts/satoshi/satoshi-400-normal.woff2') format('woff2');
font-weight: 400; font-display: swap;
}
@font-face {
font-family: 'Satoshi';
src: url('https://adom.inc/fonts/satoshi/satoshi-500-normal.woff2') format('woff2');
font-weight: 500; font-display: swap;
}
@font-face {
font-family: 'JetBrains Mono';
src: url('https://fonts.gstatic.com/s/jetbrainsmono/v24/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPx3cwhsk.woff2') format('woff2');
font-weight: 400; font-display: swap;
}
:root {
--bg: #0d1117;
--surface: #161b22;
--elevated: #1c2128;
--overlay: #21262d;
--border: #30363d;
--border-muted: #21262d;
--text: #e6edf3;
--text2: #8b949e;
--text3: #484f58;
--accent: #00b8b0;
--accent-bright: #00e6dc;
--accent-muted: rgba(0, 184, 176, 0.12);
--accent-border: rgba(0, 184, 176, 0.25);
--purple: #8C6BF7;
--purple-light: #C5B3FF;
--blue: #0061EF;
--blue-light: #64ABFF;
--teal-light: #8FD3D4;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
* { transition: background 0.15s, color 0.15s, border-color 0.15s, opacity 0.15s; }
body {
background: var(--bg);
color: var(--text);
font-family: 'Satoshi', sans-serif;
font-weight: 400;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
overflow-x: hidden;
}
h1, h2, h3, .title { font-family: 'Familjen Grotesk', sans-serif; font-weight: 700; }
code, pre, .mono { font-family: 'JetBrains Mono', monospace; }
/* ── Gradient accent bar at top ── */
.accent-bar {
width: 100%;
height: 3px;
background: linear-gradient(90deg, var(--blue) 0%, var(--accent) 40%, var(--purple) 100%);
}
/* ── Main content ── */
.container {
max-width: 820px;
width: 100%;
padding: 40px 40px 32px;
flex: 1;
display: flex;
flex-direction: column;
gap: 24px;
}
/* ── Hero ── */
.hero {
display: flex;
align-items: center;
gap: 20px;
}
.hero-icon {
width: 52px;
height: 52px;
flex-shrink: 0;
}
.hero-icon svg { width: 100%; height: 100%; }
.hero-text h1 {
font-size: 30px;
letter-spacing: -0.5px;
line-height: 1.1;
}
.hero-text h1 .pup { color: var(--accent); }
.hero-text h1 .sep { color: var(--text3); font-weight: 500; margin: 0 6px; }
.hero-text h1 .sub { color: var(--text2); font-weight: 500; font-size: 22px; }
.hero-text .tagline {
margin-top: 4px;
color: var(--text2);
font-size: 14px;
line-height: 1.5;
}
/* ── Session identity card ── */
.session-card {
background: var(--accent-muted);
border: 1px solid var(--accent-border);
border-radius: 10px;
padding: 18px 22px;
display: none; /* shown by JS when params present */
}
.session-card.visible { display: block; }
.session-card .session-heading {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--accent);
font-weight: 600;
margin-bottom: 10px;
}
.session-card .session-grid {
display: grid;
grid-template-columns: auto 1fr;
gap: 6px 16px;
align-items: baseline;
}
.session-card .session-label {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.3px;
color: var(--text3);
font-weight: 600;
white-space: nowrap;
}
.session-card .session-value {
font-size: 13px;
color: var(--text);
font-family: 'JetBrains Mono', monospace;
word-break: break-all;
}
/* ── Pipeline diagram ── */
.pipeline {
display: flex;
align-items: center;
gap: 0;
padding: 18px 22px;
background: rgba(22,27,34,0.92);
backdrop-filter: blur(16px);
border: 1px solid rgba(0,184,177,0.2);
border-radius: 10px;
box-shadow: 0 12px 40px rgba(0,0,0,0.55);
overflow-x: auto;
}
.pipeline-node {
display: flex;
flex-direction: column;
align-items: center;
gap: 5px;
flex-shrink: 0;
min-width: 90px;
}
.pipeline-node .icon {
width: 34px;
height: 34px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 17px;
}
.pipeline-node .label {
font-size: 10px;
font-weight: 500;
color: var(--text2);
text-align: center;
text-transform: uppercase;
letter-spacing: 0.4px;
}
.pipeline-arrow {
color: var(--text3);
font-size: 16px;
margin: 0 6px;
flex-shrink: 0;
margin-bottom: 16px;
}
.node-cloud .icon { background: rgba(0,97,239,0.15); color: var(--blue-light); }
.node-relay .icon { background: rgba(140,107,247,0.15); color: var(--purple-light); }
.node-desktop .icon { background: rgba(0,184,176,0.12); color: var(--accent-bright); }
.node-pup .icon { background: rgba(0,184,176,0.2); color: var(--accent-bright); }
.node-chrome .icon { background: rgba(143,211,212,0.15); color: var(--teal-light); }
/* ── Description card ── */
.desc-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 10px;
padding: 20px 24px;
}
.desc-card p {
color: var(--text2);
font-size: 13px;
line-height: 1.65;
}
.desc-card p strong { color: var(--text); font-weight: 500; }
.desc-card p code {
background: var(--overlay);
padding: 2px 6px;
border-radius: 4px;
font-size: 11px;
color: var(--accent);
}
/* ── Feature chips ── */
.features {
display: flex;
flex-direction: column;
gap: 10px;
}
.features-title {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--accent);
font-weight: 600;
}
.chip-grid {
display: flex;
flex-wrap: wrap;
gap: 7px;
}
.chip {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 5px 12px;
background: var(--elevated);
border: 1px solid var(--border-muted);
border-radius: 6px;
font-size: 11px;
color: var(--text2);
font-family: 'JetBrains Mono', monospace;
}
.chip:hover {
border-color: rgba(0,184,176,0.3);
color: var(--text);
}
.chip .dot {
width: 5px;
height: 5px;
border-radius: 50%;
background: var(--accent);
flex-shrink: 0;
}
/* ── Footer info bar ── */
.footer {
width: 100%;
background: var(--surface);
border-top: 1px solid var(--border-muted);
padding: 9px 40px;
display: flex;
align-items: center;
gap: 20px;
flex-wrap: wrap;
}
.footer-item {
display: flex;
align-items: center;
gap: 6px;
}
.footer-label {
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.3px;
color: var(--text3);
font-weight: 600;
}
.footer-value {
font-size: 11px;
color: var(--text2);
font-family: 'JetBrains Mono', monospace;
}
.footer-value.live {
color: #3fb950;
}
.footer-sep {
width: 1px;
height: 14px;
background: var(--border-muted);
}
</style>
</head>
<body>
<div class="accent-bar"></div>
<div class="container">
<!-- Hero -->
<div class="hero">
<div class="hero-icon">
<svg viewBox="0 0 1061.33 1061.38" xmlns="http://www.w3.org/2000/svg">
<path fill="#00b8b0" d="M957.65,103.68c-116.2-116.2-282.77-138.02-372.05-48.73-36.23,36.23-54.16,85.18-54.92,138.34h0s0,.07,0,.07c0,.17,0,.35,0,.52v329.6c0,3.99,3.24,7.23,7.23,7.23h326.06c54.75.14,105.25-17.79,142.42-54.97,89.28-89.28,67.46-255.85-48.74-372.05Z"/>
<path fill="#00b8b0" d="M103.68,957.7c116.2,116.2,282.77,138.02,372.05,48.73,36.23-36.23,54.16-85.18,54.92-138.34h0s0-.07,0-.07c0-.17,0-.35,0-.52v-329.6c0-3.99-3.24-7.23-7.23-7.23H197.37c-54.75-.14-105.25,17.79-142.42,54.97-89.28,89.28-67.46,255.85,48.74,372.05Z"/>
</svg>
</div>
<div class="hero-text">
<h1><span class="pup">pup</span><span class="sep">—</span><span class="sub">Adom Desktop</span></h1>
<p class="tagline">Browser automation for your desktop, controlled by Adom's cloud AI</p>
</div>
</div>
<!-- Session identity (populated by JS from URL params) -->
<div class="session-card" id="sessionCard">
<div class="session-heading">This Chrome window belongs to</div>
<div class="session-grid">
<span class="session-label">Session</span>
<span class="session-value" id="sessionId">—</span>
<span class="session-label">Profile</span>
<span class="session-value" id="profileName">—</span>
<span class="session-label">User Data</span>
<span class="session-value" id="userDataDir">—</span>
</div>
</div>
<!-- Pipeline -->
<div class="pipeline">
<div class="pipeline-node node-cloud">
<div class="icon">☁️</div>
<div class="label">Adom Cloud</div>
</div>
<div class="pipeline-arrow">→</div>
<div class="pipeline-node node-relay">
<div class="icon">🔗</div>
<div class="label">Relay Server</div>
</div>
<div class="pipeline-arrow">→</div>
<div class="pipeline-node node-desktop">
<div class="icon">🖥️</div>
<div class="label">Desktop App</div>
</div>
<div class="pipeline-arrow">→</div>
<div class="pipeline-node node-pup">
<div class="icon">🐶</div>
<div class="label">Pup Bridge</div>
</div>
<div class="pipeline-arrow">→</div>
<div class="pipeline-node node-chrome">
<div class="icon">🌐</div>
<div class="label">Chrome</div>
</div>
</div>
<!-- Description -->
<div class="desc-card">
<p>
<strong>Pup</strong> is a Node.js bridge on port <code>8851</code> that wraps a persistent
<strong>Chrome for Testing</strong> browser. Your Adom web app sends <code>browser_*</code>
commands through the cloud relay to the Desktop App, which forwards them to Pup.
Chrome launches visibly on your screen — not headless — so you can watch the AI
browse, screenshot, and debug your projects in real time.
</p>
</div>
<!-- Features -->
<div class="features">
<div class="features-title">Capabilities</div>
<div class="chip-grid">
<div class="chip"><span class="dot"></span>browser_launch</div>
<div class="chip"><span class="dot"></span>browser_screenshot</div>
<div class="chip"><span class="dot"></span>browser_navigate</div>
<div class="chip"><span class="dot"></span>browser_eval</div>
<div class="chip"><span class="dot"></span>browser_errors</div>
<div class="chip"><span class="dot"></span>browser_reload</div>
<div class="chip"><span class="dot"></span>browser_wait</div>
<div class="chip"><span class="dot"></span>browser_status</div>
<div class="chip"><span class="dot"></span>browser_close</div>
</div>
</div>
</div>
<!-- Footer -->
<div class="footer">
<div class="footer-item">
<span class="footer-label">Bridge</span>
<span class="footer-value">Puppeteer</span>
</div>
<div class="footer-sep"></div>
<div class="footer-item">
<span class="footer-label">Port</span>
<span class="footer-value">8851</span>
</div>
<div class="footer-sep"></div>
<div class="footer-item">
<span class="footer-label">Mode</span>
<span class="footer-value">headless: false</span>
</div>
<div class="footer-sep"></div>
<div class="footer-item">
<span class="footer-label">Screenshots</span>
<span class="footer-value">lossless PNG ≤ 1568px</span>
</div>
<div class="footer-sep"></div>
<div class="footer-item">
<span class="footer-label">Status</span>
<span class="footer-value live">● live</span>
</div>
</div>
<script>
// Read session info from URL query params
const params = new URLSearchParams(window.location.search);
const sessionId = params.get('sessionId');
const profile = params.get('profile');
const userDataDir = params.get('userDataDir');
if (sessionId) {
// Show the session identity card
document.getElementById('sessionCard').classList.add('visible');
document.getElementById('sessionId').textContent = sessionId;
document.getElementById('profileName').textContent = profile || sessionId;
document.getElementById('userDataDir').textContent = userDataDir || '(default)';
// Set the page title to show session info in the Chrome tab
const titleParts = ['Pup'];
titleParts.push(`session: ${sessionId}`);
if (profile && profile !== sessionId) titleParts.push(`profile: ${profile}`);
document.title = titleParts.join(' — ');
}
</script>
</body>
</html>