Commit Graph

123 Commits

Author SHA1 Message Date
mcfn 0fbdd86484 [packages/cli-v2]test: pin stealth default flags 2026-03-31 00:44:17 +08:00
mcfn e3cb34a430 [packages/cli-v2]test: cover stealth v1 contract 2026-03-31 00:37:49 +08:00
Junliang Feng abc2d56e39 [packages/cli-v2]feat: session ID format change SLOCAL-N/SCLOUD-N/SEXT-N (#189) (#401)
* [packages/cli-v2]feat: session ID format change SLOCAL-N/SCLOUD-N/SEXT-N (#189)

- SessionId.is_valid() now accepts SLOCAL-N, SCLOUD-N, SEXT-N (uppercase prefix + digits)
- auto_generate(mode, n) replaces old auto_generate(n); generates mode-prefixed IDs
- from_profile() updated to pass mode to auto_generate fallback
- SessionRegistry drops next_auto_id counter; generates IDs via per-prefix max active N scan
- generate_session_id() takes Mode instead of profile; profile no longer affects ID
- Wrap at 10000, skip occupied (active) slots

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

* [packages/cli-v2]chore: cargo fmt

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

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 22:54:24 +08:00
Junliang Feng 6afbe52f5b [packages/cli-v2]test: cover session id format change (#400) 2026-03-30 22:46:34 +08:00
4bmis 99480676d6 [packages/cli-v2]feat: add --by-ref flag to select command
Select an <option> by its snapshot ref (@eN) instead of value or text.
Resolves the ref to a remote object and passes it directly via CDP
arguments, avoiding intermediate value lookups. Includes mutual
exclusivity with --by-text, updated text output, and e2e tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 22:36:44 +08:00
mcfn.feng c5db7c5c6d [packages/cli-v2]test: add E2E tests for restart Chrome cleanup and double-close
- restart_kills_old_chrome_and_spawns_new: verify old Chrome PIDs are
  dead after restart and new PIDs are different from old ones
- double_close_returns_not_found: verify second close returns
  SESSION_NOT_FOUND without panic or crash
2026-03-30 17:48:18 +08:00
mcfn.feng 894716c00b [packages/cli-v2]fix: prevent Chrome process leak with centralized cleanup
Chrome processes were not being cleaned up in several scenarios:
- SessionEntry had no Drop impl, so dropping it leaked the Chrome process
- CdpSession had no close() method, so background reader/writer tasks leaked
- close/restart/shutdown held the registry lock during slow CDP/Chrome cleanup
- child.wait() in spawn_blocking was fire-and-forget (never awaited)

Changes:
- Add chrome_reaper module with centralized kill_and_reap helpers
- Add Drop impl on SessionEntry as last-resort backstop
- Add CdpSession::close() that drops the writer channel and fails pending requests
- Move all slow I/O (cdp.close(), Chrome kill/reap) outside registry lock
- Use kill_and_reap_async (awaited) instead of fire-and-forget spawn_blocking
- kill_and_reap_option takes ownership (sets to None) to prevent double-cleanup
2026-03-30 17:40:30 +08:00
mcfn.feng 89b40c9e0f [packages/cli-v2]test: add wait_page_ready poll and limit parallel threads
Two fixes for remaining flaky E2E failures (5-10 random tests per run):

1. Limit parallel threads to 8 (package.json test:e2e) to reduce Chrome
   process contention and CPU pressure during parallel test runs.

2. Add wait_page_ready() helper that polls document.readyState every 200ms
   (up to 2s) after session start/goto. Called in all 14 local start_session()
   functions and both harness helpers. Prevents tests from interacting with
   pages before DOM is ready under high parallel load.

Result: 441 passed, 0 failed (was 5-10 random failures per run).
2026-03-30 16:53:10 +08:00
mcfn.feng 6f3eb95d74 [packages/cli-v2]test: isolate parallel E2E tests with unique session/profile
14 test files defined local start_session() without unique_session(),
causing all parallel tests to share the default profile and hit
SESSION_STARTING rejections (238 failures).

Fix: add unique_session("s") to each local start_session(), passing
--set-session-id and --profile to browser start. This gives each test
its own isolated Chrome profile, matching the pattern in harness.rs.

Result: 238 failures → 3 flaky (unrelated to session isolation).
2026-03-30 16:16:48 +08:00
Junliang Feng 9109bf2f54 [cli-v2] fix: prevent daemon process accumulation with flock singleton (#390) 2026-03-30 15:34:07 +08:00
mcfn 9bbae2a20e [packages/cli-v2]test: cover nearby significance filter 2026-03-30 15:23:33 +08:00
mcfn.feng 15a529ccbe [packages/cli-v2]fix: address review — flock EINTR, idle timeout config, faster E2E
Fixes from dual code review (code-reviewer + codex challenge):
- P0: Invalid ACTIONBOOK_DAEMON_IDLE_TIMEOUT_SECS now falls back to default
  instead of silently disabling timeout
- P1: Replace std::thread::sleep with tokio::time::sleep in async fn
- P1: Extract parse_idle_timeout() pure function for race-free unit testing
- P2: Add ACTIONBOOK_DAEMON_HOUSEKEEPING_INTERVAL_SECS for fast E2E tests
  (idle timeout tests now run in 12s instead of 72s)
- P2: Remove manual stale file cleanup from crash_recovery test
- Minor: Handle EINTR in flock with retry loop
2026-03-30 15:20:32 +08:00
mcfn 3c7d0e93bc [packages/cli-v2]test: fix describe qualifier fixture 2026-03-30 15:07:59 +08:00
mcfn.feng 3336a07258 [packages/cli-v2]fix: prevent daemon process accumulation with flock singleton
Replace the PID file + kill(pid, 0) singleton mechanism with flock-based
locking. The kernel guarantees flock atomicity (no TOCTOU race) and
auto-releases on process exit (including SIGKILL / panic=abort), solving
the root cause of 30+ orphaned daemon processes accumulating over time.

Changes:
- Use flock(LOCK_EX | LOCK_NB) on PID file for daemon singleton
- is_daemon_running() now probes flock instead of PID + kill(pid, 0)
- Losing daemon retries 3 times, checks if winner's socket is ready
- Add idle timeout (default 30min, ACTIONBOOK_DAEMON_IDLE_TIMEOUT_SECS)
- Add has_active_sessions() helper to SessionRegistry
- Add unit tests for flock, idle timeout config, registry
- Add E2E tests for concurrent start, idle timeout, crash recovery
2026-03-30 14:59:09 +08:00
mcfn bc570c8b31 [packages/cli-v2]test: cover describe qualifiers 2026-03-30 14:39:04 +08:00
Junliang Feng 02fa15b54c [packages/cli-v2] test short tab id contract (#385) 2026-03-30 13:59:19 +08:00
mcfn 4cd3d79a28 [packages/cli-v2]test: pin short tab lookup 2026-03-30 13:42:55 +08:00
4bmis 593dceca04 Merge branch 'release/1.0.0' of github.com:actionbook/actionbook into release/1.0.0 2026-03-30 13:40:24 +08:00
mcfn 1f9a1695e9 [packages/cli-v2]test: cover short tab ids 2026-03-30 13:38:35 +08:00
mcfn.feng 15399f4ffc [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:28:24 +08:00
4bmis 8b3b1b59f2 Merge branch 'release/1.0.0' of github.com:actionbook/actionbook into release/1.0.0 2026-03-30 13:19:53 +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
4bmis c59de08a13 Fix cli-v2 global timeout handling 2026-03-30 12:18:19 +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 4ec3705cc6 Merge remote-tracking branch 'origin/release/1.0.0' into bugfix/click-ref 2026-03-30 11:39:24 +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 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 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
mcfn ec2700ab45 [packages/cli-v2]test: add condition timeout coverage 2026-03-30 01:14:44 +08:00
mcfn ba8d546973 [packages/cli-v2]test: add wait e2e coverage 2026-03-30 01:09:45 +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 dd6c46ec5b [packages/cli-v2]test: add pdf e2e coverage 2026-03-29 20:12:11 +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 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
mcfn c7a52a6cad [packages/cli-v2]test: add describe state e2e 2026-03-29 17:55:27 +08:00
mcfn 4558e2dfd0 [packages/cli-v2]test: add P1 JS exception propagation E2E tests
Add monkeypatch-based E2E tests for all three observation commands
(attrs, box, styles) to verify that when Runtime.callFunctionOn returns
exceptionDetails, the CLI surfaces ok=false / error.code=JS_EXCEPTION
instead of silently returning empty {}.

- styles_js_exception_propagated: patches window.getComputedStyle to throw
- box_js_exception_propagated: patches element.getBoundingClientRect to throw
- attrs_js_exception_propagated: patches element.attributes getter to throw

E2E suite now 13/13 green.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-29 17:48:56 +08:00
mcfn a70ed62ce0 [packages/cli-v2]test: add P1 E2E regression for JS_EXCEPTION via getComputedStyle monkeypatch 2026-03-29 17:48:56 +08:00
mcfn 41334bdc84 [packages/cli-v2]test: add P2 regression test for box no-scroll
Verifies browser box on off-screen element returns coordinates
without changing window.scrollY (read command must be stateless).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-29 17:48:56 +08:00
mcfn c2517be90c [packages/cli-v2]feat: add observation batch 2 2026-03-29 17:48:56 +08:00
mcfn.feng 5e219a8609 [packages/cli-v2]feat: implement browser screenshot command with annotate support
Add full implementation of `browser screenshot <path>` (§10.2) including:
- Core viewport screenshot via CDP Page.captureScreenshot
- --full flag for full-page capture using Page.getLayoutMetrics
- --selector flag to clip to a specific CSS element region
- --screenshot-format (png/jpeg) with extension inference
- --screenshot-quality for JPEG compression control
- --annotate flag that overlays numbered labels on interactive elements
  and returns annotations metadata (ref, number, role, name, box)

Expand RefCache to store role/name (RefEntry) so annotate can build
complete annotation metadata without re-fetching the AX tree.

Includes 11 unit tests and 12 E2E tests covering happy paths
(json/text/jpeg/full/selector/annotate) and error paths
(invalid path, session/tab not found, missing args).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 17:47:45 +08:00
asensagent e00c48fd28 feat: implement all interaction commands for CLI v1.0.0 (#361)
* test: add hover command cases

* test: add hover text error coverage

* feat: implement browser hover command

Add the `browser hover <selector>` command. Resolves the selector to a
DOM node via the shared element module, then dispatches mouseenter,
mouseover, and mousemove events via Runtime.callFunctionOn for reliable
hover semantics in headless Chrome. Wired up across CLI parsing, action
enum, router, and output formatting.

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

* test: add focus command cases

* feat: implement browser focus command

Add the `browser focus <selector>` command. Resolves the selector via
the shared element module, uses DOM.focus to set focus on the target,
and compares pre/post active element by JS reference identity (===) to
correctly report focus_changed even for anonymous elements. Wired up
across CLI parsing, action enum, router, and output formatting.

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

* test: add press command cases

* feat: implement browser press command

Add the `browser press <key-or-chord>` command. Parses single keys and
chord combinations (e.g. Control+A, Shift+Tab), validates modifier
segments, and dispatches keyDown/keyUp via CDP Input.dispatchKeyEvent
with the appropriate modifiers bitmask. Wired up across CLI parsing,
action enum, router, and output formatting.

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

* test: add drag command cases

* feat: implement browser drag command

Adds drag-and-drop via CDP Input.dispatchMouseEvent sequence:
mousePressed at source → mouseMoved along path → mouseReleased at
destination. Destination accepts either a selector or x,y coordinates.

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

* fix: drag dispatch via viewport resize + CDP Input events

CDP Input.dispatchMouseEvent requires elements to be within the visible
viewport. The drag fixture positions elements at top:780px which exceeds
the default headless Chrome viewport (~469px). Fix by temporarily
enlarging the viewport via Emulation.setDeviceMetricsOverride before
the drag sequence, then clearing the override after completion.

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

* fix: resize viewport before resolving drag coordinates

Move Emulation.setDeviceMetricsOverride before element resolution so
coordinates are captured under the final viewport. Avoids stale
coordinates on responsive pages where a post-resolution resize could
shift element positions.

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

* fix: preserve viewport width during drag, only expand height if needed

Read the current viewport dimensions and only override if the height is
too short for the drag endpoints. Width is preserved to avoid flipping
responsive breakpoints or triggering resize handlers. The override is
skipped entirely when the viewport is already tall enough.

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

* test: add upload command cases

* feat: implement browser upload command

Adds `browser upload <selector> <file...> --session --tab` which sets
files on a file input via CDP DOM.setFileInputFiles. Validates that all
paths are absolute, resolves the target node, and returns a value_summary
with file list and count. All 10 e2e tests pass.

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

* fix: require at least one file path in browser upload

Adds `#[arg(required = true, num_args = 1..)]` to the `files` field so
Clap rejects invocations with zero files at parse time, matching the
spec's `<file...>` contract. Adds a unit test for the missing-files case.

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

* test: add eval command cases

* fix: update browser eval to return typed value, type, and preview per spec

- Return `data.value` as typed JSON (number/bool/string) instead of
  always stringifying
- Add `data.type` (JS type from CDP) and `data.preview` (string repr)
- Return null context for SESSION_NOT_FOUND
- Extract exception description for better EVAL_FAILED error messages
- Update cloud eval tests to assert typed values

All 11 eval tests pass (8 interaction + 3 cloud).

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

* test: add eval context coverage

* fix: include tab URL and title in browser eval context

Fetches the current page URL and title after eval execution and returns
them in the response context, matching the spec contract for tab-scoped
commands. Tests now assert context.url and context.title are present.

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

* test: add mouse-move command cases

* feat: implement browser mouse-move command

Adds `browser mouse-move <x,y> --session --tab` which dispatches a CDP
Input.dispatchMouseEvent (mouseMoved) at the given coordinates. Parses
and validates x,y format, tracks url/focus changes, returns context with
tab URL and title. All 8 e2e tests pass.

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

* test: add cursor-position command cases

* test: add cursor-position text status check

* feat: implement browser cursor-position command

Adds cursor-position command that returns the last known cursor position
for a tab. Position is stored in SessionRegistry when mouse-move is
executed, using the existing session_id\0tab_id composite key pattern.

Wired in all 6 registration points: mod.rs, action.rs, cli.rs (enum +
to_action + command_name + context), router.rs, output.rs.

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

* fix: track cursor position in click, drag, and hover commands

cursor-position was only updated by mouse-move. Now click, drag, and
hover also store the final pointer coordinates in SessionRegistry so
that cursor-position reflects the true position after any mouse action.

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

* test: add regression test for click -> cursor-position tracking

Verifies that cursor-position reflects coordinates from a click action,
not just mouse-move. Covers the contract gap identified in review.

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

* test: add scroll command cases

* test: add scroll changed field checks

* feat: implement browser scroll commands

Adds scroll command with three subcommand forms:
- Directional: scroll up|down|left|right <pixels>
- Edge: scroll top|bottom
- Into-view: scroll into-view <selector> [--align start|center|end|nearest]

All forms support --container for scrolling within a specific element.
Tracks scroll_changed, url_changed, focus_changed in response data.
Wired in all 6 registration points.

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

* test: add scroll xpath selector coverage

* fix: use resolved CDP nodes for scroll selectors instead of querySelector

Refactored scroll to resolve selectors via element::resolve_node +
DOM.resolveNode → objectId, then use Runtime.callFunctionOn for all
scroll operations. This correctly supports CSS, XPath, and @eN selectors
per the API contract, instead of raw querySelector which only handles CSS.

Added DOM.getDocument init call to ensure XPath DOM.requestNode works.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-29 15:13:54 +08:00
mcfn 027c0d1c98 [packages/cli-v2]fix: cover xpath selector path 2026-03-29 12:32:14 +08:00