← Commit history
driving-the-desktop: full background chrome:// lifecycle (AD 1.8.176 desktop_navigate + scope:popups); v0.3.5
2 files changed
+51−33
package.json+1−1@@ -1,6 +1,6 @@ { "slug": "adom-browser-extension",- "version": "0.3.4",+ "version": "0.3.5", "type": "app", "title": "Adom Browser Extension (Chrome + Edge)", "description": "Drive your real, logged-in Chrome/Edge from the Adom ecosystem \u2014 the nbrowser_* native-browser bridge (counterpart to pup). Multi-profile driving, a read-only credential-library query over the browser's own saved logins (Chrome + Edge), and hands-free vendor login automation, so chip CAD / datasheets download past every login wall in your real signed-in session.",
skills/driving-the-desktop/SKILL.md+50−32@@ -1,12 +1,19 @@ --- name: driving-the-desktop-description: How to drive the OS desktop with adom-desktop (AD) `desktop_*` verbs when CDP/`nbrowser_*` CAN'T reach the target — the extension's OWN lifecycle (install via "Load unpacked", uninstall via "Remove", reload), `chrome://extensions` / `edge://extensions` and other `chrome://`/`edge://` pages, native file/permission dialogs, the omnibox/toolbar/tabs, and borderless popup bubbles. Match this when the user says "uninstall/reinstall/reload the extension", "install it for me", "the extension isn't loaded", "click the Remove/Load-unpacked button", "drive chrome://…", "click that OS dialog / popup / dropdown", or any browser-chrome UI outside the page. Covers the zero-math image-space click path (coordMap + space:"image"), background UI Automation (find_control → ui_click/ui_set), full-screen capture that SEES popups, modifier chords, and the hard-won traps. Needs AD ≥ 1.8.175 on the laptop + matching CLI; `--target` the laptop.+description: How to drive the OS desktop with adom-desktop (AD) `desktop_*` verbs when CDP/`nbrowser_*` CAN'T reach the target — the extension's OWN lifecycle (install via "Load unpacked", uninstall via "Remove", reload), `chrome://extensions` / `edge://extensions` and other `chrome://`/`edge://` pages, native file/permission dialogs, the omnibox/toolbar/tabs, and borderless popup bubbles. Match this when the user says "uninstall/reinstall/reload the extension", "install it for me", "the extension isn't loaded", "click the Remove/Load-unpacked button", "drive chrome://…", "click that OS dialog / popup / dropdown", or any browser-chrome UI outside the page. Covers the zero-math image-space click path (coordMap + space:"image"), background UI Automation (find_control → ui_click/ui_set), full-screen capture that SEES popups, modifier chords, and the hard-won traps. Needs AD ≥ 1.8.176 on the laptop (for `desktop_navigate` + `scope:"popups"` — the full background `chrome://` lifecycle) + matching CLI; `--target` the laptop. --- Parent skill: **driving-the-extension** When CDP/`nbrowser_*` can't reach something, drive the **OS** instead. This skill is the playbook a previous AI spent an hour re-discovering — follow it and you won't have to. +> ✅ **The full `chrome://` lifecycle is background as of AD 1.8.176** (verified end-to-end on a real+> Edge uninstall→reinstall, 2026-06-23). `desktop_navigate` opens `chrome://`/`edge://` pages with no+> foreground, and `scope:"popups"` reaches the confirm bubble. **The ONE unavoidable foreground beat** is a+> brief (~1–2s) `desktop_bring_to_front` *while the confirm bubble is up* — the bubble is a Chromium popup+> that dies on focus loss, so the window must hold focus until you click its button. Everything else —+> navigate, find, the Remove/Load-unpacked clicks, even closing the window — is background.+ ## When CDP fails → switch to desktop control - **`chrome://` / `edge://` pages** (incl. `…/extensions`, settings) are **blocked to CDP** / `nbrowser_navigate`. Only real OS input reaches them.@@ -72,44 +79,55 @@ Drive controls through the **accessibility tree** — the window is never raised it **auto-hides after 60s** (refreshed on each show/move). Extend with `ttlMs`, or `persist:true` then hide yourself. **No need to hide "to be safe"** — only hide early to clear it sooner. -## Recipe — open a `chrome://`/`edge://` page (CDP can't)-`Ctrl+N` (a NEW window so a background tab elsewhere can't steal the active tab) → `Ctrl+L` → type the URL-→ `Enter`, scoped to the new window's `hwnd`:+## Recipe — open a `chrome://`/`edge://` page (CDP can't) — BACKGROUND (AD 1.8.176)+Open a background window first (`nbrowser_open_window {profile, url:"https://example.com", focused:false}`,+then find its `hwnd`), then: ```-desktop_press_key {hwnd:<NW>, keys:["ctrl+n"]} # then find the new window's hwnd-desktop_press_key {hwnd:<NW>, keys:["ctrl+l"]}-desktop_type {hwnd:<NW>, text:"edge://extensions"}-desktop_press_key {hwnd:<NW>, keys:["enter"]}+desktop_navigate {hwnd:<W>, url:"edge://extensions"} # SetValue omnibox + posted Enter — NO foreground ```+⚠️ **Scheme must match the browser:** `chrome://…` for Chrome, `edge://…` for Edge. Give Chrome an+`edge://` URL (or vice-versa) and the omnibox **searches** the text instead of navigating. `https://` works+on either. **Always verify** with `desktop_screenshot_window` (a posted Enter is app-specific). Fallback if+a target ignores it: `desktop_bring_to_front` + `desktop_press_key ["enter"]` (brief foreground).+(`desktop_ui_set` also takes `submit:true` to set+commit an arbitrary field.) -## Recipe — UNINSTALL the extension (proven; simplified for 1.8.175)-1. Open `edge://extensions` (recipe above).-2. `find_control {hwnd:<NW>, role:"button", contains:"Remove"}`. `invokable` → `ui_click` it (background,- no bubble-focus dance). Page tree cold → retry once → else `desktop_click {x:rect.centerX,- y:rect.centerY}`.-3. The **"Remove '<name>'?" confirm is a borderless popup bubble** anchored to the toolbar icon —- invisible to `screenshot_window`, absent from `list_windows`. So: **`desktop_screenshot_screen`** → SEE- the bubble → **`desktop_click {space:"image", shotId, x, y}`** on its "Remove".- *(Pre-1.8.175 hack, no longer needed: open the bubble, then `desktop_press_key {keys:["enter"]}` with- NO `hwnd` so focus isn't stolen — the bubble auto-dismisses on focus loss.)*-4. **Verify by state, not pixels:** poll **`nbrowser_profiles`** — the `edge:`/`chrome:` key **drops** when- the SW dies. (The page render lagged/looked unchanged and burned an hour the first time.)+## Recipe — UNINSTALL the extension — BACKGROUND except one ~1s focus beat (verified AD 1.8.176)+1. `desktop_navigate {hwnd:<W>, url:"edge://extensions"}` (background). `find_control {hwnd:<W>,+ contains:"<ext name>"}` to confirm the card is present.+2. The confirm bubble is a Chromium **owned popup** that dies on focus loss, so it must hold focus while you+ click it. The working atomic sequence — **nothing between the trigger and the confirm**:+ ```+ desktop_bring_to_front {hwnd:<W>} # the ONE ~1s foreground beat+ desktop_ui_click {hwnd:<W>, role:"button", contains:"Remove"} # trigger → opens the bubble+ # wait ~1s+ desktop_ui_click {hwnd:<W>, scope:"popups", role:"button", name:"Remove"} # confirm (background Invoke)+ ```+ `scope:"popups"` enumerates the window's **owned, borderless** popups (absent from `desktop_list_windows`)+ and UIA-searches them; the bubble's window title is `Remove "<ext>" from <browser>?`. **Don't** insert a+ `screenshot`/extra `find_control` between trigger and confirm — the bubble (~1–2s lifetime) closes and+ you'll get `scope:popups found no owned/popup windows`; just re-trigger and confirm immediately.+3. **Verify by state:** poll `nbrowser_profiles` — the `edge:`/`chrome:` key **drops** when the SW dies. -## Recipe — REINSTALL (Load unpacked)-1. Click **"Load unpacked"** (find_control → `ui_click`, or image-space click) → opens a Win32 **folder- picker** (`#32770`; it DOES enumerate and has an a11y tree).-2. Put the extension dir in the **Folder:** field, then **Select Folder**:- `ui_set` the Folder edit (or `desktop_type` the path, e.g.- `C:\Users\<user>\adom-browser-extension\extension`) → `ui_click "Select Folder"`.-3. Load-unpacked reads the **on-disk** build (version may jump). Verify the `edge:`/`chrome:` key returns- to `nbrowser_profiles` (reconnects in ~1–2s).+## Recipe — REINSTALL (Load unpacked) — BACKGROUND+1. `desktop_ui_click {hwnd:<W>, contains:"Load unpacked"}` → opens a Win32 **folder picker** (`#32770`;+ enumerates in `desktop_list_windows`, has an a11y tree).+2. `desktop_ui_click {hwnd:<picker>, role:"button", contains:"Select"}` to confirm the folder.+ ⚠️ `desktop_ui_set` on the picker's edit field can return *"operation was canceled by user"* — but the+ picker **remembers the last-used dir**, so if it's already inside the extension folder, Select Folder+ grabs the right one. If it might open elsewhere, navigate the picker / set the path some other way first+ (open AD ask: reliable picker path-fill).+3. Load-unpacked reads the **on-disk** build (version may jump). Verify the `edge:`/`chrome:` key returns to+ `nbrowser_profiles` (reconnects in ~1–2s). ## Why these traps exist (so you don't re-discover them) - **"My click did nothing"** was an illusion — the click worked; the confirm bubble was just *invisible to- window-capture*. Use `screenshot_screen`.-- **SendInput foregrounds** (it lands only on the foreground window) → interrupts the user *and* dismisses- focus-sensitive bubbles. Prefer **UIA `ui_click`**; if you must SendInput to confirm a focus-sensitive- popup, **don't pass an `hwnd`** (so AD doesn't raise/steal focus).+ window-capture* (it's an owned popup). Reach it with `find_control`/`ui_click` `scope:"popups"`; to merely+ see it, `screenshot_screen`.+- **The confirm bubble lives ~1–2s and dies on focus loss.** Hold focus with `desktop_bring_to_front`,+ then trigger + `scope:"popups"` confirm with NOTHING in between (no screenshot/extra find_control).+- **SendInput foregrounds** (lands only on the foreground window) → interrupts the user. Prefer UIA+ (`ui_click`/`ui_set`/`desktop_navigate`) which is background; reserve SendInput for controls UIA can't+ reach. - **Don't pixel-hunt.** Image-space click (`space:"image"`) or `find_control` rect. Don't fetch the window rect to compute scale — `coordMap` already has it.