mirror of
https://github.com/actionbook/actionbook.git
synced 2026-09-19 03:28:06 +08:00
feature/parallel-e2e
20 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e992b91ba3 |
feat: use CDP Accessibility tree for extension mode snapshot (#203)
* [packages/actionbook-rs]feat: use CDP Accessibility.getFullAXTree for extension mode snapshot Extension mode previously used a primitive JS DOM walker (snapshot_js_fallback) that couldn't compute accessible names, resulting in empty names for links, headings, and other elements. Now extension mode uses CDP Accessibility.getFullAXTree through the extension bridge, same as isolated mode. Changes: - Add DOM.enable, Accessibility.enable, Accessibility.getFullAXTree to CDP allowlist in both Rust bridge and Chrome extension - Remove snapshot_js_fallback; extension mode now shares the CDP code path - Skip noise roles: StaticText, LineBreak, ListMarker, strong, emphasis etc. - Remove empty leaf structural nodes (empty <div>/<span> wrappers) - Rewrite compact_tree_nodes (-c) to match agent-browser: keep only ref'd/valued nodes and their ancestors Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * [packages/actionbook-rs]refactor: remove level attribute from snapshot output Level info (e.g. level=1 on listitem/heading) adds noise without actionable value for agents. Remove it from format_compact output. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * [packages/actionbook-rs]feat: add /url: metadata to snapshot output for links and images Extract URL property from CDP AX tree nodes and output as indented `- /url: <href>` metadata line under link/image elements, matching agent-browser's snapshot format. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * [packages/actionbook-rs]fix: address code review issues from Codex and self-review - Fix scope filter pruning entire subtrees: traverse children even when ancestor is outside scope (Codex P1) - Fix --interactive filter including non-interactive content roles like heading/navigation; now filters by interactive roles only (Codex P2) - Fix compact_tree_nodes ancestor marking: only mark true ancestor chain (depth-1 walk) instead of all preceding lower-depth nodes (Codex P2) - Fix cursor ref ID collision: use cache.next_ref from parse_ax_tree instead of nodes.len() which diverges after filtering - Remove dead code: "Skip empty StaticText" guard unreachable since StaticText is already in SKIP_ROLES - Fix format_compact doc comment: remove level=1 from format example since level is intentionally not emitted - Fix estimate_tokens_compact test to use realistic format_compact output Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
9895c25b8b |
[packages/actionbook-rs]fix: stop extension bridge connect/disconnect loop
When two Chrome extension instances (CWS + dev) are both installed, they compete for the single extension_tx slot on the bridge server, causing an infinite connect/replace/disconnect cycle. Fix: reject any new extension WebSocket connection before hello_ack when an active connection already exists (first-wins strategy). Same-extension SW restart is handled naturally — old WebSocket closes, handler cleans up extension_tx = None, next retry succeeds. Key insight: Chrome MV3 Service Worker WebSocket connections do NOT send the Origin header, making origin-based differentiation impossible. The fix uses a simple tx.is_closed() liveness check instead. |
||
|
|
52edfc69ca | feat(setup): prefer Chrome Web Store extension flow with local fallback | ||
|
|
c53ec19eea |
[packages/actionbook-extension]chore: bump version to 0.2.2 (#151)
Bump extension version for Chrome Web Store resubmission after removing unused scripting permission in #150. |
||
|
|
7302e2e3f3 |
[packages/actionbook-extension]fix: remove unused scripting permission (#150)
Chrome Web Store rejected the extension (violation: Purple Potassium) for requesting the `scripting` permission without using it. The extension uses chrome.debugger for CDP commands and never calls chrome.scripting APIs. - Remove `scripting` from manifest.json permissions - Remove corresponding entry from PRIVACY.md permissions table |
||
|
|
c70683da86 | [docs]docs: simplify browser workflow and make setup-first quickstart (#147) | ||
|
|
51e2af6798 | chore(extension): bump to v0.2.1 and align release docs (#132) | ||
|
|
5407dbccb7 | fix(extension): connect via fixed localhost bridge and remove native host dependency | ||
|
|
0cb25185bd | [packages/actionbook-extension]chore: bump version to 0.2.0 | ||
|
|
f68a85686b |
Refactor browser extension architecture with backend trait and auto-lifecycle (#122)
* [packages/actionbook-rs]refactor: add BrowserMode::Isolated/Extension and ExtensionConfig (Phase 1)
- Replace BrowserMode::Builtin/System with Isolated/Extension in cli.rs
- Add serde derives + new --browser-mode flag, hide deprecated --extension
- Add ExtensionConfig (port, auto_install) and mode field to BrowserConfig
- Redesign setup wizard: mode selection → mode-specific config flow
- Default display mode to Visible (matching Playwright/Puppeteer convention)
- No behavior changes; mode stored in config but not acted on yet
* [packages/actionbook-rs]refactor: add BrowserBackend trait with Isolated and Extension implementations (Phase 2)
- Define BrowserBackend async trait with ~25 methods covering lifecycle,
navigation, interaction, content extraction, and cookies
- Implement IsolatedBackend wrapping SessionManager for CDP-controlled browsers
- Implement ExtensionBackend with bridge communication and inline JS helpers
- Add async-trait dependency for object-safe dynamic dispatch
* [packages/actionbook-rs]fix: address dual review findings for Phase 1+2
- Wire --browser-mode flag and config.browser.mode to runtime via
is_extension_mode() helper, replacing all 28 cli.extension checks
- Add serde aliases (builtin→isolated, system→extension) for backward
compatible config parsing
- Implement snapshot() in both backends via shared SNAPSHOT_JS constant
- Fix IsolatedBackend::switch() to return Ok (matching current behavior)
- Harden escape_js_string with .expect() instead of unsafe fallback
- Add viewport bounds checking, clear_cookies domain warning log
- Fix immutability violations in screenshot/set_cookie params
- Use serde_json::to_value for browser_mode JSON serialization
- Add 8 unit tests for escape_js_string and js_resolve_selector
* [packages/actionbook-rs]refactor: replace all if/else branching with BrowserBackend dispatch (Phase 3)
Replace ~30 is_extension_mode() branch sites in browser.rs with single
backend.method() calls via the BrowserBackend trait, reducing the file
from ~3400 to ~1750 lines.
- Add resolve_mode() + create_backend() factory for backend instantiation
- Refactor all 28 command functions from fn(cli, config) to fn(cli, backend)
- Remove dead code: is_extension_mode, extension_send, extension_eval,
escape_js_string, js_resolve_selector, cookies_extension, inline SNAPSHOT_JS
- Unify output messages (remove "(extension)" suffixes)
- Guard ensure_cdp_override to only run in isolated mode
- Remove unused OpenResult.url field
- Extract eval_with_options in ExtensionBackend to deduplicate eval logic
* [packages/actionbook-rs]feat: add bridge auto-lifecycle and error recovery (Phase 4)
Phase 4 of the browser extension refactoring:
- Add bridge_lifecycle.rs with ensure_bridge_running() and stop_bridge()
- Auto-starts bridge daemon via setsid() when extension mode commands run
- Returns bool to track whether bridge was auto-started (shared daemon safety)
- PID file + port + process-alive validation to detect port conflicts
- SIGTERM with SIGKILL escalation and PID reuse mitigation
- Add two-layer send architecture in ExtensionBackend:
- send_once(): single attempt with auto-attach ("No tab attached" recovery)
- send(): wraps send_once with 30s retry for "Extension not connected"
and token rotation recovery via send_once (preserves auto-attach)
- Wire bridge lifecycle into create_backend() and close():
- create_backend() returns (backend, bridge_auto_started)
- close in extension mode uses close_extension() which bypasses
create_backend entirely — no bridge auto-start, best-effort detach
Review fixes included:
- Finding 1 (High): close no longer auto-starts bridge or hangs 30s
- Finding 2 (Medium): bridge detection validates PID file, not just TCP
- Finding 3 (Medium): token retry routes through send_once for auto-attach
* [packages/actionbook-rs]refactor: add bridge_lifecycle tests and Phase 5 deprecation cleanup
Phase 4 completion:
- Add 4 unit tests to bridge_lifecycle.rs (stop_noop, port_conflict,
spawn_when_free)
- All 194 tests passing
Phase 5 deprecation + cleanup:
- Add resolve_extension_port(cli, config) with config as source of truth;
compare CLI against clap default (not config) to detect explicit override
- Add CLI_DEFAULT_EXTENSION_PORT constant synced with clap default_value
- Compute mode once in run(), pass through to create_backend(mode) and
close(mode) — deprecation warning now prints exactly once
- Hide --extension-port flag (deprecated, config.toml is primary)
- Update error messages to reference config.toml instead of --extension-port
- Update design doc to reflect close_extension flow, create_backend(mode)
signature, resolve_extension_port comparison logic, and Phase 5 status
* [skills/actionbook]refactor: rewrite SKILL.md — context not control
- Rewrite from 373 to 143 lines with informational tone
- Remove rigid Phase 1/Phase 2 workflow, "CRITICAL RULE" boxes, prohibited patterns
- Remove internal concepts (area_id format, data model hierarchy, Playbook terminology)
- Update search/get response descriptions to match actual CLI output
- Add Fallback section with selector source constraint
- Remove orphaned extension-troubleshooting.md reference file
* [packages/actionbook-rs]feat: implement extension auto-install and remove token auth
Phase 6 improvements:
1. Auto-install extension during setup
- Check config.browser.extension.auto_install flag
- Download latest release from GitHub if not installed
- Show version and install path on success
- Provide friendly error and manual install hint on failure
- Use extension_installer module for correct paths
2. Remove token authentication
- Remove token reading from send_command
- Remove token field from hello handshake
- Remove server-side token validation
- Keep simple hello/hello_ack for version check
- Extension connects directly to 127.0.0.1:19222
Impact:
- First-time setup success improved (auto-install removes manual step)
- Simplified authentication (no token file management)
- Better user experience (clear feedback during installation)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* [packages/actionbook-rs]refactor: remove unused token authentication mechanism
BREAKING CHANGE: Token-based authentication removed from bridge
Changes:
- Removed token generation, file I/O, and TTL watchdog
- Removed BridgeState.token and last_activity fields
- Simplified serve() signature (no token parameter)
- Updated native messaging to return bridge_info instead of token
- Enhanced audit logging to include L1 (read-only) operations
Rationale:
- Token was never validated during handshake (Line 869: "no token required")
- Reduces complexity without sacrificing security
- Loopback validation + CDP whitelist provide sufficient protection
Security impact: NEUTRAL
- Loopback still enforced (TCP + WebSocket layers)
- CDP method whitelist still active (L1/L2/L3 risk levels)
- No authentication bypass (token was unused)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* [packages/actionbook-rs]fix: complete token removal cleanup and fix test compilation
Fixes all CRITICAL and HIGH issues identified in dual code review (claude + codex):
CRITICAL fixes:
- Fix test compilation errors (8+ locations)
* Remove generate_token(), TOKEN_PREFIX, send_command_with_token() references
* Update start_bridge() to remove token parameter
* Update hello_extension()/hello_cli() to remove token parameter
* Delete token-related tests: generate_token_format, generate_token_uniqueness, invalid_token_closes_connection, send_command_fails_when_bridge_not_running
* Delete test_token_format unit test from extension_bridge.rs
- Fix Chrome extension protocol compatibility
* Update background.js to handle both "token" (legacy) and "bridge_info" (new) responses
* Maintain backward compatibility with older extension versions
* Use dummy token "connected" for storage compatibility
- Remove token retry dead code from extension_backend.rs
* Delete "invalid token" / "Authentication failed" match arm (lines 91-101)
HIGH fixes:
- Update error messages to remove "invalid token" references
* extension_bridge.rs: "invalid token?" → "by server"
* extension_bridge.rs: "Authentication error" → "Handshake error"
- Update documentation and comments (6 locations)
* native_messaging.rs: "token exchange" → "bridge connection information"
* native_messaging.rs manifest: "bridge session tokens" → "bridge connection host"
* extension.rs: "automatic token exchange" → "automatic bridge connection"
* extension.rs: "Token auto-pairing" → "Auto-connect via native messaging"
* bridge_lifecycle.rs: "PID/token/port" → "PID/port"
- Clean up Cargo dependencies
* Remove unused rand and subtle crates (only used for token generation)
MEDIUM fixes:
- Add Extension ID whitelist for security
* Only allow official Actionbook extension ID (dpfioflkmnkklgjldmaggkodhlidkdcd)
* Document local trust security model
* Reject connections from arbitrary chrome-extension:// origins
- Fix test process leak in bridge_lifecycle.rs
* Add cleanup in ensure_bridge_attempts_spawn_when_port_free test
* Prevent background process/file leaks after successful spawns
Impact:
- Test suite now compiles and runs (was broken)
- Extension auto-connect works with new bridge (was broken)
- Reduced binary size by removing crypto dependencies
- Improved security with extension ID validation
- No test process/file leaks
Security impact: IMPROVED
- Extension ID whitelist prevents arbitrary extensions from connecting
- Removed unused crypto code (reduced attack surface)
- Documented local trust model (loopback + CDP whitelist)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* [packages/actionbook-rs]fix: resolve dual review findings from fix commit
|
||
|
|
664f96af0f | fix(review): address open-tab reuse feedback | ||
|
|
8d56c2aac1 | fix(extension): reuse initial empty tab on open | ||
|
|
8261b18e44 |
[packages/actionbook-extension]feat: add CWS variant zip without manifest key field (#110)
- Add CWS build variant that strips the `key` field from manifest.json - Update release workflow to produce both sideloading and CWS zips - Bump extension version to 0.1.2 |
||
|
|
71f1958323 |
[packages/actionbook-extension]chore: prepare Chrome Web Store publishing (#108)
* [packages/actionbook-extension]chore: prepare Chrome Web Store publishing - Bump extension version to 0.2.0 (sync with bridge protocol) - Add PRIVACY.md for Chrome Web Store submission - Add store-assets/ to .gitignore * [packages/actionbook-extension]chore: bump package.json version to 0.2.0 * [packages/actionbook-extension]chore: set version to 0.1.1 |
||
|
|
f2532d96c4 |
[packages/actionbook-extension]fix: read version from manifest dynamically instead of hardcoding (#102)
popup.html had hardcoded v0.2.0 while manifest.json was at 0.1.0. Now popup.js reads chrome.runtime.getManifest().version to stay in sync. |
||
|
|
1484b5b59c |
feat: isolated extension profile + extension stop command (#99)
* [packages/actionbook-rs]feat: add isolated Chrome profile for extension bridge mode Support running the extension bridge with a dedicated Chrome profile that contains no personal data. This adds: - `extension_isolated_profile` config field with backward-compatible default - `--isolated` CLI flag on `extension serve` - `serve_with_shutdown()` bridge refactor for external lifecycle control - `isolated_extension` orchestrator: launches Chrome with extension pre-loaded on CDP port 9333, monitors process, handles cleanup - `with_load_extension()` builder on BrowserLauncher - Setup wizard prompt for isolated vs shared extension profile - Recap and completion output updates CUE-347 * [packages/actionbook-rs]fix: harden isolated extension bridge lifecycle - Propagate bridge server errors (e.g. port-in-use) instead of swallowing them; callers now see a non-zero exit code - Reset extension_isolated_profile flag when switching to built-in browser, preventing stale config from routing to isolated mode - Replace shell-based `kill` command with libc::kill() to eliminate PATH-hijacking risk; only send signals when Chrome hasn't already exited to avoid hitting a recycled PID - Tighten "already running" detection: require both SingletonLock in the isolated profile dir AND a responsive CDP endpoint, preventing mistaken reuse of a different Chrome instance on the same port * [packages/actionbook-rs]fix: harden isolated extension bridge with CDP pipe transport - Replace --load-extension (removed in Chrome 137+) with CDP pipe transport: launch Chrome with --remote-debugging-pipe, then send Extensions.loadUnpacked over fd 3/4 post-launch - Add cdp_pipe.rs: null-terminated pipe protocol, PipeKeepAlive handle, and load_extension with blocking I/O for spawn_blocking - Add cdp_http.rs: CDP /json/list target discovery and Runtime.evaluate for injecting bridge token directly into extension storage via CDP - Add background.js storage.onChanged listener for CDP-injected tokens - Fix non-Unix isolated mode: fail early with clear error instead of silently launching without extension loading capability - Fix CLOEXEC on fd 3/4: when os_pipe fd already equals target fd number, clear FD_CLOEXEC via fcntl instead of skipping dup2 - Fix token injection safety: find_any_extension_service_worker now matches only SWs ending with /background.js to avoid injecting tokens into third-party extensions - Fix cleanup cross-contamination: serve_with_shutdown and isolated_extension only delete files belonging to their own mode, preventing concurrent bridge interference - Refactor resolve_cdp_endpoint for reuse across connect and --cdp - Add session WS URL refresh on reconnect to handle browser restarts - Update tests for pipe flags, SW filename filtering, stale WS URLs * [packages/actionbook-rs]feat: add `extension stop` command to gracefully stop bridge server Adds PID file mechanism so `actionbook extension stop` can locate and terminate the running bridge process without manual Ctrl+C or `kill`. - PID files written with 0600 permissions (atomic write) to prevent injection - Validates bridge is listening on expected port before sending SIGTERM (no PID recycling risk) - SIGKILL escalation after 2.5s timeout - Platform guards: #[cfg(unix)] with Windows taskkill fallback - Tracks isolated vs global PID files independently * [skills/actionbook]docs: update skill with extension stop command and full lifecycle - Add `actionbook extension stop` to command list - Replace manual process kill comment with concrete `extension stop` command in the cleanup lifecycle (Step 2) - Update extension mode example to include stop in cleanup * [docs/guides]docs: add extension stop command to browser extension guide - Setup Step 3: mention `extension stop` as the way to stop the bridge - New "Shutdown" section with proper cleanup order (browser close → extension stop) - Troubleshooting: reference `extension stop` for bridge restart flow * [docs/guides]docs: add isolated extension mode to browser extension guide - Overview: expand mode table with Extension (Isolated) as third option - New "Isolated Mode" section: quick start, config, comparison table, when-to-use guidance - Shutdown: separate instructions for standard vs isolated mode * [packages/actionbook-rs]fix: resolve PID by port and preserve PID file when process alive Fix two bugs in `extension stop` identified in PR review: 1. Use port files to match the requested port to the correct PID file (isolated vs standard), preventing SIGTERM to the wrong bridge when both are running on different ports. 2. Only delete the PID file when the process is confirmed dead (via kill(pid, 0)), not merely because the port probe failed. This preserves the PID file for a subsequent `extension stop --port <correct>` when the bridge is on a different port. * [packages/actionbook-rs]fix: smart cleanup of cross-mode stale files and prevent ambiguous PID selection Address follow-up review findings on extension stop and bridge startup: 1. Extract `is_pid_alive()` utility to extension_bridge for cross-platform PID liveness checks (kill(0) on Unix, tasklist on Windows). 2. Smart cross-mode cleanup at startup: instead of blindly deleting the other mode's files (breaks concurrent bridges) or never deleting them (leaves stale tokens that confuse send_command), check if the other mode's process is alive via PID probe before deciding. 3. Refuse to auto-stop when both PID files exist but no port file matches the requested port, preventing SIGTERM to the wrong bridge. 4. Make serve_with_shutdown mode-aware: only clean the current mode's stale port file instead of always cleaning the standard one. * [packages/actionbook-rs]fix: atomic PID:PORT mapping to prevent SIGTERM to wrong process Store port alongside PID in the same file (PID:PORT format) to create an atomic PID-to-port mapping. This eliminates all port-file-based guessing in `extension stop`, fixing the root cause of the P1 bug where SIGTERM could be sent to the wrong process. - write_pid_file/write_isolated_pid_file: write "PID:PORT" format - read_pid_file/read_isolated_pid_file: return Option<(u32, u16)> - stop(): deterministic PID selection via embedded port match - is_pid_alive (Windows): fix tasklist substring match (exact field) - taskkill: only delete PID file when process confirmed dead * [packages/actionbook-rs]fix: select token by port in send_command for parallel bridges Use PID:PORT mapping to select the correct token file based on target port, so parallel bridges (standard + isolated) authenticate correctly. Previously the fixed read order (standard first) caused auth failures when sending commands to the isolated bridge while standard was running. * [packages/actionbook-rs]fix: clean up Chrome and bridge on isolated extension load failure When extension loading fails (CDP error, panic, or timeout), the early return bypassed all cleanup logic, leaving the child Chrome process running and stale bridge state files on disk. Now performs shutdown signal, file cleanup, and Chrome termination before returning the error. |
||
|
|
aed25e2aea |
fix: extension auto-reconnect on token mismatch with progressive backoff (#97)
* [packages/actionbook-extension]fix: auto-reconnect on token mismatch with progressive native messaging backoff Bridge server now sends hello_error with "invalid_token" instead of silently closing the connection, allowing the extension to immediately clear the stale token and refresh via native messaging. Extension changes: - Handle invalid_token hello_error by clearing stored token and retrying native messaging immediately - Replace fixed 60s native messaging recheck with progressive backoff (3s, 5s, 10s, 30s, 60s) that never permanently gives up - Treat bridge_not_running as a transient error (not a native messaging failure) to avoid unnecessary backoff escalation - Reset fail count on successful native messaging token exchange This ensures users who install the extension after the bridge is already running can auto-connect without manual token copy-paste or bridge restart. * [skills/actionbook]docs: update SKILL.md for extension mode lifecycle and setup flow - Rewrite Phase 2 extension mode with full lifecycle commands - Move Extension Setup & Management section earlier for better flow - Add connection check commands before automation - Clarify browser close behavior in extension mode |
||
|
|
caa5b34dc2 | [packages/actionbook-extension]chore: set initial release version to 0.1.0 (#94) | ||
|
|
2d17e2ce3f |
refactor: decouple extension from CLI binary with independent release pipeline (#93)
* [packages/actionbook-extension]feat: add independent release infrastructure
- Add GitHub Actions workflow for tag-triggered releases (actionbook-extension-v*)
- Add package.json with npm run package script for local zip packaging
- Add scripts/package.js to build distributable .zip from extension files
- Add .gitignore for dist/ and node_modules/
- Rewrite README with install/release/troubleshooting docs
The extension now has its own independent release cycle via GitHub Releases,
decoupled from the CLI binary release process.
* [packages/actionbook-rs]refactor: replace embedded extension with GitHub download installer
Remove all include_str!/include_bytes! compile-time embedding of the Chrome
extension. The CLI now downloads the extension from GitHub Releases at install
time via `actionbook extension install`.
Key changes:
- Rename embedded_extension.rs → extension_installer.rs
- Add download_and_install() using reqwest with HTTPS-only, timeouts
- Add extract_zip() with zip-slip and zip-bomb protection
- Add download URL host validation (github.com whitelist)
- Add post-extraction manifest version verification
- Use semver crate for proper version comparison
- Add zip crate dependency for safe extraction
- Update extension command to use new installer module
Security measures:
- HTTPS-only with 30s request / 10s connect timeouts
- 10MB max download size, 50MB max uncompressed size
- Zip-slip protection via enclosed_name()
- Download host whitelist (github.com, githubusercontent.com)
- Manifest version verification after extraction
* [packages/actionbook-rs]fix: atomic install, better error display, and real zip test
- Display errors with thiserror format instead of Rust Debug
("Extension error: ..." not "ExtensionError("...")")
- Atomic install: extract to temp dir, verify manifest, then swap
(existing install survives if download/extract fails)
- Move tempfile from dev-dependencies to dependencies
- Add test_extract_real_extension_zip using actual packaged zip
* [packages/actionbook-rs,actionbook-extension]fix: address review findings from dual review
- Remove early return in install command that blocked automatic upgrades;
let download_and_install() handle semver comparison via new
ExtensionAlreadyUpToDate error variant
- Remove redundant zip -j9 call in package.js, keep only zip -r9
- Add pagination NOTE on fetch_latest_release()
- Make test_extract_real_extension_zip read version from manifest.json
dynamically instead of hardcoding "0.2.0"
|
||
|
|
7f32f83815 |
feat: add browser extension bridge for CLI-to-browser communication (#89)
* [packages/actionbook-rs]feat: add browser extension Implements the WebSocket bridge server and Chrome Extension skeleton for controlling the user's own browser via extension mode. New components: - Chrome Extension (MV3): background service worker, popup UI, WebSocket client connecting to bridge on localhost:19222 - Bridge server: `actionbook extension serve` - routes commands between CLI and extension with request ID rewriting and timeouts - CLI: --extension flag, `actionbook extension serve/status/ping` - Extension mode routing for `eval` and `pages` browser commands Architecture: CLI -> WebSocket -> Bridge -> Extension -> chrome.debugger * [packages/actionbook-rs]fix: merge duplicate extension_send fns and fix is_bridge_running side effect - Merge extension_send_cdp and extension_send into a single function - Replace WebSocket-based is_bridge_running with plain TCP connect to avoid orphan connections and spurious bridge warnings * [packages/actionbook-extension]feat: add offscreen keepalive, CDP event forwarding, createTab/activateTab - Add offscreen document for Service Worker keepalive (ping every 20s) - Forward CDP events from chrome.debugger.onEvent to CLI via WebSocket - Add exponential backoff reconnect (1s base, 2x growth, 30s max) - Auto-attach to active tab when no tab is attached - Add Extension.createTab and Extension.activateTab command handlers - Improve error handling for debugger detachment detection * [packages/actionbook-rs]feat: add extension mode to all browser commands (Phase 2) Add --extension flag support to all browser subcommands, routing CDP operations through the WebSocket bridge instead of direct chromiumoxide. Commands implemented: goto, back, forward, reload, open, switch, wait, click, type, fill, select, hover, focus, press, screenshot, pdf, html, text, snapshot, inspect, viewport, cookies, close, restart. Includes js_resolve_selector helper for [ref=eN] snapshot references and extension_eval helper with exception handling. Also fixes escape order bug in js_resolve_selector (backslashes must be escaped before single quotes). * [packages/actionbook-rs]test: add extension bridge integration tests and mock extension - Add [lib] crate to expose modules for integration tests - Add 9 integration tests covering: CLI without extension error, full round-trip with id remapping, error forwarding, multiple concurrent CLIs, is_bridge_running detection, connection failure - Add mock-extension.mjs for manual E2E testing with mock CDP responses * [packages/actionbook-rs]test: add Chrome E2E test script and expand bridge unit tests - Add extension-chrome-e2e.sh: real Chrome + arxiv.org E2E test covering navigation, content extraction, screenshots, cookies, tab management, interactions, and JSON output mode - Expand bridge_tests module with additional assertions and helpers * [packages/actionbook-rs]feat: add extension install/path/uninstall CLI commands Embed all 9 Chrome extension files into the Rust binary via include_str!/include_bytes! and provide CLI commands to extract them to ~/.config/actionbook/extension/ for easy Chrome sideloading. New commands: - `actionbook extension install [--force]` - `actionbook extension path` - `actionbook extension uninstall` Also updates `extension serve` to show the real installed path with outdated/not-installed warnings. All commands support --json. * [packages/actionbook-rs,actionbook-extension]feat: implement extension security Phase 0-2 Phase 0 - Connection Hardening: - Lazy connect (idle start, user-triggered connection) - Bounded reconnect with MAX_RETRIES=8 and exponential backoff - Distinguish network errors from auth failures in pre-handshake close Phase 1 - Session Token Auth: - Token generation (abk_ + 32 hex), atomic file write (0600) - Hello/hello_ack handshake protocol with token validation - Constant-time token comparison (subtle crate) to prevent timing attacks - TTL watchdog with 30min idle token rotation - Token format validation (abk_ prefix + length) in both CLI and extension Phase 2 - CDP Allowlist & Risk Levels: - CDP method allowlist with L1/L2/L3 risk classification - L3 human-in-the-loop confirmation via popup with nonce binding - Sender validation (only popup.html can approve L3 or set tokens) - Sensitive domain detection with L2→L3 risk elevation - Origin validation (loopback + chrome-extension:// only) Security hardening from dual review: - Replace all console.log with debug utility behind DEBUG_ENABLED flag - Remove token pre-population in popup input - Reject concurrent L3 requests (no slot overwrite) * [packages/actionbook-rs]chore: remove browser-extension-design.md from tracking * [actionbook-extension]chore: replace placeholder icons with Actionbook logomark Extract the logomark from docs/logo SVG and generate proper PNG icons at 16x16, 48x48, and 128x128 to replace the blue placeholder squares. * [packages/actionbook-rs,actionbook-extension]feat: switch cookie ops to chrome.cookies API with security gating Replace CDP Network.* cookie methods (which required L3 popup confirmation and always timed out in automated usage) with chrome.cookies.* API via new Extension.getCookies/setCookie/removeCookie/clearCookies commands. Security hardening from code review: - All cookie ops require a valid http(s) URL scope — no cross-domain wildcard - Cookie writes (set/remove/clear) gated by L3 confirmation on sensitive domains - Input validation for url, name, value params in background.js - Native messaging entry point validates exact extension ID (not prefix) - setCookie checks for null return (browser rejection) - clearCookies uses Promise.allSettled for parallel removal Also adds native messaging auto-connect, bridge token rotation handling, and fixes .gitignore typo (.doc -> .docs/). * [packages/actionbook-rs]test: replace arxiv.org with automation-friendly test sites Switch E2E test targets from arxiv.org (rate-limited) to the-internet.herokuapp.com and example.com for reliable CI. - Phase 2: use `browser open` to avoid chrome:// tab issues - Phase 6: login form interactions on /login (public test creds) - Phase 7: hover on /hovers `.figure`, focus link on homepage - Phase 9: example.com for new-tab test - login-result-page: check URL path instead of page title * [actionbook-extension]docs: add extension usage guide Cover installation, bridge setup, token pairing, all browser subcommands (navigation, interaction, tabs, debug, cookies, connect), Action Manual integration, and common troubleshooting. * [packages/actionbook-rs,actionbook-extension]fix: address PR #89 review — bridge security, reliability, and correctness - Add awaitPromise to Runtime.evaluate for async extension commands - Fix origin validation prefix-matching bypass (exact host matching) - Send token_expired notification before closing extension WebSocket - Return immediate error when extension channel is closed - Propagate custom bridge port to extension via native messaging - Prioritize explicit --domain over current URL in cookie ops - Retry native messaging with backoff instead of permanent disable - Use serde_json for comprehensive JS string escaping - Validate protocol version in bridge handshake with semver * [packages/actionbook-rs,actionbook-extension]fix: tab auto-attach, profile validation, and cookie clear safety 1. (Critical) Extension.createTab and Extension.activateTab now auto-attach the Chrome debugger to the new/activated tab, so subsequent CDP commands target the correct tab instead of a stale attachedTabId. 2. (High) --profile flag combined with --extension now produces a hard error instead of being silently ignored. 3. (Medium) cookies clear gains --domain, --dry-run, and --yes flags. --yes is required to actually clear (both interactive and JSON modes). --dry-run previews which cookies would be cleared without deleting. --domain scopes the clear to a specific domain. CDP mode rejects --domain/--dry-run with a clear error message. Tests: 6 new tests covering all three fixes (bridge-level + CLI parser). * [skills/actionbook]docs: fix SKILL.md extension mode documentation - Remove non-existent --path flag from extension install - Add missing extension uninstall command and --extension-port flag - Simplify Phase 2 alt to avoid redundant duplication of CDP example - Move Extension Setup section after Browser Commands for logical flow - Extract troubleshooting content to references/extension-troubleshooting.md - Fix misleading "runs in background" description for serve (it's foreground) - Replace opaque "L3 operations" term with concrete description - Add browser close safety note for extension mode * [packages/actionbook-rs,actionbook-extension]fix: use domain-based query for cookie clear to cover all paths chrome.cookies.getAll({ url }) only returns cookies whose path matches the URL path. When --domain builds https://domain/, cookies scoped to /app, /account, etc. are silently missed. Pass an explicit domain param so the extension uses getAll({ domain }) which covers all paths. |