Commit Graph

282 Commits

Author SHA1 Message Date
mcfn.feng 229ce0d844 [packages/cli-v2]fix: align e2e tests with space-separated command names + fix type clap args
- Update all e2e test assertions from dot notation ("ok browser.start")
  to space-separated ("ok browser start") matching the refactored
  COMMAND_NAME constants
- Fix type command clap panic: change from separate selector/text fields
  to `#[arg(num_args = 1..=2)] args: Vec<String>` pattern (same as fill)
  to avoid "non-required positional before required" clap assertion
- Fix storage test command_name helper to use space separator

Reduces e2e failures from 57 (on release/1.0.0 CI) to ~5 pre-existing
timing/flaky issues.
2026-03-31 20:38:35 +08:00
Junliang Feng dfe0599af8 [packages/cli-v2]fix: harden CDP session robustness (#422) 2026-03-31 19:49:01 +08:00
mcfn.feng 209f254b53 [packages/cli-v2]fix: detach orphaned CDP session on attach rollback
Address PR review: when Network.enable fails after Target.attachToTarget
succeeds, send Target.detachFromTarget before rolling back the local
mapping to prevent an unreachable browser-side session.
2026-03-31 19:47:06 +08:00
Junliang Feng 4f8356d37b [packages/cli-v2]fix: browser text returns contentDocument content for iframe elements (#203) (#424)
[packages/cli-v2]fix: return contentDocument.body.innerText for iframe/frame elements in browser text

iframe.innerText is always empty; the text content lives in the embedded document.
Detect IFRAME/FRAME via tagName and read contentDocument.body.innerText instead.
Cross-origin iframes catch SecurityError and return empty string.

Closes #203

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 19:12:50 +08:00
Junliang Feng 25aaa33fa3 [packages/cli-v2]test: cover iframe text extraction (#423) 2026-03-31 19:09:45 +08:00
mcfn.feng 89e1ccb031 [packages/cli-v2]fix: harden CDP session robustness — timeout, error propagation, cleanup
- Add 60s timeout to CdpSession::execute() to prevent indefinite hangs
  on half-open sockets or unresponsive CDP commands (was infinite wait)
- Propagate Network.enable errors in attach() with session mapping rollback
  to avoid half-initialized ghost tabs
- Return error from cdp_navigate() on WebSocket EOF instead of silent Ok(())
- Clean up event subscriptions on detach() via unsubscribe_all()
- Remove requestServedFromCache from network pending counter (Chrome CDP
  spec: it's an informational event, not a lifecycle terminator)
- Add 8 unit tests covering all fixes
2026-03-31 18:56:53 +08:00
Junliang Feng bb9f872b66 [packages/cli-v2]feat: fill/type accept coordinates and selector-less mode (#202) (#421)
[packages/cli-v2]feat: add coordinate and selector-less support to fill and type commands

- Extract ClickTarget enum and parse_target() from click.rs to element.rs (shared)
- fill: accept optional selector (CSS/XPath/@ref/x,y) or omit to fill activeElement
- type: accept optional selector (CSS/XPath/@ref/x,y) or omit to type into activeElement
- Both commands dispatch mouse click to focus when coordinates are given
- Both commands return NO_FOCUSED_ELEMENT error when no selector and no focused element

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 18:27:43 +08:00
Junliang Feng 70b3ad9059 [packages/cli-v2]test: cover fill type target modes (#420) 2026-03-31 18:15:34 +08:00
mcfn.feng ae00a43cf2 [packages/cli-v2]fix: change --wait-until default from load to domcontentloaded
load waits for ALL resources (including third-party scripts like Adobe
Analytics, Google Maps SDK) which causes timeouts on heavy pages like
Costco Travel. domcontentloaded returns when the DOM is ready —
sufficient for snapshot + click workflows.

Verified: Costco Travel Regent page loads in 1.6s with domcontentloaded
vs 60s+ timeout with load.
2026-03-31 17:35:29 +08:00
Junliang Feng 652f4e5d11 feat: add --wait-until flag to browser goto (#415) 2026-03-31 17:17:45 +08:00
4bmis 95d892be0b [packages/cli-v2]refactor: use space-separated command names instead of dot notation
Command names in JSON output and internal COMMAND_NAME constants now use
spaces (e.g., "browser snapshot") to match the CLI subcommand format,
replacing the previous dot notation (e.g., "browser.snapshot").

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 17:13:19 +08:00
Junliang Feng 7b12741daf [packages/cli-v2]refactor: carry structured profile conflict errors (#418) 2026-03-31 17:10:02 +08:00
mcfn.feng 3ed104a798 [packages/cli-v2]fix: clear event subscribers on CDP disconnect
reader_loop now clears event_subs when the WebSocket connection drops.
This ensures that event subscribers (like goto's Page.loadEventFired wait)
receive None instead of hanging forever when the session dies mid-navigation.
2026-03-31 17:06:01 +08:00
Junliang Feng bc5010b45a [packages/cli-v2]fix: clarify profile conflict recovery (#417) 2026-03-31 17:01:43 +08:00
Junliang Feng 0ef6f9667d fix(cli-v2): --version prints version string instead of help (#199) (#416)
[packages/cli-v2]fix: --version now prints version string instead of help (#199)

Intercept --version/-V in the pre-parse block before positional-arg
dispatch so it doesn't fall through to the [] => handle_help() branch.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 17:01:38 +08:00
mcfn.feng 746a5adc6a [packages/cli-v2]fix: address review — CDP session check, Page.enable error, drain stale events
- Return INTERNAL_ERROR when get_cdp_session_id is None instead of
  silently defaulting to empty string (would cause infinite hang)
- Check Page.enable result when wait is active (fail fast, not 30s timeout)
- Drain stale lifecycle events after Page.enable before navigate to
  prevent residual events from satisfying the wait prematurely
2026-03-31 16:59:59 +08:00
mcfn.feng e29d2a9a89 [packages/cli-v2]feat: add --wait-until flag to browser goto
goto now waits for page load by default instead of returning immediately
after Page.navigate. This ensures snapshot refs are stable and URLs/titles
reflect the loaded page.

Options:
- load (default): wait for Page.loadEventFired
- domcontentloaded: wait for Page.domContentEventFired (faster)
- none: return immediately (old behavior)

Uses CDP event subscription (Page.enable already called) instead of
JS polling. No internal timeout — relies on global --timeout flag.
Wire-compatible via #[serde(default)] on the new field.
2026-03-31 16:58:27 +08:00
mcfn.feng 17c1e4d43e [packages/cli-v2]feat: add daemon request-level logging
Log every command with session/tab address, status, and elapsed time.
Errors (fatal/retryable/user_action) are logged at warn/error level.
Daemon stderr is now redirected to ~/.actionbook/daemon.log.
2026-03-31 16:47:47 +08:00
Junliang Feng e335b0f931 [packages/cli-v2]test: cover version flag output (#413) 2026-03-31 15:41:22 +08:00
Junliang Feng db75d2ff3d [packages/cli-v2]fix: render eval scalar in text output (#414) 2026-03-31 15:40:56 +08:00
mcfn.feng 0f4715bb39 [packages/cli-v2]chore: bump version to 1.0.0-alpha.1 2026-03-31 14:28:20 +08:00
Junliang Feng 11a7f29384 [packages/cli-v2]fix: hint when snapshot ref missing @ prefix (#411)
[packages/cli-v2]fix: hint when snapshot ref used without @ prefix

When a selector like "e46" is passed to click/fill/etc., the error now
hints: "did you mean @e46? snapshot refs require the @ prefix"
2026-03-31 13:56:02 +08:00
Junliang Feng 37ef2171a9 [packages/cli-v2]fix: improve start session error when profile occupied (#410)
[packages/cli-v2]fix: improve SESSION_ALREADY_EXISTS error with hint

When `browser start --set-session-id` fails because the profile already
has an active session, the error now shows which profile is occupied and
includes a hint telling the user to either address the existing session
or close it first.

Before: "session already exists: SLOCAL-1" (no hint)
After:  "profile 'actionbook' already has an active session: SLOCAL-1"
  hint: "use --session SLOCAL-1 to address it, or run browser close --session SLOCAL-1 first"
2026-03-31 13:46:51 +08:00
Junliang Feng c8af7f2c7f feat(cli-v2): snapshot output improvements — no empty quotes, link URLs (#195) (#409)
[packages/cli-v2]feat: snapshot output — remove empty quotes for nameless nodes and show URL for link elements (#195)

- render_content(): omit "" suffix when name is empty (- generic instead of - generic "")
- AXNode: add url field; parse from CDP properties array for role=link nodes
- render_content(): append url=<value> inline after ref for link nodes with a URL
- truncate_to_tokens(): skip " \"\"" cost when name is empty

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 13:35:38 +08:00
Junliang Feng b655df7325 [packages/cli-v2]test: pin snapshot output edge cases (#408)
* [packages/cli-v2]test: pin snapshot output edge cases

* [packages/cli-v2]test: align snapshot url expectation
2026-03-31 12:16:45 +08:00
Junliang Feng 977c00676e [packages/cli-v2]fix: press Enter triggers native browser actions (#407)
* [packages/cli-v2]fix: send full CDP key definitions in press command

press Enter (and other functional keys) was not triggering native browser
behaviours like form submission because Input.dispatchKeyEvent was missing
critical CDP parameters (code, windowsVirtualKeyCode, text).

Two issues fixed:
- Added KeyDef mapping table covering 15 functional keys, F1-F12, and
  single printable characters with their CDP code, keyCode, and text
- Use "keyDown" type (with text) for keys that produce text (Enter, Space,
  letters, digits) so Chrome fires native actions; use "rawKeyDown" for
  pure-functional keys (Tab, Escape, arrows)

* [packages/cli-v2]test: add e2e tests for press CDP key definitions

- press_enter_sends_cdp_key_definitions: verify code="Enter", keyCode=13
- press_enter_submits_form: verify Enter triggers native form submission
- press_escape_sends_cdp_key_definitions: verify code="Escape", keyCode=27
- press_tab_sends_cdp_key_definitions: verify code="Tab", keyCode=9

* [packages/cli-v2]fix: address PR review — symbol keys and Shift modifier

- Include ASCII symbol/punctuation keys (/, ., -, ;, etc.) in printable
  key mapping so they dispatch as keyDown with text, not rawKeyDown
- Only suppress text for shortcut modifiers (Ctrl/Alt/Meta), not Shift;
  Shift+A, Shift+1 (!) etc. are text-producing and need native behaviour
2026-03-31 12:16:37 +08:00
Alex f542a72f82 [packages/cli-v2] feat: iframe content expansion and frame-aware interaction (#406)
* [root]chore: add changeset for screenshot timeout fix

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* [packages/cli-v2]feat: add iframe content expansion and frame-aware interaction support

Snapshot now automatically expands 1 level of iframe content, and all
interaction/observation commands route CDP operations to the correct
frame session for cross-origin iframe elements. This is fully transparent
to agents — no new commands or flags required.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* [packages/cli-v2]fix: complete frame routing in screenshot annotate + clarify flag semantics

- get_rect_for_object() now uses execute_for_frame() for Runtime.callFunctionOn,
  fixing iframe element annotation boxes that would fail on cross-origin objectIds
- Design doc: replace overly broad "all flags work as before" with precise per-flag
  iframe semantics (--selector/--cursor main-frame only, --depth per-frame)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* [packages/cli-v2]style: apply rustfmt formatting

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* [packages/cli-v2]style: fix clippy collapsible_if warning

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* [packages/cli-v2]test: add iframe E2E tests + RefCache unit tests + fix fill for iframe

- 7 E2E tests: snapshot expansion, interactive filter, html/click/fill/value
  on iframe refs, ref uniqueness across frames
- 4 RefCache unit tests: composite key collision, frame_id round-trip,
  cross-frame stability
- fix fill.rs: use Runtime.callFunctionOn(objectId) instead of
  document.activeElement to work correctly for iframe elements

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* [packages/cli-v2]test: add cross-origin (OOPIF) iframe E2E tests

- Second HTTP server on different port for true cross-origin iframes
- 4 new tests: xo_iframe_snapshot_expands, xo_iframe_html, xo_iframe_fill,
  xo_iframe_click — all validate execute_for_frame() session routing
- Total iframe E2E coverage: 11 tests (7 same-origin + 4 cross-origin)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* [packages/cli-v2]style: apply rustfmt to test files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* [packages/cli-v2]style: fix clippy useless_format in test harness

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* [packages/cli-v2]refactor: rename execute_in_frame → execute_on_element

Rename frame-aware CDP execution methods to better reflect semantics:
- execute_in_frame → execute_on_element (TabContext method)
- execute_for_frame → execute_for_element (standalone helper)

The old names leaked the iframe implementation detail. The new names
express the caller's intent ("execute on the resolved element") without
implying frame awareness — consistent with the cli-v2 principle that
agents should not need to know about iframe boundaries.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* [packages/cli-v2]refactor: restore execute_for_frame name for low-level helper

Layered naming convention:
- High-level (command files): ctx.execute_on_element() — named by intent
- Low-level (infrastructure): execute_for_frame() — named by mechanism

The low-level helper takes frame_id as input and routes by frame/session,
so execute_for_frame accurately describes what it does. Renaming it to
execute_for_element would obscure the mechanism it implements.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* [packages/cli-v2]test: add screenshot --annotate and scroll into-view iframe E2E tests

Complete the iframe E2E test matrix:
- iframe_screenshot_annotate_includes_iframe_elements: verifies annotated
  screenshot succeeds with iframe refs in RefCache
- iframe_scroll_into_view_works_on_iframe_element: verifies scroll into-view
  routes callFunctionOn to correct frame session

Total: 13 iframe E2E tests (9 same-origin + 4 cross-origin)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 11:25:14 +08:00
Junliang Feng 32faaa272b [packages/cli-v2]fix: stealth V1 detection bugs — Google search works (#405)
[packages/cli-v2]fix: stealth V1 detection bugs — Google search now works

Three stealth bugs that caused Google bot detection:
1. navigator.webdriver: was re-defined after delete, so 'webdriver' in navigator
   still returned true. Fixed: delete from Navigator.prototype only.
2. platform mismatch: hardcoded 'Win32' conflicted with macOS UA. Fixed: removed
   platform override, let Chrome report real value.
3. plugins instanceof: fake plugins didn't pass PluginArray instanceof check.
   Fixed: set correct prototypes via Object.setPrototypeOf.

Also fixes two Codex-reported bugs from PR #404:
- P1: new tabs (open/click --new-tab) now inherit stealth_ua from session
- P2: ensure_scheme fallback navigates to about:blank instead of passing
  blocked protocols through to Page.navigate

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-31 02:02:45 +08:00
Junliang Feng d23a681034 feat(cli-v2): stealth/anti-detection V1 (#192) (#404)
* [packages/cli-v2]feat: implement stealth/anti-detection V1 (#192)

- daemon/browser.rs: add --stealth flag to launch_chrome (5th param); add 5
  Chrome args when stealth=true; Chrome starts on about:blank (open_url removed)
- browser/stealth.rs: new file with STEALTH_JS constant (format!() for WebGL
  vendor/renderer, navigator.webdriver, cdc_ cleanup, window.chrome, plugins,
  permissions, idempotency guard)
- browser/mod.rs: register stealth module
- daemon/cdp_session.rs: attach(target_id, user_agent: Option<&str>) — when
  Some(ua): Page.enable + Page.addScriptToEvaluateOnNewDocument + UA override
- browser/session/start.rs: --stealth flag (default true); Browser.getVersion
  for UA; Page.navigate after attach for open_url; stealth stored in SessionEntry
- browser/session/restart.rs: preserve session's stealth setting on restart
- daemon/registry.rs: SessionEntry.stealth field; reserve_session_start(stealth)
- browser/navigation/goto.rs: remove Page.addScriptToEvaluateOnNewDocument
- browser/tab/open.rs, browser/interaction/click.rs: attach(id, None)
- tests/stealth_contract.rs: fix launch_chrome call (5th arg true) + add
  start_source_exposes_stealth_flags_and_defaults_them_on test

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

* [packages/cli-v2]chore: remove unused ensure_scheme from daemon/browser.rs

Fixes clippy -D warnings failure in CI.

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

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

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

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 01:06:44 +08:00
Junliang Feng 9546e7dd9a [packages/cli-v2]test: cover stealth v1 contract (#403) 2026-03-31 00:39:17 +08:00
4bmis 230bff79ce [packages/cli-v2]feat: add config version field with automatic migration from old format
Old configs (without version field) may have incompatible browser.mode values
like "isolate". On load, the config is now parsed as raw TOML first to check
for a version field. If missing, the old config is backed up to config.toml.bak
and a new config is created with version=1, browser.mode forced to "local",
and all other compatible fields copied over.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 23:54:27 +08:00
4bmis 895901be6c [packages/cli-v2]fix: use INVALID_SELECTOR error code for invalid CSS selectors
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 23:26:06 +08:00
asensagent 7fd2f1125f docs(cli): mention stable refs in snapshot help (#402) 2026-03-30 23:16:22 +08:00
Asen's Agent 40bed068ce docs(cli): mention stable refs in snapshot help
Refs are tied to backendNodeId, so they persist across snapshots as long
as the DOM node stays the same. This is a key affordance for agents
chaining commands without re-snapshotting after every step.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-30 23:12:45 +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
asensagent fc908c2ea7 feat(cli): add help for logs, query, and storage commands (#398)
* feat(cli): add help examples for logs, query, and storage commands

Add after_help with usage examples to the remaining commands that were
missing them: logs console, logs errors, query, and all five storage
subcommands (list, get, set, delete, clear). Also update the browser
help listing with new sections for Logs, Wait, Cookies, Storage, and
Query so all commands are discoverable from `actionbook browser help`.

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

* fix(cli): show query nth syntax with required <n> argument in browser help

Codex review caught that `query nth` requires an index before the
selector, but the help listed it grouped as `query one|all|nth|count
<selector>`. Split into two lines so nth shows `query nth <n> <selector>`.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-30 22:37:39 +08:00
4bmis 95430ea663 [packages/cli-v2]fix: return ELEMENT_NOT_FOUND for invalid CSS selectors
Catch CDP -32000 "DOM Error while querying" from DOM.querySelector
and return a friendly ELEMENT_NOT_FOUND with the selector in details.
For @-prefixed selectors, hint that the correct format is @eN.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 22:36:44 +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
Asen's Agent 6c1d4dc02a fix(cli): show query nth syntax with required <n> argument in browser help
Codex review caught that `query nth` requires an index before the
selector, but the help listed it grouped as `query one|all|nth|count
<selector>`. Split into two lines so nth shows `query nth <n> <selector>`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-30 18:26:10 +08:00
Junliang Feng eea025dd0a fix: prevent Chrome process leak with centralized cleanup (#399) 2026-03-30 18:02:45 +08:00
mcfn.feng e5299ad485 [packages/cli-v2]fix: delete non-default profile dirs on session close
Restore profile directory cleanup on close, but only for non-default
profiles. The default profile ("actionbook") preserves user state
(cookies, localStorage) across sessions. Test-generated profiles
(e.g. "profile-s-12345-0") are cleaned up automatically.

Profile deletion happens after Chrome has fully exited to avoid
EBUSY from held locks.
2026-03-30 18:00:23 +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 0f9aab1d70 [packages/cli-v2]fix: use chrome_reaper in launch timeout path, fix misleading doc
- browser.rs: replace inline child.kill() (no wait) with
  chrome_reaper::kill_and_reap() to prevent zombie on launch timeout
- chrome_reaper.rs: remove misleading setsid reference from doc comment
2026-03-30 17:41:52 +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
4bmis 67fc353765 [packages/cli-v2]chore: add dev/dev:check/dev:test scripts with cargo-watch
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 17:30:23 +08:00
Asen's Agent 2954f7e0c5 feat(cli): add help examples for logs, query, and storage commands
Add after_help with usage examples to the remaining commands that were
missing them: logs console, logs errors, query, and all five storage
subcommands (list, get, set, delete, clear). Also update the browser
help listing with new sections for Logs, Wait, Cookies, Storage, and
Query so all commands are discoverable from `actionbook browser help`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-30 17:11:36 +08:00
Asen's Agent ac1772f8ee fix: update argument descriptions to mention @ref alongside CSS/XPath
The clap argument descriptions for <SELECTOR> fields now read
"Selector (CSS, XPath, or @ref)" instead of generic "Target element
selector" or "CSS selector". Updated 19 command structs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-30 17:01:06 +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