Commit Graph

553 Commits

Author SHA1 Message Date
mcfn.feng 88b1ef90ec [packages/cli-v2]refactor: introduce TabContext to eliminate repetitive resolver params
Replace the 6-param pattern (cdp, target_id, selector, registry,
session_id, tab_id) with a TabContext struct created once per command.

Before: element::resolve_node(&cdp, &target_id, &sel, registry, &s, &t)
After:  ctx.resolve_node(&sel)

TabContext bundles (cdp, target_id, registry, session, tab) and exposes
resolve_node, resolve_center, resolve_object, scroll_into_view, and
resolve_object_id. Public fields cdp/target_id for direct CDP calls.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 12:09:36 +08:00
mcfn.feng e4c2a6b72a [packages/cli-v2]fix: allow too_many_arguments on assert_cookie_shape in e2e test
Pre-existing clippy lint — CI runs --all-targets which includes tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:48:45 +08:00
mcfn.feng 148649e8e8 [packages/cli-v2]fix: address PR review — synthetic ref guard + CDP error routing + AX fallback
P1: Skip CDP DOM.resolveNode for synthetic backendNodeIds (< 0, from AX
    nodes without a backing DOM element) — go straight to AX fallback.
P2: Distinguish CDP -32000 (stale node) from other errors (connection
    lost, etc.) — only return REF_STALE for true staleness, propagate
    other CDP errors through the standard error path.
Fallback: When backendNodeId resolution fails or is synthetic, use
    Accessibility.queryAXTree with the stored role + name to find the
    element in the current AX tree.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:45:09 +08:00
mcfn.feng 70d2907bb4 [packages/cli-v2]style: cargo fmt after merge with release/1.0.0
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:40:05 +08:00
mcfn.feng 4ec3705cc6 Merge remote-tracking branch 'origin/release/1.0.0' into bugfix/click-ref 2026-03-30 11:39:24 +08:00
mcfn.feng 8a7ab9d818 [packages/cli-v2]feat: implement @eN snapshot ref resolution for all selector commands
Wire up snapshot refs (@e1, @e5, etc.) so all commands that accept a
<selector> argument can use them. Previously resolve_ref() was a stub
returning UNSUPPORTED_OPERATION.

- Add reverse lookup (ref_to_id) to RefCache for O(1) ref→backendNodeId
- Add peek_ref_cache() to SessionRegistry for read-only access
- Implement resolve_ref: @eN → backendNodeId → objectId → nodeId via CDP
- Thread registry context through resolve_node, resolve_element_center,
  and resolve_selector_object (22 call sites updated)
- Error codes: REF_NOT_FOUND, REF_STALE, INVALID_ARGUMENT

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:36:05 +08:00
mcfn.feng 7a1cc2207a [packages/cli-v2]fix: misc fixes from cookies, logs, wait, and CDP session
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:35:46 +08:00
mcfn 1af3161317 [packages/cli-v2]feat: add -v short flag and version subcommand
- Add short alias -v to --version flag
- Add `version` subcommand that calls the same handle_version() logic

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 11:10:16 +08:00
Junliang Feng dfc4e2011c feat(cli-v2): browser storage commands (Batch 9, #167) (#379)
* [packages/cli-v2]feat: implement browser storage commands (Batch 9)

Add browser local-storage and session-storage commands (list/get/set/
delete/clear) via Runtime.evaluate on window.localStorage/sessionStorage.
Tab-level addressing (--session + --tab). Shared StorageKind enum drives
JS object selection and response storage field. clear <key> removes a
specific key (action: "clear", affected: 1), matching spec §14.5.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [packages/cli-v2]fix: correct CDP null detection in storage get

CDP represents JS null as {type:"object",subtype:"null"}, not type:"null".
Update cold-miss check to test for subtype=="null" when type is "object".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 10:52:32 +08:00
mcfn fbfffeec6c [packages/cli-v2]test: assert storage set context 2026-03-30 10:33:54 +08:00
mcfn fb7c338a81 [packages/cli-v2]test: add storage e2e coverage 2026-03-30 10:27:13 +08:00
mcfn 58d9894a67 [packages/cli-v2]test: relax cookies expires assertion 2026-03-30 10:19:06 +08:00
mcfn 9db064eb86 [packages/cli-v2]fix: correct Runtime.evaluate pointer for hostname in set.rs
CDP Runtime.evaluate response nests the value at result.result.value,
not result.value. Wrong pointer caused implicit-domain derivation to always
fall back to None, triggering "domain/url required" CDP error.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 09:43:47 +08:00
mcfn 28bbac1ca8 [packages/cli-v2]fix: route cookie CDP ops through tab-scoped session (Robin B1)
All Network.getAllCookies, Network.setCookie, Network.deleteCookies calls
now route via execute_on_tab using the first active tab's target_id instead
of execute_browser (browser-scope). Local Chrome does not expose Network.*
at browser scope — fixes CDP error -32601 on real E2E runs.

Also defaults path to "/" in set.rs when --domain is omitted (§13.3).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 09:39:08 +08:00
mcfn 067687375a [packages/cli-v2]fix: cookies set domain derivation and normalize_domain lowercase (B1+B2)
B1: when --domain is omitted, derive domain from window.location.hostname
evaluated on the first active tab; prepend leading dot for subdomain matching.
B2: normalize_domain now lowercases in addition to stripping the leading dot
to handle mixed-case domain comparisons (e.g. Example.COM vs example.com).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 09:32:56 +08:00
mcfn f79d112828 [packages/cli-v2]feat: implement browser cookies commands (Batch 8)
Add browser.cookies.list/get/set/delete/clear commands with CDP-level
Network.getAllCookies, Network.setCookie, Network.deleteCookies support.
Session-level only (no --tab); list/get are observations, set/delete/clear
are actions. Includes domain filtering, field mapping (httpOnly→http_only,
sameSite→same_site, expires=-1→null), and text/JSON output formatting.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 09:28:49 +08:00
mcfn f3845687fc [packages/cli-v2]test: pin cookies miss contract 2026-03-30 09:12:29 +08:00
mcfn 2eb6b2c0d2 [packages/cli-v2]test: add cookies e2e coverage 2026-03-30 09:06:49 +08:00
Junliang Feng e7979fd226 browser wait commands — element/navigation/network-idle/condition (Batch 7) (#375) 2026-03-30 02:02:26 +08:00
mcfn fc892950b0 [packages/cli-v2]fix: move Network.enable to attach() so pending tracking starts at tab birth
Root cause of B3 false-positive: subscribing to Network events inside
`wait network-idle` only sees requests that start AFTER the wait call.
Requests already in-flight (e.g. fetch/new Image() started before wait)
are permanently missed, so pending stays 0 and the idle quiet window
expires immediately.

Fix — session-level Network tracking:
- `CdpSession` gains `tab_net_pending: Arc<Mutex<HashMap<String, i64>>>`
- `attach()` calls `Network.enable` on the tab immediately after storing
  the session ID, before any user command runs
- `reader_loop` maintains `tab_net_pending` from Network domain events:
  +1 on requestWillBeSent, -1 on loadingFinished/loadingFailed/
  requestServedFromCache (clamped to 0)
- New `network_pending(cdp_session_id) -> i64` returns the live counter
- `detach()` cleans up the counter entry

`network_idle.rs` simplified:
- Removes per-wait event-channel subscription (subscribe-after-request
  race is gone by design)
- Polls `cdp.network_pending()` — reflects all requests since attach
- Keeps JS guard (readyState + img.complete) as secondary check
- Quiet window: pending==0 && js_idle held for 500ms

New unit test: test_network_pending_counter verifies counter increments,
decrements on terminal events, and clamps at 0.
Updated test_attach_detach to expect the new Network.enable exchange.
178/178 unit tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 01:57:36 +08:00
mcfn 0c0c2774b2 [packages/cli-v2]fix: implement CDP Network event subscription for network-idle wait
Add event routing infrastructure to CdpSession:
- New `event_subs` field keyed by "{cdp_session_id}:{method}"
- reader_loop now routes CDP events (messages without "id") to subscribers
- `subscribe_events(cdp_session_id, method)` returns mpsc::Receiver<Value>
- `get_cdp_session_id(target_id)` looks up the flat-session ID
- 2 new unit tests: test_event_routing, test_event_not_routed_to_wrong_session

Rewrite network_idle.rs to use CDP Network domain events:
- Subscribe to requestWillBeSent/loadingFinished/loadingFailed/requestServedFromCache
  before calling Network.enable (avoids the subscribe-enable race)
- Track in-flight pending count; decrement on terminal events
- Require pending == 0 AND js_idle (readyState + img.complete) for 500ms quiet
  window before declaring idle
- Fixes B3 false-positive: JS-only heuristics missed non-DOM fetch/XHR/script
  requests; CDP events cover all network traffic

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 01:50:43 +08:00
mcfn 07933ec52b [packages/cli-v2]fix: network-idle B3 — add img.complete + in-flight entry checks (#156)
Performance Resource Timing entries only appear after completion so the
previous responseEnd-only filter missed in-flight requests. Add two more
layers before the Performance API check:

1. document.readyState guard (page must be complete)
2. img.complete check — detects <img> elements still loading
3. responseEnd === 0 entries (W3C Resource Timing Level 2 in-flight)
   + recent entries within 500ms idle window

Any of the three layers returning pending > 0 keeps the wait loop alive.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 01:42:19 +08:00
mcfn 3ad54d122c [packages/cli-v2]fix: 3 wait handler corrections from Robin-Cx review (#156)
B1 wait navigation: use OR instead of AND for url_changed/readyState check.
Same-URL navigations (reload) never change URL — returning on either condition
matches actionbook-rs reference and handles both cases correctly.

B2 wait condition: arrays and objects are always truthy in JS regardless of
whether they are empty. Fix Rust truthiness to use _ => true for both,
matching actionbook-rs reference (waiting.rs:173).

B3 wait network-idle: replace readyState-only check with Performance Resource
Timing API inspection. Count resources with responseEnd < 500ms ago;
network is idle only when that count reaches zero. Matches actionbook-rs
reference (waiting.rs:98-120).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 01:37:50 +08:00
mcfn e3110ced6b [packages/cli-v2]feat: implement browser wait commands (Batch 7, #156)
Add four wait modes polled via CDP Runtime.evaluate:
- browser wait element <selector>  — polls document.querySelector until found
- browser wait navigation           — polls URL change + readyState=complete
- browser wait network-idle         — polls document.readyState=complete
- browser wait condition <expr>     — polls JS expression until truthy

All modes support --timeout (default 30000ms), --session, --tab.
TIMEOUT errors are retryable. SESSION_NOT_FOUND sets context=null;
TAB_NOT_FOUND sets context.tab_id=null. Text output: elapsed_ms + target/observed_value.
Registered in action.rs, cli.rs, router.rs, output.rs, browser/mod.rs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 01:29:16 +08:00
Junliang Feng aaabc03745 fix: logs hook timing and console.error separation (#158) (#374) 2026-03-30 01:23:23 +08:00
mcfn 6557a339e9 [packages/cli-v2]fix: call Page.enable before addScriptToEvaluateOnNewDocument (#158)
Chrome silently ignores Page.addScriptToEvaluateOnNewDocument unless the
Page domain is enabled on the session. Add Page.enable call (idempotent)
before the script registration in goto.rs to fix B1: page-load logs now
captured after browser goto.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 01:21:12 +08:00
mcfn ec2700ab45 [packages/cli-v2]test: add condition timeout coverage 2026-03-30 01:14:44 +08:00
mcfn d61b48f164 [packages/cli-v2]fix: logs hook timing and console.error separation (#158)
B1: Register ENSURE_LOG_CAPTURE_JS via Page.addScriptToEvaluateOnNewDocument
in goto.rs before navigation so the hook runs at document start, capturing
page-load console calls before any user scripts execute.

B2: Remove console.error double-push to __ab_error_logs. Errors store now
captures only uncaught exceptions (window error event) and unhandled
promise rejections — not explicit console.error calls.

12/12 logs E2E tests still pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 01:11:31 +08:00
mcfn ba8d546973 [packages/cli-v2]test: add wait e2e coverage 2026-03-30 01:09:45 +08:00
mcfn 01bf745a04 [packages/cli-v2]feat: implement browser logs console + logs errors (Batch 6)
Add observation/logs_console.rs and observation/logs_errors.rs with a
JS hook (ENSURE_LOG_CAPTURE_JS) that monkey-patches console.* methods
and listens for window error/unhandledrejection events. Hook is also
injected in browser eval so console calls are captured before browser
logs console is called. All 12 E2E tests pass (12/12).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 00:12:39 +08:00
mcfn b5906c858e [packages/cli-v2]test: extend logs filter coverage 2026-03-29 23:59:59 +08:00
mcfn 0e530e3965 [packages/cli-v2]test: add logs e2e coverage 2026-03-29 23:55:26 +08:00
mcfn.feng 29ce2f380b [packages/cli-v2]chore: merge release/1.0.0 and resolve cli-v2 conflicts
Integrate new commands from release/1.0.0 (pdf, query, hover, focus,
press, drag, upload, mouse-move, cursor-position, scroll, etc.) with
screenshot implementation. Deduplicate base64 dependency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 23:39:25 +08:00
mcfn.feng 72aac4209a Revert "[root]chore: merge main and resolve actionbook-rs conflicts"
This reverts commit fb4901d320, reversing
changes made to 44e620a6c1.
2026-03-29 23:36:04 +08:00
mcfn.feng fb4901d320 [root]chore: merge main and resolve actionbook-rs conflicts
Accept main's version for all actionbook-rs conflicts (not modified
by this branch's cli-v2 screenshot changes).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 23:29:22 +08:00
mcfn e858761c60 [packages/cli-v2]fix: add browser.pdf to is_action set in output.rs
Without this the text formatter skips the "ok browser.pdf" line,
failing pdf_text_output E2E test at pdf.rs:164.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 23:29:22 +08:00
mcfn d1b35af548 [packages/cli-v2]feat: implement browser pdf command (Batch 5)
CDP Page.printToPDF → base64 decode → write to path.
Registers pdf.rs in mod.rs/action.rs/cli.rs/router.rs/output.rs.
Text output: path: {path} line per §10.3.
Errors: ARTIFACT_WRITE_FAILED, SESSION_NOT_FOUND, TAB_NOT_FOUND.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 23:25:01 +08:00
mcfn.feng 44e620a6c1 [packages/cli-v2]fix: reject unsupported screenshot format values
Return INVALID_ARGUMENT for unknown --screenshot-format values instead
of silently coercing to PNG, preventing mismatched artifacts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 23:20:41 +08:00
mcfn dd6c46ec5b [packages/cli-v2]test: add pdf e2e coverage 2026-03-29 20:12:11 +08:00
Junliang Feng 0c9c8409fe [packages/cli-v2]feat: browser query command (one/all/nth/count) 2026-03-29 20:07:36 +08:00
Junliang Feng 20cd08a733 [packages/cli-v2] test: add query e2e coverage (#366) 2026-03-29 19:49:49 +08:00
mcfn 526ddb1318 [packages/cli-v2]fix: return text for hidden elements in query results
Hidden elements should still report their text content (e.g. "Item Hidden").
Removes the isVisible guard on text extraction so all matched elements
include their text regardless of visibility state.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-29 19:32:02 +08:00
mcfn 9bb090ecec [packages/cli-v2]fix: remove try/catch around querySelectorAll in query JS
Let querySelectorAll exceptions propagate to Runtime.evaluate's
exceptionDetails so the Rust-level JS_EXCEPTION guard fires correctly.
Fixes P1 blocker from Chopper review.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-29 19:31:28 +08:00
mcfn 95d0d3e8bb [packages/cli-v2]feat: implement browser query command (one/all/nth/count)
Adds observation/query.rs with 4 subcommand modes per §10.7:
- one: exactly 1 match or ELEMENT_NOT_FOUND/MULTIPLE_MATCHES
- all: returns all matches (0+ is success)
- nth: 1-based index or INDEX_OUT_OF_RANGE
- count: returns match count only (0+ is success)

Extended CSS selectors: :visible, :contains(), :has(), :enabled,
:disabled, :checked. Real nth-of-type DOM position computation.

Registered in cli.rs, action.rs, router.rs, output.rs, mod.rs.
Added fatal_with_details() helper to ActionResult.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-29 19:27:04 +08:00
asensagent d98e1bfd06 feat: add agent-friendly help messages to all CLI commands (#365)
feat: add agent-friendly help messages with examples to all CLI commands

Add after_help with usage examples and contextual notes to all 35 browser
commands, plus top-level help and browser group help screens. Follows
progressive disclosure: top-level → browser group → per-command --help.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-29 19:23:59 +08:00
mcfn 9e2d7321b6 [packages/cli-v2]test: cover disabled query state 2026-03-29 19:09:38 +08:00
mcfn 6e1bcca0fe [packages/cli-v2]test: add query e2e coverage 2026-03-29 19:00:43 +08:00
mcfn a06d78cb89 [packages/cli-v2]feat: implement browser describe + browser state commands
Add observation commands for element description (role, name, tag,
attributes, state, nearby context) and element state (visible, enabled,
checked, focused, editable, selected). Both commands follow the
established observation pattern with CDP Runtime.callFunctionOn,
exceptionDetails → JS_EXCEPTION handling, and proper error envelopes.

All 17 E2E tests in describe_state.rs pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-29 18:24:49 +08:00
mcfn a95299fccd [packages/cli-v2]test: assert element not found details 2026-03-29 18:04:14 +08:00
mcfn 5541396a4d [packages/cli-v2]test: extend describe state coverage 2026-03-29 18:00:37 +08:00