From c6a33b63387e2d065853e93289c6f44d95e63815 Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Tue, 3 Mar 2026 08:00:14 -0600 Subject: [PATCH 01/12] fix(windows): resolve daemon startup failures and Git Bash compatibility (#582) * fix(windows): resolve daemon startup failures and Git Bash compatibility Three root causes behind 27 open Windows issues: 1. Path::canonicalize() returns \\?\ prefixed paths on Windows that Node.js cannot parse, preventing daemon startup. Strip the prefix before passing to Node. (fixes #522, #390, #56, #25, #37, #89) 2. Git Bash/MSYS2 translates Unix-style paths and resolves node to a shell wrapper script. Use node.exe explicitly and set MSYS_NO_PATHCONV/MSYS2_ARG_CONV_EXCL to prevent argument mangling. (fixes #148, #108, #171) 3. postinstall fixWindowsShims() hardcoded x64 arch and did not verify the native binary exists before rewriting shims. Now detects arch dynamically and validates the binary path. (fixes #262) Also: - Error messages now show TCP port on Windows instead of Unix socket path - Windows CI expanded to test full daemon lifecycle (open, snapshot, close) * fix(windows): strip \\?\ prefix in auth-cli path (fixes #579) Same canonicalize() issue as the daemon spawn path, but in run_auth_cli() which passes the script path to Node.js. --- .github/workflows/ci.yml | 17 +++++++++++++++++ cli/src/connection.rs | 38 ++++++++++++++++++++++++++++++-------- cli/src/main.rs | 16 ++++++++++++++++ scripts/postinstall.js | 32 ++++++++++++++------------------ 4 files changed, 77 insertions(+), 26 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 870f7cd..33eb95a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -173,6 +173,23 @@ jobs: } shell: pwsh + - name: Test daemon lifecycle (open, snapshot, close) + run: | + $env:PATH = "$pwd\bin;$env:PATH" + Write-Host "--- Opening page ---" + bin/agent-browser-win32-x64.exe open https://example.com + if ($LASTEXITCODE -ne 0) { Write-Error "open failed"; exit 1 } + Write-Host "--- Taking snapshot ---" + $snapshot = bin/agent-browser-win32-x64.exe snapshot + if ($LASTEXITCODE -ne 0) { Write-Error "snapshot failed"; exit 1 } + Write-Host $snapshot + Write-Host "--- Closing browser ---" + bin/agent-browser-win32-x64.exe close + if ($LASTEXITCODE -ne 0) { Write-Error "close failed"; exit 1 } + Write-Host "--- Windows daemon lifecycle test passed ---" + shell: pwsh + timeout-minutes: 5 + serverless-chromium: name: Serverless Chromium (@sparticuz/chromium) runs-on: ubuntu-latest diff --git a/cli/src/connection.rs b/cli/src/connection.rs index dc4de8c..6405985 100644 --- a/cli/src/connection.rs +++ b/cli/src/connection.rs @@ -355,6 +355,17 @@ pub fn ensure_daemon( let exe_path = env::current_exe().map_err(|e| e.to_string())?; // Canonicalize to resolve symlinks (e.g., npm global bin symlink -> actual binary) let exe_path = exe_path.canonicalize().unwrap_or(exe_path); + // On Windows, canonicalize() returns \\?\ prefixed extended-length paths. + // Node.js cannot handle these, so strip the prefix. + #[cfg(windows)] + let exe_path = { + let p = exe_path.to_string_lossy(); + if let Some(stripped) = p.strip_prefix(r"\\?\") { + PathBuf::from(stripped) + } else { + exe_path + } + }; let exe_dir = exe_path.parent().unwrap(); let mut daemon_paths = vec![ @@ -404,10 +415,11 @@ pub fn ensure_daemon( { use std::os::windows::process::CommandExt; - // On Windows, call node directly. Command::new handles PATH resolution (node.exe or node.cmd) - // and automatically quotes arguments containing spaces. - let mut cmd = Command::new("node"); - cmd.arg(daemon_path); + // Use node.exe explicitly to avoid Git Bash/MSYS2 shell wrapper resolution + let mut cmd = Command::new("node.exe"); + cmd.arg(daemon_path) + .env("MSYS_NO_PATHCONV", "1") + .env("MSYS2_ARG_CONV_EXCL", "*"); apply_daemon_env(&mut cmd, session, opts); // CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS @@ -431,10 +443,20 @@ pub fn ensure_daemon( thread::sleep(Duration::from_millis(100)); } - Err(format!( - "Daemon failed to start (socket: {})", - get_socket_dir().join(format!("{}.sock", session)).display() - )) + #[cfg(unix)] + let endpoint_info = format!( + "socket: {}", + get_socket_dir() + .join(format!("{}.sock", session)) + .display() + ); + #[cfg(windows)] + let endpoint_info = format!( + "port: 127.0.0.1:{}", + get_port_for_session(session) + ); + + Err(format!("Daemon failed to start ({})", endpoint_info)) } fn connect(session: &str) -> Result { diff --git a/cli/src/main.rs b/cli/src/main.rs index c56a423..fa1550e 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -31,6 +31,15 @@ use std::process::Command as ProcessCommand; fn run_auth_cli(cmd: &serde_json::Value, json_mode: bool) -> ! { let exe_path = env::current_exe().unwrap_or_default(); let exe_path = exe_path.canonicalize().unwrap_or(exe_path); + #[cfg(windows)] + let exe_path = { + let p = exe_path.to_string_lossy(); + if let Some(stripped) = p.strip_prefix(r"\\?\") { + PathBuf::from(stripped) + } else { + exe_path + } + }; let exe_dir = exe_path.parent().unwrap_or(std::path::Path::new(".")); let mut script_paths = vec![ @@ -233,6 +242,13 @@ fn main() { libc::signal(libc::SIGPIPE, libc::SIG_DFL); } + // Prevent MSYS/Git Bash path translation from mangling arguments + #[cfg(windows)] + { + env::set_var("MSYS_NO_PATHCONV", "1"); + env::set_var("MSYS2_ARG_CONV_EXCL", "*"); + } + let args: Vec = env::args().skip(1).collect(); let flags = parse_flags(&args); let clean = clean_args(&args); diff --git a/scripts/postinstall.js b/scripts/postinstall.js index 6301cf8..8cf2a88 100644 --- a/scripts/postinstall.js +++ b/scripts/postinstall.js @@ -187,46 +187,42 @@ async function fixUnixSymlink() { * We overwrite them to invoke the native .exe directly. */ async function fixWindowsShims() { - // Check if this is a global install by looking for npm's global prefix let npmBinDir; try { npmBinDir = execSync('npm prefix -g', { encoding: 'utf8' }).trim(); } catch { - return; // Not a global install or npm not available + return; } - // The shims are in the npm prefix directory (not prefix/bin on Windows) const cmdShim = join(npmBinDir, 'agent-browser.cmd'); const ps1Shim = join(npmBinDir, 'agent-browser.ps1'); - // Only fix if shims exist (indicates global install) + // Shims may not exist yet during postinstall (npm creates them after + // lifecycle scripts). If missing, fall back: the JS wrapper at + // bin/agent-browser.js handles Windows correctly via child_process.spawn. if (!existsSync(cmdShim)) { return; } - // Path to native binary relative to npm prefix - const relativeBinaryPath = 'node_modules\\agent-browser\\bin\\agent-browser-win32-x64.exe'; + // Detect architecture so ARM64 Windows is handled correctly + const cpuArch = arch() === 'arm64' ? 'arm64' : 'x64'; + const relativeBinaryPath = `node_modules\\agent-browser\\bin\\agent-browser-win32-${cpuArch}.exe`; + const absoluteBinaryPath = join(npmBinDir, relativeBinaryPath); + + // Only rewrite shims if the native binary actually exists + if (!existsSync(absoluteBinaryPath)) { + return; + } try { - // Overwrite .cmd shim const cmdContent = `@ECHO off\r\n"%~dp0${relativeBinaryPath}" %*\r\n`; writeFileSync(cmdShim, cmdContent); - // Overwrite .ps1 shim - const ps1Content = `#!/usr/bin/env pwsh -$basedir = Split-Path $MyInvocation.MyCommand.Definition -Parent -$exe = "" -if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { - $exe = ".exe" -} -& "$basedir/${relativeBinaryPath.replace(/\\/g, '/')}" $args -exit $LASTEXITCODE -`; + const ps1Content = `#!/usr/bin/env pwsh\r\n$basedir = Split-Path $MyInvocation.MyCommand.Definition -Parent\r\n& "$basedir\\${relativeBinaryPath}" $args\r\nexit $LASTEXITCODE\r\n`; writeFileSync(ps1Shim, ps1Content); console.log('✓ Optimized: shims point to native binary (zero overhead)'); } catch (err) { - // Permission error or other issue - not critical, JS wrapper still works console.log(`⚠ Could not optimize shims: ${err.message}`); console.log(' CLI will work via Node.js wrapper (slightly slower startup)'); } From 62241b50e93fe079bc9e7044ca8d8032ec30922e Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Tue, 3 Mar 2026 08:08:02 -0600 Subject: [PATCH 02/12] chore: add patch changeset for release (#589) --- .changeset/release-ccb73f55.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/release-ccb73f55.md diff --git a/.changeset/release-ccb73f55.md b/.changeset/release-ccb73f55.md new file mode 100644 index 0000000..09f84d6 --- /dev/null +++ b/.changeset/release-ccb73f55.md @@ -0,0 +1,5 @@ +--- +"agent-browser": patch +--- + +Fixed Windows compatibility issues including proper handling of extended-length path prefixes from canonicalize(), prevention of MSYS/Git Bash path translation that could mangle arguments, and improved daemon startup reliability. Also added ARM64 Windows support in postinstall shims and expanded CI testing with a full daemon lifecycle test on Windows. From 857c0b25df11db4a351c96ff536fba7019495b7d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 3 Mar 2026 08:21:58 -0600 Subject: [PATCH 03/12] chore: version packages (#591) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/release-ccb73f55.md | 5 ----- CHANGELOG.md | 6 ++++++ cli/Cargo.lock | 2 +- cli/Cargo.toml | 2 +- package.json | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-) delete mode 100644 .changeset/release-ccb73f55.md diff --git a/.changeset/release-ccb73f55.md b/.changeset/release-ccb73f55.md deleted file mode 100644 index 09f84d6..0000000 --- a/.changeset/release-ccb73f55.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"agent-browser": patch ---- - -Fixed Windows compatibility issues including proper handling of extended-length path prefixes from canonicalize(), prevention of MSYS/Git Bash path translation that could mangle arguments, and improved daemon startup reliability. Also added ARM64 Windows support in postinstall shims and expanded CI testing with a full daemon lifecycle test on Windows. diff --git a/CHANGELOG.md b/CHANGELOG.md index e39867f..9b63bae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # agent-browser +## 0.15.3 + +### Patch Changes + +- 62241b5: Fixed Windows compatibility issues including proper handling of extended-length path prefixes from canonicalize(), prevention of MSYS/Git Bash path translation that could mangle arguments, and improved daemon startup reliability. Also added ARM64 Windows support in postinstall shims and expanded CI testing with a full daemon lifecycle test on Windows. + ## 0.15.2 ### Patch Changes diff --git a/cli/Cargo.lock b/cli/Cargo.lock index e46d98b..13ec141 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -4,7 +4,7 @@ version = 4 [[package]] name = "agent-browser" -version = "0.15.2" +version = "0.15.3" dependencies = [ "base64", "dirs", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index e168aee..84fb6f1 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "agent-browser" -version = "0.15.2" +version = "0.15.3" edition = "2021" description = "Fast browser automation CLI for AI agents" license = "Apache-2.0" diff --git a/package.json b/package.json index a9547a4..3fca6e4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "agent-browser", - "version": "0.15.2", + "version": "0.15.3", "description": "Headless browser automation CLI for AI agents", "type": "module", "main": "dist/daemon.js", From 51f5fa484c910913fa51bbc8f12d064282926b68 Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Tue, 3 Mar 2026 15:15:57 -0600 Subject: [PATCH 04/12] native (#594) * Native Rust rewrite of agent-browser daemon Single-binary Rust implementation replacing the Node.js/Playwright daemon with direct CDP (Chrome DevTools Protocol) communication. Includes full command parity, WebDriver/Safari/iOS backend routing, request tracking, frame context management, CDP protocol codegen, and comprehensive tests. * improvements * fix ci * fixes * faster builds --- .github/workflows/ci.yml | 47 +- .github/workflows/release.yml | 14 +- AGENTS.md | 51 + README.md | 45 +- cli/Cargo.lock | 2671 +- cli/Cargo.toml | 20 + cli/build.rs | 481 + cli/cdp-protocol/browser_protocol.json | 31262 +++++++++++++++++++++++ cli/cdp-protocol/js_protocol.json | 3810 +++ cli/src/commands.rs | 133 +- cli/src/connection.rs | 159 +- cli/src/flags.rs | 144 +- cli/src/main.rs | 52 +- cli/src/native/actions.rs | 5254 ++++ cli/src/native/auth.rs | 315 + cli/src/native/browser.rs | 1113 + cli/src/native/cdp/chrome.rs | 417 + cli/src/native/cdp/client.rs | 163 + cli/src/native/cdp/mod.rs | 3 + cli/src/native/cdp/types.rs | 537 + cli/src/native/cookies.rs | 87 + cli/src/native/daemon.rs | 266 + cli/src/native/diff.rs | 195 + cli/src/native/e2e_tests.rs | 1295 + cli/src/native/element.rs | 718 + cli/src/native/interaction.rs | 707 + cli/src/native/mod.rs | 45 + cli/src/native/network.rs | 399 + cli/src/native/parity_tests.rs | 625 + cli/src/native/policy.rs | 216 + cli/src/native/providers.rs | 274 + cli/src/native/recording.rs | 203 + cli/src/native/screenshot.rs | 147 + cli/src/native/snapshot.rs | 736 + cli/src/native/state.rs | 607 + cli/src/native/storage.rs | 94 + cli/src/native/stream.rs | 385 + cli/src/native/tracing.rs | 373 + cli/src/native/webdriver/appium.rs | 201 + cli/src/native/webdriver/backend.rs | 142 + cli/src/native/webdriver/client.rs | 318 + cli/src/native/webdriver/ios.rs | 235 + cli/src/native/webdriver/mod.rs | 6 + cli/src/native/webdriver/safari.rs | 80 + cli/src/native/webdriver/types.rs | 97 + cli/src/output.rs | 185 +- cli/src/validation.rs | 5 +- docs/src/app/configuration/page.mdx | 4 +- docs/src/app/native-mode/page.mdx | 85 + docs/src/lib/docs-navigation.ts | 1 + docs/src/lib/page-titles.ts | 1 + skills/agent-browser/SKILL.md | 15 + 52 files changed, 55160 insertions(+), 278 deletions(-) create mode 100644 cli/build.rs create mode 100644 cli/cdp-protocol/browser_protocol.json create mode 100644 cli/cdp-protocol/js_protocol.json create mode 100644 cli/src/native/actions.rs create mode 100644 cli/src/native/auth.rs create mode 100644 cli/src/native/browser.rs create mode 100644 cli/src/native/cdp/chrome.rs create mode 100644 cli/src/native/cdp/client.rs create mode 100644 cli/src/native/cdp/mod.rs create mode 100644 cli/src/native/cdp/types.rs create mode 100644 cli/src/native/cookies.rs create mode 100644 cli/src/native/daemon.rs create mode 100644 cli/src/native/diff.rs create mode 100644 cli/src/native/e2e_tests.rs create mode 100644 cli/src/native/element.rs create mode 100644 cli/src/native/interaction.rs create mode 100644 cli/src/native/mod.rs create mode 100644 cli/src/native/network.rs create mode 100644 cli/src/native/parity_tests.rs create mode 100644 cli/src/native/policy.rs create mode 100644 cli/src/native/providers.rs create mode 100644 cli/src/native/recording.rs create mode 100644 cli/src/native/screenshot.rs create mode 100644 cli/src/native/snapshot.rs create mode 100644 cli/src/native/state.rs create mode 100644 cli/src/native/storage.rs create mode 100644 cli/src/native/stream.rs create mode 100644 cli/src/native/tracing.rs create mode 100644 cli/src/native/webdriver/appium.rs create mode 100644 cli/src/native/webdriver/backend.rs create mode 100644 cli/src/native/webdriver/client.rs create mode 100644 cli/src/native/webdriver/ios.rs create mode 100644 cli/src/native/webdriver/mod.rs create mode 100644 cli/src/native/webdriver/safari.rs create mode 100644 cli/src/native/webdriver/types.rs create mode 100644 docs/src/app/native-mode/page.mdx diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 33eb95a..095327b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -78,24 +78,13 @@ jobs: with: targets: ${{ matrix.target }} - - name: Cache Cargo dependencies - uses: actions/cache@v4 + - name: Cache Rust build artifacts + uses: Swatinem/rust-cache@v2 with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - cli/target/ - key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ hashFiles('cli/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-cargo-${{ matrix.target }}- - - - name: Build release binary - run: cargo build --release --manifest-path cli/Cargo.toml --target ${{ matrix.target }} + workspaces: cli - name: Run Rust tests - run: cargo test --manifest-path cli/Cargo.toml --target ${{ matrix.target }} + run: cargo test --profile ci --manifest-path cli/Cargo.toml --target ${{ matrix.target }} windows-integration: name: Windows Integration Test @@ -122,18 +111,10 @@ jobs: with: targets: x86_64-pc-windows-msvc - - name: Cache Cargo dependencies - uses: actions/cache@v4 + - name: Cache Rust build artifacts + uses: Swatinem/rust-cache@v2 with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - cli/target/ - key: windows-cargo-x86_64-pc-windows-msvc-${{ hashFiles('cli/Cargo.lock') }} - restore-keys: | - windows-cargo-x86_64-pc-windows-msvc- + workspaces: cli - name: Build Rust CLI run: cargo build --release --manifest-path cli/Cargo.toml --target x86_64-pc-windows-msvc @@ -258,18 +239,10 @@ jobs: with: targets: ${{ matrix.target }} - - name: Cache Cargo dependencies - uses: actions/cache@v4 + - name: Cache Rust build artifacts + uses: Swatinem/rust-cache@v2 with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - cli/target/ - key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ hashFiles('cli/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-cargo-${{ matrix.target }}- + workspaces: cli - name: Build Rust CLI run: cargo build --release --manifest-path cli/Cargo.toml --target ${{ matrix.target }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b3b9721..0e9159f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -97,18 +97,10 @@ jobs: linker = "x86_64-w64-mingw32-gcc" EOF - - name: Cache Cargo dependencies - uses: actions/cache@v4 + - name: Cache Rust build artifacts + uses: Swatinem/rust-cache@v2 with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - cli/target/ - key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ hashFiles('cli/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-cargo-${{ matrix.target }}- + workspaces: cli - name: Build with zigbuild if: matrix.use_zigbuild diff --git a/AGENTS.md b/AGENTS.md index 50bfdde..6ec3399 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -26,6 +26,57 @@ This applies to changes that either human users or AI agents would need to know In the `docs/src/app/` MDX files, always use HTML `` syntax for tables (not markdown pipe tables). This matches the existing convention across the docs site. +## Dual Architecture (Node.js + Native) + +The codebase has two daemon implementations: + +- **Node.js/Playwright** (default) -- `src/daemon.ts`, `src/actions.ts`, `src/browser.ts`, and the rest of `src/` +- **Rust/Native** (experimental, `--native` or `AGENT_BROWSER_NATIVE=1`) -- `cli/src/native/daemon.rs`, `cli/src/native/actions.rs`, `cli/src/native/browser.rs`, and the rest of `cli/src/native/` + +When modifying browser automation logic (commands, actions, protocol handling), changes **must** be made in **both** paths: + +| Node.js Path | Native Path | +|---|---| +| `src/actions.ts` | `cli/src/native/actions.rs` | +| `src/browser.ts` | `cli/src/native/browser.rs` | +| `src/daemon.ts` | `cli/src/native/daemon.rs` | +| `src/protocol.ts` | `cli/src/native/cdp/client.rs` | +| `src/snapshot.ts` | `cli/src/native/snapshot.rs` | +| `src/state-utils.ts` | `cli/src/native/state.rs` | + +New commands must be implemented in both paths, or explicitly stubbed in the native path with a clear `"Not yet implemented: {action}"` error. The goal is eventual full migration to native, but until then both paths must stay in sync. + +## Testing + +### Unit Tests + +```bash +cd cli && cargo test +``` + +Runs all unit tests (~320 tests). These are fast and don't require Chrome. + +### End-to-End Tests + +```bash +cd cli && cargo test e2e -- --ignored --test-threads=1 +``` + +Runs 18 e2e tests that launch real headless Chrome instances and exercise the full native daemon command pipeline. Requirements: + +- Chrome must be installed +- Must run serially (`--test-threads=1`) to avoid Chrome instance contention +- Tests are `#[ignore]`'d so they don't run during normal `cargo test` + +The e2e tests live in `cli/src/native/e2e_tests.rs` and cover: launch/close, navigation, snapshots, screenshots, form interaction, cookies, storage, tabs, element queries, viewport/emulation, domain filtering, diff, state management, error handling, and Phase 8 commands. + +### Linting and Formatting + +```bash +cd cli && cargo fmt -- --check # Check formatting +cd cli && cargo clippy # Lint +``` + ## Source Code Reference diff --git a/README.md b/README.md index 662384a..8c8cb67 100644 --- a/README.md +++ b/README.md @@ -495,6 +495,7 @@ This is useful for multimodal AI models that can reason about visual layout, unl | `--action-policy ` | Path to action policy JSON file (or `AGENT_BROWSER_ACTION_POLICY` env) | | `--confirm-actions ` | Action categories requiring confirmation (or `AGENT_BROWSER_CONFIRM_ACTIONS` env) | | `--confirm-interactive` | Interactive confirmation prompts; auto-denies if stdin is not a TTY (or `AGENT_BROWSER_CONFIRM_INTERACTIVE` env) | +| `--native` | [Experimental] Use native Rust daemon instead of Node.js (or `AGENT_BROWSER_NATIVE` env) | | `--config ` | Use a custom config file (or `AGENT_BROWSER_CONFIG` env) | | `--debug` | Debug output | @@ -910,12 +911,50 @@ await browser.stopScreencast(); agent-browser uses a client-daemon architecture: 1. **Rust CLI** (fast native binary) - Parses commands, communicates with daemon -2. **Node.js Daemon** - Manages Playwright browser instance -3. **Fallback** - If native binary unavailable, uses Node.js directly +2. **Node.js Daemon** (default) - Manages Playwright browser instance +3. **Native Daemon** (experimental, `--native`) - Pure Rust daemon using direct CDP, no Node.js required +4. **Fallback** - If native binary unavailable, uses Node.js directly The daemon starts automatically on first command and persists between commands for fast subsequent operations. -**Browser Engine:** Uses Chromium by default. The daemon also supports Firefox and WebKit via the Playwright protocol. +**Browser Engine:** Uses Chromium by default. The default Node.js daemon also supports Firefox and WebKit via Playwright. The experimental native daemon speaks Chrome DevTools Protocol (CDP) directly and supports Chromium-based browsers and Safari (via WebDriver). + +## Experimental: Native Mode + +The native daemon is a pure Rust implementation that communicates with Chrome directly via CDP, eliminating the Node.js and Playwright dependencies. It is currently **experimental** and opt-in. + +### Enabling Native Mode + +```bash +# Via flag +agent-browser --native open example.com + +# Via environment variable (recommended for persistent use) +export AGENT_BROWSER_NATIVE=1 +agent-browser open example.com +``` + +Or add to your config file (`agent-browser.json`): + +```json +{"native": true} +``` + +### What's Different + +| | Default (Node.js) | Native (`--native`) | +|---|---|---| +| **Runtime** | Node.js + Playwright | Pure Rust binary | +| **Protocol** | Playwright protocol | Direct CDP / WebDriver | +| **Install size** | Larger (Node.js + npm deps) | Smaller (single binary) | +| **Browser support** | Chromium, Firefox, WebKit | Chromium, Safari (via WebDriver) | +| **Stability** | Stable | Experimental | + +### Known Limitations + +- Firefox and WebKit are not yet supported (Chromium and Safari only) +- Some Playwright-specific features (tracing format, HAR export) are not available +- The native daemon and Node.js daemon share the same session socket, so you cannot run both simultaneously for the same session. Use `agent-browser close` before switching modes. ## Platforms diff --git a/cli/Cargo.lock b/cli/Cargo.lock index 13ec141..2b31ed8 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -2,19 +2,193 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "agent-browser" version = "0.15.3" dependencies = [ + "aes-gcm", + "async-trait", "base64", "dirs", - "getrandom", + "futures-util", + "getrandom 0.2.17", + "image", "libc", + "reqwest", "serde", "serde_json", + "sha2", + "similar", + "tokio", + "tokio-tungstenite", + "url", + "uuid", "windows-sys 0.52.0", ] +[[package]] +name = "aligned" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685" +dependencies = [ + "as-slice", +] + +[[package]] +name = "aligned-vec" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" +dependencies = [ + "equator", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" + +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "as-slice" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "av-scenechange" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394" +dependencies = [ + "aligned", + "anyhow", + "arg_enum_proc_macro", + "arrayvec", + "log", + "num-rational", + "num-traits", + "pastey", + "rayon", + "thiserror 2.0.18", + "v_frame", + "y4m", +] + +[[package]] +name = "av1-grain" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cfddb07216410377231960af4fcab838eaa12e013417781b78bd95ee22077f8" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375082f007bd67184fb9c0374614b29f9aaa604ec301635f72338bb65386a53d" +dependencies = [ + "arrayvec", +] + [[package]] name = "base64" version = "0.22.1" @@ -22,10 +196,82 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] -name = "bitflags" -version = "2.10.0" +name = "bit_field" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "bitstream-io" +version = "4.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757" +dependencies = [ + "core2", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "built" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64" + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cc" +version = "1.2.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] [[package]] name = "cfg-if" @@ -33,6 +279,142 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "data-encoding" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "dirs" version = "5.0.1" @@ -54,6 +436,232 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equator" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" +dependencies = [ + "equator-macro", +] + +[[package]] +name = "equator-macro" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "exr" +version = "1.74.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be" +dependencies = [ + "bit_field", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fax" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" +dependencies = [ + "fax_derive", +] + +[[package]] +name = "fax_derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-macro", + "futures-sink", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.17" @@ -65,18 +673,479 @@ dependencies = [ "wasi", ] +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "gif" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5df2ba84018d80c213569363bdcd0c64e6933c67fe4c1d60ecf822971a3c35e" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "h2" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", +] + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "image" +version = "0.25.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a" +dependencies = [ + "bytemuck", + "byteorder-lite", + "color_quant", + "exr", + "gif", + "image-webp", + "moxcms", + "num-traits", + "png", + "qoi", + "ravif", + "rayon", + "rgb", + "tiff", + "zune-core 0.5.1", + "zune-jpeg 0.5.12", +] + +[[package]] +name = "image-webp" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "imgref" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8" + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "iri-string" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "lebe" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" + [[package]] name = "libc" version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +[[package]] +name = "libfuzzer-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d" +dependencies = [ + "arbitrary", + "cc", +] + [[package]] name = "libredox" version = "0.1.12" @@ -87,18 +1156,325 @@ dependencies = [ "libc", ] +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", +] + [[package]] name = "memchr" version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "moxcms" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97" +dependencies = [ + "num-traits", + "pxfm", +] + +[[package]] +name = "native-tls" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "openssl" +version = "0.10.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "openssl-sys" +version = "0.9.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "option-ext" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pastey" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.105" @@ -108,6 +1484,46 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "profiling" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "pxfm" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a041e753da8b807c9255f28de81879c78c876392ff2469cde94799b2896b9d" + +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quote" version = "1.0.43" @@ -117,17 +1533,308 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rav1e" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b" +dependencies = [ + "aligned-vec", + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av-scenechange", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "paste", + "profiling", + "rand 0.9.2", + "rand_chacha 0.9.0", + "simd_helpers", + "thiserror 2.0.18", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef69c1990ceef18a116855938e74793a5f7496ee907562bd0857b6ac734ab285" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_users" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom", + "getrandom 0.2.17", "libredox", - "thiserror", + "thiserror 1.0.69", ] +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "js-sys", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "rgb" +version = "0.8.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustix" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "schannel" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + [[package]] name = "serde" version = "1.0.228" @@ -171,6 +1878,111 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + +[[package]] +name = "similar" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "2.0.114" @@ -182,13 +1994,76 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1" +dependencies = [ + "fastrand", + "getrandom 0.4.1", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + [[package]] name = "thiserror" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", ] [[package]] @@ -202,18 +2077,473 @@ dependencies = [ "syn", ] +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tiff" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af9605de7fee8d9551863fd692cce7637f548dbd9db9180fcc07ccc6d26c336f" +dependencies = [ + "fax", + "flate2", + "half", + "quick-error", + "weezl", + "zune-jpeg 0.4.21", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.49.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" +dependencies = [ + "futures-util", + "log", + "native-tls", + "tokio", + "tokio-native-tls", + "tungstenite", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "native-tls", + "rand 0.8.5", + "sha1", + "thiserror 1.0.69", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + [[package]] name = "unicode-ident" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb" +dependencies = [ + "getrandom 0.4.1", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "v_frame" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" +dependencies = [ + "cfg-if", + "futures-util", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "web-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "weezl" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -232,6 +2562,24 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -256,13 +2604,30 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -275,6 +2640,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -287,6 +2658,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -299,12 +2676,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -317,6 +2706,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -329,6 +2724,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -341,6 +2742,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -353,8 +2760,256 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "y4m" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zmij" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fc5a66a20078bf1251bde995aa2fdcc4b800c70b5d92dd2c62abc5c60f679f8" + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zune-jpeg" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" +dependencies = [ + "zune-core 0.4.12", +] + +[[package]] +name = "zune-jpeg" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "410e9ecef634c709e3831c2cfdb8d9c32164fae1c67496d5b68fff728eec37fe" +dependencies = [ + "zune-core 0.5.1", +] diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 84fb6f1..7d14dd5 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -11,6 +11,17 @@ serde_json = "1.0" dirs = "5.0" base64 = "0.22" getrandom = "0.2" +tokio = { version = "1", features = ["rt-multi-thread", "macros", "net", "io-util", "time", "sync", "signal"] } +tokio-tungstenite = { version = "0.24", features = ["native-tls"] } +futures-util = "0.3" +url = "2" +uuid = { version = "1", features = ["v4"] } +image = "0.25" +reqwest = { version = "0.12", features = ["json"] } +sha2 = "0.10" +aes-gcm = "0.10" +async-trait = "0.1" +similar = "2" [target.'cfg(unix)'.dependencies] libc = "0.2" @@ -18,8 +29,17 @@ libc = "0.2" [target.'cfg(windows)'.dependencies] windows-sys = { version = "0.52", features = ["Win32_System_Threading", "Win32_Foundation"] } +[build-dependencies] +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" + [profile.release] opt-level = 3 lto = true codegen-units = 1 strip = true + +[profile.ci] +inherits = "release" +lto = "thin" +codegen-units = 16 diff --git a/cli/build.rs b/cli/build.rs new file mode 100644 index 0000000..c3b06dc --- /dev/null +++ b/cli/build.rs @@ -0,0 +1,481 @@ +use std::collections::HashSet; +use std::env; +use std::fs; +use std::path::Path; + +fn main() { + let protocol_dir = Path::new("cdp-protocol"); + let out_dir = env::var("OUT_DIR").unwrap(); + let out_path = Path::new(&out_dir).join("cdp_generated.rs"); + + let browser_path = protocol_dir.join("browser_protocol.json"); + let js_path = protocol_dir.join("js_protocol.json"); + + if !browser_path.exists() && !js_path.exists() { + fs::write( + &out_path, + "// No protocol JSON files found in cdp-protocol/\n", + ) + .unwrap(); + return; + } + + let mut all_domains: Vec = Vec::new(); + + for path in [&browser_path, &js_path] { + if !path.exists() { + continue; + } + println!("cargo:rerun-if-changed={}", path.display()); + let content = fs::read_to_string(path).unwrap(); + let protocol: ProtocolSpec = match serde_json::from_str(&content) { + Ok(p) => p, + Err(e) => { + eprintln!("cargo:warning=Failed to parse {}: {}", path.display(), e); + continue; + } + }; + all_domains.extend(protocol.domains); + } + + // Collect all known type IDs per domain for cross-domain resolution + let mut domain_types: std::collections::HashMap> = + std::collections::HashMap::new(); + for domain in &all_domains { + let mut types = HashSet::new(); + for td in &domain.types { + types.insert(td.id.clone()); + } + domain_types.insert(domain.domain.clone(), types); + } + + // Known recursive struct fields that need Box wrapping + let recursive_fields: HashSet<(&str, &str, &str)> = [ + ("DOM", "Node", "contentDocument"), + ("DOM", "Node", "templateContent"), + ("DOM", "Node", "importedDocument"), + ("Accessibility", "AXNode", "sources"), + ("Runtime", "StackTrace", "parent"), + ] + .into_iter() + .collect(); + + let mut output = String::new(); + output.push_str("use serde::{Deserialize, Serialize};\n\n"); + + for domain in &all_domains { + generate_domain(domain, &domain_types, &recursive_fields, &mut output); + } + + fs::write(&out_path, &output).unwrap(); +} + +#[allow(dead_code)] +#[derive(serde::Deserialize)] +struct ProtocolSpec { + domains: Vec, +} + +#[allow(dead_code)] +#[derive(serde::Deserialize, Clone)] +struct Domain { + domain: String, + #[serde(default)] + types: Vec, + #[serde(default)] + commands: Vec, + #[serde(default)] + events: Vec, +} + +#[allow(dead_code)] +#[derive(serde::Deserialize, Clone)] +struct TypeDef { + id: String, + #[serde(rename = "type", default)] + type_kind: String, + #[serde(default)] + properties: Vec, + #[serde(rename = "enum", default)] + enum_values: Vec, + #[serde(default)] + description: Option, +} + +#[allow(dead_code)] +#[derive(serde::Deserialize, Clone)] +struct Command { + name: String, + #[serde(default)] + parameters: Vec, + #[serde(default)] + returns: Vec, + #[serde(default)] + description: Option, +} + +#[allow(dead_code)] +#[derive(serde::Deserialize, Clone)] +struct Event { + name: String, + #[serde(default)] + parameters: Vec, + #[serde(default)] + description: Option, +} + +#[allow(dead_code)] +#[derive(serde::Deserialize, Clone)] +struct Property { + name: String, + #[serde(rename = "type", default)] + type_kind: Option, + #[serde(rename = "$ref", default)] + ref_type: Option, + #[serde(default)] + optional: bool, + #[serde(default)] + description: Option, + #[serde(default)] + items: Option>, + #[serde(rename = "enum", default)] + enum_values: Vec, +} + +#[allow(dead_code)] +#[derive(serde::Deserialize, Clone)] +struct ItemType { + #[serde(rename = "type", default)] + type_kind: Option, + #[serde(rename = "$ref", default)] + ref_type: Option, +} + +fn to_pascal_case(s: &str) -> String { + let mut result = String::new(); + let mut capitalize = true; + for c in s.chars() { + if c == '_' || c == '-' || c == '.' { + capitalize = true; + } else if capitalize { + result.push(c.to_ascii_uppercase()); + capitalize = false; + } else { + result.push(c); + } + } + result +} + +fn to_snake_case(s: &str) -> String { + let mut result = String::new(); + let chars: Vec = s.chars().collect(); + for (i, &c) in chars.iter().enumerate() { + if c.is_uppercase() && i > 0 { + // Only insert underscore at transitions from lowercase to uppercase, + // or when an uppercase sequence ends (e.g. "DOM" -> "dom", not "d_o_m") + let prev_upper = chars[i - 1].is_uppercase(); + let next_lower = chars.get(i + 1).map_or(false, |n| n.is_lowercase()); + if !prev_upper || next_lower { + result.push('_'); + } + } + result.push(c.to_ascii_lowercase()); + } + result +} + +/// Resolve a $ref type reference. Cross-domain refs like "Page.FrameId" become +/// `super::cdp_page::FrameId`. Same-domain refs are used directly. +fn resolve_ref( + r: &str, + current_domain: &str, + domain_types: &std::collections::HashMap>, +) -> String { + let parts: Vec<&str> = r.split('.').collect(); + if parts.len() == 2 { + let ref_domain = parts[0]; + let ref_type = parts[1]; + if ref_domain == current_domain { + to_pascal_case(ref_type) + } else { + // Check if this type actually exists in the referenced domain + if domain_types + .get(ref_domain) + .map_or(false, |t| t.contains(ref_type)) + { + format!( + "super::cdp_{}::{}", + to_snake_case(ref_domain), + to_pascal_case(ref_type) + ) + } else { + // Fall back to serde_json::Value for unknown cross-domain refs + "serde_json::Value".to_string() + } + } + } else { + to_pascal_case(r) + } +} + +fn map_type_in_domain( + prop: &Property, + current_domain: &str, + domain_types: &std::collections::HashMap>, +) -> String { + if let Some(ref r) = prop.ref_type { + let type_name = resolve_ref(r, current_domain, domain_types); + if prop.optional { + format!("Option<{}>", type_name) + } else { + type_name + } + } else if let Some(ref t) = prop.type_kind { + let base = match t.as_str() { + "string" => "String".to_string(), + "integer" => "i64".to_string(), + "number" => "f64".to_string(), + "boolean" => "bool".to_string(), + "object" => "serde_json::Value".to_string(), + "any" => "serde_json::Value".to_string(), + "array" => { + if let Some(ref items) = prop.items { + let inner = if let Some(ref r) = items.ref_type { + resolve_ref(r, current_domain, domain_types) + } else { + match items.type_kind.as_deref().unwrap_or("any") { + "string" => "String".to_string(), + "integer" => "i64".to_string(), + "number" => "f64".to_string(), + "boolean" => "bool".to_string(), + _ => "serde_json::Value".to_string(), + } + }; + format!("Vec<{}>", inner) + } else { + "Vec".to_string() + } + } + _ => "serde_json::Value".to_string(), + }; + if prop.optional { + format!("Option<{}>", base) + } else { + base + } + } else if prop.optional { + "Option".to_string() + } else { + "serde_json::Value".to_string() + } +} + +fn is_rust_keyword(s: &str) -> bool { + matches!( + s, + "type" + | "self" + | "Self" + | "super" + | "move" + | "ref" + | "fn" + | "mod" + | "use" + | "pub" + | "let" + | "mut" + | "const" + | "static" + | "if" + | "else" + | "for" + | "while" + | "loop" + | "match" + | "return" + | "break" + | "continue" + | "as" + | "in" + | "impl" + | "trait" + | "struct" + | "enum" + | "where" + | "async" + | "await" + | "dyn" + | "box" + | "yield" + | "override" + | "crate" + | "extern" + ) +} + +fn generate_domain( + domain: &Domain, + domain_types: &std::collections::HashMap>, + recursive_fields: &HashSet<(&str, &str, &str)>, + output: &mut String, +) { + let mod_name = to_snake_case(&domain.domain); + output.push_str(&format!( + "#[allow(dead_code, non_snake_case, non_camel_case_types, clippy::enum_variant_names)]\npub mod cdp_{} {{\n", + mod_name + )); + output.push_str(" use super::*;\n\n"); + + for type_def in &domain.types { + if !type_def.enum_values.is_empty() { + // Deduplicate enum variants (some CDP enums have duplicated PascalCase forms) + let mut seen_variants = HashSet::new(); + output.push_str(" #[derive(Debug, Clone, Serialize, Deserialize)]\n"); + output.push_str(&format!(" pub enum {} {{\n", type_def.id)); + for val in &type_def.enum_values { + let mut variant = to_pascal_case(val); + if variant == "Self" { + variant = "SelfValue".to_string(); + } + if variant.chars().next().map_or(false, |c| c.is_ascii_digit()) { + variant = format!("V{}", variant); + } + if seen_variants.insert(variant.clone()) { + output.push_str(&format!( + " #[serde(rename = \"{}\")]\n {},\n", + val, variant + )); + } + } + output.push_str(" }\n\n"); + } else if type_def.type_kind == "object" && !type_def.properties.is_empty() { + output.push_str( + " #[derive(Debug, Clone, Serialize, Deserialize)]\n #[serde(rename_all = \"camelCase\")]\n", + ); + output.push_str(&format!(" pub struct {} {{\n", type_def.id)); + for prop in &type_def.properties { + let field_name = to_snake_case(&prop.name); + let field_name = if is_rust_keyword(&field_name) { + format!("r#{}", field_name) + } else { + field_name + }; + let mut rust_type = map_type_in_domain(prop, &domain.domain, domain_types); + + // Wrap recursive fields in Box + if recursive_fields.contains(&( + domain.domain.as_str(), + type_def.id.as_str(), + prop.name.as_str(), + )) { + if rust_type.starts_with("Option<") { + let inner = &rust_type[7..rust_type.len() - 1]; + rust_type = format!("Option>", inner); + } else { + rust_type = format!("Box<{}>", rust_type); + } + } + + if prop.optional { + output + .push_str(" #[serde(skip_serializing_if = \"Option::is_none\")]\n"); + } + output.push_str(&format!(" pub {}: {},\n", field_name, rust_type)); + } + output.push_str(" }\n\n"); + } else if type_def.type_kind == "object" && type_def.properties.is_empty() { + output.push_str(&format!( + " pub type {} = serde_json::Value;\n\n", + type_def.id + )); + } else if type_def.type_kind == "array" { + output.push_str(&format!( + " pub type {} = Vec;\n\n", + type_def.id + )); + } else if type_def.type_kind == "string" && type_def.enum_values.is_empty() { + output.push_str(&format!(" pub type {} = String;\n\n", type_def.id)); + } else if type_def.type_kind == "integer" { + output.push_str(&format!(" pub type {} = i64;\n\n", type_def.id)); + } else if type_def.type_kind == "number" { + output.push_str(&format!(" pub type {} = f64;\n\n", type_def.id)); + } + } + + for cmd in &domain.commands { + let pascal_name = to_pascal_case(&cmd.name); + + if !cmd.parameters.is_empty() { + output.push_str( + " #[derive(Debug, Clone, Serialize, Deserialize)]\n #[serde(rename_all = \"camelCase\")]\n", + ); + output.push_str(&format!(" pub struct {}Params {{\n", pascal_name)); + for param in &cmd.parameters { + let field_name = to_snake_case(¶m.name); + let field_name = if is_rust_keyword(&field_name) { + format!("r#{}", field_name) + } else { + field_name + }; + let rust_type = map_type_in_domain(param, &domain.domain, domain_types); + if param.optional { + output + .push_str(" #[serde(skip_serializing_if = \"Option::is_none\")]\n"); + } + output.push_str(&format!(" pub {}: {},\n", field_name, rust_type)); + } + output.push_str(" }\n\n"); + } + + if !cmd.returns.is_empty() { + output.push_str( + " #[derive(Debug, Clone, Serialize, Deserialize)]\n #[serde(rename_all = \"camelCase\")]\n", + ); + output.push_str(&format!(" pub struct {}Result {{\n", pascal_name)); + for ret in &cmd.returns { + let field_name = to_snake_case(&ret.name); + let field_name = if is_rust_keyword(&field_name) { + format!("r#{}", field_name) + } else { + field_name + }; + let rust_type = map_type_in_domain(ret, &domain.domain, domain_types); + if ret.optional { + output + .push_str(" #[serde(skip_serializing_if = \"Option::is_none\")]\n"); + } + output.push_str(&format!(" pub {}: {},\n", field_name, rust_type)); + } + output.push_str(" }\n\n"); + } + } + + for event in &domain.events { + if !event.parameters.is_empty() { + let pascal_name = to_pascal_case(&event.name); + output.push_str( + " #[derive(Debug, Clone, Serialize, Deserialize)]\n #[serde(rename_all = \"camelCase\")]\n", + ); + output.push_str(&format!(" pub struct {}Event {{\n", pascal_name)); + for param in &event.parameters { + let field_name = to_snake_case(¶m.name); + let field_name = if is_rust_keyword(&field_name) { + format!("r#{}", field_name) + } else { + field_name + }; + let rust_type = map_type_in_domain(param, &domain.domain, domain_types); + if param.optional { + output + .push_str(" #[serde(skip_serializing_if = \"Option::is_none\")]\n"); + } + output.push_str(&format!(" pub {}: {},\n", field_name, rust_type)); + } + output.push_str(" }\n\n"); + } + } + + output.push_str("}\n\n"); +} diff --git a/cli/cdp-protocol/browser_protocol.json b/cli/cdp-protocol/browser_protocol.json new file mode 100644 index 0000000..0ff2f79 --- /dev/null +++ b/cli/cdp-protocol/browser_protocol.json @@ -0,0 +1,31262 @@ +{ + "version": { + "major": "1", + "minor": "3" + }, + "domains": [ + { + "domain": "Accessibility", + "experimental": true, + "dependencies": [ + "DOM" + ], + "types": [ + { + "id": "AXNodeId", + "description": "Unique accessibility node identifier.", + "type": "string" + }, + { + "id": "AXValueType", + "description": "Enum of possible property types.", + "type": "string", + "enum": [ + "boolean", + "tristate", + "booleanOrUndefined", + "idref", + "idrefList", + "integer", + "node", + "nodeList", + "number", + "string", + "computedString", + "token", + "tokenList", + "domRelation", + "role", + "internalRole", + "valueUndefined" + ] + }, + { + "id": "AXValueSourceType", + "description": "Enum of possible property sources.", + "type": "string", + "enum": [ + "attribute", + "implicit", + "style", + "contents", + "placeholder", + "relatedElement" + ] + }, + { + "id": "AXValueNativeSourceType", + "description": "Enum of possible native property sources (as a subtype of a particular AXValueSourceType).", + "type": "string", + "enum": [ + "description", + "figcaption", + "label", + "labelfor", + "labelwrapped", + "legend", + "rubyannotation", + "tablecaption", + "title", + "other" + ] + }, + { + "id": "AXValueSource", + "description": "A single source for a computed AX property.", + "type": "object", + "properties": [ + { + "name": "type", + "description": "What type of source this is.", + "$ref": "AXValueSourceType" + }, + { + "name": "value", + "description": "The value of this property source.", + "optional": true, + "$ref": "AXValue" + }, + { + "name": "attribute", + "description": "The name of the relevant attribute, if any.", + "optional": true, + "type": "string" + }, + { + "name": "attributeValue", + "description": "The value of the relevant attribute, if any.", + "optional": true, + "$ref": "AXValue" + }, + { + "name": "superseded", + "description": "Whether this source is superseded by a higher priority source.", + "optional": true, + "type": "boolean" + }, + { + "name": "nativeSource", + "description": "The native markup source for this value, e.g. a ` +
actionPolicy--action-policystring
confirmActions--confirm-actionsstring
confirmInteractive--confirm-interactiveboolean
native--nativeboolean (experimental)
headers--headersstring (JSON)
@@ -149,7 +150,7 @@ agent-browser --headed open example.com # same as --headed true agent-browser --headed true open example.com # explicit ``` -This applies to all boolean flags: `--headed`, `--debug`, `--json`, `--ignore-https-errors`, `--allow-file-access`, `--auto-connect`, `--content-boundaries`, `--confirm-interactive`. +This applies to all boolean flags: `--headed`, `--debug`, `--json`, `--ignore-https-errors`, `--allow-file-access`, `--auto-connect`, `--content-boundaries`, `--confirm-interactive`, `--native`. ## Extensions Merging @@ -184,6 +185,7 @@ These environment variables configure additional daemon and runtime behavior: AGENT_BROWSER_ACTION_POLICYPath to action policy JSON file.(none) AGENT_BROWSER_CONFIRM_ACTIONSComma-separated action categories requiring confirmation.(none) AGENT_BROWSER_CONFIRM_INTERACTIVEEnable interactive confirmation prompts (auto-denies if stdin is not a TTY).(disabled) + AGENT_BROWSER_NATIVEUse the experimental native Rust daemon instead of Node.js/Playwright.(disabled) diff --git a/docs/src/app/native-mode/page.mdx b/docs/src/app/native-mode/page.mdx new file mode 100644 index 0000000..d74ff7a --- /dev/null +++ b/docs/src/app/native-mode/page.mdx @@ -0,0 +1,85 @@ +import { pageMetadata } from "@/lib/page-metadata" + +export const metadata = pageMetadata("native-mode") + +# Native Mode (Experimental) + +agent-browser includes an experimental native Rust daemon that communicates with Chrome directly via the Chrome DevTools Protocol (CDP), eliminating the Node.js and Playwright dependencies entirely. + +## Enabling Native Mode + +Native mode is opt-in. Enable it with the `--native` flag or the `AGENT_BROWSER_NATIVE` environment variable. + +### CLI Flag + +```bash +agent-browser --native open example.com +agent-browser --native snapshot +agent-browser --native close +``` + +### Environment Variable + +Set `AGENT_BROWSER_NATIVE=1` to avoid passing the flag on every command: + +```bash +export AGENT_BROWSER_NATIVE=1 +agent-browser open example.com +agent-browser snapshot +agent-browser close +``` + +### Config File + +Add `"native": true` to your `agent-browser.json`: + +```json +{"native": true} +``` + +## Architecture Comparison + + + + + + + + + + + + +
Default (Node.js)Native (--native)
RuntimeNode.js + PlaywrightPure Rust binary
ProtocolPlaywright protocolDirect CDP / WebDriver
Install sizeLarger (Node.js + npm deps)Smaller (single binary)
Browser supportChromium, Firefox, WebKitChromium, Safari (via WebDriver)
StabilityStableExperimental
+ +## What Works + +All core commands are supported in native mode: + +- Navigation: `open`, `back`, `forward`, `reload` +- Interaction: `click`, `fill`, `type`, `press`, `hover`, `select`, `check`, `uncheck`, `scroll`, `focus`, `clear`, `upload`, `drag` +- Observation: `snapshot`, `screenshot`, `eval`, `get text/html/value/attr/count/box/styles`, `is visible/enabled/checked` +- State: `cookies get/set/clear`, `storage local/session`, `state save/load/list` +- Tabs: `tab new/list/close`, tab switching +- Emulation: `set viewport`, `set device`, `set geo`, user agent, timezone, locale +- Streaming: WebSocket screencast and remote input +- Diffing: `diff snapshot`, `diff url` +- Recording: `record start/stop` +- Profiling: `profiler start/stop`, `trace start/stop` + +## Known Limitations + +- **Firefox and WebKit** are not yet supported (Chromium and Safari only) +- **Playwright trace format** is not available (native tracing uses Chrome's built-in tracing) +- **HAR export** is not available +- **Network route interception** uses CDP Fetch domain instead of Playwright's route API + +## Switching Between Modes + +The native daemon and Node.js daemon share the same session socket. You cannot run both simultaneously for the same session. Close the current daemon before switching: + +```bash +agent-browser close +export AGENT_BROWSER_NATIVE=1 +agent-browser open example.com +``` diff --git a/docs/src/lib/docs-navigation.ts b/docs/src/lib/docs-navigation.ts index ee16b22..ca65766 100644 --- a/docs/src/lib/docs-navigation.ts +++ b/docs/src/lib/docs-navigation.ts @@ -37,6 +37,7 @@ export const navigation: NavSection[] = [ { name: "Profiler", href: "/profiler" }, { name: "iOS Simulator", href: "/ios" }, { name: "Security", href: "/security" }, + { name: "Native Mode (Experimental)", href: "/native-mode" }, ], }, { diff --git a/docs/src/lib/page-titles.ts b/docs/src/lib/page-titles.ts index e4b21d7..f9a4602 100644 --- a/docs/src/lib/page-titles.ts +++ b/docs/src/lib/page-titles.ts @@ -14,6 +14,7 @@ export const PAGE_TITLES: Record = { profiler: "Profiler", ios: "iOS Simulator", security: "Security", + "native-mode": "Native Mode (Experimental)", changelog: "Changelog", }; diff --git a/skills/agent-browser/SKILL.md b/skills/agent-browser/SKILL.md index 9f6e9be..0153ce4 100644 --- a/skills/agent-browser/SKILL.md +++ b/skills/agent-browser/SKILL.md @@ -485,6 +485,21 @@ Priority (lowest to highest): `~/.agent-browser/config.json` < `./agent-browser. | [references/profiling.md](references/profiling.md) | Chrome DevTools profiling for performance analysis | | [references/proxy-support.md](references/proxy-support.md) | Proxy configuration, geo-testing, rotating proxies | +## Experimental: Native Mode + +agent-browser has an experimental native Rust daemon that communicates with Chrome directly via CDP, bypassing Node.js and Playwright entirely. It is opt-in and not recommended for production use yet. + +```bash +# Enable via flag +agent-browser --native open example.com + +# Enable via environment variable (avoids passing --native every time) +export AGENT_BROWSER_NATIVE=1 +agent-browser open example.com +``` + +The native daemon supports Chromium and Safari (via WebDriver). Firefox and WebKit are not yet supported. All core commands (navigate, snapshot, click, fill, screenshot, cookies, storage, tabs, eval, etc.) work identically in native mode. Use `agent-browser close` before switching between native and default mode within the same session. + ## Ready-to-Use Templates | Template | Description | From 9d0454d229344caff01781153bb93c2782384ee2 Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Tue, 3 Mar 2026 15:45:20 -0600 Subject: [PATCH 05/12] fix: switch from native-tls to rustls for cross-compilation (#595) The native PR introduced tokio-tungstenite and reqwest with native-tls, which depends on openssl-sys (C library). This breaks the release workflow's cargo-zigbuild cross-compilation on Linux because zig's C compiler can't find the system OpenSSL headers. Switch to rustls (pure Rust TLS) which has zero C dependencies and cross-compiles trivially. Also shrinks the dependency tree. --- cli/Cargo.lock | 452 ++++++++++++++----------------------------------- cli/Cargo.toml | 4 +- 2 files changed, 132 insertions(+), 324 deletions(-) diff --git a/cli/Cargo.lock b/cli/Cargo.lock index 2b31ed8..be27f0e 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -279,6 +279,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "cipher" version = "0.4.4" @@ -295,32 +301,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - [[package]] name = "core2" version = "0.4.0" @@ -453,15 +433,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - [[package]] name = "equator" version = "0.4.2" @@ -513,12 +484,6 @@ dependencies = [ "zune-inflate", ] -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - [[package]] name = "fax" version = "0.2.6" @@ -564,33 +529,12 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "foldhash" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" version = "1.2.2" @@ -669,8 +613,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -680,9 +626,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasip2", + "wasm-bindgen", ] [[package]] @@ -718,25 +666,6 @@ dependencies = [ "weezl", ] -[[package]] -name = "h2" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "half" version = "2.7.1" @@ -818,7 +747,6 @@ dependencies = [ "bytes", "futures-channel", "futures-core", - "h2", "http", "http-body", "httparse", @@ -844,22 +772,7 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", -] - -[[package]] -name = "hyper-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -dependencies = [ - "bytes", - "http-body-util", - "hyper", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", + "webpki-roots 1.0.5", ] [[package]] @@ -880,11 +793,9 @@ dependencies = [ "percent-encoding", "pin-project-lite", "socket2", - "system-configuration", "tokio", "tower-service", "tracing", - "windows-registry", ] [[package]] @@ -1156,12 +1067,6 @@ dependencies = [ "libc", ] -[[package]] -name = "linux-raw-sys" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" - [[package]] name = "litemap" version = "0.8.1" @@ -1183,6 +1088,12 @@ dependencies = [ "imgref", ] +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "maybe-rayon" version = "0.1.1" @@ -1199,12 +1110,6 @@ version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - [[package]] name = "miniz_oxide" version = "0.8.9" @@ -1236,23 +1141,6 @@ dependencies = [ "pxfm", ] -[[package]] -name = "native-tls" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "new_debug_unreachable" version = "1.0.6" @@ -1336,50 +1224,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" -[[package]] -name = "openssl" -version = "0.10.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" -dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "openssl-probe" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" - -[[package]] -name = "openssl-sys" -version = "0.9.111" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "option-ext" version = "0.2.0" @@ -1416,12 +1260,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkg-config" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - [[package]] name = "png" version = "0.18.1" @@ -1524,6 +1362,61 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.18", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "quote" version = "1.0.43" @@ -1687,29 +1580,26 @@ checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ "base64", "bytes", - "encoding_rs", "futures-core", - "h2", "http", "http-body", "http-body-util", "hyper", "hyper-rustls", - "hyper-tls", "hyper-util", "js-sys", "log", - "mime", - "native-tls", "percent-encoding", "pin-project-lite", + "quinn", + "rustls", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", "tokio", - "tokio-native-tls", + "tokio-rustls", "tower", "tower-http", "tower-service", @@ -1717,6 +1607,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots 1.0.5", ] [[package]] @@ -1740,17 +1631,10 @@ dependencies = [ ] [[package]] -name = "rustix" -version = "1.1.3" +name = "rustc-hash" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.61.2", -] +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustls" @@ -1759,6 +1643,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" dependencies = [ "once_cell", + "ring", "rustls-pki-types", "rustls-webpki", "subtle", @@ -1771,6 +1656,7 @@ version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" dependencies = [ + "web-time", "zeroize", ] @@ -1797,38 +1683,6 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" -[[package]] -name = "schannel" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "security-framework" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" -dependencies = [ - "bitflags", - "core-foundation 0.10.1", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "semver" version = "1.0.27" @@ -2014,40 +1868,6 @@ dependencies = [ "syn", ] -[[package]] -name = "system-configuration" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" -dependencies = [ - "bitflags", - "core-foundation 0.9.4", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "tempfile" -version = "3.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1" -dependencies = [ - "fastrand", - "getrandom 0.4.1", - "once_cell", - "rustix", - "windows-sys 0.61.2", -] - [[package]] name = "thiserror" version = "1.0.69" @@ -2112,6 +1932,21 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.49.0" @@ -2139,16 +1974,6 @@ dependencies = [ "syn", ] -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - [[package]] name = "tokio-rustls" version = "0.26.4" @@ -2167,23 +1992,12 @@ checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" dependencies = [ "futures-util", "log", - "native-tls", + "rustls", + "rustls-pki-types", "tokio", - "tokio-native-tls", + "tokio-rustls", "tungstenite", -] - -[[package]] -name = "tokio-util" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", + "webpki-roots 0.26.11", ] [[package]] @@ -2268,8 +2082,9 @@ dependencies = [ "http", "httparse", "log", - "native-tls", "rand 0.8.5", + "rustls", + "rustls-pki-types", "sha1", "thiserror 1.0.69", "utf-8", @@ -2355,12 +2170,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version_check" version = "0.9.5" @@ -2503,6 +2312,34 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.5", +] + +[[package]] +name = "webpki-roots" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "weezl" version = "0.1.12" @@ -2515,35 +2352,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" -[[package]] -name = "windows-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" -dependencies = [ - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - [[package]] name = "windows-sys" version = "0.48.0" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 7d14dd5..4b35757 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -12,12 +12,12 @@ dirs = "5.0" base64 = "0.22" getrandom = "0.2" tokio = { version = "1", features = ["rt-multi-thread", "macros", "net", "io-util", "time", "sync", "signal"] } -tokio-tungstenite = { version = "0.24", features = ["native-tls"] } +tokio-tungstenite = { version = "0.24", features = ["rustls-tls-webpki-roots"] } futures-util = "0.3" url = "2" uuid = { version = "1", features = ["v4"] } image = "0.25" -reqwest = { version = "0.12", features = ["json"] } +reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls-webpki-roots"] } sha2 = "0.10" aes-gcm = "0.10" async-trait = "0.1" From 05018b309aaffef4706604e7bfc2039930ba0fe7 Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Tue, 3 Mar 2026 16:09:39 -0600 Subject: [PATCH 06/12] prepare v0.16.0 (#596) --- .changeset/native-rust-daemon.md | 5 +++ docs/src/app/changelog/page.mdx | 56 ++++++++++++++++++++++++++++++++ docs/src/app/page.mdx | 3 +- 3 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 .changeset/native-rust-daemon.md diff --git a/.changeset/native-rust-daemon.md b/.changeset/native-rust-daemon.md new file mode 100644 index 0000000..89621c1 --- /dev/null +++ b/.changeset/native-rust-daemon.md @@ -0,0 +1,5 @@ +--- +"agent-browser": minor +--- + +Added experimental native Rust daemon (`--native` flag, `AGENT_BROWSER_NATIVE=1` env, or `"native": true` in config). The native daemon communicates with Chrome directly via CDP, eliminating Node.js and Playwright dependencies. Supports 150+ commands with full parity to the default Node.js daemon. Includes WebDriver backend for Safari/iOS, CDP protocol codegen, request tracking, frame context management, and comprehensive e2e and parity tests. diff --git a/docs/src/app/changelog/page.mdx b/docs/src/app/changelog/page.mdx index 7000a48..e07bb71 100644 --- a/docs/src/app/changelog/page.mdx +++ b/docs/src/app/changelog/page.mdx @@ -4,6 +4,62 @@ export const metadata = pageMetadata("changelog") # Changelog +## v0.16.0 + +

March 2026

+ +### New Features + +- **Native Rust daemon (experimental).** A pure Rust daemon that communicates with Chrome directly via the Chrome DevTools Protocol (CDP), eliminating Node.js and Playwright dependencies entirely. Enable with `--native`, `AGENT_BROWSER_NATIVE=1`, or `"native": true` in your config file. Supports 150+ commands with full parity to the default Node.js daemon. + +```bash +# Via flag +agent-browser --native open example.com + +# Via environment variable +export AGENT_BROWSER_NATIVE=1 +agent-browser open example.com +``` + +Or add to `agent-browser.json`: + +```json +{"native": true} +``` + +### Architecture + + + + + + + + + + + + +
Default (Node.js)Native (--native)
RuntimeNode.js + PlaywrightPure Rust binary
ProtocolPlaywright protocolDirect CDP / WebDriver
Install sizeLarger (Node.js + npm deps)Smaller (single binary)
Browser supportChromium, Firefox, WebKitChromium, Safari (via WebDriver)
StabilityStableExperimental
+ +### What's Supported + +All core commands work in native mode: navigation, interaction (click, fill, type, press, hover, scroll, drag), observation (snapshot, screenshot, eval), state management (cookies, storage, state save/load), tabs, emulation (viewport, device, timezone, locale, geolocation), streaming, diffing, recording, and profiling. + +The native daemon also includes a WebDriver backend for Safari and iOS support. + +### Known Limitations + +- Firefox and WebKit are not yet supported (Chromium and Safari only) +- Playwright trace format is not available (uses Chrome's built-in tracing) +- HAR export is not available +- Network route interception uses CDP Fetch domain instead of Playwright's route API +- The native and Node.js daemons share the same session socket. Use `agent-browser close` before switching between modes. + +See the [Native Mode](/native-mode) page for full details. + +--- + ## v0.15.0

February 2026

diff --git a/docs/src/app/page.mdx b/docs/src/app/page.mdx index 4ba14a6..82ce664 100644 --- a/docs/src/app/page.mdx +++ b/docs/src/app/page.mdx @@ -59,7 +59,8 @@ has a unique ref like `@e1`, `@e2`. This provides: Client-daemon architecture for optimal performance: 1. **Rust CLI** - Parses commands, communicates with daemon -2. **Node.js Daemon** - Manages Playwright browser instance +2. **Node.js Daemon** (default) - Manages Playwright browser instance +3. **Native Daemon** (experimental, `--native`) - Pure Rust daemon using direct CDP, no Node.js required Daemon starts automatically and persists between commands. From c10981413f8506d3af87efa00c33fb4084294881 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 3 Mar 2026 16:18:08 -0600 Subject: [PATCH 07/12] chore: version packages (#597) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/native-rust-daemon.md | 5 ----- CHANGELOG.md | 6 ++++++ cli/Cargo.lock | 2 +- cli/Cargo.toml | 2 +- package.json | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-) delete mode 100644 .changeset/native-rust-daemon.md diff --git a/.changeset/native-rust-daemon.md b/.changeset/native-rust-daemon.md deleted file mode 100644 index 89621c1..0000000 --- a/.changeset/native-rust-daemon.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"agent-browser": minor ---- - -Added experimental native Rust daemon (`--native` flag, `AGENT_BROWSER_NATIVE=1` env, or `"native": true` in config). The native daemon communicates with Chrome directly via CDP, eliminating Node.js and Playwright dependencies. Supports 150+ commands with full parity to the default Node.js daemon. Includes WebDriver backend for Safari/iOS, CDP protocol codegen, request tracking, frame context management, and comprehensive e2e and parity tests. diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b63bae..1f5e5dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # agent-browser +## 0.16.0 + +### Minor Changes + +- 05018b3: Added experimental native Rust daemon (`--native` flag, `AGENT_BROWSER_NATIVE=1` env, or `"native": true` in config). The native daemon communicates with Chrome directly via CDP, eliminating Node.js and Playwright dependencies. Supports 150+ commands with full parity to the default Node.js daemon. Includes WebDriver backend for Safari/iOS, CDP protocol codegen, request tracking, frame context management, and comprehensive e2e and parity tests. + ## 0.15.3 ### Patch Changes diff --git a/cli/Cargo.lock b/cli/Cargo.lock index be27f0e..ba44420 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -45,7 +45,7 @@ dependencies = [ [[package]] name = "agent-browser" -version = "0.15.3" +version = "0.16.0" dependencies = [ "aes-gcm", "async-trait", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 4b35757..0e014a8 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "agent-browser" -version = "0.15.3" +version = "0.16.0" edition = "2021" description = "Fast browser automation CLI for AI agents" license = "Apache-2.0" diff --git a/package.json b/package.json index 3fca6e4..4f6171a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "agent-browser", - "version": "0.15.3", + "version": "0.16.0", "description": "Headless browser automation CLI for AI agents", "type": "module", "main": "dist/daemon.js", From 7f42eed031b292a2779713e9a56525a190eda949 Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Tue, 3 Mar 2026 16:45:57 -0600 Subject: [PATCH 08/12] faster ci (#598) --- .github/workflows/ci.yml | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 095327b..3ca1706 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,13 +55,30 @@ jobs: run: pnpm test rust: + name: Rust + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Rust toolchain + uses: dtolnay/rust-toolchain@stable + + - name: Cache Rust build artifacts + uses: Swatinem/rust-cache@v2 + with: + workspaces: cli + + - name: Run Rust tests + run: cargo test --profile ci --manifest-path cli/Cargo.toml + + rust-cross: name: Rust (${{ matrix.os }} - ${{ matrix.target }}) + if: github.event_name == 'push' runs-on: ${{ matrix.os }} strategy: matrix: include: - - os: ubuntu-latest - target: x86_64-unknown-linux-gnu - os: macos-latest target: aarch64-apple-darwin - os: macos-latest @@ -88,8 +105,9 @@ jobs: windows-integration: name: Windows Integration Test + if: github.event_name == 'push' runs-on: windows-latest - needs: rust + needs: rust-cross steps: - name: Checkout repository @@ -204,8 +222,9 @@ jobs: global-install: name: Global Install (${{ matrix.os }}) + if: github.event_name == 'push' runs-on: ${{ matrix.os }} - needs: rust + needs: rust-cross strategy: matrix: include: From 324a9e4e0c21c1a9fb6239023fd1d9f3f70df1a2 Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Tue, 3 Mar 2026 17:26:22 -0600 Subject: [PATCH 09/12] windows 8 cores (#599) * windows 8 cores * add workflow dispatch --- .github/workflows/ci.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3ca1706..6c0e539 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,7 @@ on: branches: [main] pull_request: branches: [main] + workflow_dispatch: jobs: version-sync: @@ -74,7 +75,7 @@ jobs: rust-cross: name: Rust (${{ matrix.os }} - ${{ matrix.target }}) - if: github.event_name == 'push' + if: github.event_name != 'pull_request' runs-on: ${{ matrix.os }} strategy: matrix: @@ -83,7 +84,7 @@ jobs: target: aarch64-apple-darwin - os: macos-latest target: x86_64-apple-darwin - - os: windows-latest + - os: windows-latest-8-cores target: x86_64-pc-windows-msvc steps: @@ -105,8 +106,8 @@ jobs: windows-integration: name: Windows Integration Test - if: github.event_name == 'push' - runs-on: windows-latest + if: github.event_name != 'pull_request' + runs-on: windows-latest-8-cores needs: rust-cross steps: @@ -222,7 +223,7 @@ jobs: global-install: name: Global Install (${{ matrix.os }}) - if: github.event_name == 'push' + if: github.event_name != 'pull_request' runs-on: ${{ matrix.os }} needs: rust-cross strategy: @@ -234,7 +235,7 @@ jobs: - os: macos-latest target: aarch64-apple-darwin binary: agent-browser-darwin-arm64 - - os: windows-latest + - os: windows-latest-8-cores target: x86_64-pc-windows-msvc binary: agent-browser-win32-x64.exe From 56260f68b04f0c2e50b61d6c46c7872c07158d02 Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Tue, 3 Mar 2026 17:45:23 -0600 Subject: [PATCH 10/12] Native: auto-detect sandbox/container environments for Chrome launch (#602) Fixes #600 Three improvements to `--native` Chrome launching: - `find_chrome()` now falls back to Playwright's browser cache (`~/.cache/ms-playwright/`) when no system Chrome is found - Auto-detect containers/VMs (root, Docker, Podman, cgroups) and inject `--no-sandbox` - Chrome stderr is now captured and included in launch error messages, with a hint when sandbox errors are detected --- cli/src/native/cdp/chrome.rs | 222 ++++++++++++++++++++++++++++++++++- 1 file changed, 220 insertions(+), 2 deletions(-) diff --git a/cli/src/native/cdp/chrome.rs b/cli/src/native/cdp/chrome.rs index eb86942..a0a3620 100644 --- a/cli/src/native/cdp/chrome.rs +++ b/cli/src/native/cdp/chrome.rs @@ -127,6 +127,10 @@ pub fn launch_chrome(options: &LaunchOptions) -> Result { args.extend(options.args.iter().cloned()); + if should_disable_sandbox(&args) { + args.push("--no-sandbox".to_string()); + } + let mut child = Command::new(&chrome_path) .args(&args) .stdin(Stdio::null()) @@ -149,18 +153,81 @@ pub fn launch_chrome(options: &LaunchOptions) -> Result { fn wait_for_ws_url(reader: BufReader) -> Result { let deadline = std::time::Instant::now() + Duration::from_secs(30); let prefix = "DevTools listening on "; + let mut stderr_lines: Vec = Vec::new(); for line in reader.lines() { if std::time::Instant::now() > deadline { - return Err("Timeout waiting for Chrome DevTools URL".to_string()); + return Err(chrome_launch_error( + "Timeout waiting for Chrome DevTools URL", + &stderr_lines, + )); } let line = line.map_err(|e| format!("Failed to read Chrome stderr: {}", e))?; if let Some(url) = line.strip_prefix(prefix) { return Ok(url.trim().to_string()); } + stderr_lines.push(line); } - Err("Chrome exited before providing DevTools URL".to_string()) + Err(chrome_launch_error( + "Chrome exited before providing DevTools URL", + &stderr_lines, + )) +} + +fn chrome_launch_error(message: &str, stderr_lines: &[String]) -> String { + let relevant: Vec<&String> = stderr_lines + .iter() + .filter(|l| { + let lower = l.to_lowercase(); + lower.contains("error") + || lower.contains("fatal") + || lower.contains("sandbox") + || lower.contains("namespace") + || lower.contains("permission") + || lower.contains("cannot") + || lower.contains("failed") + || lower.contains("abort") + }) + .collect(); + + if relevant.is_empty() { + if stderr_lines.is_empty() { + return format!("{} (no stderr output from Chrome)", message); + } + let last_lines: Vec<&String> = stderr_lines.iter().rev().take(5).collect(); + return format!( + "{}\nChrome stderr (last {} lines):\n {}", + message, + last_lines.len(), + last_lines + .into_iter() + .rev() + .map(|s| s.as_str()) + .collect::>() + .join("\n ") + ); + } + + let hint = if relevant.iter().any(|l| { + let lower = l.to_lowercase(); + lower.contains("sandbox") || lower.contains("namespace") + }) { + "\nHint: try --args \"--no-sandbox\" (required in containers, VMs, and some Linux setups)" + } else { + "" + }; + + format!( + "{}\nChrome stderr:\n {}{}", + message, + relevant + .iter() + .map(|s| s.as_str()) + .collect::>() + .join("\n "), + hint + ) } pub fn find_chrome() -> Option { @@ -177,6 +244,10 @@ pub fn find_chrome() -> Option { return Some(p); } } + + if let Some(p) = find_playwright_chromium() { + return Some(p); + } } #[cfg(target_os = "linux")] @@ -197,6 +268,10 @@ pub fn find_chrome() -> Option { } } } + + if let Some(p) = find_playwright_chromium() { + return Some(p); + } } #[cfg(target_os = "windows")] @@ -369,6 +444,106 @@ fn get_chrome_user_data_dirs() -> Vec { dirs } +/// Returns true if Chrome's sandbox should be disabled because the environment +/// doesn't support it (containers, VMs, running as root). +fn should_disable_sandbox(existing_args: &[String]) -> bool { + if existing_args.iter().any(|a| a == "--no-sandbox") { + return false; // already set by user + } + + #[cfg(unix)] + { + // Root user -- standard container default, Chrome sandbox requires non-root + if unsafe { libc::geteuid() } == 0 { + return true; + } + + // Docker container + if Path::new("/.dockerenv").exists() { + return true; + } + + // Podman container + if Path::new("/run/.containerenv").exists() { + return true; + } + + // Generic container detection: cgroup contains docker/kubepods/lxc + if let Ok(cgroup) = std::fs::read_to_string("/proc/1/cgroup") { + if cgroup.contains("docker") + || cgroup.contains("kubepods") + || cgroup.contains("lxc") + { + return true; + } + } + } + + false +} + +/// Search Playwright's browser cache for a Chromium binary. +/// This is where `agent-browser install` (via `npx playwright install chromium`) puts it. +fn find_playwright_chromium() -> Option { + let mut search_dirs = Vec::new(); + + if let Ok(custom) = std::env::var("PLAYWRIGHT_BROWSERS_PATH") { + search_dirs.push(PathBuf::from(custom)); + } + + if let Some(home) = dirs::home_dir() { + search_dirs.push(home.join(".cache/ms-playwright")); + } + + for dir in &search_dirs { + if !dir.is_dir() { + continue; + } + if let Ok(entries) = std::fs::read_dir(dir) { + let mut matches: Vec = entries + .filter_map(|e| e.ok()) + .filter(|e| { + e.file_name() + .to_str() + .map(|n| n.starts_with("chromium-")) + .unwrap_or(false) + }) + .filter_map(|e| { + let candidate = build_playwright_binary_path(&e.path()); + if candidate.exists() { + Some(candidate) + } else { + None + } + }) + .collect(); + // Sort descending so the newest version wins + matches.sort(); + matches.reverse(); + if let Some(p) = matches.into_iter().next() { + return Some(p); + } + } + } + + None +} + +#[cfg(target_os = "linux")] +fn build_playwright_binary_path(chromium_dir: &Path) -> PathBuf { + chromium_dir.join("chrome-linux64/chrome") +} + +#[cfg(target_os = "macos")] +fn build_playwright_binary_path(chromium_dir: &Path) -> PathBuf { + chromium_dir.join("chrome-mac/Chromium.app/Contents/MacOS/Chromium") +} + +#[cfg(target_os = "windows")] +fn build_playwright_binary_path(chromium_dir: &Path) -> PathBuf { + chromium_dir.join("chrome-win/chrome.exe") +} + fn expand_tilde(path: &str) -> String { if let Some(rest) = path.strip_prefix('~') { if let Some(home) = dirs::home_dir() { @@ -414,4 +589,47 @@ mod tests { let result = read_devtools_active_port(Path::new("/nonexistent")); assert!(result.is_none()); } + + #[test] + fn test_should_disable_sandbox_skips_if_already_set() { + let args = vec!["--headless=new".to_string(), "--no-sandbox".to_string()]; + assert!(!should_disable_sandbox(&args)); + } + + #[test] + fn test_chrome_launch_error_no_stderr() { + let msg = chrome_launch_error("Chrome exited", &[]); + assert!(msg.contains("no stderr output")); + } + + #[test] + fn test_chrome_launch_error_with_sandbox_hint() { + let lines = vec![ + "some log line".to_string(), + "Failed to move to new namespace: sandbox error".to_string(), + ]; + let msg = chrome_launch_error("Chrome exited", &lines); + assert!(msg.contains("sandbox error")); + assert!(msg.contains("Hint:")); + assert!(msg.contains("--no-sandbox")); + } + + #[test] + fn test_chrome_launch_error_generic() { + let lines = vec![ + "info line".to_string(), + "another info line".to_string(), + ]; + let msg = chrome_launch_error("Chrome exited", &lines); + assert!(msg.contains("last 2 lines")); + } + + #[test] + fn test_find_playwright_chromium_nonexistent() { + // With no Playwright cache, should return None + std::env::set_var("PLAYWRIGHT_BROWSERS_PATH", "/nonexistent/path"); + let result = find_playwright_chromium(); + std::env::remove_var("PLAYWRIGHT_BROWSERS_PATH"); + assert!(result.is_none()); + } } From c4180c8cb1168cec46c0b633687f5d828c2f8ac5 Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Tue, 3 Mar 2026 17:51:29 -0600 Subject: [PATCH 11/12] chore: add patch changeset for release (#603) --- .changeset/release-fe7a1566.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/release-fe7a1566.md diff --git a/.changeset/release-fe7a1566.md b/.changeset/release-fe7a1566.md new file mode 100644 index 0000000..35fd338 --- /dev/null +++ b/.changeset/release-fe7a1566.md @@ -0,0 +1,5 @@ +--- +"agent-browser": patch +--- + +Improved Chrome launch reliability by automatically detecting containerized environments (Docker, Podman, Kubernetes) and enabling --no-sandbox when needed. Added support for discovering Playwright-installed Chromium browsers and enhanced error messages with helpful diagnostics when Chrome fails to launch. From a493d02c666286471f974287b725efae88579338 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 3 Mar 2026 17:59:02 -0600 Subject: [PATCH 12/12] chore: version packages (#604) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/release-fe7a1566.md | 5 ----- CHANGELOG.md | 6 ++++++ cli/Cargo.lock | 2 +- cli/Cargo.toml | 2 +- package.json | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-) delete mode 100644 .changeset/release-fe7a1566.md diff --git a/.changeset/release-fe7a1566.md b/.changeset/release-fe7a1566.md deleted file mode 100644 index 35fd338..0000000 --- a/.changeset/release-fe7a1566.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"agent-browser": patch ---- - -Improved Chrome launch reliability by automatically detecting containerized environments (Docker, Podman, Kubernetes) and enabling --no-sandbox when needed. Added support for discovering Playwright-installed Chromium browsers and enhanced error messages with helpful diagnostics when Chrome fails to launch. diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f5e5dc..c598aa5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # agent-browser +## 0.16.1 + +### Patch Changes + +- c4180c8: Improved Chrome launch reliability by automatically detecting containerized environments (Docker, Podman, Kubernetes) and enabling --no-sandbox when needed. Added support for discovering Playwright-installed Chromium browsers and enhanced error messages with helpful diagnostics when Chrome fails to launch. + ## 0.16.0 ### Minor Changes diff --git a/cli/Cargo.lock b/cli/Cargo.lock index ba44420..ba20afb 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -45,7 +45,7 @@ dependencies = [ [[package]] name = "agent-browser" -version = "0.16.0" +version = "0.16.1" dependencies = [ "aes-gcm", "async-trait", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 0e014a8..bec5b01 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "agent-browser" -version = "0.16.0" +version = "0.16.1" edition = "2021" description = "Fast browser automation CLI for AI agents" license = "Apache-2.0" diff --git a/package.json b/package.json index 4f6171a..a949d0f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "agent-browser", - "version": "0.16.0", + "version": "0.16.1", "description": "Headless browser automation CLI for AI agents", "type": "module", "main": "dist/daemon.js",