Commit Graph
100 Commits
Author SHA1 Message Date
leeguooooo 0c7534d9b2 chore(release): 0.27.0-fork.48 — iframe-proxy toggle (#4) + stealth status (#5)
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
2026-06-11 23:33:26 +09:00
leeguooooo ad4fb14ed9 feat: stealth status self-check command (issue #5)
Local stealth verification with no external detector: reports mode (connect vs
launch), live fingerprint probes (navigator.webdriver / window.chrome / plugins /
UA-headless) as pass/fail, and an audit of the active overrides for the path
(incl. the iframe-proxy state from #4). `--json` for a stable shape agents can
gate a sensitive flow on. Distinct from `doctor` (install/env health).
2026-06-11 23:33:24 +09:00
leeguooooo a976287f03 fix(stealth): AGENT_BROWSER_DISABLE_IFRAME_PROXY for a clean 0% CreepJS (issue #4)
--launch mode scored ~20% stealth on CreepJS because the srcdoc-iframe
contentWindow Proxy trips `hasIframeProxy` — the proxy that hides automation is
itself a fingerprintable tell (violates this fork's own "native > JS lies" rule).
Add a config-driven opt-out (no detectable global): AGENT_BROWSER_DISABLE_IFRAME_PROXY=1
drops the patch via __abStealth.disableIframeProxy → the iframe IIFE early-returns
→ clean 0% CreepJS, trading the niche srcdoc-iframe masking. Default keeps current
behavior. README now documents the --launch 20% honestly and scopes the headline
0% to the extension-connect path. Verified: launch + srcdoc page intact with the
toggle; stealth tests green (config strip-prefix kept in sync).
2026-06-11 23:25:24 +09:00
leeguooooo 649fa4ce94 chore(release): 0.27.0-fork.47 — tab-drift pin, snapshot -c keeps interactive, stale-ref guidance
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
2026-06-11 23:15:38 +09:00
leeguooooo 3ac69e822a fix: keep interactive nodes in snapshot -c; better stale-ref guidance (issue #2/#3)
- snapshot -c (compact) now always keeps lines with an interactive ARIA role
  (button/link/textbox/combobox/option/…), not only `ref=`/`": "` lines — so a
  clickable control can't vanish from compact output and leave the agent clicking
  an empty ref (issue #2 P1). Additive: only ever keeps more. compact tests green.
- stale-ref error now leads with "take a fresh snapshot" and points to the `eval`
  fallback for ref-churning SPAs, and demotes AGENT_BROWSER_VERIFY_REF=0 to a
  flagged last resort instead of presenting it as the fix (issue #3 P1).
2026-06-11 23:15:37 +09:00
leeguooooo d7a0ed85f9 fix(tabs): pin the active tab by target_id — stop command drift (issue #2/#3 P0)
The session's active tab was a bare index into `pages`, which drifts when a
foreign/user/other-session tab is passively discovered, a tab closes, or the list
reorders — so `eval`/`screenshot`/`snapshot`/`click` could land on the wrong page.
With login state that's a safety bug (a fetch firing on the wrong origin), and it
made screenshot disagree with snapshot/eval.

Pin the intended tab by stable target_id (`active_target_id`), set on every
explicit open / tab new / tab switch / connect. `active_session_id` and
`active_target_id` resolve through it (falling back to the index only if the
pinned tab is gone), so all commands stick to the agent's tab regardless of
passive churn — and they all agree.

Verified (--cdp, multi-tab): a window.open foreign tab no longer drifts eval;
tab new / switch re-pin correctly.
2026-06-11 23:10:29 +09:00
leeguooooo 9eaa5495ae chore(release): 0.27.0-fork.46 — cap --annotate legend
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
2026-06-11 22:58:58 +09:00
leeguooooo 68734fcb36 fix(screenshot): cap the --annotate legend (don't flood the terminal)
Dense pages produced hundreds of legend lines on stdout (Hermes: HN dumped 320).
Print the first 40 with a "… and N more" summary; every marker is still drawn in
the image, and --json still returns the full list.
2026-06-11 22:58:56 +09:00
leeguooooo 4b33dbadb4 chore(release): 0.27.0-fork.45 — pick combobox atomic op
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
2026-06-11 22:55:35 +09:00
leeguooooo fc73ee6c90 feat(pick): atomic combobox select for react-select / ARIA / native (issue #2 P1)
The biggest manual-cost point in the dogfood reports: `select @ref` is a silent
no-op on non-native dropdowns, and click+wait+Enter on react-select/ARIA/portal
menus took ~20 turns of hand-written eval to get right.

New `pick <selector> --option "<text>"` does it atomically in one in-page async
routine: native <select> → set value + input/change; custom widget → focus +
open (pointer/mouse sequence), poll up to 2.5s for the option to render anywhere
(portals included), match by visible text, scroll it in, fire the full
pointer/mouse sequence. ERRORS loudly if the option never appears — no silent
success.

Verified headless: native <select> → "Gamma"; portal combobox → "欧洲"
(non-ASCII); missing option → explicit error. Documented in the skill.
2026-06-11 22:55:32 +09:00
leeguooooo 28d3748c06 chore(release): 0.27.0-fork.44 — eval origin stamp, type --focused, humanize bogus warn
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
2026-06-11 22:48:32 +09:00
leeguooooo fa47a0b8e5 feat: eval prints its origin URL, type --focused, AGENT_BROWSER_HUMANIZE bogus warn
- eval now prints `eval @ <url>` to stderr (stdout stays the raw value) so an
  agent can catch tab drift — e.g. a logged-in fetch that hit the wrong origin —
  before trusting the result. Mitigates the issue #2/#3 P0 safety concern. (eval
  already returned the origin; the default output just never surfaced it.)
- `type --focused <text>`: type into the currently-focused element with no
  selector, for custom widgets that move focus to a hidden input (issue #2 P3).
- AGENT_BROWSER_HUMANIZE set to an unrecognized value now warns once (like the
  --humanize flag) instead of being silently ignored (Hermes #3).
2026-06-11 22:48:31 +09:00
leeguooooo 36c593631c chore(release): 0.27.0-fork.43 — issue/Hermes batch 1 (silent-click, live env, eval --file, tab-list, docs)
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
2026-06-11 22:35:35 +09:00
leeguooooo b92757412d fix(click): occlusion guard for selector clicks — no more silent false success
A coordinate click resolved from a CSS selector (incl. the getByText/find path's
located node) skipped the occlusion check that @ref clicks already get, so an
overlay on top made the click land on the overlay while still reporting ✓ Done —
the worst failure mode for an agent (Hermes #1, issue #2/#3). Now: if the click
point doesn't hit the target (elementFromPoint isn't the element / a descendant /
an ancestor wrapper), dispatch through the DOM instead, which fires the real
handler. Best-effort probe (a flaky check never blocks the normal path); skipped
for strict CLICK_MODE=coord and non-left/multi-clicks.

Verified: occluded button click hits 0→1 (was silent ✓Done); normal click
unaffected.
2026-06-11 22:34:12 +09:00
leeguooooo 6ecda4d706 fix: per-invocation env (CLICK_MODE / HUMANIZE) reaches a running daemon
Root cause behind Hermes #1 (CLICK_MODE=dom "does nothing") and #2 (--humanize
"does nothing"): both are env vars the daemon reads, but the daemon's env is
frozen at spawn — set them on a command to an already-running daemon and they
were silently ignored. (Confirmed: setting CLICK_MODE=dom at daemon spawn made
dom_click fire; setting it later did not.)

Fix: the client forwards AGENT_BROWSER_CLICK_MODE / AGENT_BROWSER_HUMANIZE in the
command envelope (_clickMode/_humanize); execute_command applies them per command
— mirrors CLICK_MODE into the process env (interaction::click reads it fresh) and
sets the humanize session level. Each command is authoritative.

Verified on an already-running daemon: CLICK_MODE=dom now fires dom_click
(hits 0→1); --humanize human typing applies.
2026-06-11 22:29:39 +09:00
leeguooooo 123510db2b feat: eval --file, tab-list URL truncation, skill doc fixes (issue #2/#3 + Hermes)
- `eval --file <path>`: read JS from a file, sent verbatim — avoids shell-mangling
  of non-ASCII identifiers/strings (Chinese), quotes, and large scripts (issue #3).
- `tab list`: truncate multi-KB URLs (JWT/OTP login links) middle-out with a char
  count so the list stays readable (issue #3).
- skill: fix the snapshot example to match real output
  (`- role "name" [ref=eN]`, not `@e1 [role]`); document that eval runs in the
  page MAIN world with persistent state (top-level `const` collides — use IIFE /
  window / unique names) and to prefer --file/--stdin/-b for non-ASCII or big JS.
2026-06-11 22:22:07 +09:00
leeguooooo abb65c632b chore(release): 0.27.0-fork.42 — close session-owned tabs on exit (no tab leak)
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
2026-06-11 21:54:44 +09:00
leeguooooo e803bffbbb fix(tabs): close the session's own tabs on exit (stop leaking into the user's Chrome)
When connected to the user's real Chrome, mgr.close() disconnected but never
closed the tabs the session opened — so every session (especially one that
failed before calling close, or a forgotten one) left its tabs piling up in the
user's browser. Idle-timeout and shutdown have the same exit path.

Track the target_ids this session creates via Target.createTarget in
`created_targets` (only ever our own tabs — never the user's existing tabs, which
the raw-CDP path attaches to, nor other sessions'). On close(), for the connected
path (not a launched browser, which Browser.close handles wholesale), close each
of those targets — the extension maps Target.closeTarget → chrome.tabs.remove.

Verified against a throwaway --cdp Chrome: open + 2 `tab new` → 3 pages; `close`
→ back to 1 (our 2 closed, the pre-existing tab untouched).
2026-06-11 21:54:42 +09:00
leeguooooo 96ee2f9758 chore(release): 0.27.0-fork.41 — relay self-heals silently (no user action on blip)
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
2026-06-11 21:30:38 +09:00
leeguooooo 0a3d2a91a6 fix(connect): self-heal the relay silently — ~15s retry, no user action
fork.40 errored after 5s ("reload the extension"), which still pushed the problem
onto the user. Extend the relay-reconnect wait to ~15s when the extension is
installed: enough for the MV3 service worker to wake and reconnect on its own
(onStartup after a Chrome restart, or the keepalive alarm). The loop re-checks
the relay file each iteration, so a mid-wait recovery is picked up instantly and
the full window is only spent when the extension is genuinely down. End users no
longer have to do anything when the relay blips.
2026-06-11 21:30:37 +09:00
leeguooooo 1e5dfd35cb chore(release): 0.27.0-fork.40 — no consent-dialog fallback when extension is installed
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
2026-06-11 21:17:03 +09:00
leeguooooo b6b2ca56ca fix(connect): never fall back to the consent-dialog raw port when the extension is installed
Root cause of the recurring "Allow remote debugging?" dialog: when the ab-connect
relay was momentarily down (MV3 service worker drops the relay-url file across a
Chrome restart / idle wake), auto_connect_cdp silently fell through to the raw
:9222 DevToolsActivePort path — which pops Chrome 136+'s consent modal, the exact
thing the extension exists to avoid. Even a relay-aware build hit this if it
connected during the blip.

Fix: if the native-messaging host is installed (connect::host_installed() — the
durable signal that the user chose the extension path), auto_connect retries the
relay for ~5s while the SW reconnects, and then ERRORS with an actionable message
instead of attaching to a raw debug port. The raw :9222 path now runs only when
no extension is set up (where the dialog is expected). `--cdp <port>` still forces
the raw path explicitly.
2026-06-11 21:17:02 +09:00
leeguooooo 6d740093dc chore(release): 0.27.0-fork.39 — cookies set --curl preserves full attributes
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
2026-06-11 21:09:04 +09:00
leeguooooo c884fb4f57 fix(cookies): preserve full cookie attributes in cookies set --curl JSON import
The JSON-array branch of parse_curl_cookies dropped every field except
name/value, so importing a full cookie export (httpOnly session tokens,
per-domain cookies spanning multiple hosts, secure/sameSite/expiry) could
not reconstruct a usable auth state — a single --domain override cannot
cover an export that spans .chatgpt.com, .openai.com, etc.

Pass through url/domain/path/secure/httpOnly/sameSite/expires when present,
accepting common aliases from DevTools / EditThisCookie exports
(http_only, same_site, no_restriction, expirationDate). Bare {name,value}
exports are unchanged. Added a round-trip test.
2026-06-11 21:09:04 +09:00
leeguooooo 57ef011817 docs: document humanize (human-like input) + silent operation
- README + README.zh: new Anti-detection subsections — "Human-like input
  (behavioural stealth)" (curved trajectories / jitter / cadence / eased
  scroll-drag, adaptive per-page escalation, off|fast|human) with the
  trajectory contrast table, and "Silent operation" (background tabs, no
  foreground stealing, focus-emulated). Added AGENT_BROWSER_HUMANIZE to the
  tuning-knobs table.
- skill core: agents told operation is silent by default and how/when to use
  --humanize (leave on auto; force human for known behavioural targets).
2026-06-11 21:04:35 +09:00
leeguooooo 2c3bcb8f3d chore(release): 0.27.0-fork.38 — silent operation (no foreground tab stealing)
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
2026-06-11 20:12:28 +09:00
leeguooooo 5a61a64559 feat(stealth): silent operation — never steal the user's foreground tab
Driving the user's real Chrome should not yank their view around. Now the agent
operates entirely in the background:
- New tabs are created with `background: true` (CreateTargetParams) so opening
  one never foregrounds it (the ab-connect extension already used active:false;
  this covers the raw-CDP path too).
- Dropped the two AUTO `Page.bringToFront` calls (auto-connect fresh tab, and the
  internal active-page switch). The explicit `bringToFront` command is untouched —
  surfacing a tab stays opt-in.
- enable_domains now sets `Emulation.setFocusEmulationEnabled(true)` so a
  backgrounded agent tab still renders (screenshots work), isn't render-throttled,
  and reports document.hasFocus()/visibilityState='visible' — which also removes
  the "tab is hidden the whole session" bot tell.

Verified headless: hasFocus=true/visible while backgrounded; click + screenshot
still work. Default behaviour, no flag.
2026-06-11 20:11:22 +09:00
leeguooooo 1c2e594003 chore(release): 0.27.0-fork.37 — complete humanize (bbox jitter + wheel/drag easing)
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
2026-06-11 19:53:27 +09:00
leeguooooo 9bd6587278 feat(stealth): in-bbox landing jitter + eased wheel/drag (humanize v4)
Completes the humanize suite:
- Clicks land on a jittered point inside the element's box (Fast/Human) instead
  of its exact centre. `resolve_element_center` now also returns the element
  width/height (box_model_dims); the CSS-selector path reports zero size → land
  on centre (no jitter, no regression). Jitter is clamped to the inner box so the
  click never misses.
- Wheel scrolls split into eased, jittered segments (humanize::scroll_segments,
  unit-tested) instead of one instant jump.
- Drag follows the curved trajectory at Fast/Human (linear 10-step at Off).

Off is unchanged throughout. 9/9 unit tests; verified headless — jittered click
still lands (→ iana.org), segmented scroll moves the page.
2026-06-11 19:52:33 +09:00
leeguooooo df53b1a70e chore(release): 0.27.0-fork.36 — human-like input stealth (humanize)
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
Ships the humanize feature: human-like cursor trajectories (Bézier + easing +
press dwell), variable typing cadence, an adaptive per-navigation anti-bot
detector that auto-escalates guarded pages to Human, and `--humanize` /
AGENT_BROWSER_HUMANIZE control. Default Off → unchanged for ordinary sites.
2026-06-11 19:42:07 +09:00
leeguooooo a6f0193779 feat(stealth): humanized typing cadence + --humanize flag (v3)
- Typing: type_text_into_active_context now uses variable, human-like
  inter-keystroke gaps from humanize::keystroke_delays when no explicit --delay
  is given (Fast/Human); Off stays instant. Explicit --delay still wins.
- CLI: `--humanize off|fast|human` surfaces AGENT_BROWSER_HUMANIZE so the
  session's daemon (a child that inherits this env) applies it, overriding the
  adaptive detector. Invalid values warn and are ignored.

Verified headless: `--humanize human` + type lands "hello world" correctly.
Deferred: in-bbox landing jitter (helper ready, needs bbox threaded) + wheel/drag
easing.
2026-06-11 19:40:32 +09:00
leeguooooo bab58991fe feat(stealth): adaptive anti-bot detection drives humanize level (v2)
After each navigation, probe the loaded page for known behavioural anti-bot
vendor fingerprints — cookies (_abck/Akamai, _px/PerimeterX, datadome,
reese84/Imperva, …), script URLs, and window globals — and escalate this
session to HumanizeLevel::Human when one is present, else fall back to the Off
baseline. So ordinary sites run at full speed (instant clicks) and only pages
actually guarded by behavioural detection pay for human-like motion.

`AGENT_BROWSER_HUMANIZE` still forces a fixed level and short-circuits the probe.
Best-effort: a failed probe leaves the level unchanged. Verified end-to-end
(headless --launch): a HUMANIZE=human click on example.com traverses the curved
trajectory and lands correctly (→ iana.org), identical outcome to Off.
2026-06-11 19:29:49 +09:00
leeguooooo c5d4c8908d feat(stealth): human-like click trajectories (humanize v1)
Behavioural stealth: a click that teleports the cursor to an element's exact
centre with no approach path and zero press/release delay is a tell that
advanced anti-bot vendors (Akamai/PerimeterX/DataDome) flag, even though our CDP
events are isTrusted.

New `native::humanize` module — pure, unit-tested motion maths (cubic-Bézier
eased trajectories, in-bounds landing jitter, variable keystroke cadence, and an
anti-bot vendor detector) plus a small daemon-wide runtime (current level + last
cursor + per-action seed). `dispatch_click` now moves along a curved,
decelerating path from the last cursor position and dwells before releasing.

Three levels off|fast|human. Default is Off → byte-for-byte the old teleport, so
nothing changes until opted in. `AGENT_BROWSER_HUMANIZE=human` forces it now;
the adaptive per-navigation detector (set_detected_level) and type/wheel/drag
coverage land next. 8/8 unit tests; fmt + clippy clean.
2026-06-11 19:24:07 +09:00
leeguooooo 9ac8bae981 chore(release): 0.27.0-fork.35 — embed Store-install + feedback + stale-binary guidance in skill
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
Ships the post-fork.34 skill updates into the binary's embedded `skills get core`:
- lead extension setup with the one-click Chrome Web Store install
- nudge agents to file UX feedback at the GitHub issues page
- when an agent hits the "Allow remote debugging?" dialog, self-check the version
  and upgrade a stale (<fork.30) build / remove a shadowing npm-pnpm copy
Plus the README opening rewrite (hook-first vs Claude-in-Chrome / web-access /
Playwright) and Chinese README — docs only, ride along.
2026-06-11 18:35:12 +09:00
leeguooooo 3302762a32 docs(skill): guide agents to detect + upgrade a stale build on the consent dialog
Other users will hit the same "古董二进制" problem — an agent-browser-stealth
predating the relay-preference (fork.30) ignores the extension relay and pops
Chrome 136+'s "Allow remote debugging?" dialog. So when an agent hits that dialog,
the skill now says to:
1. check `agent-browser --version`; if < 0.27.0-fork.30, upgrade via install.sh
   (the npm registry lags — Releases are source of truth), and remove any stale
   npm/pnpm copy that shadows it (a tool bundling its own pinned copy needs that
   upgraded too);
2. otherwise install the Store extension.
Stops the "keep retrying the raw port" loop.
2026-06-11 18:32:32 +09:00
leeguooooo 73cf32edc8 docs(skill): encourage agents to file UX feedback to GitHub issues
Add a prominent callout near the top of the core skill asking agents that hit a
rough edge (confusing error, stale @ref, occluded click, flaky wait, missing
feature) to open a quick issue at
https://github.com/leeguooooo/agent-browser-stealth/issues with the command and
expected-vs-actual. Agent-filed friction is the cheapest, highest-signal way to
sharpen the tool (the Hermes dogfood runs already proved this).
2026-06-11 18:30:29 +09:00
leeguooooo 85fd019f62 docs: lead with the hook, not a feature matrix
The comparison read like a checklist (Runtime.enable leak, permissions footprint
— proof, not pitch). Reframe the opening around the visceral wins:
- headline: "Give your AI agent the browser you already live in"
- pain→relief lead: no fresh Chrome, no re-login, no captcha walls; you watch it
  work and take the wheel on 2FA; undetectable because it IS your browser (0% bot)
- three plain "why not just use X?" lines (Playwright/browser-use, Claude in
  Chrome, raw debug port) instead of a wall of checkmarks
- the honest feature matrix moves into a collapsible "Full feature comparison".
Mirrored in README.zh.md.
2026-06-11 17:40:14 +09:00
leeguooooo 9b4d924e48 docs: make the comparison table honest (CreepJS, Runtime.enable, multi-agent)
Reader fact-checked the table — three rows overclaimed:
- CreepJS: all real-Chrome tools (Claude in Chrome, web-access, us) score ~0%; it
  is NOT a win vs them. Reframe as "real-browser fingerprint" ( for all three,
   for Playwright/Puppeteer); note ours is the measured one.
- Runtime.enable leak: mark Claude in Chrome "—" (not independently tested) rather
  than ; web-access/Playwright leak, ours is off by default (rebrowser-verified).
- Multi-agent: web-access CAN run parallel sub-agents (shared browser), so not .
  The real differentiator is per-session ISOLATED, command-scoped tab groups.
Added footnotes spelling out the caveats. Same fixes in README.zh.md.
2026-06-11 17:34:07 +09:00
leeguooooo 9ad011d93c docs: add "why not X" comparison up top + Chinese README
- README opens with a head-to-head vs Claude in Chrome / web-access (raw CDP) /
  Playwright·Puppeteer·browser-use: the only tool that drives your own logged-in
  Chrome, from any agent, with no consent popup, undetectably (CreepJS 0%), and
  multi-agent — addresses the recurring "why not just use <alternative>" question.
- add README.zh.md (简体中文) with a language switcher in both files.
2026-06-11 17:30:19 +09:00
leeguooooo ebd220274b docs(README): lead "connect to your Chrome" with the Chrome Web Store extension
The extension is live on the Web Store, so make the one-click, no-popup extension
path the recommended setup (native messaging — no debug port, no token, no "Allow
remote debugging?" dialog, restart-stable). Demote the raw --remote-debugging-port
method to a collapsed "Alternative" that notes it pops the consent dialog.
2026-06-11 17:24:49 +09:00
leeguooooo 7a4559ac96 chore(release): 0.27.0-fork.34 — Web Store live: store-targeted force-install + skill store-install guidance
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
Ships the post-publish changes now that agent-browser-stealth is live on the
Chrome Web Store (knfcmbamhjmaonkfnjhldjedeobeafmk):
- force-install (.mobileconfig) targets the Store extension id (5d202c0)
- skill leads extension setup with the one-click Store install; agents that hit
  the "Allow remote debugging?" dialog now tell the user to install the Store
  build instead of retrying the raw-port path (bc96229)
- native-messaging host already allow-lists both the Store and Load-unpacked ids
2026-06-11 17:21:50 +09:00
leeguooooo bc9622994e docs(skill): lead extension setup with the published Chrome Web Store build
The extension is now live on the Web Store
(knfcmbamhjmaonkfnjhldjedeobeafmk). Update the skill so agents:
- install from the Store (one-click, restart-stable, auto-updating) as the
  primary path, with Load-unpacked demoted to a dev fallback (it can be disabled
  on Chrome restart, silently dropping the relay).
- when they DO hit the "Allow remote debugging?" dialog (relay not live → raw-port
  fallback), stop retrying and tell the user to install the Store extension once,
  rather than repeatedly popping the consent dialog.
2026-06-11 17:10:52 +09:00
leeguooooo 5d202c06a6 fix(connect): force-install targets the Web Store extension id
agent-browser-stealth is now published (id knfcmbamhjmaonkfnjhldjedeobeafmk). The
.mobileconfig force-install pulls from the Web Store update server, which serves
the extension under its STORE id — so the forcelist must use STORE_EXTENSION_ID,
not the local Load-unpacked id. (The native-messaging host already allows both
ids.)
2026-06-11 17:06:35 +09:00
leeguooooo 0966c630a7 fix(install): correct Windows global-install native-shim (wrong package dir)
Global Install (windows) failed "Verify shim points to native binary": the CLI
worked (JS wrapper) but the shim didn't point at the native .exe. Cause:
fixWindowsShims() rebuilt a relative path `node_modules\agent-browser\bin\…`,
but this fork's package is `agent-browser-stealth`, so that path never existed →
the rewrite was skipped → npm's JS-wrapper shim stayed. Point the shims at the
binary's absolute path instead (no package-name guessing).

Also: npm frequently creates the .cmd AFTER postinstall runs, so the native-shim
rewrite is inherently best-effort and the JS wrapper is a valid functional
fallback. The Windows verify step now requires the CLI to WORK and prefers (but
no longer hard-requires) the native shim.
2026-06-10 17:09:58 +09:00
leeguooooo d1f574013d ci: fix the two downstream jobs (global-install npm pack, windows-integration open)
These jobs ran for the first time once the Windows matrix hang was fixed:

- Global Install: `npm pack` runs the `prepare` script (`husky`), but husky isn't
  installed in that job (no devDeps) → "husky: not found", exit 127. Guard it:
  `prepare: husky || true` (husky's recommended pattern for envs without devDeps;
  still installs hooks for local dev when husky is present).
- Windows Integration: `agent-browser open` defaults to auto-connect and looked
  for an existing Chrome on a debug port, which a fresh CI runner lacks → "Could
  not connect". A CI smoke test should spawn its own browser: use `--launch`.
2026-06-10 16:47:36 +09:00
leeguooooo c3b8855252 test(e2e): de-flake cross-domain state save (drop httpbin.org)
e2e_save_state_cross_domain navigated to httpbin.org as "domain A", which is an
unreliable external service — when it was slow/unreachable in CI the page didn't
load on that origin, so its localStorage origin was missing from the saved state
and the test failed intermittently. Cookies/localStorage are set client-side via
CDP, so the page just needs to load reliably: use example.org (IANA-reserved,
like example.com) instead. Match full hostnames so the two example.* origins
don't alias. Verified locally: passes deterministically.
2026-06-10 16:22:07 +09:00
leeguooooo a9ff0a3fea ci: fmt the doctor_cli cfg_attr (Format check failed on the prior commit) 2026-06-10 15:46:18 +09:00
leeguooooo af50605a3b ci: stop the Windows matrix hang + fail-fast timeouts
The Rust (windows) matrix job hung for hours (GitHub's 6h default) because the
`doctor_offline_quick_json_emits_valid_payload` integration test spawns the real
CLI and `doctor --offline --quick` does not exit on Windows while its stdout is
captured — so `Command::output()` blocks forever. (The 767-test main suite and
the `doctor --help` test both pass on Windows; only this check hangs. macOS/Linux
matrix is unaffected.) This was masked until now because fail-fast used to cancel
the Windows job whenever the macOS lightpanda test failed first.

- skip that one test on Windows (`#[cfg_attr(windows, ignore = …)]`) with a note
  to investigate the Windows doctor exit/pipe behavior; still runs on Linux/macOS.
- add `timeout-minutes: 30` to the rust-cross matrix and native-e2e jobs so a
  hung test fails fast with a readable log instead of running to the 6h default.
2026-06-10 15:36:25 +09:00
leeguooooo 9b1f98b966 fix: polish two Hermes follow-up cosmetics (invalid-selector wording, empty url glob)
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
- invalid CSS selector now errors "Invalid selector '<sel>': <reason>" instead of
  the misleading "Element not found" — the coordinate path (resolve_by_selector)
  now also inspects exception_details, matching resolve_element_object_id.
- `wait --url ""` is rejected at parse time ("needs a non-empty pattern") rather
  than silently matching any URL. Unit test added.

Not changed: verb-less `find role X` defaulting to a click. That default is a
deliberate, tested decision (test_find_role_default_subaction_click_when_no_action);
changing it to locate-and-report is a design choice left to the maintainer.
2026-06-10 15:12:34 +09:00
leeguooooo cf4c27d13d fix: resolve Hermes-found CLI bugs (wait --url, find role, invalid selector, polish)
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
- wait --url: the arg parser never read `--timeout`, so a non-matching pattern
  waited the large default and wedged the daemon. Parse it. Also: matching was a
  literal substring (`includes`) so globs never matched — convert `**`/`*`/`?`
  globs to an anchored regex. And `poll_until_true` now bounds each probe with a
  timeout and tolerates transient navigation errors, so a hung `Runtime.evaluate`
  can never block past the deadline (un-wedges the daemon).
- find role <role> [--name]: the query was `[role="X"], X`, which matches a
  literal <X> tag / explicit attribute but NOT implicit-role elements — so
  `find role link` (<a href>) and `find role heading` (<h1>) never matched. Add a
  proper ARIA-role → implicit-element map and broaden accessible-name matching
  (aria-label/title/alt/value/text).
- click on a syntactically-invalid selector returned `✓ Done`: querySelector
  throws, and Runtime.evaluate returned the thrown DOMException as an objectId
  that was clicked as if it were the element. Check exception_details → error.
- output: a title-less page now prints `✓ <url>` instead of an empty title line.
- docs(skill): tab refs are `t2`, not `2` (SKILL.md, electron).

Verified live (isolated launch): wait --url glob matches instantly; non-matching
honors --timeout (2s) and leaves the daemon responsive; find role link/heading
match; invalid selector errors. Unit tests added for the glob + role map + parse.
2026-06-10 14:48:49 +09:00
leeguooooo 372eaf2ef6 docs(README): add how-it-works + architecture diagrams and "why the extension" comparison
- assets/how-it-works.png: CLI → extension (native messaging) → your real Chrome
- assets/architecture.png: tab groups / service worker / native messaging / CLI
- comparison table vs raw-CDP-port tools (web-access) and chrome.debugger
  (Claude in Chrome): the extension never triggers Chrome 136+'s "Allow remote
  debugging?" consent dialog, keeps Runtime.enable off (rebrowser clean), scores
  0% on CreepJS, and gives per-session tab groups for concurrent agents.
2026-06-10 14:13:16 +09:00
leeguooooo dcefc729e8 chore(release): 0.27.0-fork.31 — Web Store submission ready + two install paths
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
- extension: popup status page (paired/not-paired) so the listing has standalone
  UI; renamed agent-browser-stealth + new icon (earlier in this line)
- store: upload zip strips manifest "key" (the Web Store forbids it); the unpacked
  dir + .crx keep it. Submitted for review (item knfcmbamhjmaonkfnjhldjedeobeafmk).
- connect: native-messaging allowed_origins lists BOTH the local Load-unpacked id
  (ciiljdlhd…) and the store-assigned id (knfc…), so either install path pairs.
- ci: launch-based jobs opt into AGENT_BROWSER_ALLOW_HEADLESS for display-less
  runners (fixes Native E2E); + version-sync/dashboard/fmt/clippy/flaky-test repairs.
- docs: skill documents both install methods (Load unpacked now, Web Store later).
2026-06-10 14:02:26 +09:00
leeguooooo f4a8f79a22 docs(store): add missing tabGroups permission justification 2026-06-10 13:55:03 +09:00
leeguooooo 6cf74817d8 feat(connect): allow the Web Store extension id in native-messaging origins
Store upload strips manifest 'key', so the published build gets id
knfcmbamhjmaonkfnjhldjedeobeafmk (not the local ciiljdlhd). Add a
STORE_EXTENSION_ID const and list both origins in allowed_origins so either the
local Load-unpacked build or the store build can reach the native host.
2026-06-10 13:44:32 +09:00
leeguooooo 14ffd30417 fix(extension): strip manifest "key" from the Web Store upload zip
The Chrome Web Store rejects uploads whose manifest contains a "key" field
("manifest must not contain 'key'") — it assigns its own id. pack-extension.sh
intentionally kept "key" in the zip, so every upload failed. Now the script
stages a copy and removes "key" for the zip only; the unpacked DIR and the signed
.crx keep "key" so local Load-unpacked + managed force-install stay pinned to
ciiljdlhd…. After the first store upload, add the store-assigned id to the
native-messaging allowed_origins (connect.rs EXTENSION_ID) so the store build pairs.
2026-06-10 13:34:45 +09:00
leeguooooo 17686fdbf8 feat(extension): add popup status page (paired/not-paired) for Web Store review
The biggest Web Store rejection risk for a CLI-bridge extension is "non-functional
without external software." Give ab-connect a visible standalone UI: a branded
popup that shows whether the native-messaging link to the local agent-browser CLI
is live (Connected + attached tab count, or Not paired with the install hint),
plus a one-line privacy statement (no tracking, no remote server) and a repo link.

- manifest: action.default_popup = popup.html; bump 0.4.0 -> 0.4.1
- background.js: track hostConnected; respond to {type:'ab-status'} from the popup
  and nudge a reconnect on open
- popup.html/popup.js: dark/cyan branded status page (MV3-CSP-safe: external JS,
  no inline handlers), with a safety timeout so it never hangs on "Checking…"
- repacked ab-connect.zip/.crx
2026-06-10 13:25:50 +09:00
leeguooooo 22532d756c ci: allow headless in launch-based jobs (e2e, windows-integration)
This fork forbids headless by default (always-headed for stealth, fork.27), but
CI runners have no display, so launched Chrome failed to start — every Native E2E
test errored at 'Chrome Launch attempt failed'. Opt the launch-based jobs into the
documented AGENT_BROWSER_ALLOW_HEADLESS=1 escape (designed for display-less
servers). global-install doesn't launch Chrome, so it's untouched.
2026-06-10 12:27:14 +09:00
leeguooooo 68e2e351b1 fix(clippy): use sort_by_key in findurl (clippy 1.96 unnecessary_sort_by)
CI's stable toolchain is clippy 1.96, which flags unnecessary_sort_by that local
1.94 did not. hits.sort_by(|a,b| b.date_added.cmp(&a.date_added)) -> sort_by_key
with Reverse.
2026-06-10 12:04:39 +09:00
leeguooooo d95d32831e docs(store): rename listing/privacy to agent-browser-stealth 2026-06-10 11:57:25 +09:00
leeguooooo 1a4c440d9e ci: fix long-broken CI (version-sync, dead dashboard job, fmt, clippy, flaky test)
The fork's CI had never been green. Pre-existing failures:
- version-sync: check-version-sync.js read packages/dashboard/package.json,
  which doesn't exist in this fork (workspace is just "."). Drop the dashboard
  comparison; check package.json vs cli/Cargo.toml only.
- Dashboard job: `pnpm install --filter dashboard` for a non-existent package.
  Remove the job.
- Format check: repo was never `cargo fmt`-clean. Ran cargo fmt (mechanical).
- Clippy -D warnings (newly enforced on Rust 1.94 stable): manual_contains in
  commands.rs (.iter().any()->.contains()), question_mark in element.rs
  (if-let-Err -> ?), result_large_err on the tungstenite handshake callback in
  connect.rs (allow — the Result type is fixed by the accept_hdr_async contract).
- rust-cross: lightpanda::waits_for_ready_without_logs spawns a real process +
  binds a socket with timing assumptions; flaky in CI. Marked #[ignore].

Also: skill docs note fork.30's relay-preferred auto-connect (plain
`agent-browser open` is dialog-free once the ab-connect extension is loaded) and
the extension's new "agent-browser-stealth" display name.
2026-06-10 11:49:11 +09:00
leeguooooo d1fbdaadeb chore(release): 0.27.0-fork.30 — stealth: navigator overrides on prototype, not instance
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
rebrowser's navigatorWebdriver probe checks Object.getOwnPropertyNames(navigator)
== [] (real Chrome keeps navigator members on Navigator.prototype). The launch-mode
stealth script defined language/languages/userAgentData/contacts as instance
own-properties, leaking them as an automation tell.

- add __abRedefineNavProto(name, getterImpl): redefines a navigator member on the
  PROTOTYPE with a native-masked getter toString, then deletes any instance shadow
  (mirrors the existing vendor patch). Falls back to instance only if proto is locked.
- convert language/languages/userAgentData to it; make the contacts block prototype-first.

After: Object.getOwnPropertyNames(navigator) == [], values intact, getters native,
rebrowser navigatorWebdriver 🟢, runtimeEnableLeak/pwInitScripts 🟢, sannysoft 0 fails.
2026-06-10 11:35:35 +09:00
leeguooooo 839aaa5586 chore(release): 0.27.0-fork.29 — plugin overflowTest fix, popup-free auto-connect, ab-connect rebrand+icon, README
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
- stealth(plugins): stop overwriting real native navigator.plugins in headed
  mode (the JS fake had a non-native item(), broken uint32 wrap → incolumitas
  overflowTest FAIL, and an anachronistic Native Client plugin). Leave native
  plugins untouched when present; modernize the headless-escape fallback to the
  real 5 PDF-viewer set with masked-native item()/namedItem().
- connect: auto_connect_cdp() now prefers the dialog-free ab-connect relay over
  the raw :9222 CDP port, so Chrome 136+'s "Allow remote debugging?" consent
  modal no longer fires when the extension relay is live. Gated by a bare-TCP
  relay_is_live() probe (+3 unit tests).
- extension: rename ab-connect to "agent-browser-stealth" + new stealth icon set
  (16/32/48/128).
- docs(README): hero/shield/fingerprint images, expanded detector results
  (CreepJS 0% stealth, incolumitas all-OK, BrowserScan CDP-clean), and a
  "Verify it yourself" section. .gitignore: allow assets/ + extension icons.
2026-06-10 11:17:41 +09:00
leeguooooo a7f9c24fdb chore(release): 0.27.0-fork.28 — skill docs (headed default, tab groups) embedded
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
2026-06-10 09:58:35 +09:00
leeguooooo 42ade7b4e8 docs(skill): headed-default/headless-forbidden + per-session tab groups + stealth ranking
Update the served skill (skill-data/core, embedded into the binary) for tonight's
changes: --headed is the default and headless is FORBIDDEN (was wrongly 'default
is headless'); each --session on the extension-connect path gets its own colored
tab group with no cross-talk; anti-detection ranking real-Chrome(extension) >
headed-launch > headless(forbidden). Needs a rebuild so standalone installs'
embedded skill reflects it.
2026-06-10 09:58:33 +09:00
leeguooooo 2dabed973e chore(release): 0.27.0-fork.27 — forbid headless (always headed for stealth)
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
2026-06-10 09:52:40 +09:00
leeguooooo dd2deff06c feat(stealth): forbid headless — always launch headed
Headless Chrome is a bot-detection tell: creepjs scores ~33% headless even with
--headless=new, while a headed window with a real GPU scores 0%. Since this is a
stealth fork, headless is now forbidden — build_chrome_args ignores the headless
LaunchOption and never emits --headless/--enable-unsafe-swiftshader/forced
--window-size. The only escape is AGENT_BROWSER_ALLOW_HEADLESS=1 for genuinely
display-less servers (discouraged — forfeits stealth).

Verified locally: default launch (no env) is headed (webdriver=false,
platform=MacIntel, no --headless flag); creepjs headed = 0% headless vs 33%
headless. chrome.rs: 48 tests pass incl. forbids-headless + escape.
2026-06-10 09:52:39 +09:00
leeguooooo 340886293a chore(release): 0.27.0-fork.26 — stealth navigator.platform=MacIntel (anti-detection fix)
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
2026-06-10 08:46:43 +09:00
leeguooooo fc1699a526 fix(stealth): navigator.platform = MacIntel/Win32/Linux x86_64 (was UA-CH value)
platform_string() feeds the CDP Emulation.setUserAgentOverride 'platform' field,
which sets the LEGACY navigator.platform. It was returning the UA-CH form
("macOS"/"Linux") — but real Chrome reports navigator.platform = "MacIntel" on
macOS and "Linux x86_64" on Linux. "macOS" contradicts the UA's "Intel Mac OS X"
and is a trivial bot-detection tell (platform vs UA mismatch). UA-CH
(navigator.userAgentData.platform via platform_hint) stays "macOS"/"Windows"/
"Linux" — that form is correct there.

Verified locally on bot.sannysoft.com (all rows green incl. navigator.platform=
MacIntel) + eval probes: webdriver false, no Headless in UA, real WebGL
(Apple M3 Metal, not SwiftShader), plugins/permissions consistent.
2026-06-10 08:46:42 +09:00
leeguooooo 4bcfe74514 chore(release): 0.27.0-fork.25 — relay liveness fix (Browser.getVersion local) stops reconnect-storm drift
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
2026-06-10 01:38:19 +09:00
leeguooooo bb41c24c08 fix(connect): relay answers Browser.getVersion locally (stops reconnect storm)
ROOT CAUSE of per-session command drift on the extension path: the daemon's
liveness check (`is_connection_alive` → `Browser.getVersion`) is a BROWSER-level
command. The relay only answered Target.* locally and forwarded the rest, so
Browser.getVersion went to the extension, which can only do per-tab
chrome.debugger → it errored → CdpClient saw TransportError → connection deemed
DEAD → the daemon closed + reconnected + re-ran discover_and_attach_targets on
EVERY command. Each re-discover rebuilds pages from the relay's minimal
targetInfo and resets active_page_index=0, so eval/get-title/screenshot drifted
to the first tab (about:blank / a foreign focused tab).

Reproduced locally (throwaway Chrome + Extensions.loadUnpacked + fork.24 nm-host):
trace showed discover_and_attach_targets running on every command (pages
before=0) and [ev] active_idx reset to 0.

Fix: relay answers Browser.getVersion locally with a stub version (like
getTargets), so the liveness probe succeeds → connection stays alive → no
reconnect/re-discover → the session's active tab is preserved. Pairs with
fork.24's add_background_page. relay.rs: 10 unit tests.
2026-06-10 01:38:18 +09:00
leeguooooo 75bd1d21a7 chore(release): 0.27.0-fork.24 — passive tab discovery no longer hijacks active tab (per-session control)
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
2026-06-10 00:42:16 +09:00
leeguooooo 06c75af46a fix(connect): passively-discovered tabs no longer steal the active tab
After connect+grouping worked, follow-up eval/get-title/screenshot drifted to a
foreign tab: on a shared browser, Target.targetCreated events for tabs the user
or OTHER agent sessions open stream in and are drained on every command. The
drain path routed them through add_page(), which sets active_page_index to the
new page — so the session's active tab silently jumped to a foreign tab and its
commands landed there.

Add BrowserManager::add_background_page() (push without touching active, dedup by
target_id) and use it in the event-drain path. Explicit opens (tab new, the
add-and-switch paths) keep using add_page() and still focus the new tab.

Closes the last gap in concurrent multi-agent: each session now drives its OWN
tab regardless of other sessions'/the user's tab activity.
2026-06-10 00:42:16 +09:00
leeguooooo 312bb0d65b chore(release): 0.27.0-fork.23 — tolerate minimal targetInfo from relay (extension connect getTargets)
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
2026-06-10 00:07:59 +09:00
leeguooooo cff003c333 fix(connect): tolerate minimal targetInfo (relay re-announce omits title/url)
After the connect fix, extension connect reached the relay but Target.getTargets
failed: 'missing field title'. The ab-connect relay builds targets from the
extension's synthesized Target.attachedToTarget; the re-announce path
(reannounceAttachedTabs) emits a minimal targetInfo {targetId,type,attached}
with no title/url, so strict deserialize of TargetInfo blew up the whole
getTargets response.

Make TargetInfo.title/url #[serde(default)] (empty) — tolerant of minimal CDP
targetInfo from the relay (and the occasional real-CDP omission). Titles
re-populate from Target.targetInfoChanged / page events after attach.
2026-06-10 00:07:58 +09:00
leeguooooo f2b0c2ea9b chore(release): 0.27.0-fork.22 — extension connect uses relay URL (fixes --session connect hang)
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
2026-06-09 23:39:42 +09:00
leeguooooo ea58bce19e fix(connect): extension connect now uses the relay URL (was falling through to auto-connect)
`extension connect` rewrote argv to ["connect", <relay-url>] but the connect path
reads flags.cdp — parsed earlier from the original argv ("extension connect" →
None). So the relay URL was dropped and the daemon ran AUTO-CONNECT, grabbing
whatever Chrome it could discover: a stale remote-debugging Chrome on :9222
(indefinite hang), or triggering Chrome's "Allow remote debugging?" prompt on
machines without one. This is the EAGAIN/hang hermes hit on --session connect.

Fix: set flags.cdp = Some(relay_url) (+ disable auto_connect) in the
extension-connect branch so the daemon connects to the live relay endpoint.
Diagnosed via local repro (trace showed connect_cdp resolving ws://...:9222/
devtools/browser/... instead of the relay's ws://...:<port>/<guid>).
2026-06-09 23:39:41 +09:00
leeguooooo afb68ded93 chore(release): 0.27.0-fork.21 — multi-client relay (concurrent agents)
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
2026-06-09 22:47:30 +09:00
leeguooooo a6631cd7d8 fix(connect): multi-client relay — concurrent agents no longer cross-talk
The nm-host fanned extension→client messages over a broadcast channel and
forwarded commands under the client's own id, so two sessions connected to one
relay collided: command replies went to every client and ids overlapped → the
2nd session's connect hung (EAGAIN after 30s×5) and responses cross-talked.

Now the relay demultiplexes:
- each forwarded command is re-keyed to a relay-global id mapped to (client,
  original_id); the extension's reply routes back to ONLY that client with its
  original id restored (relay.rs: pending map + ClientId)
- CDP events fan out to all clients (they ignore unknown sessions)
- nm-host keeps a client_id -> sender registry instead of a broadcast; clients
  are unregistered + their pending dropped on disconnect

Unblocks concurrent multi-agent on one shared Chrome (each --session its own tab
group from fork.20). relay.rs: 9 unit tests incl. cross-client id isolation.
2026-06-09 22:47:30 +09:00
leeguooooo 4f630e29ad chore(release): 0.27.0-fork.20 — per-session tab groups (ab-connect 0.4.0)
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
2026-06-09 21:17:54 +09:00
leeguooooo d232763ff7 feat(connect): per-session Chrome tab groups on the shared real browser
Shared browser, separate tab groups: when an agent drives the user's real Chrome
via ab-connect, every tab it opens lands in a Chrome tab group named after its
--session (stable color per name). Each agent's tabs stay visually separated from
other agents' and from the user's own (ungrouped) tabs. Visibility is NOT
restricted — all agents still see all tabs (per design).

- CreateTargetParams gains an optional non-CDP `agentGroup` hint (skip-if-none),
  so a strict real-Chrome endpoint never receives it
- BrowserManager.agent_group(): Some(session) only when ws_url == the live
  ab-connect relay URL (never on launched/direct CDP); DAEMON_SESSION set at
  daemon start supplies the name; emitted at all createTarget sites (transient
  storage target stays None)
- ab-connect: +tabGroups permission; Target.createTarget reads agentGroup and
  groups the new tab (create/reuse by title, deterministic color), best-effort
- extension 0.3.0 -> 0.4.0; re-signed crx + zip (id unchanged)

Needs the v0.4.0 extension reloaded + a build with this change to take effect.
2026-06-09 21:17:53 +09:00
leeguooooo 85f4635358 chore(release): 0.27.0-fork.19 — new extension id (Web Store signing key) + store-aware install
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
Transport (native messaging + extension connect) works today via Load unpacked.
Silent force-install is pending the Chrome Web Store listing going live (off-store
force-install is [BLOCKED] on unmanaged Chrome 149).
2026-06-09 19:20:45 +09:00
leeguooooo 726e9d4ea3 chore(store): add listing screenshot (force-add past png ignore) 2026-06-09 19:16:04 +09:00
leeguooooo ff8a340269 chore(store): add 1280x800 listing screenshot + bake in GitHub Pages privacy URL 2026-06-09 19:15:49 +09:00
leeguooooo c1fa237183 chore: add .nojekyll for GitHub Pages (serve privacy policy as-is) 2026-06-09 19:10:32 +09:00
leeguooooo f6b21461e9 feat(connect): pivot extension install to Chrome Web Store path
Verified on Chrome 149 (unmanaged macOS): a force-install policy pointing at a
SELF-HOSTED crx is tagged [BLOCKED] in chrome://policy ("Error, Warning") — Chrome
refuses off-Web-Store force-installs on non-cloud-managed browsers. So the
self-hosted-crx approach cannot work on consumer Chrome; the extension must ship
via the Chrome Web Store (same reason codex/claude do).

- UPDATE_URL -> Chrome Web Store update endpoint; add STORE_URL (one-click Add to
  Chrome) as the guaranteed path + headless fallback
- install instructions now offer: A) one-click store link, B) silent profile
  force-install (works once published), with Load-unpacked as the pre-publish stopgap
- build extensions/ab-connect.zip (CWS upload package; manifest "key" kept so the
  published id stays ciiljdlhdpfckdcfkphgmfalanpdejep)
- extensions/store/{SUBMISSION.html,privacy.html}: full listing copy, permission
  justifications (debugger is the review-sensitive one), privacy policy
- drop dead self-hosted extensions/updates.xml; pack-extension.sh now builds the zip

Not released yet — force-install only works after the store listing is Published.
2026-06-09 19:03:10 +09:00
leeguooooo e8ef57bf00 feat(connect): force-install ab-connect via Chrome config profile (no Load-unpacked GUI)
Chrome 149 killed every GUI-free way to load an *unpacked* extension into the
real profile: --load-extension removed in Chrome 142 (incl. the
--disable-features workaround), local-.crx external install blocked on macOS
since Chrome 44, remote-debugging-port killed in Chrome 136. So agents were
stuck automating the chrome://extensions Load-unpacked native file dialog —
unworkable.

`extension install` now writes a macOS configuration profile that force-installs
the signed .crx from a hosted update_url (ExtensionInstallForcelist policy). One
approval in System Settings (a single fixed Install button — cua-driver-friendly,
unlike a file dialog) → Chrome force-installs + auto-updates the extension on next
launch. No token, no per-use confirmation, and binary-install users no longer
need the extensions/ folder (crx is fetched from the URL).

- pin a stable signing key; new extension id ciiljdlhdpfckdcfkphgmfalanpdejep
- ship signed extensions/ab-connect.crx + extensions/updates.xml (raw GH host)
- scripts/pack-extension.sh re-signs with the stable key; .secrets/*.pem ignored
- uninstall removes the profile file + prints `profiles remove` hint
2026-06-09 18:27:51 +09:00
leeguooooo 9efcb56651 chore(release): bump to 0.27.0-fork.18 — extension connect (zero-token native-messaging control of real Chrome) + click reliability + eval-first/find-url/site-notes
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
2026-06-09 17:32:44 +09:00
leeguooooo 091a4ec02e docs(skills): teach agents the extension-connect flow + computer-use for setup
So an agent can operate the zero-confirmation real-Chrome feature itself:
- SKILL.md: tool matrix gains "the user's own already-open, logged-in window →
  extension connect", plus a short section pointing at the flow.
- commands.md: the one-time "Load unpacked" is a privileged GUI step the CLI
  can't do — call it out that the agent can perform it with a computer-use /
  GUI-automation tool (cua-driver), with the live gotchas (synthetic-keystroke
  tools like peekaboo don't reach Chrome; cua-driver does; the native file
  dialog may need the user to pick the folder).
2026-06-09 17:14:50 +09:00
leeguooooo 6c0f5cbaa1 feat(connect): attach existing tabs + extension connect one-command UX
Completes the zero-confirmation real-Chrome feature.

- Drive the user's EXISTING logged-in tabs (not just newly-created ones):
  extension attachTab now treats "already attached" (a lingering chrome.debugger
  binding after a service-worker restart) as success and announces the tab
  anyway, instead of skipping it. The nm-host also sends {method:"attachAll"}
  when an agent-browser CDP client connects, so the daemon doesn't race an empty
  target list.
- `agent-browser extension connect` auto-discovers the relay's CDP url
  (~/.agent-browser/relay-cdp-url) and attaches — no copying a ws URL. Rewrites
  into the normal `connect <url>` flow; `extension install/status/uninstall`
  unchanged.
- Skill docs: a "drive your real, logged-in Chrome (extension)" section.

Verified end-to-end: `extension connect` listed the user's real tabs (Lark,
LINUX DO, Rakuten, Discord) and read a logged-in Lark doc's title — zero token,
zero confirmation. Full suite 768 passed.
2026-06-09 17:11:59 +09:00
leeguooooo 0d72e0d889 feat(connect): bridge native-messaging host to a CDP endpoint — end-to-end works
The __nm-host now exposes a Chrome-compatible CDP WebSocket endpoint and bridges
it to the extension over native messaging via the relay translation core
(relay.rs): incoming raw CDP commands are answered locally for browser-level
Target discovery or forwarded to the extension as forwardCDPCommand; the
extension's forwardCDPEvent/results are relayed back as raw CDP.

Security without a token or user interaction: the ws URL carries an unguessable
guid and is written to ~/.agent-browser/relay-cdp-url (perms 600), so only this
user's agent-browser can drive the browser — mirroring how Chrome guards its own
remote-debugging URL.

Verified end-to-end on real Chrome: `agent-browser connect <relay-url>` then an
eval navigated a tab and read back "Example Domain | https://example.com/" —
abs → CDP → relay → native messaging → extension → chrome.debugger → real tab,
zero token, zero confirmation. Adds the tokio io-std feature for the host's
stdio.

Remaining polish: re-attach the user's EXISTING tabs after a service-worker
restart (currently attaches new tabs cleanly; existing ones need detach+reattach
since chrome.debugger may still be bound), and an `open --extension` UX that
reads relay-cdp-url so the URL isn't passed by hand.
2026-06-09 16:54:29 +09:00
leeguooooo 528de4230f feat(connect): native-messaging transport — zero-token connect to real Chrome
Optimal architecture (chosen over the WS+token copy): the ab-connect extension
talks to a local agent-browser native-messaging host. No localhost port, no
token — Chrome authenticates the extension to the host by id. This is the
codex/claude-style "install once, no per-use confirmation" model.

- extensions/ab-connect: rewritten transport WebSocket+token → native messaging
  (chrome.runtime.connectNative). Pinned the extension id via a manifest `key`
  (→ bdoiejojpjogcjojeladhioioijhgade) so the host manifest can authorize it.
  Kept the proven chrome.debugger attach + Target.attachedToTarget emulation;
  dropped WS/token/options. Rebranded to "agent-browser connect".
- cli connect.rs: `agent-browser extension install` writes the native-messaging
  host manifest (Chrome/Chromium/Edge/Brave) + a launcher; hidden `__nm-host`
  speaks the 4-byte-length native-messaging framing.

Validated end-to-end on real Chrome: Chrome spawned the host (origin matched the
pinned id) and the extension attached the user's real logged-in tabs, streaming
Target.attachedToTarget over native messaging — zero token, zero port.

Next: bridge the host to the daemon relay (relay.rs) + CdpClient so
`agent-browser click/eval/...` drives those tabs.
2026-06-09 16:39:38 +09:00
leeguooooo 7f672494c1 feat(connect): relay translation core (envelope <-> raw CDP + Target emulation)
Pure, unit-tested core of the daemon-side relay that bridges the ab-connect
extension to the existing CdpClient. The extension exposes per-tab
chrome.debugger + synthesized Target events; CdpClient expects a browser-level
endpoint. So RelayState:

- answers Target.getTargets / attachToTarget / setDiscoverTargets LOCALLY from
  targets learned via the extension's forwardCDPEvent(Target.attachedToTarget),
  returning the extension's cb-tab-N sessionId (consumes those synth events
  rather than double-forwarding them);
- forwards every other command as a forwardCDPCommand envelope (carrying
  method/params/sessionId);
- maps forwardCDPCommand responses and forwardCDPEvent events back to raw CDP;
- validates the connect-handshake token; emits challenge/ping.

Keeps CdpClient and browser.rs unchanged. 8 unit tests; clippy clean. Still
inert — the tokio WS server + `connect` command wire it next.
2026-06-09 14:59:54 +09:00
leeguooooo 8a8106ad75 feat(connect): vendor MV3 connect extension (adapted from openclaw-browser-relay)
First step toward zero-confirmation direct connect to the user's real Chrome:
Chrome 136 killed --remote-debugging-port on the default profile, so the only
sanctioned way to drive the user's live logged-in window is an extension using
chrome.debugger (same approach as Codex/Claude, whose extensions are closed).

Vendors the MIT-licensed openclaw-browser-relay extension into
extensions/ab-connect/, rebranded to "agent-browser connect" (NOTICE.md keeps
attribution). It already handles the hard parts: chrome.debugger auto-attach all
tabs, new-tab auto-attach, MV3 service-worker keepalive (alarms) + reconnect,
sessionId↔tab mapping, token auth, and a CDP-over-WebSocket envelope
(connect handshake / forwardCDPCommand / forwardCDPEvent / ping-pong).

Inert for now — not wired. Next: an abs-daemon relay that speaks this envelope
and bridges it to the existing CdpClient (raw CDP), then a `connect` command.
2026-06-09 14:54:03 +09:00
leeguooooo f9cc31d003 chore(release): bump to 0.27.0-fork.17 — eval-first skill + find-url (local bookmark search) + site-notes convention
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
2026-06-05 17:15:49 +09:00
leeguooooo a7a3f924b0 docs(skills): site-notes convention for remembering site quirks
Borrow web-access's site-experience persistence as an agent-workflow convention
(no CLI code): keep one markdown file per domain under
~/.agent-browser/site-patterns/<domain>.md. Read it before working a domain
(hints, not guarantees); update it after learning something durable — working
selectors, required hidden fields, anti-bot traps, login needs. Makes repeat
visits fast instead of re-solving the same page every run.
2026-06-05 16:55:47 +09:00
leeguooooo 7572c34229 feat(find-url): search local Chrome/Edge bookmarks by keyword
Borrow web-access's find-url: locate an internal system or a previously-saved
page that public search can't reach, without opening a browser.

- `agent-browser find-url <keywords> [--browser chrome|edge] [--profile X]
  [--limit N] [--json]` — local command, no daemon. All keywords must match a
  bookmark's name or url; results are most-recently-added first.
- Cross-platform Bookmarks JSON paths (macOS / Linux / Windows), zero new deps
  (serde_json). Skips javascript:/data: bookmarklets.
- Skill docs: "pick the cheapest tool" matrix now points at find-url, plus a
  commands.md section.

Bookmarks only for now — visited-history is a locked SQLite DB and would need a
SQLite dependency (deferred to avoid C-dep cross-compile risk in the release
pipeline).
2026-06-05 16:54:46 +09:00
leeguooooo 06f5f9e8f1 docs(skills): lead with eval-first + tool-choice matrix
Real dogfooding showed the skill pushed agents straight into the fragile
snapshot/@ref path. Reframe the core guidance toward how a developer actually
drives a real browser:

- "Pick the cheapest tool" matrix: WebSearch / WebFetch+curl for static, reach
  for agent-browser only when you need a real logged-in / interactive / dynamic
  browser. Plus: don't hand-build deep URLs — use links found by interacting.
- "Two ways to drive a page": structured (@ref/find) is convenient but lossy &
  fragile; eval-first (`eval "<js>"`) is the real DOM — read hidden inputs,
  Shadow DOM, form.elements/.validity, or el.click() directly. Drop to eval the
  moment the structured path fights you, instead of retrying it.
- Escalation ladder rewritten (refs → find → CSS → eval) and a note to retry a
  no-op click with AGENT_BROWSER_CLICK_MODE=dom.

Doc-only; closes the biggest part of the "abs feels worse than web-access" gap.
2026-06-05 16:44:29 +09:00
leeguooooo 5f50ca075c chore(release): bump to 0.27.0-fork.16 — click reliability (scroll-into-view + DOM fallback) + skill docs (console opt-in, CLICK_MODE, form/hidden-input eval)
Release binaries / Build macOS ARM64 (push) Has been cancelled
Release binaries / Build macOS x64 (push) Has been cancelled
Release binaries / Build Linux ARM64 (push) Has been cancelled
Release binaries / Build Linux musl ARM64 (push) Has been cancelled
Release binaries / Build Linux musl x64 (push) Has been cancelled
Release binaries / Build Linux x64 (push) Has been cancelled
Release binaries / Build Windows x64 (push) Has been cancelled
Release binaries / Attach binaries to GitHub Release (push) Has been cancelled
2026-06-05 11:00:57 +09:00
leeguooooo e7548c3eb5 fix(click): scroll into view + DOM-dispatch fallback for reliable clicks
Real-world dogfooding surfaced clicks that resolve a valid @ref but still miss:

- Scroll the target into view before computing click coordinates
  (scrollIntoViewIfNeeded). Without it, an element below the fold — or revealed
  after a scroll/popup — yields off-viewport coordinates and the click lands on
  whatever occupies that screen point.
- Fall back to a DOM-dispatched `.click()` when the coordinate path fails (a
  persistent floating layer failing the occlusion guard, or coordinates that
  won't resolve). The DOM dispatch targets the intended element directly instead
  of a screen point, so an overlay or portal can't divert it.
- AGENT_BROWSER_CLICK_MODE: "" (default: scroll + coordinate + DOM fallback),
  "coord" (strict coordinate, hard-fail on occlusion), "dom" (always
  element.click() — best for autocomplete/menu <li> that close on input blur).

Fallback is limited to left single-clicks (DOM .click() can't express
right/middle/double). Non-left/multi and "coord" mode keep the original error.

Docs: README knob table + skill commands.md gain CLICK_MODE, a click-reliability
note, and a "debug forms/hidden inputs with eval" section (snapshot doesn't show
hidden inputs — the fast path to bugs like a hidden point_choice=none).

6 click/interaction e2e green; full suite 760 passed.
2026-06-05 10:58:28 +09:00
leeguooooo b77a1e4568 docs(skills): document console-capture opt-in + stealth env knobs
console/errors capture is off by default in this fork (Runtime.enable is a
detectable CDP signal). Update the agent-facing skill docs so agents don't
treat empty console output as a bug:

- commands.md: new "Stealth / anti-detection knobs" env-var block
  (CAPTURE_CONSOLE, TIMEZONE, BLOCK_WEBRTC, HIDE_CANVAS, ADAPTIVE_REF) plus a
  heads-up note; annotate the console/errors lines.
- dogfood/slack SKILL.md: note that console/errors need
  AGENT_BROWSER_CAPTURE_CONSOLE=1.
2026-06-04 17:08:14 +09:00