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
Adopted the wiki's server-side anonymous render (adom/wiki#126, Colby's ?view=public param, which was pup's own ask): the public wiki view is now a URL param, not an isolated profile. wikiView:public keeps the window on the shared adom-you profile and appends ?view=public, which the server renders exactly as a signed-out visitor (session untouched, byte-identical, private pages 404). pup_wiki_set_view now toggles IN PLACE by reloading each wiki tab with the param appended or stripped: no relaunch, no cookie wipe, no profile switch; the jar-relaunch machinery survives only as a fallback for servers that ignore the param and for legacy iso-jar windows coming home. This also removes the main source of leaked iso-* profiles. Catalog, hints, and skills updated to teach the param model.
4 files changed
+92−15
adom-bridge-puppeteer-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "puppeteer",- "version": "2.0.131",- "url": "https://wiki.adom.inc/download/adom/pup-bridge/2.0.131/adom-bridge-puppeteer-v2.0.131.zip",- "sha256": "afa7275964089d3d5c803cfd652063c2aac16805ea7976b0e96161f51bb332ce",- "size": 885109,+ "version": "2.0.132",+ "url": "https://wiki.adom.inc/download/adom/pup-bridge/2.0.132/adom-bridge-puppeteer-v2.0.132.zip",+ "sha256": "8dc87b24a8aef2dd1f07aec964f5d0d80ed60a733f0774624f72d98378846426",+ "size": 886958, "verbPrefixes": [ "browser_", "desktop_recorder_",
src/BRIDGE_VERSION+1−1@@ -1 +1 @@-2.0.131+2.0.132
src/bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "puppeteer", "displayName": "Puppeteer (Chrome for Testing + Native)",- "version": "2.0.131",+ "version": "2.0.132", "description": "Drives a browser ALREADY on the machine \u2014 installed Chrome, else Microsoft Edge (every Windows PC ships with Edge) \u2014 launched with a FRESH isolated profile, so the common case has NO ~150 MB Chrome-for-Testing download and it never touches the user's real tabs/logins. Binds its local server to LOOPBACK (127.0.0.1) only, so no Windows Firewall 'allow Node through the firewall' prompt ever pops. Chrome for Testing is fetched only as a LAST RESORT if the machine has no Chromium browser at all. SPAWN-VERIFIED fallback: pup tries each detected browser (Chrome \u2192 Edge \u2192 CfT) and actually launches it, falling through to the next if one won't start (and handling browsers like Edge that relaunch onto another PID), so a corrupt/partial install never hard-fails an open. The browser that launches is cached as the default (persisted across restarts) so it isn't re-probed each time. On spawn the bridge SELF-WARMS: detects browsers + disk, and only if the box has no Chrome/Edge and no cached CfT does it background-fetch CfT so the first open is instant. pup_use pins a specific browser (chrome | edge | cft | auto); {browser:'chrome',install:true} downloads & installs REAL Google Chrome (user-scoped, no admin) on an Edge-only box. pup_readiness (READ-ONLY) reports which browser will be used + the full candidate list + the default + free disk. Every failure returns a specific errorCode + _hint (chrome_install_no_disk, chrome_download_network, chrome_install_permission, chrome_stable_not_detected, \u2026). Launches with FULL browser permissions by default for full debug (strictPermissions:true opts out). Headful automation: open/close windows+tabs, navigate, screenshot, eval JS, record. pup is a GENERIC, non-profile browser only \u2014 every session is a fresh isolated Chrome/Edge process, never the user's real signed-in browser; profile defaults to sessionId. To drive the user's REAL Chrome/Edge (their profile, cookies, SSO), use the Adom browser EXTENSION's nbrowser_* verbs \u2014 a separate bridge, not pup's job. macOS (v1.9.74+): full parity with mac-native mechanics \u2014 background-by-default is the app-level hide (windows stay drivable/screenshottable while hidden), raise/lower map to unhide+activate/hide, pup_alert_window posts a native toast whose click focuses the window, pup_use {browser:'chrome',install:true} installs Chrome from the official dmg, and taskbar identity/badging remains Windows-only.", "homepage": "https://wiki.adom.inc/adom/adom-bridge", "author": "Adom Inc.",
src/server.js+86−9@@ -4792,6 +4792,13 @@ function isWikiUrl(u) { try { return /(^|\.)wiki\.adom\.inc$/i.test(new URL(u).h // treatment on ANY adom.inc host (wiki + other Adom properties). NON-Adom sites (ti.com, // digikey.com, even adom.cloud proxies) get NOTHING — no auth routing, hints, or glyph. function isAdomUrl(u) { try { const h = new URL(u).hostname; return h === 'adom.inc' || /\.adom\.inc$/i.test(h); } catch (e) { return false; } }+// v2.0.132 (adom/wiki#126, Colby's server-side anonymous render — pup's own ask, now adopted):+// `?view=public` on any wiki page GET makes the SERVER render that one request exactly as a+// signed-out visitor sees it (the request's user is nulled; the real session cookie is untouched;+// verified byte-identical to a no-cookie request, and an owner's private page 404s under it). So+// the public view no longer needs an isolated anonymous profile jar at all — it is a URL rewrite.+function wikiPublicUrl(u) { try { const x = new URL(u); x.searchParams.set('view', 'public'); return x.toString(); } catch (e) { return u; } }+function wikiStripPublicParam(u) { try { const x = new URL(u); if (x.searchParams.get('view') === 'public') { x.searchParams.delete('view'); } return x.toString(); } catch (e) { return u; } } // Ask the page (using ITS cookies) whether the wiki session is authenticated. // v1.9.93 (John: "the jump list still blips out") — an INDETERMINATE auth probe must never be // reported as "logged out". The old version collapsed three different outcomes into {authed:false}:@@ -4947,18 +4954,18 @@ async function setWikiModeGlyph(page, label) { function wikiViewHint(url, rawView, auth) { if (!isAdomUrl(url)) return ''; if (rawView === 'authed') {- if (auth && auth.authed) return ` 🔓 Wiki opened LOGGED IN BY DEFAULT (as ${auth.user || 'the user'}) — pup auto-signed you in from the desktop token, no prompt. You see private source, drafts, owner-only cards, reply/edit. To instead see the ANONYMOUS/PUBLIC view (exactly what the world sees — e.g. verify a publish didn't leak private source), reopen with noAutoLogin:true (or wikiView:"public"), an isolated logged-out jar.`;+ if (auth && auth.authed) return ` 🔓 Wiki opened LOGGED IN BY DEFAULT (as ${auth.user || 'the user'}) — pup auto-signed you in from the desktop token, no prompt. You see private source, drafts, owner-only cards, reply/edit. To instead see the ANONYMOUS/PUBLIC view (exactly what the world sees — e.g. verify a publish didn't leak private source), reopen with noAutoLogin:true (or wikiView:"public") — the server then renders it exactly as a signed-out visitor (?view=public, adom/wiki#126), same window, session untouched.`; return ` 🔑 Wiki authed view requested, but NO ONE has done the one-time login yet (or the ~30-day session expired) — so this window is currently logged-out. A thread can't self-login (it's the user's SSO). Offer it: pup_raise_os_window {foregroundReason:"sign into the Adom wiki"} → user does the hydrogen.adom.inc SSO ONCE → then EVERY thread's wikiView:"authed" is silently logged in for ~30 days (shared vault profile). Tell the user it's a one-time login that unlocks the logged-in view everywhere.`; } if (rawView === 'public') {- // EXPLICIT public — an isolated anonymous jar, genuinely logged out. This is the one true- // "what the world sees" view.- return ` (📖 Wiki PUBLIC view — an ISOLATED anonymous jar, genuinely LOGGED OUT, exactly what the world sees. Use this to verify a publish didn't leak private source. For the user's logged-in view (private source, drafts, owner cards, replying), use wikiView:"authed".)`;+ // EXPLICIT public — the server renders the request anonymously via ?view=public+ // (adom/wiki#126): the one true "what the world sees" view, no isolated jar needed.+ return ` (📖 Wiki PUBLIC view — the SERVER rendered this page exactly as a signed-out visitor sees it (?view=public, adom/wiki#126: request user nulled, byte-identical to logged-out, private pages 404). The window stays on the shared profile with the session untouched. Use this to verify a publish didn't leak private source. For the user's logged-in view, use wikiView:"authed".)`; } // OMITTED wikiView — the shared adom-you profile. LOGGED IN if the user has signed into the wiki // (the default, since the wiki login lives on the shared profile now), logged out otherwise. This // is NOT guaranteed anonymous — do not treat it as "the public view".- return ` (📖 Wiki on the SHARED profile — LOGGED IN as the user if they've signed into the wiki (the usual case, one shared login serves every window), logged out only if they never have. This is NOT the guaranteed-anonymous view: to see exactly what the world sees (e.g. confirm a publish didn't leak private source), pass wikiView:"public" (isolated logged-out jar). For the explicit logged-in vault view, pass wikiView:"authed".)`;+ return ` (📖 Wiki on the SHARED profile — LOGGED IN as the user if they've signed into the wiki (the usual case, one shared login serves every window), logged out only if they never have. This is NOT the guaranteed-anonymous view: to see exactly what the world sees (e.g. confirm a publish didn't leak private source), pass wikiView:"public" (server-rendered anonymous view via ?view=public — same window, session untouched). For the explicit logged-in view, pass wikiView:"authed".)`; } async function fetchPageFavicon(page) {@@ -11735,12 +11742,12 @@ const server = http.createServer(async (req, res) => { }; sendJSON(res, { success: true, bridge: 'puppeteer', verbs: [ V('pup_my_windows', 'START HERE for window management: everything YOUR thread owns — windows, tabs, urls, titles, state — with ready-to-run next steps. Zero args. You NEVER need an hwnd, desktop_find_window, or desktop_screenshot_window for a pup window: pup keys every window to its owning --ai-thread name and resolves ids for you — omit sessionId in any pup verb to target your own window, or pass "ai-thread" to address a thread by name (the CLI\'s --ai-thread flag, same spelling). pup_screenshot {} screenshots it; pup_reload {} refreshes it.', { 'ai-thread': 'optional str — inspect another thread\'s windows (read-only listing); default = the calling thread. The CLI flag\'s exact spelling; aiThread also accepted' }, 10, {}),- V('pup_open_window', 'Open a generic pup window — a fresh isolated Chrome/Edge process (never your real signed-in browser), fully drivable via CDP. ALWAYS opens in the BACKGROUND (does not steal focus; never disturbs the user\'s main work; still fully screenshottable/drivable) UNLESS you pass foreground:true — do that ONLY when the user explicitly wants to watch. Pass width+height (+ optional x,y, screen px) to place it at a specific size/position for screenshot framing — this NO LONGER foregrounds it (sizing stays in the background too; default unsized is maximized). profile defaults to the durable shared \'adom-you\' profile (logins/localStorage persist + are shared across all pup windows); isolated:true for a private throwaway. OWNERSHIP: sessionIds are shared across ALL AI threads — use a task-prefixed sessionId + owner:"<your-task>" so pup can refuse other threads stealing your window (reusing an existing id with a new url navigates it away; declared cross-owner reuse errors with session_owned_by_another_thread unless takeover:true). (For the user\'s REAL Chrome/Edge, use the Adom extension\'s nbrowser_* verbs.)', { sessionId: 'optional str — OMIT IT (recommended): your thread gets its OWN window keyed on your --ai-thread name; the first open creates it, later opens become TABS in it, and no other thread can touch it. Pass an explicit id only when you need a SEPARATE window (task-prefix it, e.g. "mytask-web")', url: 'optional str', profile: 'optional str — a named durable jar; DEFAULT is the shared "adom-you" profile (signed-in state shared across windows). isolated:true overrides to a throwaway', isolated: 'optional bool — private throwaway profile: fresh-login tests, acting as a different user. REASON-GATED (v2.0.101): requires isolationReason or the open is refused — every window defaults to the shared adom-you profile', isolationReason: 'required WITH isolated:true — why this window must NOT ride the shared adom-you profile (min 10 chars; logged, auditable). webSecurity:false and wikiView:public carry their own built-in reason', webSecurity: 'optional false — dev escape hatch for cross-origin eval; FORCES isolation so insecure flags never touch the credentialed profile', owner: 'optional str — your thread/task label; lets pup protect this window from other threads', takeover: 'optional bool — claim a window owned by another thread (only when the user explicitly asks)', foreground: 'optional bool — request showing the window on the user\'s screen. GATED (v1.8.70): requires foregroundReason too, else the open succeeds but stays BACKGROUND with foregroundDenied set. Only when the USER explicitly asked to watch.', foregroundReason: 'required WITH foreground:true — quote/paraphrase what the user said that justifies putting a window on their screen (min 10 chars; logged + recorded in lastAgentUpdate, auditable)', width: 'optional int px — sizes for screenshot framing, stays background', height: 'optional int px', x: 'optional int px', y: 'optional int px', wikiView: 'optional "authed"|"public" — for wiki.adom.inc URLs. THREE distinct cases: (1) OMIT = the shared adom-you profile, which is LOGGED IN as the user if they have signed into the wiki (the usual case — one shared login serves every window); this is NOT anonymous. (2) "public" = an ISOLATED anonymous jar, genuinely LOGGED OUT — exactly what the world sees; use this to verify a publish did not leak private source. (3) "authed" = the explicit logged-in vault view (30-day SSO; first use may need a one-time login — response says wikiLoginNeeded). To compare "what I see" vs "what the world sees", open one "authed" (or omit) AND one "public" — do NOT rely on omit for the public view, it shows the logged-in shared profile.', appName: 'optional str — the APP this window is driving (e.g. "shotlog"); shown in the hover tooltip', appIconB64: 'optional base64 PNG — override the app icon worn in the taskbar overlay. Usually UNNECESSARY: pup auto-fetches the page\'s own <link rel="icon"> favicon (the adom-ui-design standard artifact every app must serve) and wears it automatically. An app-looking URL with NO favicon gets a reprimand hint.', updateNote: 'optional str — a short "what I did" note, surfaced back in list_tabs/list_windows as lastAgentUpdate so the user knows what changed', autoFlash: 'optional bool (default true) — mutating verbs auto-flash this window\'s taskbar (debounced, never steals focus) so the user notices the agent touched it; pass false to make this window silent by default', silent: 'optional bool — suppress the auto-flash for just this call' }, 60, { sessionId: 'mytask-web', owner: 'mytask', url: 'https://example.com', width: 960, height: 540 }),+ V('pup_open_window', 'Open a generic pup window — a fresh isolated Chrome/Edge process (never your real signed-in browser), fully drivable via CDP. ALWAYS opens in the BACKGROUND (does not steal focus; never disturbs the user\'s main work; still fully screenshottable/drivable) UNLESS you pass foreground:true — do that ONLY when the user explicitly wants to watch. Pass width+height (+ optional x,y, screen px) to place it at a specific size/position for screenshot framing — this NO LONGER foregrounds it (sizing stays in the background too; default unsized is maximized). profile defaults to the durable shared \'adom-you\' profile (logins/localStorage persist + are shared across all pup windows); isolated:true for a private throwaway. OWNERSHIP: sessionIds are shared across ALL AI threads — use a task-prefixed sessionId + owner:"<your-task>" so pup can refuse other threads stealing your window (reusing an existing id with a new url navigates it away; declared cross-owner reuse errors with session_owned_by_another_thread unless takeover:true). (For the user\'s REAL Chrome/Edge, use the Adom extension\'s nbrowser_* verbs.)', { sessionId: 'optional str — OMIT IT (recommended): your thread gets its OWN window keyed on your --ai-thread name; the first open creates it, later opens become TABS in it, and no other thread can touch it. Pass an explicit id only when you need a SEPARATE window (task-prefix it, e.g. "mytask-web")', url: 'optional str', profile: 'optional str — a named durable jar; DEFAULT is the shared "adom-you" profile (signed-in state shared across windows). isolated:true overrides to a throwaway', isolated: 'optional bool — private throwaway profile: fresh-login tests, acting as a different user. REASON-GATED (v2.0.101): requires isolationReason or the open is refused — every window defaults to the shared adom-you profile', isolationReason: 'required WITH isolated:true — why this window must NOT ride the shared adom-you profile (min 10 chars; logged, auditable). webSecurity:false and wikiView:public carry their own built-in reason', webSecurity: 'optional false — dev escape hatch for cross-origin eval; FORCES isolation so insecure flags never touch the credentialed profile', owner: 'optional str — your thread/task label; lets pup protect this window from other threads', takeover: 'optional bool — claim a window owned by another thread (only when the user explicitly asks)', foreground: 'optional bool — request showing the window on the user\'s screen. GATED (v1.8.70): requires foregroundReason too, else the open succeeds but stays BACKGROUND with foregroundDenied set. Only when the USER explicitly asked to watch.', foregroundReason: 'required WITH foreground:true — quote/paraphrase what the user said that justifies putting a window on their screen (min 10 chars; logged + recorded in lastAgentUpdate, auditable)', width: 'optional int px — sizes for screenshot framing, stays background', height: 'optional int px', x: 'optional int px', y: 'optional int px', wikiView: 'optional "authed"|"public" — for wiki.adom.inc URLs. THREE distinct cases: (1) OMIT = the shared adom-you profile, LOGGED IN as the user if they have signed into the wiki (the usual case); this is NOT anonymous. (2) "public" = the SERVER renders the page exactly as a signed-out visitor sees it via the ?view=public param (adom/wiki#126: request user nulled, byte-identical to logged-out, private pages 404) — the window STAYS on the shared profile, session untouched, no isolated jar; use this to verify a publish did not leak private source. (3) "authed" = the explicit logged-in view (30-day SSO; first use may need a one-time login — response says wikiLoginNeeded). To compare "what I see" vs "what the world sees", open one "authed" (or omit) AND one "public" tab — do NOT rely on omit for the public view.', appName: 'optional str — the APP this window is driving (e.g. "shotlog"); shown in the hover tooltip', appIconB64: 'optional base64 PNG — override the app icon worn in the taskbar overlay. Usually UNNECESSARY: pup auto-fetches the page\'s own <link rel="icon"> favicon (the adom-ui-design standard artifact every app must serve) and wears it automatically. An app-looking URL with NO favicon gets a reprimand hint.', updateNote: 'optional str — a short "what I did" note, surfaced back in list_tabs/list_windows as lastAgentUpdate so the user knows what changed', autoFlash: 'optional bool (default true) — mutating verbs auto-flash this window\'s taskbar (debounced, never steals focus) so the user notices the agent touched it; pass false to make this window silent by default', silent: 'optional bool — suppress the auto-flash for just this call' }, 60, { sessionId: 'mytask-web', owner: 'mytask', url: 'https://example.com', width: 960, height: 540 }), V('pup_open_tab', 'Open a new tab in an EXISTING session/window (returns {tabId, tabCount, activeTabId} — no need to diff list_tabs). This is the right verb for multi-URL work: ONE window with many tabs (one taskbar button), not many windows. The window stays in the background; the tab auto-flashes the taskbar (debounced) so the user notices. The USER can click the window\'s taskbar button at any time to view it — pup never pulls it back down.', { sessionId: 'required str', url: 'optional str', updateNote: 'optional str — short "what I did" note surfaced in list_tabs as lastAgentUpdate', silent: 'optional bool — suppress the auto-flash for this call' }, 30, { sessionId: 'work', url: 'https://example.com' }), V('pup_close_tab', 'Close a tab.', { sessionId: 'required str', tabId: 'optional str (default active)' }, 15, { sessionId: 'work' }), V('pup_close_window', 'Close a window/session (its tabs go too).', { sessionId: 'required str' }, 20, { sessionId: 'work' }), V('pup_window_info', 'Everything about ONE pup window as JSON — owner AI thread, container, profile, tabs, age, last agent action, and the window\'s hwnd (for desktop_screenshot_window when you need the browser chrome in frame). For an AI caller this is a PURE DATA query: nothing is drawn on the user\'s screen. Visual surfaces are user-initiated only — a jump-list click shows the native taskbar caption, a toolbar-popup click shows the in-page floaty card. showCaption:true opts an AI caller into the caption ONLY when the user asked to have the window identified on screen. Read-only, never owner-gated.', { sessionId: 'optional str', showCaption: 'optional bool — draw the ~12s taskbar caption too; ONLY when the user asked to see which window this is' }, 15, {}),- V('pup_wiki_set_view', 'Flip a WIKI pup window between the logged-in and public view IN PLACE (relaunches the same window under the other cookie jar). This is the callback for the per-window taskbar right-click jump-list task (WINDOWS ONLY — macOS has no taskbar or jump list, so this callback never fires there; flip views with pup_open_window {wikiView} instead) — it is a USER-initiated action (the window is brought on-screen). AI threads should NOT call this; pass wikiView:"authed"|"public" on pup_open_window instead.', { sessionId: 'required str', view: 'required "authed"|"public"' }, 45, { sessionId: 'wiki-bmv080', view: 'authed' }),+ V('pup_wiki_set_view', 'Flip a WIKI pup window between the logged-in and public view IN PLACE: every wiki tab reloads with the server-side ?view=public param appended (public) or stripped (authed) — adom/wiki#126 renders the request exactly as a signed-out visitor while the session stays untouched, so there is no relaunch, no cookie wipe, no profile switch (the legacy jar relaunch survives only as a fallback for servers that ignore the param). This is the callback for the per-window taskbar jump-list task (WINDOWS ONLY) — a USER-initiated action. AI threads should NOT call this; pass wikiView:"authed"|"public" on pup_open_window instead.', { sessionId: 'required str', view: 'required "authed"|"public"' }, 45, { sessionId: 'wiki-bmv080', view: 'authed' }), V('pup_navigate', 'Navigate the active tab to a URL.', { sessionId: 'required str', url: 'required str' }, 45, { sessionId: 'work', url: 'https://example.com' }), V('pup_back', 'History back.', { sessionId: 'required str' }, 20, { sessionId: 'work' }), V('pup_forward', 'History forward.', { sessionId: 'required str' }, 20, { sessionId: 'work' }),@@ -11781,7 +11788,7 @@ const server = http.createServer(async (req, res) => { V('pup_raise_os_window', 'Bring a pup window to the FOREGROUND (user\'s screen). GATED (v1.8.70): REFUSED with foreground_reason_required unless you pass foregroundReason — a quote/paraphrase of what the user said that justifies it (min 10 chars; logged, recorded in lastAgentUpdate, auditable). Allowed ONLY when the user EXPLICITLY must SEE it (a demo, recording, "show me"). It STEALS focus — to just signal "I updated this window", use pup_alert_window (or the auto-flash), NOT raise. After the show-me: pup_lower_os_window to return it to the background. Alias: pup_focus_window. IMPORTANT: if you are foregrounding in order to CLICK a native dialog or a cross-origin FedCM/Google account chooser, do NOT raise-then-click yourself — use pup_assisted_click, which waits for the user to actually be on the window (alt-tab aware) and narrates via captions, so you never act while they are not ready.', { sessionId: 'optional str', foregroundReason: 'REQUIRED — why the user asked to see this window' }, 15, { foregroundReason: 'user said "show me the wiki page"' }), V('pup_login', 'Fill (and optionally submit) an HTML login form in the CURRENT tab, in the BACKGROUND — no Chrome autofill dropdown, no foregrounding, React/Vue-safe. Creds from the keychain vault (matched on the tab URL; captured automatically as you log in, or stored via credential_set / save:true) or passed explicitly. Passwords are NEVER echoed back. Vault-matched pages AUTO-FILL on load (_autoLogin on the response). SUBMIT IS REASON-GATED: submit:true requires submitReason AND makes you confirm the live domain (the refusal regurgitates the exact host so a lookalike/phishing site is caught before the secret is sent). pup_configure {autoLogin:"off"} disables auto-fill; {captureLogins:"off"} stops auto-capture.', { sessionId: 'optional str — omit to target the window your own thread owns', username: 'optional str', password: 'optional str — with username, fills these instead of the vault', submit: 'optional bool — submit after filling; REQUIRES submitReason', submitReason: 'required WITH submit:true — why you are confident the CURRENT domain is the correct, intended site to send these credentials to (min 10 chars; the refusal shows you the live host to confirm; logged, auditable)', save: 'optional bool — with explicit creds, store them (OS keychain) so next time is automatic', host: 'optional str — override the vault host key when saving', realKeystrokes: 'optional bool — type with REAL key events instead of a background .value-set. Needed for React-controlled MASKED fields (Gusto/Keycloak SSO) that ignore .value and would submit an empty password. It briefly FOREGROUNDS the window (real keystrokes only register on-screen). Use when a normal fill returned filledPassword:false / reactMaskedFieldSuspected, or when the login-recipe for the site says so.' }, 20, { sessionId: 'work', submit: true, submitReason: 'confirmed the tab is on the real vendor domain I intended to sign into' }), V('pup_credentials', 'List saved logins (host + username only; passwords are in the OS keychain and NEVER returned). pup captures these automatically as you sign into sites and auto-fills them in the background on return visits.', {}, 10, {}),- V('pup_reap_profiles', 'Delete LEAKED isolated profile directories — the iso-* throwaway jars minted by isolated:true / wikiView:public opens whose window is long gone (they used to pile up, 100s per machine). Only removes an iso-* dir that no live session or session file owns, that no Chrome is holding, and that is >15 min old; durable jars (adom-you, named profiles) are NEVER touched. Runs automatically every 15 min too. dryRun:true previews the list without deleting.', { dryRun: 'optional bool — preview which iso-* profiles would be removed, delete nothing' }, 20, { dryRun: true }),+ V('pup_reap_profiles', 'Delete LEAKED isolated profile directories — the iso-* throwaway jars minted by isolated:true opens (and by wikiView:public before 2.0.132, when the public view moved to the server-side ?view=public param) whose window is long gone (they used to pile up, 100s per machine). Only removes an iso-* dir that no live session or session file owns, that no Chrome is holding, and that is >15 min old; durable jars (adom-you, named profiles) are NEVER touched. Runs automatically every 15 min too. dryRun:true previews the list without deleting.', { dryRun: 'optional bool — preview which iso-* profiles would be removed, delete nothing' }, 20, { dryRun: true }), V('pup_move_to_workspace', 'MOVE this pup window\'s page into the user\'s Hydrogen workspace: the active tab\'s URL is handed to Hydrogen (ah) on this machine via its Browser Picker (webview by default, honoring the user\'s per-domain choice), then the pup window closes — its owner initiated the move. keepWindow:true keeps the pup window open too. Needs Hydrogen running locally (hydrogen_unreachable otherwise; the window is left untouched on failure). Cloud Hydrogen (hc) is not yet supported.', { sessionId: 'optional str', keepWindow: 'optional bool — hand the URL over but keep the pup window open' }, 25, {}), V('pup_uninstall', 'DESTRUCTIVE — cleanly remove pup\'s WHOLE footprint from this machine: the HKCU AppUserModelId Adom.Pup registry keys, all staged extensions, ALL pup profiles + sessions, the OS-keychain credential vault, pup-settings.json, the badge-debug temp dir, and the jump-list launcher. ALWAYS preview first with dryRun:true (removes nothing, returns the plan). To actually remove, pass confirm:"uninstall". keepCredentials:true preserves the saved-login vault; keepProfiles:true preserves profiles/sessions. Chrome-for-Testing + firewall rules are SHARED and left alone unless removeChrome:true / removeFirewallRules:true. Closes every window + quits every browser first to release profile locks. (The bridge itself is managed by Adom Bridge — remove pup from Bridge to stop it re-seeding.)', { dryRun: 'optional bool — preview the removal plan without removing anything (do this first)', confirm: 'required str "uninstall" to actually remove', keepCredentials: 'optional bool — keep the saved-login vault', keepProfiles: 'optional bool — keep pup profiles + sessions', removeChrome: 'optional bool — also delete the Chrome-for-Testing install (shared; default keep)', removeFirewallRules: 'optional bool — also remove the CfT firewall rules (needs admin)' }, 60, { dryRun: true }), V('pup_alert_window', 'Nudge the user about a pup window WITHOUT stealing focus — on Windows it flashes the taskbar button; on macOS (no taskbar, and no API to bounce another app\'s Dock tile) it unhides the window at its z-position and posts an AD toast whose click focuses it. Same contract on both, so call it identically; just never tell a Mac user to look for a flashing icon. NOTE (v1.8.23): mutating verbs already AUTO-FLASH (debounced, one per ~5s burst per window) so the user always knows the agent touched a window — use this for an explicit end-of-task "it\'s ready" nudge on top of that. STRONGLY prefer this (or the auto-flash) over pup_raise_os_window, which steals focus and can leak a mid-typed keystroke into the page.', { sessionId: 'optional str', stop: 'optional bool — CLEAR the highlight instead of flashing' }, 15, {}),@@ -11960,7 +11967,15 @@ const server = http.createServer(async (req, res) => { // or wikiView:"public"). noAutoLogin is the plain-English opt-out John described. if (isAdomUrl(args.url) && args.noAutoLogin) args.wikiView = 'public'; if (isAdomUrl(args.url) && !args.wikiView) { args.wikiView = 'authed'; args._wikiDefaultedAuthed = true; }- if (args.wikiView === 'public' && isAdomUrl(args.url)) args.isolated = true;+ // v2.0.132 (adom/wiki#126 adopted — John: "implement that instead"): the public view is+ // now a SERVER-RENDERED URL param, not an isolated anonymous jar. ?view=public makes the+ // server render exactly what a signed-out visitor sees (session untouched, byte-identical,+ // private pages 404), so the window stays on the shared adom-you profile — no throwaway+ // iso-* profile is minted (which was also the main source of the leaked-profile pile).+ // An explicit isolated:true still isolates (with its own reason) if a caller insists.+ if (args.wikiView === 'public' && isAdomUrl(args.url) && args.isolated !== true) {+ args.url = wikiPublicUrl(args.url);+ } if (args.isolated === true) { // v2.0.101 (John: "ALWAYS open in adom-you for consistency; an alternate profile only // when the AI specifically asks AND gives a reason why"). Isolation is REASON-GATED@@ -13085,6 +13100,68 @@ const server = http.createServer(async (req, res) => { console.log(`[wikiview] "${sessionId}" user clicked "${view}" while pup thought it was already ${curView} — re-asserting the view (harmless reload), NOT skipping (this is why the click "did nothing")`); // fall through to the switch logic below, which navigates/asserts the requested view }+ // v2.0.132 (adom/wiki#126 — Colby shipped pup's own ask; John: "implement that instead"):+ // THE PRIMARY PATH. ?view=public makes the server render any page GET exactly as a+ // signed-out visitor (request's user nulled; the real session untouched; byte-identical;+ // private pages 404). So the toggle is now a plain IN-PLACE reload of every wiki tab with+ // the param appended (public) or stripped (authed): no relaunch, no cookie wipe, no+ // profile switch, works on the shared jar, and the user just sees tabs reload in their+ // window — exactly what John expected all along. The jar-relaunch machinery below+ // survives only as the fallback for old servers that ignore the param, and for windows+ // still sitting on a legacy iso-* jar (their authed direction falls through to the+ // relaunch home to adom-you).+ if (!/^iso-/.test(session.profileName || '') || view === 'public') {+ try {+ const _wTabs = (session.tabs || []).filter(t => { try { return isAdomUrl(t.page.url()); } catch (e) { return false; } });+ if (_wTabs.length) {+ for (const t of _wTabs) {+ let tu = null; try { tu = t.page.url(); } catch (e) {}+ if (!tu) continue;+ const target = view === 'public' ? wikiPublicUrl(tu) : wikiStripPublicParam(tu);+ try { await t.page.goto(target, { waitUntil: 'domcontentloaded', timeout: 25000 }); } catch (e) {}+ }+ const _at = _wTabs.find(t => t.tabId === session.activeTabId) || _wTabs[0];+ let _a = null; try { _a = await checkWikiAuth(_at.page); } catch (e) {}+ const _renderedAuthed = !!(_a && _a.authed);+ if (view === 'public' && _renderedAuthed) {+ // Old server: the param was ignored. Strip it back off and fall through to the+ // legacy isolated-jar relaunch below.+ console.log(`[wikiview] "${sessionId}" server ignored ?view=public (old wiki build?) — restoring URLs and falling back to the jar relaunch`);+ for (const t of _wTabs) { let tu = null; try { tu = t.page.url(); } catch (e) {} if (tu) { try { await t.page.goto(wikiStripPublicParam(tu), { waitUntil: 'domcontentloaded', timeout: 25000 }); } catch (e) {} } }+ } else if (view === 'authed' && !_renderedAuthed) {+ // Param stripped but the jar holds no live wiki session — continue into the+ // existing mint / auto-sign / manual-login machinery below with a clean URL.+ console.log(`[wikiview] "${sessionId}" param strip left the window logged out (no live session) — continuing to the sign-in path`);+ url = wikiStripPublicParam(url);+ } else {+ session._wikiView = view;+ try { await setWikiAuthedState(session, sessionId, view === 'authed', {}); } catch (e) {}+ try { await setWikiModeGlyph(_at.page, view === 'authed' ? '● Logged in' : '○ Public'); } catch (e) {}+ session._wikiJumplistKey = undefined;+ updateWikiJumplist(session, sessionId).catch(() => {});+ updateThumbnailTooltip(session, sessionId).catch(() => {});+ const _userClickP = USER_CALLERS.has(_callerThread) || USER_CALLERS.has(args.caller && args.caller.aiThread);+ if (_userClickP) {+ session._foreground = true;+ try { await session.page.bringToFront(); } catch (e) {}+ try { await osRaiseSessionWindow(session, sessionId, 'you clicked the taskbar view switch'); } catch (e) {}+ }+ wikiCaption(view === 'authed'+ ? `Adom wiki: now the logged-in view${_a && _a.user ? ` (${_a.user})` : ''}`+ : '\u{1F4D6} Adom wiki: now the PUBLIC view — the server rendered it exactly as a signed-out visitor sees it', 6000).catch(() => {});+ console.log(`[wikiview] "${sessionId}" -> ${view} IN PLACE via ?view=public (adom/wiki#126) — ${_wTabs.length} tab(s) reloaded, no relaunch, no profile switch`);+ sendJSON(res, { success: true, output: JSON.stringify({+ ok: true, view, inPlace: true, serverParam: true, tabsReloaded: _wTabs.length,+ url: (() => { try { return _at.page.url(); } catch (e) { return null; } })(),+ _hint: view === 'public'+ ? 'PUBLIC view via the server-side ?view=public param (adom/wiki#126): the server renders exactly what a signed-out visitor sees while the window stays on the shared profile — session untouched, no relaunch, no isolated jar. Verify a publish leaked nothing, then flip back any time.'+ : 'Logged-in view restored by stripping ?view=public — the shared-profile session was untouched throughout, so no re-login was needed.',+ }) });+ return;+ }+ }+ } catch (e) { console.log(`[wikiview] "${sessionId}" param path failed (${e.message}) — falling back to the legacy machinery`); }+ } // v1.9.63 — IN-PLACE view switch, no close-and-relaunch. // // A wiki session cookie is per-PROFILE and a profile's userDataDir is fixed at Chrome