← Commit history

Own the dev-mode install: AD stays generic, we orchestrate load-unpacked. Add installing-the-extension skill; correct INSTALL-FLOW framing (day-one path until CWS)

John Lauer ·575c9dedaf · ·parent ae7e537
5 files changed +115−9
docs/INSTALL-FLOW.md+18−7
@@ -91,13 +91,24 @@ by `install/install.ps1`, which:   the Edge key with `-Edge`). HKCU means **no UAC prompt**.  ### 4. Get the extension into Chrome and/or Edge-This is the piece that is **not** done by the bridge. The extension is loaded one of three ways:-- **Chrome Web Store / Edge Add-ons (production).** The user installs it (or a managed-machine policy-  force-installs it by store ID). Required for silent install on a normal, non-enterprise machine.-- **adom-desktop loads it for you (dev / unmanaged).** You can ask AD to do the `Load unpacked` for-  you: the `driving-the-desktop` skill drives `chrome://extensions` / `edge://extensions` at the OS-  level (`desktop_navigate` + `desktop_ui_click`, in the background) to toggle Developer mode and Load-  unpacked the `extension/` folder. This is how "ask adom-desktop to install it for me" works.+This is the piece that is **not** done by the bridge, and - important - **not built into adom-desktop+either**. AD deliberately stays **generic**: it exposes only OS-automation primitives+(`desktop_navigate`, `desktop_find_control`, `desktop_ui_click`, `desktop_ui_set`,+`desktop_screenshot`). The **extension-specific dev-mode install is orchestrated by code WE own in this+repo** (the `installing-the-extension` + `driving-the-desktop` skills), which drive those generic verbs+to perform the load-unpacked. AD provides the clicks; we own the recipe. Three ways the extension gets+loaded:++- **Chrome Web Store / Edge Add-ons (production) - but ~2 months out.** The user installs it (or a+  managed policy force-installs it by store ID); required for silent install on a normal, non-enterprise+  machine. Store review is slow (budget ~2 months for the broad-permission set), **so until then,+  dev-mode load-unpacked below is the day-one install path for real users.**+- **Dev-mode "Load unpacked", driven by our own orchestration over AD's generic verbs.** Our installer+  (see `installing-the-extension`) opens `chrome://extensions` / `edge://extensions` in a background+  window, toggles **Developer mode**, clicks **Load unpacked**, fills the native folder picker to the+  `extension/` folder, then **verifies the card loaded + captures the extension ID**, syncs the host+  manifest's `allowed_origins` to that ID, and confirms `nbrowser_status` connects. This is the code we+  own and keep hardening; the folder-picker step is the fragile part we iterate on. - **Manual load-unpacked.** The user does it themselves (the *Fork and develop* guide shows it).  Chrome and Edge are independent: load it in whichever the user wants, or both. The host is registered
install.sh+1−1
@@ -3,7 +3,7 @@ # (The extension itself loads into Chrome/Edge via "Load unpacked" from extension/, or the Web Store.) set -e DEST="${HOME}/.claude/skills"-for name in cross-platform-dev driving-the-extension native-browser-safety native-browser-recording driving-the-extension-demo driving-the-desktop driving-sites driving-gusto reading-google-messages; do+for name in cross-platform-dev driving-the-extension native-browser-safety installing-the-extension native-browser-recording driving-the-extension-demo driving-the-desktop driving-sites driving-gusto reading-google-messages; do   mkdir -p "$DEST/$name"   cp "skills/$name/SKILL.md" "$DEST/$name/SKILL.md"   echo "installed skill: $name"
package.json+1
@@ -17,6 +17,7 @@     "skills/cross-platform-dev/SKILL.md",     "skills/driving-the-extension/SKILL.md",     "skills/native-browser-safety/SKILL.md",+    "skills/installing-the-extension/SKILL.md",     "skills/native-browser-recording/SKILL.md",     "skills/driving-the-extension-demo/SKILL.md",     "skills/driving-the-desktop/SKILL.md",
skills/installing-the-extension/SKILL.mdadded+94
@@ -0,0 +1,94 @@+---+name: installing-the-extension+description: How WE (this repo) install the Adom browser extension into the user's real Chrome/Edge in DEVELOPER MODE ("Load unpacked"), driving adom-desktop's GENERIC OS verbs. This is OUR orchestration, not an adom-desktop feature - AD only exposes primitives (desktop_navigate / desktop_find_control / desktop_ui_click / desktop_ui_set / desktop_screenshot); the recipe, hardening, and recovery live here. This is the DAY-ONE install path: Chrome Web Store / Edge Add-ons review is ~2 months out, so until then dev-mode load-unpacked is how a real user gets the extension. Use when the user says "install the extension", "install it for me", "load the extension into my browser", "set up the browser extension", "the extension isn't loaded yet". For the low-level click mechanics (popups, the PWA guard, uninstall/reload), see driving-the-desktop. Part of the adom-browser-extension.+---+Part of **adom-browser-extension**. Low-level OS-driving mechanics: **driving-the-desktop**. Safety rules: **native-browser-safety**.++# Installing the extension (dev-mode Load unpacked) - the orchestration WE own++> **Whose code is this?** Ours. adom-desktop stays **generic** - it only gives us OS primitives+> (`desktop_navigate`, `desktop_find_control`, `desktop_ui_click`, `desktop_ui_set`,+> `desktop_screenshot`). The sequence that turns those into "the Adom extension is now loaded and+> connected" is **this skill** (plus the repo's `install/` scripts). AD never grows a "dev-mode+> install" verb; we own and harden the recipe. **This is the day-one path** until the Chrome Web Store /+> Edge Add-ons listings clear review (budget ~2 months for the broad-permission set), so it has to be+> clean and repeatable, and we keep iterating it.++## What makes this tractable: the extension ID is deterministic+The extension's `manifest.json` pins a `key`, so its ID is the **same on every machine**+(`nncjgceobmlenfliamnibncjojpedbcc`). That means the native-messaging **host manifest's+`allowed_origins` is known ahead of time** (`host/host-manifest.json` already hardcodes it) - we do+**not** have to capture the ID at install time to wire the host. So the only genuinely hard part is+getting "Load unpacked" to pick the right folder; everything else is deterministic.++## Preconditions (do these once, before driving any UI)+1. **The `extension/` folder is on the laptop** at a known path (e.g. `C:\Users\<user>\adom-browser-extension\extension`). Get it there from the package/repo (`push_file` the folder, or the user clones it). Load-unpacked reads the **on-disk** build, so this path is what the picker must select.+2. **The native-messaging host is registered** (`install/install.ps1`, or its steps): host manifest written with `allowed_origins` = the pinned ID, and `HKCU\...\NativeMessagingHosts\inc.adom.native_browser` pointing at it (Chrome and/or Edge). No UAC.+3. **The bridge is installed** in AD's cache (`bridge_install`, once). See the install-flow doc.++## The flow (background; never foreground the user's work)+Open a fresh background window you own, get its `hwnd`, and drive **that** (never the user's tabs):+```+nbrowser_open_window {profile, url:"about:blank", focused:false}   # your own window; note hwnd <W>+desktop_navigate {hwnd:<W>, url:"chrome://extensions"}             # edge://extensions for Edge - scheme MUST match the browser+desktop_screenshot_window {hwnd:<W>}                               # verify the page loaded (posted Enter is app-specific)+```++### Step A - ensure Developer mode is ON (don't assume)+The page's **Developer mode** is a toggle (top-right on Chrome, bottom-left on Edge). Read its state,+flip it only if off:+```+desktop_find_control {hwnd:<W>, contains:"Developer mode"}         # returns the toggle + its toggle state+# if it reports off/unchecked:+desktop_ui_click {hwnd:<W>, contains:"Developer mode"}             # background Invoke -> the Load unpacked / Pack / Update row appears+```+Verify the **Load unpacked** button is now present (`find_control {hwnd:<W>, contains:"Load unpacked"}`).+If it isn't, Developer mode didn't flip - re-read and retry, don't proceed blind.++### Step B - Load unpacked -> the native folder picker (THE fragile step we harden)+```+desktop_ui_click {hwnd:<W>, contains:"Load unpacked"}             # opens a Win32 folder picker (#32770; it DOES enumerate in desktop_list_windows + has an a11y tree)+```+Now select the `extension/` folder. This is the step that breaks most often, so be deliberate:+- **Preferred:** put the exact path into the picker's path/edit field and commit. `desktop_ui_set` on+  the picker edit can return *"operation was canceled by user"* on some builds - if it does, fall back+  to: focus the picker, `desktop_press_key` the path via clipboard paste (set clipboard, Ctrl+V), then+  Enter; or type into the "Folder:" combo and Enter.+- **Last resort (works because the picker remembers the last dir):** if the picker already opens inside+  the `extension/` folder, just `desktop_ui_click {hwnd:<picker>, role:"button", contains:"Select"}`.+- **Always verify the chosen path** with `desktop_screenshot_window {hwnd:<picker>}` BEFORE clicking+  Select - selecting the wrong folder loads a broken/old build silently.++> ⚠️ **This is the active iteration target.** The reliable, build-independent way to fill the picker+> path is exactly what we are hardening (a deterministic `install/load-unpacked` helper that sets the+> path and confirms, with retries + a screenshot check). Treat the path-fill as "verify, don't assume",+> and prefer the helper script once it lands.++### Step C - verify it actually loaded AND connected (two separate checks)+1. **Card present:** `desktop_find_control {hwnd:<W>, contains:"Adom"}` - the extension card shows on+   the page. (Optional: read the card's ID and confirm it equals the pinned ID.)+2. **Connected to the bridge:** poll `nbrowser_status` / `nbrowser_profiles` - the `chrome:`/`edge:`+   key appears (and the popup shows "connected") within ~1-3s once the SW starts. A loaded-but-not-+   connected extension usually means the profile has no open window (SW asleep) or the host manifest /+   HKCU registration is missing or has the wrong ID - check the preconditions.++### Step D - first useful action+Drive a real verb in a window **you** open (see `native-browser-safety`): e.g.+`nbrowser_open_window {url:"https://www.digikey.com", focused:false}` -> `nbrowser_screenshot`.++## Chrome AND Edge+They are independent. Repeat the flow per browser (matching scheme: `chrome://` vs `edge://`; the toggle+sits top-right on Chrome, bottom-left on Edge). The host is registered per browser, so each connects its+own host. Load into whichever the user wants, or both.++## Recovery / common failures+- **Load unpacked missing** -> Developer mode is off (Step A) or the page didn't navigate (re-`desktop_navigate`, verify by screenshot).+- **Picker selected the wrong folder** -> the silent killer; always screenshot the picker path before Select.+- **Card present but never connects** -> SW asleep (open a window in that profile), or host not registered / wrong `allowed_origins` ID (re-run `install/install.ps1`).+- **"refusing to drive a PWA / installed-app window"** -> you grabbed an installed-app window; open a fresh `normal` window (see the PWA guard in `driving-the-desktop`).++## Honest status+The end-to-end recipe works; the **folder-picker path-fill** is the part we are actively making robust+(a deterministic helper instead of "remembers the last dir"). Until the Web Store listings clear review,+this dev-mode flow is the real install path, so getting it clean is a priority - report any picker+flakiness so we can harden the helper.
uninstall.sh+1−1
@@ -1,5 +1,5 @@ #!/usr/bin/env bash DEST="${HOME}/.claude/skills"-for name in cross-platform-dev driving-the-extension native-browser-safety native-browser-recording driving-the-extension-demo driving-the-desktop driving-sites driving-gusto reading-google-messages; do+for name in cross-platform-dev driving-the-extension native-browser-safety installing-the-extension native-browser-recording driving-the-extension-demo driving-the-desktop driving-sites driving-gusto reading-google-messages; do   rm -rf "$DEST/$name" && echo "removed skill: $name" done