← Commit history
SKILLPACK migration (SDK 2026-07-03, issue #68): dev/publish skills are now real skills/<name>/SKILL.md with user-invocable:false + DEVELOPER descriptions, listed explicitly in files[]/install.sh; removed dead dev-skills/
5 files changed
+162−4
install.sh+3−2@@ -2,9 +2,10 @@ # Adom Native Browser — installs the bundled skills so the AI knows the nbrowser_* surface. # (The extension itself loads into Chrome/Edge via "Load unpacked" from extension/, or the Web Store.) # Installs to BOTH agent skill homes: ~/.claude/skills AND ~/.codex/skills (a container may run-# Claude OR Codex — Bridge SDK Artifact 2). Only the USER skills ship; dev-skills/ stay source-only.+# Claude OR Codex — Bridge SDK Artifact 2). ALL skills ship as skills/<name>/SKILL.md (SKILLPACK+# layout); the dev/publish skills carry user-invocable:false so users are never offered them. set -e-SKILLS="adom-browser-extension cross-platform-dev driving-the-extension native-browser-safety installing-the-extension uninstalling-the-extension native-browser-recording driving-the-extension-demo driving-the-desktop driving-sites driving-gusto reading-google-messages"+SKILLS="adom-browser-extension cross-platform-dev driving-the-extension native-browser-safety installing-the-extension uninstalling-the-extension native-browser-recording driving-the-extension-demo driving-the-desktop driving-sites driving-gusto reading-google-messages developing-the-bridge publishing-the-bridge" for HOME_DIR in "${HOME}/.claude/skills" "${HOME}/.codex/skills"; do for name in $SKILLS; do mkdir -p "$HOME_DIR/$name"
package.json+3−1@@ -1,6 +1,6 @@ { "slug": "adom-browser-extension",- "version": "0.3.15",+ "version": "0.3.16", "type": "app", "title": "Adom Native Browser Extension (Chrome + Edge)", "description": "Drive your real, logged-in Chrome/Edge from the Adom ecosystem: 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.",@@ -15,6 +15,8 @@ "README.md", "docs/INSTALL-FLOW.md", "skills/adom-browser-extension/SKILL.md",+ "skills/publishing-the-bridge/SKILL.md",+ "skills/developing-the-bridge/SKILL.md", "skills/cross-platform-dev/SKILL.md", "skills/driving-the-extension/SKILL.md", "skills/native-browser-safety/SKILL.md",
skills/developing-the-bridge/SKILL.mdadded+76@@ -0,0 +1,76 @@+---+name: developing-the-bridge+description: "DEVELOPER skill for maintainers of the adom-browser-extension (native-browser) bridge - NOT for driving it. Read this before editing bridge/server.js, host/native-host.js, or extension/. Covers the architecture (bridge + native-messaging host + MV3 extension, and how AD provisions Node), the #1 rule that every nbrowser_* verb must return rich _hint/_next/related/pitfalls via the VERB_META table, the verb contract, local testing, the AD-vs-bridge boundary, hard-won runtime gotchas (chrome.tabs.goBack fails on debugger-driven tabs; UIA Invoke fails on Win32 dialogs), and the safety invariants. Everyday users never need this."+user-invocable: false+---+Parent skill: **adom-browser-extension**. This is a maintainer-only skill (not user-invocable). Bridge+SDK: https://wiki.adom.inc/adom/adom-desktop-bridges++# Developing the native-browser bridge++## ⭐ THE loudest rule: the AI reads your verb OUTPUT, not this file+AD relays every field of a verb response verbatim to the calling AI. A verb that returns+`{success:true}` and nothing else makes the AI guess and retry-loop. EVERY `nbrowser_*` response MUST+carry a rich **`_hint`** (what just happened + the obvious next verb + the trap) plus `_next` /+`related` / `pitfalls`. This is the single highest-leverage thing in the whole bridge.++Mechanism in `server.js`: a single **`VERB_META`** table is the source of truth. It feeds BOTH+`nbrowser_describe` (per-verb hint/related/pitfalls) AND a `decorateHint(verb, r)` wrapper that stamps+top-level `_hint`/`related`/`_next` onto EVERY dispatch response. Add a verb → add its VERB_META entry+in the same commit, or it ships hint-less.++## The 3 components (know which one you're touching)+1. **The bridge** (`bridge/server.js`) — AD spawns it (`spawn.kind:"node"`), so **AD provisions Node**+ (v1.9.63 runtime contract; you never install Node for it). Binds `127.0.0.1:0` on+ `process.env.ADOM_BIND_HOST || '127.0.0.1'` (never `0.0.0.0` → no firewall prompt). Writes a+ discovery file `~/.adom/bridges/native-browser/host.json` + `node-path.txt`. Serves `/status`+ (health chip: led/summary/tooltip, AND a top-level `version` AD reads as `liveVersion`) + `/command`+ for AD, plus a loopback TCP listener the host dials.+2. **The native-messaging host** (`host/native-host.js` via `native-host.bat`) — **Chrome launches it**,+ NOT AD, so it does NOT inherit AD's managed-runtime PATH. It reuses AD's Node via+ `node-path.txt` (published by the bridge = `process.execPath`, backslash-normalized) and falls back+ to PATH `node`. This is the ONLY place system-Node dependence could bite a fresh machine — keep the+ `.bat`'s two-branch resolution intact.+3. **The MV3 extension** (`extension/`) — runs inside Chrome/Edge, no runtime. SW `connectNative` →+ host → bridge loopback. Deterministic ID (`manifest.json` pins `key`) so the host manifest's+ `allowed_origins` is known ahead of time.++Data path: container → relay → AD → bridge `/command` → loopback → host stdio → extension SW → CDP/native.++## Hard-won runtime gotchas (extension side)+- **`chrome.tabs.goBack`/`goForward` throw on tabs we drive.** On a tab navigated via+ `chrome.tabs.update({url})` with the debugger lazily attached, `chrome.tabs.goBack` throws *"Cannot+ find a next page in history"* even when `window.history.length` proves there IS a back entry. **Route+ back/forward through the PAGE's own history** instead: `chrome.scripting.executeScript({target:{tabId},+ func: () => history.back()})`. Reliable, and stays banner-free (no debugger attach). (Fixed in ext+ 0.0.16; verified on ADOMBASELINE.)+- **UIA Invoke doesn't fire on Win32 common-dialog buttons / some Edge controls.** `desktop_ui_click`+ (UIA Invoke) returns `success:true` but the dialog's OK/Select-Folder/Reload button doesn't actuate.+ Fall back to a foreground `desktop_click {x,y}` (SendInput) at the control's screen rect. (See+ installing-the-extension for the picker + Reload cases.)+- **Keyboard chords route through AD's `desktop_press_key`** (full modifier chords) - don't reimplement+ OS-level modifier handling in the bridge. `nbrowser_press_key` is for in-page key events (CDP).++## Verb contract+Request `{command, args}`; response `{success, output(JSON string), error?, errorCode?}` + the hints.+`nbrowser_readiness` is READ-ONLY (never spawns/installs) — mirror `bridge_readiness`. Long ops: return+a job id + `statusVerb`, don't block. Declare per-verb budgets >60s in `bridge.json` `timeouts`+(v1.9.79) so AD doesn't cut them at 60s. `nbrowser_describe` returns the full catalog.++## Test locally+`node -c bridge/server.js` (syntax). Run `server.js` standalone → it binds ephemeral + writes the+discovery file; `GET 127.0.0.1:<port>/status` → led/summary/tooltip + `version`. End-to-end:+`nbrowser_readiness` through AD (`--target <laptop>`), then open bg window → screenshot → close.++## The boundary with AD (don't file bridge bugs as AD bugs)+AD owns: spawn/reap (`refresh_bridges` force-restarts a running bridge as of v1.9.76; `restart_bridge`+is the explicit primitive), stable port, supervisor, cache install + auto-update, status+classification, the GUI card, `liveVersion`/`staleProcess` reporting. YOU own: verbs + hints,+`/status`, `nbrowser_describe`, the wiki page + triggers, the manifest + zip, the skills pkg. A generic+missing capability (new lifecycle verb, relay field) → file against AD; a misbehaving `nbrowser_*` verb+→ fix here.++## Safety invariants (never regress — one wiped a user's other AI threads)+Only ever drive a window YOU opened; NEVER navigate/reload/close the user's existing window/tab (incl.+OS `desktop_navigate`). Background by default; foreground is the user's choice. `nbrowser_credentials`+read-only, never returns passwords; auto-login gated on `confirm:true`+`host:`. Honor the blocklist.
skills/publishing-the-bridge/SKILL.mdadded+79@@ -0,0 +1,79 @@+---+name: publishing-the-bridge+description: "PUBLISH skill for maintainers of the adom-browser-extension (native-browser) bridge - NOT for driving it. The exact, proven recipe to cut a release: bump versions in lockstep, build the .bin payload (never .zip - the wiki silently rejects it), upload via the base64 files API, POST the manifest, push source, and land it with refresh_bridges. Covers the bridge.json contracts we declare (timeouts) and one we deliberately don't (detect), the SKILLPACK skills-packaging layout, and the standing show-in-pup rule. Everyday users never need this."+user-invocable: false+---+Parent skill: **adom-browser-extension**. Maintainer-only skill (not user-invocable). Bridge SDK:+https://wiki.adom.inc/adom/adom-desktop-bridges++# Publishing the native-browser bridge++## What "the bridge" actually is (3 artifacts, 3 places)+1. **Runtime** — `bridge/` (server.js + bridge.json) shipped as a `.bin` payload + a manifest on the+ wiki page. AD streams it to `bridges-cache/native-browser/` and spawns it.+2. **Skills pkg** — the `skills/<name>/SKILL.md`s, published via `adom-wiki pkg publish --org adom`,+ installed into the container's `~/.claude/skills` + `~/.codex/skills`.+3. **Discovery** — `discovery_triggers` + `discovery_pitch` on `page.json`.++## bridge.json contracts we declare (and one we deliberately don't)+- **`timeouts`** (AD v1.9.79+): AD hard-caps every bridge verb at 60s unless you declare per-verb HTTP+ budgets. Ours match the bridge's own `timeoutFor()` (`screenshot|record|fetch_url|input_dispatch` →+ 120s) plus `wait` (blocks up to its 120s cap): `verbs {wait:125, fetch_url:120, input_dispatch:120}`+ + `prefixes [{screenshot:120},{record:120}]`, `default:60`, and `statusVerb:"nbrowser_status"` (AD+ returns `{stillRunning:true,statusVerb}` on its own timeout → caller polls). Keep the describe+ `timeoutSeconds` in `server.js` aligned. Note: the adom-desktop **CLI has its own ~30s client+ timeout**, separate from AD's bridge budget — you can't observe a >30s verb via a bare CLI call.+- **`detect` — intentionally OMITTED.** `detect` is for a HOST APP AD detects/gates. Native-browser+ drives the user's REAL browser via the extension, so the authoritative "is it usable" signal is+ **extension-connected** (`nbrowser_readiness`), not a browser binary on disk — and our read-only+ verbs are always forwarded regardless. A single `hostApp:"Google Chrome"` would MISREPORT the common+ Edge-only machine (verified real: ADOMBASELINE runs fine on Edge with no Chrome), and Edge is+ universal on Windows so "browser installed" is near-zero signal. So we do NOT declare `detect`.++## Release the RUNTIME — the exact, proven sequence+Deps: none (core `http`/`net`), so the payload is source-only (no `node_modules`).++1. **Bump the version in THREE places, together** (they drift if you don't):+ - `bridge/bridge.json` `version` (what AD reads + reports),+ - `bridge/package.json` `version`,+ - `bridge/BRIDGE_VERSION`.+ `server.js` reads `VERSION` from `bridge.json` (authoritative) so it can't drift — keep it that way.+ AD reads that same `version` off your `/status` JSON as `liveVersion`, so keep `/status` accurate.+2. **Build the zip with files at the ROOT** (there is no `zip` binary — use Python `zipfile`):+ members = `bridge.json`, `package.json`, `server.js`, `BRIDGE_VERSION`. Name it+ `native-browser-bridge-v<ver>.bin` (⚠ `.bin`, NOT `.zip` — the wiki SILENTLY rejects `.zip`+ uploads: returns `ok:true, commit:null` and stores nothing).+3. **sha256 + byte size** → write `native-browser-bridge-manifest.json`+ `{manifest_version:1, version, url:"native-browser-bridge-v<ver>.bin", sha256, size, released_at}`.+4. **Upload the `.bin`** via the base64 files API (repo push drops binaries):+ `adom-wiki api -X POST /api/pages/adom/adom-browser-extension/files --body '{"files":[{"path":"...bin","content":"<b64>","encoding":"base64"}],...}'` (CLI `--body` cap ~128 KB; the ~28 KB zip is fine).+5. **Push manifest + source as TEXT** with `adom-wiki repo push adom-browser-extension --files native-browser-bridge-manifest.json bridge/bridge.json bridge/package.json bridge/server.js bridge/BRIDGE_VERSION ...`.+6. **Land it:** `refresh_bridges {name:"native-browser"}` → verify `bridge_check_updates` shows+ `current==latest==<ver>, updateAvailable:false` AND `liveVersion==<ver>` (no `staleProcess`).++## Reaping is fixed (AD v1.9.76) — the old orphan trap is gone+Earlier, `refresh_bridges` could leave a stale PID serving old code (a node bridge doesn't lock the+cache dir, so a clean swap didn't reap). **AD v1.9.76 fixed this:** `refresh_bridges` now force-restarts+a running bridge after an update, `restart_bridge {name}` is a real verb (kill tracked+port-orphan ++respawn on the stable port), and `bridge_check_updates` surfaces `liveVersion` + `staleProcess`+directly. So: after a release, just `refresh_bridges` (or `restart_bridge` for an explicit restart) and+confirm `liveVersion` matches — no more digging in `host.json` or manual `taskkill`.+(`bridge_pause`/`bridge_resume` are a ROUTING gate only; they never kill the process.)++## Publish the SKILLS pkg — the SKILLPACK layout (the old dev-skills/ rule is DEAD)+`adom-wiki pkg publish --org adom`. **Every skill — user, dev, AND publish — is a real+`skills/<name>/SKILL.md`.** Scope dev/publish skills by AUDIENCE, not filename: frontmatter+`user-invocable: false` + a description that opens "DEVELOPER skill for maintainers…" so everyday users+are never offered them while maintainers still install the full set. List **every** skill path+EXPLICITLY (no globs) in `package.json` `files[]` AND `install.sh`/`uninstall.sh`. Deploy to BOTH+`~/.claude/skills` and `~/.codex/skills`. (The old "hide dev docs as `dev-skills/*.md` so they don't+ship" rule is dead as of the 2026-07-03 SDK update; pup migrated to this layout too.)++## Discovery+Edit `page.json` `discovery_triggers` (30+ user-task phrasings, login/forms niche — pup can't do+logins) + `discovery_pitch` (an INSTRUCTION). Push `page.json` **LAST** (a pkg publish clobbers it and+drops the hero). Verify: `adom-wiki discover preview --json "<7 real user queries>"` → land top-3.++## After ANY wiki change: show John in pup (standing rule)+Open the live page in a maximized pup window + shotlog + hand him the phone URL. Never just report+"published."
uninstall.sh+1−1@@ -1,6 +1,6 @@ #!/usr/bin/env bash # Removes the bundled skills from BOTH agent skill homes (Claude + Codex).-SKILLS="adom-browser-extension cross-platform-dev driving-the-extension native-browser-safety installing-the-extension uninstalling-the-extension native-browser-recording driving-the-extension-demo driving-the-desktop driving-sites driving-gusto reading-google-messages"+SKILLS="adom-browser-extension cross-platform-dev driving-the-extension native-browser-safety installing-the-extension uninstalling-the-extension native-browser-recording driving-the-extension-demo driving-the-desktop driving-sites driving-gusto reading-google-messages developing-the-bridge publishing-the-bridge" for HOME_DIR in "${HOME}/.claude/skills" "${HOME}/.codex/skills"; do for name in $SKILLS; do rm -rf "$HOME_DIR/$name" && echo "removed $name from $HOME_DIR"