Commit Graph

6 Commits

Author SHA1 Message Date
mcfn.feng fac7647c6c [packages/actionbook-rs]fix: align tab ID to 1-based (t1, t2, ...) per spec and add tab management E2E assertions
- Fix next_tab_id counter to start from 1 instead of 0
- Fix hardcoded "t0" in browser start response (router.rs)
- Update TabId doc comment to reflect t1, t2, ... convention
- Rewrite browser_tab.rs with full JSON envelope assertions for
  list-tabs, new-tab (browser open), and close-tab commands
- Update all e2e test files to use 1-based tab IDs (t1, t2, t3)
- Fix harness ensure_no_sessions() field name: "id" -> "session_id"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 17:28:14 +08:00
Junliang Feng 625994d0cd feat: semantic session_id (Phase A-1) + E2E update (#283)
* [packages/actionbook-rs]feat: semantic session_id (#t21, #t25)

Replace numeric SessionId(u32) with semantic SessionId(String) validated
against ^[a-z][a-z0-9-]{0,63}$. Auto-generates local-1, local-2, ...
when --set-session-id is not provided. Add --set-session-id flag to
browser start for explicit naming. Update all 86 E2E tests from s0→local-1.

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

* [packages/actionbook-rs]fix: address review feedback on PR #283

- Tighten SessionId validator from {0,63} to {1,63} (min 2 chars)
- Profile-based auto-generation: explicit --profile derives session ID
  from profile name (default, default-2, ...); no --profile uses local-N
- Add --set-session-id contract E2E tests (explicit, invalid, conflict)
- Add profile-based auto-generation E2E test
- Update clap help text from "s0" to "local-1" examples

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

* [packages/actionbook-rs]fix: resolve rebase conflicts with PR #282 formatter

- Add set_session_id: None to Action::StartSession in formatter tests
- Update SessionId(0) → SessionId::new_unchecked("local-1") in formatter
- Update test assertions from "s0" to "local-1" session IDs

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

* [packages/actionbook-rs]fix: add clippy allow for too_many_arguments on handle_start_session

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

* [packages/actionbook-rs]fix: prevent auto-generated session ID collision with explicit IDs

Auto-generate path now loops to skip IDs already occupied (e.g. by
--set-session-id local-1). Adds UT covering the collision scenario.

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

* [packages/actionbook-rs]fix: resolve rebase conflicts with PR #284 formatter tests

Update SessionId(0) → SessionId::new_unchecked("local-1") and
"s0" → "local-1" in new tests added by PR #284.

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

* [packages/actionbook-rs]fix: profile-based session ID sanitization and multi-session support

- SessionId::from_profile() now sanitizes profile names: lowercase,
  replace non-alphanumeric with hyphens, collapse consecutive hyphens,
  strip leading/trailing hyphens. Falls back to local-N if invalid.
- Local mode session_exists gate now only applies when no explicit
  --profile is passed, allowing work, work-2, work-3 with explicit profiles.
- Added 6 UTs covering sanitization edge cases and fallback behavior.

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

* [packages/actionbook-rs]fix: reserve room for suffix in long-profile session IDs

Truncate sanitized profile base before appending collision suffix
to prevent suffix from being erased by the 64-char limit. Ensures
distinct session IDs even for very long profile names.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 03:09:27 +08:00
Junliang Feng 99815cc2dc fix: add SessionGuard to prevent E2E test cascade failures (#275)
* [packages/actionbook-rs]fix: add SessionGuard to prevent E2E test cascade failures

Add a RAII SessionGuard that calls ensure_no_sessions() on both creation
and drop. This ensures sessions are cleaned up even when a test panics
mid-execution, preventing one test failure from cascading into all
subsequent tests (which was causing 64/65 E2E failures).

- Replace all bare ensure_no_sessions() calls with SessionGuard::new()
- Add SessionGuard to 8 tests that were missing session cleanup entirely
- Guard runs cleanup on drop (including panic unwind), not just on success

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

* [packages/actionbook-rs]chore: fix cargo fmt formatting in E2E test imports

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 22:34:04 +08:00
Junliang Feng 0d3af40bf3 [root]feat: add clippy + fmt lint check to CI (#261)
* [root]feat: add clippy + fmt lint check to CI

Add a new Lint job that runs cargo fmt --check and cargo clippy -D warnings
as a code quality gate. Lint status is included in test summary.

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

* [packages/actionbook-rs]fix: format all code and fix clippy warnings

- Run cargo fmt --all to enforce consistent formatting
- Fix clippy warnings: use clamp(), if-let, contains_key, unwrap_or_default
- Add targeted #[allow] for structural patterns (too_many_arguments,
  large_enum_variant, result_large_err, never_loop)
- CI clippy config: -D warnings with -A dead_code/unused-imports/deprecated
  to allow TDD-style development while catching real quality issues

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

* [packages/actionbook-rs]fix: remove dead depth_cache allocation in benchmark

Remove unused HashMap allocation that was silenced with _ prefix.
Addresses review feedback on PR #261.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 16:42:04 +08:00
Junliang Feng b2fcaead44 [packages/actionbook-rs]feat: daemon_v2 — unified session+daemon architecture (Phase 1+2) (#258)
* [packages/actionbook-rs]feat: add daemon_v2 module — Phase 1 session+daemon architecture rewrite

Implement the v1.0.0 daemon architecture based on root cause analysis and
approved design doc. This is Phase 1 (Local mode) of the Strangler Fig migration.

Key design decisions:
- Thick Daemon: all browser logic in daemon, CLI is ~500 line RPC client
- Per-Session Async Task: session间并行, session内串行 (bounded(32) channel)
- BrowserBackend trait (Factory + Session handle): unified local/extension/cloud
- 3-layer protocol: CLI Action (~50 variants) / BackendOp (CDP primitives) / Wire
- Typed errors with recovery hints (Retryable/UserAction/Fatal)

New modules (daemon_v2/):
- types.rs: SessionId/TabId/WindowId newtypes, Mode enum
- action.rs: ~22 Action variants (Phase 1 subset)
- action_result.rs: ActionResult with hint on every error variant
- backend_op.rs: 15 CDP-level primitive operations
- wire.rs: length-prefix framing (4 bytes LE + JSON)
- backend/: BrowserBackendFactory + BackendSession traits, LocalBackend impl
- registry.rs: in-memory session registry with monotonic IDs
- session_actor.rs: per-session tokio task, delegates to action_handler
- action_handler.rs: Action -> BackendOp compilation (14 commands)
- router.rs: request routing (global/session/tab level dispatch)
- server.rs: UDS listener with 0o600 permissions, multi-request connections
- daemon_main.rs: daemon entry point with persistence + recovery
- persistence.rs: atomic state file write (temp+fsync+rename)
- recovery.rs: crash recovery planning per backend type
- cli_v2.rs: thin CLI client with 3-level parameter validation
- client.rs: DaemonClient RPC with 30s timeout
- formatter.rs: ActionResult terminal formatting

Tests: 166 (158 unit + 8 E2E, 2 Chrome-only ignored)

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

* [packages/actionbook-rs]fix: address Codex adversarial review — 5 critical fixes

1. CRITICAL: CDP target routing — LocalBackendSession now tracks
   target_id -> sessionId via Target.attachToTarget { flatten: true }.
   Page-scoped commands include sessionId in CDP JSON.

2. HIGH: StartSession implemented in Router — creates backend via
   factory, spawns SessionActor, registers in registry. Enforces
   1-profile-1-session constraint for Local mode.

3. HIGH: Session Close lifecycle — actor calls backend.shutdown(),
   breaks run loop, router removes session from registry.

4. HIGH: Persistence wired up — save_state() called after
   StartSession/CloseSession, periodic 30s save, save on shutdown.

5. MEDIUM: Wire protocol timeouts — 5 min connection idle timeout,
   30s read timeout on length prefix (slow-loris protection).

Tests: 162 daemon_v2 tests pass.

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

* [packages/actionbook-rs]feat: Phase 2 — ExtensionBackend + CloudBackend + multi-factory router

Add Extension and Cloud browser backends, completing the unified 3-mode
architecture. All sessions now route through the same daemon core
regardless of browser mode.

ExtensionBackend (WS + Token hybrid):
- Daemon-internal WS server (ExtensionBridge) on random localhost port
- One-time token auth via native messaging (Chrome-verified channel)
- Token: 32 random bytes, constant-time comparison, invalidated after use
- BridgeRequest/BridgeResponse protocol for CDP command forwarding
- 1 extension = 1 session constraint enforced in router

CloudBackend (WSS + TLS + auth headers):
- tokio-tungstenite with TLS for remote wss:// endpoints
- Custom auth headers on WS upgrade handshake
- Reconnection with exponential backoff (1s-30s, 5 attempts)
- Health check via separate WSS connection (avoids stream interleaving)
- Single-connection aware (cloud endpoints may reject parallel WS)

Router multi-factory:
- HashMap<Mode, Arc<dyn BrowserBackendFactory>> replaces single factory
- StartSession dispatches to correct factory by mode
- Local: factory.start(), Extension: factory.attach(), Cloud: factory.attach()
- Cloud validates --cdp-endpoint required
- Extension enforces 1-session limit

CLI updates:
- --cdp-endpoint flag on browser start
- Cloud mode validation at CLI level (before daemon round-trip)

Tests: 208 unit + 13 E2E (221 total), zero regressions

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

* [packages/actionbook-rs]feat: Phase 3 — complete all ~60 Action handlers

Add all remaining observation, interaction, waiting, and data actions
to achieve full PRD command coverage.

Observation (15 new): Pdf, Title, Url, Value, Attr, Attrs, Describe,
State, Box, Styles, Viewport, Query, InspectPoint, LogsConsole, LogsErrors

Interaction (9 new): Select, Hover, Focus, Press (modifier-aware chord
parsing), Drag (4-step mouse sequence), Upload (DOM.setFileInputFiles),
Scroll (JS scrollBy), MouseMove, CursorPosition

Waiting (3 new): WaitNavigation (URL change + readyState poll),
WaitNetworkIdle (Performance API poll), WaitCondition (JS expression poll)

Data (10 new): CookiesList/Get/Set/Delete/Clear (CDP Network.*),
StorageList/Get/Set/Delete/Clear (JS localStorage/sessionStorage)

Session (1 new): RestartSession (close + re-start same profile/mode)

All 3 backends updated (local/extension/cloud) with new CDP translations.
CLI clap definitions added for all new commands.

Action coverage: 22 -> 60 variants (exceeds design doc target of ~50)
Tests: 208+ pass, cargo check clean

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

* [packages/actionbook-rs]feat: route browser commands through daemon_v2

Switch `actionbook browser *` commands to route through daemon_v2
by default. Non-browser commands (search, config, setup, etc.)
continue using v0.x path.

- main.rs: try CliV2::try_parse() first for browser commands
- cli_v2.rs: daemon auto-start (fork + poll socket readiness)
- cli.rs: --v1 hidden flag for v0.x escape hatch
- daemon serve-v2: foreground daemon command for debugging

Routing: browser commands -> daemon_v2, everything else -> v0.x
Escape hatch: --v1 forces old path during transition

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

* [packages/actionbook-rs]chore: daemon_v2 cleanup — clippy, codex review fixes, test fixes

- Fix all clippy warnings in daemon_v2/ (unnecessary_unwrap, needless_lifetimes,
  map_entry, collapsible_if, redundant_closure, io_other_error, etc.)
- Replace 3 production unwrap() with safe if-let patterns
- Fix build_state_snapshot hardcoded Local checkpoint (now per-mode)
- Fix health() false positive on JSON parse failure (local.rs)
- Log open_url navigation errors instead of swallowing (local.rs)
- Add doc comments to public Registries methods and OpResult constructors
- Fix E2E test assertion for updated router error code
- Zero clippy warnings, zero dead_code warnings, 208 tests pass
- Chrome E2E tests also pass (2 previously ignored tests now green)

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

* [packages/actionbook-rs]fix: address Codex quality review — 6 fixes

1. router.rs: build_state_snapshot now produces mode-specific checkpoint
   (Local/Extension/Cloud) instead of hardcoding Local for all sessions
2. extension.rs: replace production unwrap() on WS rejection with fallback
3. router.rs: replace production unwrap() on cdp_endpoint with match+fatal
4. router.rs+action.rs: pass ws_headers through to Cloud backend AttachSpec
5. local.rs: log open_url navigation errors instead of swallowing
6. local.rs: health() returns connected=false on JSON parse failure

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

* [packages/actionbook-rs]refactor: clean break — delete v0.x, rename daemon_v2 to daemon

Remove all v0.x code that daemon_v2 replaces. No backward compat,
no --v1 escape hatch. daemon is the only path.

Deleted (-20,946 lines):
- src/daemon/ (old v1 daemon)
- src/browser/session.rs (old 5,846-line SessionManager)
- src/browser/router.rs (old BrowserDriver)
- src/browser/extension_backend.rs
- src/commands/browser.rs (old 6,709-line command handler)
- src/commands/app.rs (Electron, depended on SessionManager)
- src/commands/batch.rs (depended on BrowserDriver)
- tests/app_command_test.rs

Renamed: daemon_v2/ -> daemon/ (all import paths updated)

Cleaned:
- main.rs: daemon CLI is the only browser path, no v1/v2 routing
- cli.rs: removed all browser-only flags and enums
- browser/mod.rs: removed session, router, extension_backend
- commands/daemon.rs: uses new daemon module directly

Tests: 311 unit + 167 integration pass. Zero errors.

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

* [packages/cli]docs: enhance Actionbook CLI architecture and Rust engineering guidelines

Added detailed documentation on the architecture of the Actionbook CLI, emphasizing the three-layer decoupling of the CLI, Daemon, and Browser layers. Included Rust engineering constraints such as the use of mature crates, feature flags for compilation, and guidelines for error handling and output formatting. Also outlined security measures, output/error handling, and best practices for maintaining a minimal binary size. This documentation aims to improve code quality and maintainability across the project.

* [packages/actionbook-rs]chore: add .gitignore to exclude docs directory

Created a new .gitignore file in the actionbook-rs package to prevent the inclusion of the 'docs/' directory in version control.

* [packages/actionbook-rs]fix: address PR #258 Codex review — 4 fixes

P1-1: server.rs — move timeout inside loop (per-read idle timeout,
not per-connection lifetime). Active connections no longer killed.

P1-2: cli_v2.rs — screenshot path now passed through Action, base64
decoded and written to disk after daemon returns.

P1-3: cloud.rs — health probe false positive fixed. Now checks inner
Result with is_ok_and(|r| r.is_ok()) instead of just is_ok().

P2-1: router.rs — TOCTOU race on StartSession fixed. Single atomic
lock: check + allocate ID + insert placeholder. Backend start happens
outside lock. Concurrent requests see Starting placeholder and fail.

Tests: 357 pass.

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

* [packages/actionbook-rs]fix: merge release/1.0.0 + update E2E tests for daemon v2 CLI

- Merge release/1.0.0 (picks up E2E framework from PR #254, CI from PR #257)
- Remove stale daemon_v2/ that came back from merge (replaced by daemon/)
- Update E2E harness: remove --headless --no-daemon global flags (deleted in clean break)
- Update browser_basic test: use v2 CLI format (browser start --mode local --headless,
  browser goto -s s0 -t t0, browser snapshot -s s0 -t t0, browser close -s s0)

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

* [packages/actionbook-rs]fix: add --json and --profile global flags to CliV2

CliV2::try_parse() failed when `actionbook --json browser snapshot ...`
was invoked because --json wasn't a recognized flag. This caused fallthrough
to the old CLI which no longer has `browser` subcommand.

Add --json and --profile as global flags on CliV2 struct so the daemon
CLI parser accepts them. Fixes CI E2E test failure.

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

* [packages/actionbook-rs]chore: remove docs/ from tracked files

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

* [packages/actionbook-rs]fix: show daemon CLI errors for browser commands, not legacy fallback

When `actionbook browser open url` fails to parse (e.g. missing --session),
the CliV2 try_parse error was silently swallowed and the legacy CLI would
report "unrecognized subcommand 'browser'" — completely misleading.

Now: if args contain "browser", always show the daemon CLI error message
(e.g. "the following required arguments were not provided: --session").
Only fall through to legacy CLI for non-browser commands.

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-26 15:39:04 +08:00
Junliang Feng 6100fcb3d1 [packages/actionbook-rs]feat: E2E test framework + browser_basic tests (#254)
* [packages/actionbook-rs]feat: add E2E test framework with browser_basic tests

New E2E test infrastructure:
- tests/e2e/main.rs: single binary entry point (avoids per-file link overhead)
- tests/e2e/harness.rs: shared env isolation, CLI runners, assertions
- tests/e2e/browser_basic.rs: 4 tests (open/goto arxiv.org/snapshot/close)
- .github/workflows/test.yml: CI workflow (Ubuntu + headless Chrome)

Gated by RUN_E2E_TESTS=true, run with --test-threads=1.

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

* [packages/actionbook-rs]fix: address review feedback on E2E tests

1. Remove unnecessary `unsafe impl Sync for IsolatedEnv` — all fields
   already implement Sync automatically.
2. Merge 4 sequential browser_basic tests into a single test function
   to guarantee execution order (Rust does not guarantee test ordering).

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

* [packages/actionbook-rs]fix: fix E2E harness HOME override breaking Chrome CDP

On macOS, overriding HOME causes Chrome to fail to bind its CDP port.
Changed harness to only override XDG_CONFIG_HOME and XDG_DATA_HOME for
actionbook config isolation, and added --no-daemon flag to avoid daemon
process env inheritance issues.

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

---------

Co-authored-by: Actionbook <actionbook@Junliangs-MacBook-Air.local>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 23:03:25 +08:00