Runtime.evaluate without awaitPromise returns the Promise object itself,
which serializes to {} since Promises have no enumerable properties.
Setting awaitPromise: true makes CDP wait for resolution, matching
Playwright's behavior.
* [packages/cli]feat: restore extension browser mode with bridge relay
Add a WebSocket bridge server inside the daemon that transparently
relays CDP messages between the Chrome extension and CdpSession.
Extension mode was removed in v1.0.0; this restores it with minimal
changes by reusing the existing cloud-mode CDP connection flow.
- daemon/bridge.rs: WS bridge on port 19222, hello handshake with
extension origin validation, bidirectional CDP message relay
- daemon/server.rs: spawn bridge as tokio task (non-fatal on bind fail)
- daemon/registry.rs: store SharedBridgeState for session start access
- browser/session/start.rs: execute_extension() reusing cloud CDP flow
- setup: unblock extension mode in wizard and --browser flag
- CdpSession: zero changes
- Extension side: zero changes
* [packages/cli]fix: use Extension API for tab discovery in extension mode
Extension bridge requires Extension.attachTab before any CDP command
can be relayed. Replaced Target.getTargets + Target.attachToTarget
with Extension.createTab / Extension.attachActiveTab flow. Added
CdpSession.register_extension_tab() to register tabs without CDP
flat-session handshake. Local and cloud modes are unaffected.
* [packages/cli]fix: close tabs and detach debugger in extension mode
Session close and close-tab now use Extension.closeTab / Extension.detachTab
in extension mode instead of Target.closeTarget which the bridge doesn't
support. Added Extension.closeTab to the extension that detaches the
debugger and removes the tab via chrome.tabs.remove. Local and cloud
modes are unaffected.
* [packages/cli]fix: revert extension JS change, use detachTab for close
Extension is loaded from CWS — local JS changes don't take effect.
Reverted Extension.closeTab addition to background.js. Session close
and close-tab now use Extension.detachTab to release the debugger in
extension mode. Tabs are left open since CLI doesn't own the browser.
* [packages/cli]fix: read default browser mode from config instead of hardcoding Local
When --mode is not specified, browser start now reads browser.mode from
config.toml (e.g. mode = "extension") instead of always falling back to
Local mode. This allows users with extension mode configured to connect
to their existing browser without explicitly passing --mode extension.
* [packages/cli]fix: build.rs track branch ref for accurate BUILD_VERSION in worktrees
build.rs only watched .git/HEAD which doesn't change on new commits in a
worktree (it stays as ref: refs/heads/branch-name). This caused stale
BUILD_VERSION, preventing daemon version-mismatch detection when the
npm-installed binary shared the same cached hash.
Now also watches the actual ref file and packed-refs via --git-common-dir
so BUILD_VERSION updates on every new commit.
Also adds a guard in start.rs Local mode section to reject non-Local
modes explicitly instead of silently launching a local Chrome.
* [packages/cli]fix: clippy/fmt fixes and reject concurrent CDP clients in bridge
- Fix rustfmt formatting (collapsed if chains, import order, line width)
- Fix clippy collapsible-if warnings
- Reject concurrent CDP clients in bridge relay to prevent response
channel hijacking between sessions
* [packages/cli]fix: suppress result_large_err for accept_hdr_async callback
The closure signature is dictated by tokio-tungstenite's API and cannot
use Box<Err>. Allow the lint on this specific closure.
* [root]chore: add changeset for CLI v1.2.0 (extension mode restore)
* test(browser): add render_yaml unit tests
* test(browser): extend render_yaml edge coverage
* feat(browser): save snapshot as Playwright-style YAML
Add render_yaml() to snapshot_transform.rs that converts a flat AXNode
list into a nested Playwright-style YAML DSL string. Format rules:
- Container nodes (children or URL): `- role "name" [ref=eN]:`
- Leaf with ref: `- role "name" [ref=eN]`
- Leaf with value (no ref): `- role "name": value`
- Leaf with name only (no ref): `- role: name`
- URL rendered as child: `- /url: <url>`
- [cursor=pointer] added when cursor_info has cursor:pointer hint
Switch build_output() to call render_yaml() instead of render_content(),
and change snapshot file extension from .txt to .yaml.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(browser): escape name and value in render_yaml leaf nodes
- Use escaped_name (not node.name) in the inline leaf branch
- Escape node.value with the same rules to prevent injection
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(browser): render value inline even when ref is present in render_yaml
Textbox/combobox nodes are interactive and typically have both a ref and
a value. Previously, the ref branch dropped the value entirely. Now the
value is appended inline: `- textbox "Label" [ref=eN]: value`.
Also moves escaped_value computation to the top of the loop so it can be
shared across both the ref and no-ref branches.
Adds unit test for the ref+value path.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(browser): update snapshot e2e for yaml artifacts
- assert data.path ends with .yaml
- snapshot_content() validates .yaml extension before reading
- rename assert_content_has_refs to assert_content_is_yaml_snapshot
- add assertions: no legacy `url=` format, at least one YAML container line
Co-Authored-By: cli-roger-test <cli-roger-test@local>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* style: fix rustfmt formatting in render_yaml tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: rustfmt formatting and update link URL test for YAML format
Apply rustfmt-required formatting:
- Expand format!() call with 3+ args to multi-line
- Expand assert!(cond, msg) to multi-line
- Expand assert_eq! with long string to multi-line
Update test_build_output_renders_link_urls_from_ax_properties:
- Old format: `url=https://...` inline
- New YAML format: `- link "Docs" [ref=e1]:` + child `- /url: https://...`
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* style: fix final rustfmt issue in link URL test assertion
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: replace map_or(false) with is_some_and per clippy
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: cli-roger-test <cli-roger-test@local>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
docs: remove search and get commands from help and skills
Search and get commands are being removed from the CLI. This removes
all references from help output, SKILL.md, and command-reference.md.
The workflow now starts directly with browser automation via snapshot.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Merged pre-click (url+focus) and post-click (url+title+focus) state reads
each into a single JS evaluate, eliminating 3 serial main-thread round-trips
per click. Also removed wait_for_navigation's fixed 2s wait — now exits
immediately when URL hasn't changed (DOM-only interactions like card expand).
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Support multiple selectors in click command: click @e1 @e2 @e3
- Execute clicks sequentially with fail-fast on first error
- Batch response includes all results with url_changed/focus_changed per click
- Optimize wait_for_navigation: return immediately on URL change detection, avoid polling for DOM-only interactions
- Format text output for batch clicks (clicks count + target list)
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Increase DevTools URL discovery timeout from 15s to 30s for CI environments
- Relax snap_selector_flag_limits_subtree assertion to allow ±2 node tolerance
- Increase E2E test workflow timeout to 20 minutes
- P1: expand_undiscovered_oopifs now uses frame_ids expanded in the
current pass (returned by expand_iframes) instead of persisted cache.
Prevents stale cache entries from causing OOPIF frames to be skipped.
- P1: remap_frame_id_for_backend_nodes skips if the new (frame_id, bid)
key already exists, preserving stable ref_ids across snapshots.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- browser.rs: keep native-ish stealth flags, drop duplicate WebRTC line
- cdp_session.rs: keep apply_stealth() refactor over inline version
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>