Commit Graph

1086 Commits

Author SHA1 Message Date
mcfn.feng e391594317 [packages/cli-v2]fix: cloud_concurrent_eval_multi_tab assertion — compare numbers not strings
eval "1+1" returns number 2, not string "2". The test used as_str()
which returns None for numbers, causing the assertion to always fail.
Fix: compare with assert_eq!(value, 2) directly. Remove #[ignore].

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 13:25:14 +08:00
mcfn.feng 2febbe0f49 [packages/cli-v2]fix: address PR review — scope profile cleanup to local sessions + release lock early
P1: Only delete profile directory for local sessions (those with a
    Chrome child process). Cloud sessions don't own a local profile
    and could share a profile name with an active local session.
P2: Release the registry lock before slow I/O (Chrome kill, recursive
    directory removal) to avoid blocking concurrent session operations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 12:45:14 +08:00
mcfn.feng 15726ad7b1 [packages/cli-v2]fix: wait navigation race condition + ignore flaky cloud concurrent test
- wait navigation: use two-phase detection (URL change → readyState
  complete) instead of OR logic that false-positives on initial page's
  readyState=complete before navigation fires
- cloud_concurrent_eval_multi_tab: mark #[ignore] — known CDP
  multiplexing issue mis-routes concurrent responses across tabs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 12:42:53 +08:00
mcfn.feng e51ec6ec9d [packages/cli-v2]feat: enable parallel E2E tests with profile cleanup
- browser close now removes the Chrome profile directory after killing
  the process, preventing disk accumulation from test runs
- Remove --test-threads=1 from CI — each test already uses a unique
  session/profile via unique_session(), so parallel execution is safe
- Profile isolation: each test gets its own Chrome user-data-dir,
  no shared state between concurrent tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 12:31:54 +08:00
Junliang Feng 05d5b49eec fix(cli-v2): implement @eN snapshot ref resolution for click and all selector commands (#381) 2026-03-30 11:51:05 +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
Junliang Feng afaa85ae7f feat(cli-v2): add -v short flag and version subcommand (#380) 2026-03-30 11:10:48 +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
Junliang Feng 1210a1020c [packages/cli-v2] test browser storage e2e coverage (#378) 2026-03-30 10:35:56 +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
Junliang Feng b2da35b417 feat(cli-v2): browser cookies commands (Batch 8, #162) (#377) 2026-03-30 10:21:37 +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
Junliang Feng 9e61ae7b3a [packages/cli-v2] test browser cookies e2e coverage (#376) 2026-03-30 09:15:14 +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
Junliang Feng 628aa62c2c [packages/cli-v2] test: add browser wait e2e coverage (#373) 2026-03-30 01:18:08 +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
Junliang Feng e7eaff019a [packages/cli-v2]feat: implement browser logs console + errors commands 2026-03-30 01:00:15 +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
Junliang Feng d52a099bec [packages/cli-v2]test: add browser logs console/errors E2E tests (Batch 6) 2026-03-30 00:02:23 +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
Junliang Feng 0afcfa098b [packages/cli-v2]feat: implement browser screenshot command (#362) 2026-03-29 23:40:34 +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
Junliang Feng 21bbefef22 [packages/cli-v2]feat: implement browser pdf command 2026-03-29 23:31:16 +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