diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 3560fe6..f7b1f3b 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -1,6 +1,6 @@ { "$schema": "https://anthropic.com/claude-code/marketplace.schema.json", - "name": "agent-browser", + "name": "chrome-use", "description": "Browser automation for AI agents", "owner": { "name": "Vercel", @@ -8,11 +8,11 @@ }, "plugins": [ { - "name": "agent-browser", + "name": "chrome-use", "description": "Automates browser interactions for web testing, form filling, screenshots, and data extraction", "source": "./", "strict": false, - "skills": ["./skills/agent-browser"], + "skills": ["./skills/chrome-use"], "category": "development" } ] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 43bde2c..aaa5580 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -122,7 +122,7 @@ jobs: runs-on: windows-latest needs: rust-cross # Headless-forbidden fork on a headless CI runner — opt into the escape so - # `agent-browser open` can launch Chrome. + # `chrome-use open` can launch Chrome. env: AGENT_BROWSER_ALLOW_HEADLESS: "1" @@ -145,13 +145,13 @@ jobs: - name: Copy CLI binary to bin directory run: | - Copy-Item cli/target/x86_64-pc-windows-msvc/release/agent-browser.exe bin/agent-browser-win32-x64.exe + Copy-Item cli/target/x86_64-pc-windows-msvc/release/chrome-use.exe bin/chrome-use-win32-x64.exe - - name: Test agent-browser install command + - name: Test chrome-use install command run: | $env:PATH = "$pwd\bin;$env:PATH" for ($i = 1; $i -le 3; $i++) { - bin/agent-browser-win32-x64.exe install + bin/chrome-use-win32-x64.exe install if ($LASTEXITCODE -eq 0) { exit 0 } Write-Host "Attempt $i failed, retrying in 10 seconds..." Start-Sleep -Seconds 10 @@ -167,14 +167,14 @@ jobs: # --launch: spawn a standalone browser. Without it, `open` defaults to # auto-connect and looks for an existing Chrome on a debug port — which # a fresh CI runner doesn't have, so it errors "Could not connect". - bin/agent-browser-win32-x64.exe --launch open https://example.com + bin/chrome-use-win32-x64.exe --launch 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 + $snapshot = bin/chrome-use-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 + bin/chrome-use-win32-x64.exe close if ($LASTEXITCODE -ne 0) { Write-Error "close failed"; exit 1 } Write-Host "--- Windows daemon lifecycle test passed ---" shell: pwsh @@ -190,13 +190,13 @@ jobs: include: - os: ubuntu-latest target: x86_64-unknown-linux-gnu - binary: agent-browser-linux-x64 + binary: chrome-use-linux-x64 - os: macos-latest target: aarch64-apple-darwin - binary: agent-browser-darwin-arm64 + binary: chrome-use-darwin-arm64 - os: windows-latest target: x86_64-pc-windows-msvc - binary: agent-browser-win32-x64.exe + binary: chrome-use-win32-x64.exe steps: - name: Checkout repository @@ -222,23 +222,23 @@ jobs: - name: Copy CLI binary to bin directory (Unix) if: runner.os != 'Windows' - run: cp cli/target/${{ matrix.target }}/release/agent-browser bin/${{ matrix.binary }} + run: cp cli/target/${{ matrix.target }}/release/chrome-use bin/${{ matrix.binary }} - name: Copy CLI binary to bin directory (Windows) if: runner.os == 'Windows' - run: Copy-Item cli/target/${{ matrix.target }}/release/agent-browser.exe bin/${{ matrix.binary }} + run: Copy-Item cli/target/${{ matrix.target }}/release/chrome-use.exe bin/${{ matrix.binary }} - name: Test npm global install run: | npm pack - npm install -g agent-browser-*.tgz - agent-browser --version + npm install -g chrome-use-*.tgz + chrome-use --version shell: bash - name: Verify symlink points to native binary (Unix) if: runner.os != 'Windows' run: | - SYMLINK=$(npm prefix -g)/bin/agent-browser + SYMLINK=$(npm prefix -g)/bin/chrome-use TARGET=$(readlink "$SYMLINK") echo "Symlink: $SYMLINK" echo "Target: $TARGET" @@ -257,13 +257,13 @@ jobs: # can't happen and the JS wrapper — which spawns the native binary — is # the valid fallback). Require functionality; prefer, but don't require, # the native shim. - $ver = agent-browser --version - if ($LASTEXITCODE -ne 0) { Write-Error "agent-browser --version failed"; exit 1 } + $ver = chrome-use --version + if ($LASTEXITCODE -ne 0) { Write-Error "chrome-use --version failed"; exit 1 } echo "CLI version: $ver" - $content = Get-Content "$(npm prefix -g)\agent-browser.cmd" -Raw + $content = Get-Content "$(npm prefix -g)\chrome-use.cmd" -Raw echo "Shim content:" echo $content - if ($content -match "agent-browser-win32-x64\.exe") { + if ($content -match "chrome-use-win32-x64\.exe") { echo "OK: shim points directly to the native binary (zero overhead)" } else { echo "INFO: shim uses the JS wrapper fallback (functional; native-shim optimization not applied)" diff --git a/.github/workflows/release-binaries.yml b/.github/workflows/release-binaries.yml index 7f38b0d..ba8e805 100644 --- a/.github/workflows/release-binaries.yml +++ b/.github/workflows/release-binaries.yml @@ -27,13 +27,13 @@ jobs: fail-fast: false matrix: include: - - { name: Linux x64, os: ubuntu-latest, target: x86_64-unknown-linux-gnu, asset: agent-browser-linux-x64, use_zigbuild: true, ext: '' } - - { name: Linux ARM64, os: ubuntu-latest, target: aarch64-unknown-linux-gnu, asset: agent-browser-linux-arm64, use_zigbuild: true, ext: '' } - - { name: Linux musl x64, os: ubuntu-latest, target: x86_64-unknown-linux-musl, asset: agent-browser-linux-musl-x64, use_zigbuild: true, ext: '' } - - { name: Linux musl ARM64, os: ubuntu-latest, target: aarch64-unknown-linux-musl, asset: agent-browser-linux-musl-arm64, use_zigbuild: true, ext: '' } - - { name: Windows x64, os: ubuntu-latest, target: x86_64-pc-windows-gnu, asset: agent-browser-win32-x64, use_zigbuild: false, ext: '.exe' } - - { name: macOS x64, os: macos-latest, target: x86_64-apple-darwin, asset: agent-browser-darwin-x64, use_zigbuild: false, ext: '' } - - { name: macOS ARM64, os: macos-latest, target: aarch64-apple-darwin, asset: agent-browser-darwin-arm64, use_zigbuild: false, ext: '' } + - { name: Linux x64, os: ubuntu-latest, target: x86_64-unknown-linux-gnu, asset: chrome-use-linux-x64, use_zigbuild: true, ext: '' } + - { name: Linux ARM64, os: ubuntu-latest, target: aarch64-unknown-linux-gnu, asset: chrome-use-linux-arm64, use_zigbuild: true, ext: '' } + - { name: Linux musl x64, os: ubuntu-latest, target: x86_64-unknown-linux-musl, asset: chrome-use-linux-musl-x64, use_zigbuild: true, ext: '' } + - { name: Linux musl ARM64, os: ubuntu-latest, target: aarch64-unknown-linux-musl, asset: chrome-use-linux-musl-arm64, use_zigbuild: true, ext: '' } + - { name: Windows x64, os: ubuntu-latest, target: x86_64-pc-windows-gnu, asset: chrome-use-win32-x64, use_zigbuild: false, ext: '.exe' } + - { name: macOS x64, os: macos-latest, target: x86_64-apple-darwin, asset: chrome-use-darwin-x64, use_zigbuild: false, ext: '' } + - { name: macOS ARM64, os: macos-latest, target: aarch64-apple-darwin, asset: chrome-use-darwin-arm64, use_zigbuild: false, ext: '' } steps: - name: Checkout uses: actions/checkout@v6 @@ -87,13 +87,13 @@ jobs: run: | set -euo pipefail mkdir -p dist - src="cli/target/${{ matrix.target }}/release/agent-browser${{ matrix.ext }}" - # The binary inside every archive is named `agent-browser` (or .exe); + src="cli/target/${{ matrix.target }}/release/chrome-use${{ matrix.ext }}" + # The binary inside every archive is named `chrome-use` (or .exe); # install.sh extracts that fixed name regardless of platform. - cp "$src" "dist/agent-browser${{ matrix.ext }}" - chmod +x "dist/agent-browser${{ matrix.ext }}" || true + cp "$src" "dist/chrome-use${{ matrix.ext }}" + chmod +x "dist/chrome-use${{ matrix.ext }}" || true ( cd dist - tar czf "${{ matrix.asset }}.tar.gz" "agent-browser${{ matrix.ext }}" + tar czf "${{ matrix.asset }}.tar.gz" "chrome-use${{ matrix.ext }}" if command -v sha256sum >/dev/null 2>&1; then sha256sum "${{ matrix.asset }}.tar.gz" > "${{ matrix.asset }}.tar.gz.sha256" else diff --git a/AGENTS.md b/AGENTS.md index 763c6c6..1f4bc8e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,7 +19,7 @@ When adding or changing user-facing features (new flags, commands, behaviors, en 1. `cli/src/output.rs` — `--help` output (flags list, examples, environment variables) 2. `README.md` — Options table, relevant feature sections, examples -3. `skill-data/core/SKILL.md` (and its `references/`) — so AI agents know about the feature when they load the core skill. Edit `skill-data/core/SKILL.md` for overview/workflow changes; edit `skill-data/core/references/*.md` for detailed reference content. Do **not** put feature content in `skills/agent-browser/SKILL.md` — that file is an intentionally thin discovery stub for `npx skills add` and exists only to redirect agents to `agent-browser skills get core`. +3. `skill-data/core/SKILL.md` (and its `references/`) — so AI agents know about the feature when they load the core skill. Edit `skill-data/core/SKILL.md` for overview/workflow changes; edit `skill-data/core/references/*.md` for detailed reference content. Do **not** put feature content in `skills/chrome-use/SKILL.md` — that file is an intentionally thin discovery stub for `npx skills add` and exists only to redirect agents to `chrome-use skills get core`. 4. `docs/src/app/` — the Next.js docs site (MDX pages) 5. Inline doc comments in the relevant source files @@ -167,13 +167,13 @@ Stop the instance when done (avoids cost): Run unit tests on Windows: ```bash -./scripts/windows-debug/run.sh "cd C:\agent-browser && cargo test --manifest-path cli\Cargo.toml" +./scripts/windows-debug/run.sh "cd C:\chrome-use && cargo test --manifest-path cli\Cargo.toml" ``` Run e2e tests on Windows: ```bash -./scripts/windows-debug/run.sh "cd C:\agent-browser && cargo test e2e --manifest-path cli\Cargo.toml -- --ignored --test-threads=1" +./scripts/windows-debug/run.sh "cd C:\chrome-use && cargo test e2e --manifest-path cli\Cargo.toml -- --ignored --test-threads=1" ``` Check bootstrap progress (first boot only): @@ -182,7 +182,7 @@ Check bootstrap progress (first boot only): ./scripts/windows-debug/run.sh "Get-Content C:\bootstrap.log" ``` -The repo lives at `C:\agent-browser` on the instance. Rust, Git, and Chrome are pre-installed. The `run.sh` wrapper automatically adds cargo and git to PATH. +The repo lives at `C:\chrome-use` on the instance. Rust, Git, and Chrome are pre-installed. The `run.sh` wrapper automatically adds cargo and git to PATH. diff --git a/README.md b/README.md index 21c971b..67d11b2 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ -# agent-browser-stealth +# chrome-use **English** · [简体中文](README.zh.md) -![agent-browser-stealth](assets/hero.png) +![chrome-use](assets/hero.png) -Stealth fork of [agent-browser](https://github.com/vercel-labs/agent-browser) — connects to your real Chrome, shares your login sessions, and is undetectable by anti-bot systems. +**chrome-use** drives your real, logged-in Chrome from any AI agent — it shares your existing login sessions and is undetectable by anti-bot systems because it *is* your real browser. Part of the `*-use` family ([iphone-use](https://github.com/leeguooooo) drives your real iPhone; chrome-use drives your real Chrome). -For basic usage, commands, and API reference, see the [upstream documentation](https://github.com/vercel-labs/agent-browser). +Originally based on [vercel-labs/agent-browser](https://github.com/vercel-labs/agent-browser) (Apache-2.0); now a standalone project — the stealth/extension-relay architecture, anti-detection, humanize, multi-agent isolation, and CLI have diverged substantially. ## Give your AI agent the browser you already live in **No fresh Chrome. No re-login. No "are you a robot?" walls.** -agent-browser-stealth points **any** agent — Claude Code, Cursor, Codex, your own scripts — at the **Chrome you're already signed into everything on**. It clicks in *your* window, so you watch it work and grab the wheel the moment it hits a 2FA prompt or captcha. And because it's literally your real browser (over a one-click extension, native messaging — no debug port), sites read it as 100% human: **[CreepJS scores it 0% bot](#anti-detection).** +chrome-use points **any** agent — Claude Code, Cursor, Codex, your own scripts — at the **Chrome you're already signed into everything on**. It clicks in *your* window, so you watch it work and grab the wheel the moment it hits a 2FA prompt or captcha. And because it's literally your real browser (over a one-click extension, native messaging — no debug port), sites read it as 100% human: **[CreepJS scores it 0% bot](#anti-detection).** **Why not just use…** @@ -23,7 +23,7 @@ agent-browser-stealth points **any** agent — Claude Code, Cursor, Codex, your
Full feature comparison (the receipts) -| | [Claude in Chrome](https://www.anthropic.com/claude/chrome) | web-access / raw CDP port | Playwright · Puppeteer · browser-use | **agent-browser-stealth** | +| | [Claude in Chrome](https://www.anthropic.com/claude/chrome) | web-access / raw CDP port | Playwright · Puppeteer · browser-use | **chrome-use** | |---|:---:|:---:|:---:|:---:| | Works with **any** agent / CLI (not one app) | ❌ Claude only | ✅ | ✅ | ✅ | | Drives your **real, logged-in** Chrome | ✅ | ✅ | ❌ fresh empty profile | ✅ | @@ -37,15 +37,15 @@ agent-browser-stealth points **any** agent — Claude Code, Cursor, Codex, your
-## Why this fork? +## Why chrome-use? real but undetectable fingerprint -**agent-browser** launches a fresh browser with an empty profile. You need to log in again, and websites can detect it's automated. +**Typical browser automation** (Playwright, Puppeteer, or a fresh `--launch`) opens a brand-new browser with an empty profile. You have to log in again, and websites can tell it's automated. -**agent-browser-stealth** connects to your existing Chrome. Your cookies, sessions, and browser fingerprint are all real — because it IS your real browser. +**chrome-use** connects to your existing Chrome. Your cookies, sessions, and browser fingerprint are all real — because it IS your real browser. -| | agent-browser | agent-browser-stealth | +| | chrome-use | chrome-use | |---|---|---| | Browser | Launches new Chrome | Connects to your Chrome | | Login state | Empty, need to re-login | Your existing sessions | @@ -57,7 +57,7 @@ agent-browser-stealth points **any** agent — Claude Code, Cursor, Codex, your ![how it works](assets/how-it-works.png) -Your **agent-browser CLI** talks to a tiny **browser extension** over Chrome +Your **chrome-use CLI** talks to a tiny **browser extension** over Chrome **native messaging** — a local inter-process channel, *no network socket, no token, no remote server*. The extension uses `chrome.debugger` to drive the tabs you target in **your own, already-logged-in Chrome**, then hands results back to @@ -76,7 +76,7 @@ Other local tools drive Chrome over a raw `--remote-debugging-port` (CDP). Since consent dialog — and the port has to be enabled up front. Our extension uses native messaging instead: **install once, then zero per-use confirmation.** -| | **agent-browser-stealth** (this extension) | web-access (raw CDP port) | Claude in Chrome (chrome.debugger) | +| | **chrome-use** (this extension) | web-access (raw CDP port) | Claude in Chrome (chrome.debugger) | |---|---|---|---| | Connect method | native messaging — no port, no token | `--remote-debugging-port` | `chrome.debugger` | | **"Allow remote debugging?" popup** | **never** ✅ | **every connection** 🔴 | no | @@ -84,7 +84,7 @@ native messaging instead: **install once, then zero per-use confirmation.** | `Runtime.enable` (CDP) leak¹ | **off by default → clean** ✅ | domain enabled | n/a | | CreepJS stealth score² | **0% stealth · 0% headless** ✅ | real Chrome | real Chrome | | Per-session tab groups / concurrent agents | **yes** ✅ | no | no | -| Built for the agent-browser CLI | yes | a separate proxy | a single-app assistant | +| Built for the chrome-use CLI | yes | a separate proxy | a single-app assistant | > ¹ Verified against [rebrowser-bot-detector](https://bot-detector.rebrowser.net/): > our relay reports `runtimeEnableLeak: 🟢 No leak` and `navigatorWebdriver: 🟢`. @@ -97,18 +97,18 @@ native messaging instead: **install once, then zero per-use confirmation.** ## Install ```bash -curl -fsSL https://raw.githubusercontent.com/leeguooooo/agent-browser-stealth/main/install.sh | sh +curl -fsSL https://raw.githubusercontent.com/leeguooooo/chrome-use/main/install.sh | sh ``` -Downloads the prebuilt binary for your platform from the latest [GitHub Release](https://github.com/leeguooooo/agent-browser-stealth/releases) and installs `agent-browser` (+ the `abs` alias). No npm, no tokens. +Downloads the prebuilt binary for your platform from the latest [GitHub Release](https://github.com/leeguooooo/chrome-use/releases) and installs `chrome-use` (+ the `abs` alias). No npm, no tokens.
Other ways to install -- **Pin a version:** `AGENT_BROWSER_VERSION=v0.27.0-fork.12 curl -fsSL https://raw.githubusercontent.com/leeguooooo/agent-browser-stealth/main/install.sh | sh` +- **Pin a version:** `AGENT_BROWSER_VERSION=v0.27.0-fork.12 curl -fsSL https://raw.githubusercontent.com/leeguooooo/chrome-use/main/install.sh | sh` - **Custom location:** `AGENT_BROWSER_BIN_DIR=$HOME/bin curl -fsSL … | sh` -- **Windows:** download `agent-browser-win32-x64.tar.gz` from the [Releases page](https://github.com/leeguooooo/agent-browser-stealth/releases) and put `agent-browser.exe` on your PATH. -- **npm (legacy):** `npm install -g agent-browser-stealth` — still published, but GitHub Releases is the primary channel now. +- **Windows:** download `chrome-use-win32-x64.tar.gz` from the [Releases page](https://github.com/leeguooooo/chrome-use/releases) and put `chrome-use.exe` on your PATH. +- **npm (legacy):** `npm install -g chrome-use` — still published, but GitHub Releases is the primary channel now.
### Install the AI agent skills @@ -116,14 +116,14 @@ Downloads the prebuilt binary for your platform from the latest [GitHub Release] The repo ships SKILL.md files for Claude Code, Cursor, etc. Pull them into the current project with [skills.sh](https://skills.sh): ```bash -npx skills add leeguooooo/agent-browser-stealth +npx skills add leeguooooo/chrome-use ``` -This drops `skills/agent-browser` (and the specialized `skill-data/{core,electron,slack,dogfood,agentcore,vercel-sandbox}`) into your project so your AI agent gets the right usage patterns and pre-approved bash permissions for `agent-browser`, `agent-browser-stealth`, and `abs`. +This drops `skills/chrome-use` (and the specialized `skill-data/{core,electron,slack,dogfood,agentcore,vercel-sandbox}`) into your project so your AI agent gets the right usage patterns and pre-approved bash permissions for `chrome-use`, `chrome-use`, and `abs`. ## Command names -`agent-browser`, `agent-browser-stealth`, and `abs` are **the same binary** — +`chrome-use`, `chrome-use`, and `abs` are **the same binary** — `abs` is just a short alias. There is no separate "stealth executable"; stealth is a runtime behavior (see [Anti-detection](#anti-detection) below), applied automatically based on whether you attach to your real Chrome or `--launch` a @@ -132,15 +132,15 @@ fresh one. ## Setup: connect to your Chrome **Recommended — the browser extension (one click, no popups).** Install the -[**agent-browser-stealth** extension from the Chrome Web Store](https://chromewebstore.google.com/detail/agent-browser-stealth/knfcmbamhjmaonkfnjhldjedeobeafmk), +[**chrome-use** extension from the Chrome Web Store](https://chromewebstore.google.com/detail/chrome-use/knfcmbamhjmaonkfnjhldjedeobeafmk), then register the local bridge once: ```bash -agent-browser extension install # register the native-messaging host (one-time) -agent-browser open https://x.com/home +chrome-use extension install # register the native-messaging host (one-time) +chrome-use open https://x.com/home ``` -`agent-browser open` then drives your real, logged-in Chrome over **native +`chrome-use open` then drives your real, logged-in Chrome over **native messaging** — no debug port, no token, and **no "Allow remote debugging?" dialog, ever**. The extension auto-updates and survives Chrome restarts, so it stays connected with zero per-use confirmation (ideal for unattended/agent use). @@ -148,7 +148,7 @@ connected with zero per-use confirmation (ideal for unattended/agent use).
Alternative — raw remote-debugging port (pops a consent dialog) -Without the extension, agent-browser attaches over the Chrome DevTools Protocol, +Without the extension, chrome-use attaches over the Chrome DevTools Protocol, which Chrome only exposes when **launched with a remote-debugging port** (a startup flag — the `chrome://inspect` toggle alone is not enough): @@ -160,13 +160,13 @@ google-chrome --remote-debugging-port=9222 # Windows: add --remote-debugging-port=9222 to your Chrome shortcut's target ``` -Then `agent-browser open ` auto-discovers the port. On first attach, +Then `chrome-use open ` auto-discovers the port. On first attach, **Chrome 136+ shows an "Allow remote debugging?" dialog** — click Allow once (it persists for that Chrome session). The extension above avoids this entirely.
**No setup / don't want to touch your real Chrome?** Use -`agent-browser --launch open ` to spawn a fresh isolated stealth browser +`chrome-use --launch open ` to spawn a fresh isolated stealth browser (full anti-detection patches applied; see below). This always works without any port setup and is what CI uses automatically. @@ -174,13 +174,13 @@ port setup and is what CI uses automatically. ```bash # Connect to your Chrome and navigate -agent-browser open https://example.com +chrome-use open https://example.com # Everything works through your logged-in browser -agent-browser click "Post" -agent-browser click 449 320 # …or click a raw viewport coordinate -agent-browser fill "Title" "Hello World" -agent-browser screenshot ./page.png +chrome-use click "Post" +chrome-use click 449 320 # …or click a raw viewport coordinate +chrome-use fill "Title" "Hello World" +chrome-use screenshot ./page.png ``` The agent operates in your Chrome — you'll see tabs opening, pages loading, clicks happening in real time. You can take over at any point (e.g. solve a CAPTCHA), then let the agent continue. @@ -191,16 +191,16 @@ Spawn a separate browser instead of attaching to your running Chrome: ```bash # Throwaway: fresh, EMPTY profile — no cookies, no login (good for CI/testing) -agent-browser --launch open https://example.com +chrome-use --launch open https://example.com # Keep your login: launch with your real Chrome profile (cookies/sessions intact) -agent-browser --launch --profile auto open https://x.com/home +chrome-use --launch --profile auto open https://x.com/home # or name it explicitly: --profile Default / --profile "Profile 1" ``` > ⚠️ Plain `--launch` (no `--profile`) uses a **temporary empty profile** — you will > NOT be logged into anything. For logged-in sites use `--profile auto` (picks the -> Chrome profile you used most recently) or `--profile `. agent-browser prints +> Chrome profile you used most recently) or `--profile `. chrome-use prints > a warning when you `--launch` without a profile. In CI environments, standalone mode is used automatically. @@ -271,18 +271,17 @@ We deliberately **don't ship our own bot detector** — the strongest, most hone | `AGENT_BROWSER_ADAPTIVE_REF` | on | When a saved `@ref` moves and the role/name re-query fails, relocate it by fingerprint similarity (high score + clear margin required, else it fails loudly). `0` disables. | | `AGENT_BROWSER_CLICK_MODE` | _(auto)_ | Click strategy. Default scrolls the target into view, dispatches a coordinate click, and falls back to a DOM `.click()` if a floating layer occludes the point. `dom` always uses `.click()` (best for autocomplete/menu items that close on blur); `coord` is strict coordinate-only (hard-fail on occlusion). | -## Differences from upstream +## What makes chrome-use different -Based on [agent-browser v0.27.0](https://github.com/vercel-labs/agent-browser). Changes: +- **Auto-connect is default** — `chrome-use open ` drives your existing Chrome instead of launching a new one +- **Extension-relay transport** — a one-click Chrome Web Store extension + native messaging, so there's no debug port and no "Allow remote debugging?" dialog +- **CDP-native stealth** — anti-detection via Chrome/CDP overrides rather than JS patches; zero patches when attached to your real Chrome, full patches only for `--launch` +- **Humanize** — human-like cursor trajectories + adaptive anti-bot handling +- **Multi-agent isolation** — concurrent agents share one real Chrome via per-session tab groups, no cross-talk +- **Silent operation** — runs in the background; never steals your foreground tab -- **Auto-connect is default** — `agent-browser open ` connects to your Chrome instead of launching a new one -- **CDP-native stealth** — `Emulation.setAutomationOverride` instead of JS patches -- **Dual stealth mode** — zero patches for real Chrome, full patches for `--launch` mode -- **`--launch` / `--new` flag** — explicitly start a standalone browser -- **CI auto-detection** — standalone mode when `CI` env var is set - -All upstream features (commands, snapshots, screenshots, recordings, tabs, sessions, etc.) work the same. See the [upstream repo](https://github.com/vercel-labs/agent-browser) for full documentation. +Originally based on [vercel-labs/agent-browser](https://github.com/vercel-labs/agent-browser) (Apache-2.0); the projects have since diverged substantially. ## License -Apache-2.0 (same as upstream) +Apache-2.0 diff --git a/README.zh.md b/README.zh.md index a13a46f..582d0df 100644 --- a/README.zh.md +++ b/README.zh.md @@ -1,18 +1,18 @@ -# agent-browser-stealth +# chrome-use [English](README.md) · **简体中文** -![agent-browser-stealth](assets/hero.png) +![chrome-use](assets/hero.png) -[agent-browser](https://github.com/vercel-labs/agent-browser) 的隐身分支 —— 直接连接**你自己**正在用的、已登录的 Chrome,复用你的登录态,对反爬/反自动化系统**完全不可检测**。 +**chrome-use** 让任意 AI agent 直接操作你自己正在用的、已登录的 Chrome —— 复用你的登录态,对反爬/反自动化系统**完全不可检测**,因为它**就是**你的真实浏览器。属于 `*-use` 家族(iphone-use 驱动你的真实 iPhone,chrome-use 驱动你的真实 Chrome)。 -基础用法、命令与 API 参考见[上游文档](https://github.com/vercel-labs/agent-browser)。 +最初基于 [vercel-labs/agent-browser](https://github.com/vercel-labs/agent-browser)(Apache-2.0);现已是独立项目 —— 隐身/扩展中继架构、反检测、humanize、多 agent 隔离与 CLI 都已大幅分化。 ## 把你**已经登录好**的浏览器,交给你的 AI agent **不用开新 Chrome。不用重新登录。不用跟"你是不是机器人"较劲。** -agent-browser-stealth 让**任意** agent(Claude Code、Cursor、Codex、你自己的脚本)直接操作你**已经登录了所有网站**的那个 Chrome。它在**你的窗口里**点击,你看着它干活,撞到 2FA / 验证码的瞬间你接管一下,它接着跑。因为它**就是你的真实浏览器**(一键装的扩展、原生消息、无调试端口),网站眼里它 100% 是人:**[CreepJS 实测 0% 机器人](#反检测)。** +chrome-use 让**任意** agent(Claude Code、Cursor、Codex、你自己的脚本)直接操作你**已经登录了所有网站**的那个 Chrome。它在**你的窗口里**点击,你看着它干活,撞到 2FA / 验证码的瞬间你接管一下,它接着跑。因为它**就是你的真实浏览器**(一键装的扩展、原生消息、无调试端口),网站眼里它 100% 是人:**[CreepJS 实测 0% 机器人](#反检测)。** **为什么不用……** @@ -23,7 +23,7 @@ agent-browser-stealth 让**任意** agent(Claude Code、Cursor、Codex、你
完整对比矩阵(要细节的看这里) -| | [Claude in Chrome](https://www.anthropic.com/claude/chrome) | web-access / 裸 CDP 端口 | Playwright · Puppeteer · browser-use | **agent-browser-stealth** | +| | [Claude in Chrome](https://www.anthropic.com/claude/chrome) | web-access / 裸 CDP 端口 | Playwright · Puppeteer · browser-use | **chrome-use** | |---|:---:|:---:|:---:|:---:| | **任意** agent / CLI 都能用(不绑单一 app) | ❌ 仅 Claude | ✅ | ✅ | ✅ | | 驱动你**真实、已登录**的 Chrome | ✅ | ✅ | ❌ 全新空 profile | ✅ | @@ -37,15 +37,15 @@ agent-browser-stealth 让**任意** agent(Claude Code、Cursor、Codex、你
-## 为什么要 fork +## 为什么选 chrome-use 真实但不可检测的指纹 -**agent-browser**(上游)启动的是空 profile 的全新浏览器:你得重新登录,网站也能看出是自动化。 +**常规浏览器自动化**(Playwright / Puppeteer,或全新 `--launch`)启动的是空 profile 的全新浏览器:你得重新登录,网站也能看出是自动化。 -**agent-browser-stealth** 连接你**现有**的 Chrome —— cookies、会话、浏览器指纹全是真的,因为它**就是**你的真实浏览器。 +**chrome-use** 连接你**现有**的 Chrome —— cookies、会话、浏览器指纹全是真的,因为它**就是**你的真实浏览器。 -| | agent-browser | agent-browser-stealth | +| | 常规自动化 | chrome-use | |---|---|---| | 浏览器 | 启动新 Chrome | 连接你的 Chrome | | 登录态 | 空,要重新登 | 你现有的会话 | @@ -57,7 +57,7 @@ agent-browser-stealth 让**任意** agent(Claude Code、Cursor、Codex、你 ![工作原理](assets/how-it-works.png) -你的 **agent-browser CLI** 通过 Chrome **原生消息(native messaging)** 和一个小**浏览器扩展**通信 —— 这是本机进程间通道,**无网络端口、无 token、无远程服务器**。扩展用 `chrome.debugger` 驱动你指定的标签页(在你**已登录**的 Chrome 里),再把结果交还给 CLI。全程都在你本机。 +你的 **chrome-use CLI** 通过 Chrome **原生消息(native messaging)** 和一个小**浏览器扩展**通信 —— 这是本机进程间通道,**无网络端口、无 token、无远程服务器**。扩展用 `chrome.debugger` 驱动你指定的标签页(在你**已登录**的 Chrome 里),再把结果交还给 CLI。全程都在你本机。 ![架构](assets/architecture.png) @@ -66,34 +66,34 @@ agent-browser-stealth 让**任意** agent(Claude Code、Cursor、Codex、你 ## 安装 ```bash -curl -fsSL https://raw.githubusercontent.com/leeguooooo/agent-browser-stealth/main/install.sh | sh +curl -fsSL https://raw.githubusercontent.com/leeguooooo/chrome-use/main/install.sh | sh ``` -从最新的 [GitHub Release](https://github.com/leeguooooo/agent-browser-stealth/releases) 下载对应平台的预编译二进制,安装 `agent-browser`(以及 `abs` 别名)。无需 npm,无需 token。 +从最新的 [GitHub Release](https://github.com/leeguooooo/chrome-use/releases) 下载对应平台的预编译二进制,安装 `chrome-use`(以及 `abs` 别名)。无需 npm,无需 token。 ### 安装 AI agent skills ```bash -npx skills add leeguooooo/agent-browser-stealth +npx skills add leeguooooo/chrome-use ``` -把 `skills/agent-browser` 拉进当前项目,让你的 AI agent 拿到正确的用法和预授权的 bash 权限。 +把 `skills/chrome-use` 拉进当前项目,让你的 AI agent 拿到正确的用法和预授权的 bash 权限。 ## 连接你的 Chrome -**推荐 —— 浏览器扩展(一键,无弹窗)。** 从 Chrome 应用商店安装 [**agent-browser-stealth** 扩展](https://chromewebstore.google.com/detail/agent-browser-stealth/knfcmbamhjmaonkfnjhldjedeobeafmk),再注册一次本地桥: +**推荐 —— 浏览器扩展(一键,无弹窗)。** 从 Chrome 应用商店安装 [**chrome-use** 扩展](https://chromewebstore.google.com/detail/chrome-use/knfcmbamhjmaonkfnjhldjedeobeafmk),再注册一次本地桥: ```bash -agent-browser extension install # 注册原生消息 host(一次性) -agent-browser open https://x.com/home +chrome-use extension install # 注册原生消息 host(一次性) +chrome-use open https://x.com/home ``` -之后 `agent-browser open` 就通过**原生消息**驱动你真实、已登录的 Chrome —— 无调试端口、无 token、**永远不弹 "Allow remote debugging?"**。扩展自动更新、重启不掉,零确认(适合无人值守 / agent 场景)。 +之后 `chrome-use open` 就通过**原生消息**驱动你真实、已登录的 Chrome —— 无调试端口、无 token、**永远不弹 "Allow remote debugging?"**。扩展自动更新、重启不掉,零确认(适合无人值守 / agent 场景)。
备选 —— 裸 remote-debugging 端口(会弹同意框) -不装扩展时,agent-browser 退回用 CDP 连接,而 Chrome 只在带 remote-debugging 端口启动时才暴露它: +不装扩展时,chrome-use 退回用 CDP 连接,而 Chrome 只在带 remote-debugging 端口启动时才暴露它: ```bash # macOS @@ -103,19 +103,19 @@ google-chrome --remote-debugging-port=9222 # Windows: 给 Chrome 快捷方式 target 加 --remote-debugging-port=9222 ``` -然后 `agent-browser open ` 自动发现端口。首次连接 **Chrome 136+ 会弹 "Allow remote debugging?"** —— 点一次 Allow(该 Chrome 会话内持续有效)。上面的扩展则完全避开这个框。 +然后 `chrome-use open ` 自动发现端口。首次连接 **Chrome 136+ 会弹 "Allow remote debugging?"** —— 点一次 Allow(该 Chrome 会话内持续有效)。上面的扩展则完全避开这个框。
## 用法 ```bash # 连接你的 Chrome 并导航 -agent-browser open https://example.com +chrome-use open https://example.com # 一切都在你已登录的浏览器里进行 -agent-browser click "Post" -agent-browser fill "Title" "Hello World" -agent-browser screenshot ./page.png +chrome-use click "Post" +chrome-use fill "Title" "Hello World" +chrome-use screenshot ./page.png ``` Agent 在你的 Chrome 里操作 —— 你能实时看到开标签、加载、点击。任意时刻都能接管(比如手动过验证码),然后让 agent 继续。 @@ -124,10 +124,10 @@ Agent 在你的 Chrome 里操作 —— 你能实时看到开标签、加载、 ```bash # 临时:全新空 profile —— 无 cookie 无登录(适合 CI / 测试) -agent-browser --launch open https://example.com +chrome-use --launch open https://example.com # 保留登录:用你真实的 Chrome profile 启动 -agent-browser --launch --profile auto open https://x.com/home +chrome-use --launch --profile auto open https://x.com/home ``` ## 反检测 @@ -167,18 +167,17 @@ agent-browser --launch --profile auto open https://x.com/home 操作你的真实 Chrome 不该打断你的工作。agent **全程在后台操作**:新标签后台打开(在自己的彩色会话标签组里),**从不强制把标签拽到前台**,并用 `Emulation.setFocusEmulationEnabled` 让每个 agent 标签照常渲染、`document.hasFocus()` / `visibilityState` 仍报 `visible`。于是截图正常、页面不被降频,"标签全程隐藏"也不会变成新的机器人信号。你在自己的标签里照常工作,agent 在旁边默默干活。(想置顶某个标签仍可显式调用命令。) -## 与上游的差异 +## chrome-use 的独特之处 -基于 [agent-browser v0.27.0](https://github.com/vercel-labs/agent-browser): +- **默认 auto-connect** —— `chrome-use open` 连你现有的 Chrome 而非启新的 +- **扩展中继传输** —— 一键安装的 Chrome 商店扩展 + 原生消息,无调试端口、无 "Allow remote debugging?" 弹框 +- **CDP 原生隐身** —— 反检测走 Chrome/CDP 覆盖而非 JS 补丁;连真实 Chrome 零补丁,仅 `--launch` 用全补丁 +- **Humanize** —— 类人光标轨迹 + 自适应反爬处理 +- **多 agent 隔离** —— 多个 agent 通过 per-session 标签组共享同一个真实 Chrome,互不串扰 +- **静默运行** —— 后台操作,绝不抢你的前台标签 -- **默认 auto-connect** —— `agent-browser open` 连你的 Chrome 而非启新的 -- **CDP 原生隐身** —— `Emulation.setAutomationOverride` 而非 JS 补丁 -- **双隐身模式** —— 真实 Chrome 零补丁,`--launch` 全补丁 -- **`--launch` / `--new`** —— 显式启动独立浏览器 -- **CI 自动检测** —— 设了 `CI` 环境变量时走独立模式 - -所有上游功能(命令、快照、截图、录制、标签、会话等)保持一致。 +最初基于 [vercel-labs/agent-browser](https://github.com/vercel-labs/agent-browser)(Apache-2.0);两个项目已大幅分化。 ## License -Apache-2.0(与上游一致) +Apache-2.0 diff --git a/bin/agent-browser b/bin/agent-browser deleted file mode 100755 index 7d8ac2e..0000000 Binary files a/bin/agent-browser and /dev/null differ diff --git a/bin/agent-browser.d b/bin/agent-browser.d deleted file mode 100644 index 216b3b1..0000000 --- a/bin/agent-browser.d +++ /dev/null @@ -1 +0,0 @@ -/Users/leo/github.com/agent-browser/cli/target/release/agent-browser: /Users/leo/github.com/agent-browser/cli/build.rs /Users/leo/github.com/agent-browser/cli/cdp-protocol/browser_protocol.json /Users/leo/github.com/agent-browser/cli/cdp-protocol/js_protocol.json /Users/leo/github.com/agent-browser/cli/src/color.rs /Users/leo/github.com/agent-browser/cli/src/commands.rs /Users/leo/github.com/agent-browser/cli/src/connection.rs /Users/leo/github.com/agent-browser/cli/src/flags.rs /Users/leo/github.com/agent-browser/cli/src/install.rs /Users/leo/github.com/agent-browser/cli/src/main.rs /Users/leo/github.com/agent-browser/cli/src/output.rs /Users/leo/github.com/agent-browser/cli/src/validation.rs diff --git a/bin/agent-browser.js b/bin/chrome-use.js similarity index 96% rename from bin/agent-browser.js rename to bin/chrome-use.js index 1aba489..ced1235 100755 --- a/bin/agent-browser.js +++ b/bin/chrome-use.js @@ -1,7 +1,7 @@ #!/usr/bin/env node /** - * Cross-platform CLI wrapper for agent-browser + * Cross-platform CLI wrapper for chrome-use * * This wrapper enables npx support on Windows where shell scripts don't work. * For global installs, postinstall.js patches the shims to invoke the native @@ -62,7 +62,7 @@ function getBinaryName() { } const ext = os === 'win32' ? '.exe' : ''; - return `agent-browser-${osKey}-${archKey}${ext}`; + return `chrome-use-${osKey}-${archKey}${ext}`; } function main() { diff --git a/cli/Cargo.lock b/cli/Cargo.lock index 1d54be8..1819443 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -43,41 +43,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "agent-browser-stealth" -version = "0.27.0-fork.51" -dependencies = [ - "aes-gcm", - "async-trait", - "base64", - "chrono", - "dirs", - "futures-util", - "getrandom 0.2.17", - "hex", - "hmac", - "image", - "include_dir", - "libc", - "regex-lite", - "reqwest", - "rust-embed", - "serde", - "serde_json", - "sha2", - "similar", - "socket2", - "tempfile", - "time", - "tokio", - "tokio-tungstenite", - "url", - "urlencoding", - "uuid", - "windows-sys 0.52.0", - "zip", -] - [[package]] name = "aligned" version = "0.4.3" @@ -305,6 +270,41 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chrome-use" +version = "1.0.0" +dependencies = [ + "aes-gcm", + "async-trait", + "base64", + "chrono", + "dirs", + "futures-util", + "getrandom 0.2.17", + "hex", + "hmac", + "image", + "include_dir", + "libc", + "regex-lite", + "reqwest", + "rust-embed", + "serde", + "serde_json", + "sha2", + "similar", + "socket2", + "tempfile", + "time", + "tokio", + "tokio-tungstenite", + "url", + "urlencoding", + "uuid", + "windows-sys 0.52.0", + "zip", +] + [[package]] name = "chrono" version = "0.4.44" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 85261df..4c66534 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -1,17 +1,17 @@ [package] -name = "agent-browser-stealth" -version = "0.27.0-fork.51" +name = "chrome-use" +version = "1.0.0" edition = "2021" description = "Fast browser automation CLI for AI agents" license = "Apache-2.0" -repository = "https://github.com/leeguooooo/agent-browser-stealth" -homepage = "https://github.com/leeguooooo/agent-browser-stealth" +repository = "https://github.com/leeguooooo/chrome-use" +homepage = "https://github.com/leeguooooo/chrome-use" readme = "../README.md" keywords = ["browser", "automation", "ai", "cdp", "chrome"] categories = ["command-line-utilities", "web-programming"] [[bin]] -name = "agent-browser" +name = "chrome-use" path = "src/main.rs" [dependencies] diff --git a/cli/src/commands.rs b/cli/src/commands.rs index 4535f7a..18ecfd7 100644 --- a/cli/src/commands.rs +++ b/cli/src/commands.rs @@ -48,12 +48,12 @@ impl ParseError { } ParseError::MissingArguments { context, usage } => { format!( - "Missing arguments for: {}\nUsage: agent-browser {}", + "Missing arguments for: {}\nUsage: chrome-use {}", context, usage ) } ParseError::InvalidValue { message, usage } => { - format!("{}\nUsage: agent-browser {}", message, usage) + format!("{}\nUsage: chrome-use {}", message, usage) } ParseError::InvalidSessionName { name } => session_name_error(name), } @@ -1003,7 +1003,7 @@ fn parse_command_inner(args: &[String], flags: &Flags) -> Result { let name = rest.get(1).ok_or_else(|| ParseError::MissingArguments { context: "auth save".to_string(), - usage: "agent-browser auth save --url --username --password ", + usage: "chrome-use auth save --url --username --password ", })?; let mut url = None; @@ -1048,7 +1048,7 @@ fn parse_command_inner(args: &[String], flags: &Flags) -> Result --url --username --password ", + usage: "chrome-use auth save --url --username --password ", }); } } @@ -1058,17 +1058,17 @@ fn parse_command_inner(args: &[String], flags: &Flags) -> Result --url --username --password [--password-stdin]", + usage: "chrome-use auth save --url --username --password [--password-stdin]", })?; let user_val = username.ok_or_else(|| ParseError::MissingArguments { context: "auth save".to_string(), - usage: "agent-browser auth save --url --username --password [--password-stdin]", + usage: "chrome-use auth save --url --username --password [--password-stdin]", })?; if !password_stdin && password.is_none() { return Err(ParseError::MissingArguments { context: "auth save".to_string(), - usage: "agent-browser auth save --url --username --password [--password-stdin]", + usage: "chrome-use auth save --url --username --password [--password-stdin]", }); } @@ -1099,7 +1099,7 @@ fn parse_command_inner(args: &[String], flags: &Flags) -> Result { let name = rest.get(1).ok_or_else(|| ParseError::MissingArguments { context: "auth login".to_string(), - usage: "agent-browser auth login ", + usage: "chrome-use auth login ", })?; Ok(json!({ "id": id, "action": "auth_login", "name": name })) } @@ -1107,14 +1107,14 @@ fn parse_command_inner(args: &[String], flags: &Flags) -> Result { let name = rest.get(1).ok_or_else(|| ParseError::MissingArguments { context: "auth delete".to_string(), - usage: "agent-browser auth delete ", + usage: "chrome-use auth delete ", })?; Ok(json!({ "id": id, "action": "auth_delete", "name": name })) } Some("show") => { let name = rest.get(1).ok_or_else(|| ParseError::MissingArguments { context: "auth show".to_string(), - usage: "agent-browser auth show ", + usage: "chrome-use auth show ", })?; Ok(json!({ "id": id, "action": "auth_show", "name": name })) } @@ -1129,14 +1129,14 @@ fn parse_command_inner(args: &[String], flags: &Flags) -> Result { let cid = rest.first().ok_or_else(|| ParseError::MissingArguments { context: "confirm".to_string(), - usage: "agent-browser confirm ", + usage: "chrome-use confirm ", })?; Ok(json!({ "id": id, "action": "confirm", "confirmationId": cid })) } "deny" => { let cid = rest.first().ok_or_else(|| ParseError::MissingArguments { context: "deny".to_string(), - usage: "agent-browser deny ", + usage: "chrome-use deny ", })?; Ok(json!({ "id": id, "action": "deny", "confirmationId": cid })) } @@ -1247,7 +1247,7 @@ fn parse_command_inner(args: &[String], flags: &Flags) -> Result parse_get(&rest, &id), // Top-level shortcuts for `get ` status reads — users naturally type - // `agent-browser url` / `cdp-url` / `title` without the `get` prefix + // `chrome-use url` / `cdp-url` / `title` without the `get` prefix // (and expect `cdp-url`/`cdp_url` to work interchangeably). "url" | "cdp-url" | "cdp_url" | "title" | "html" | "text" | "value" | "count" | "box" | "styles" | "attr" => { @@ -2404,7 +2404,7 @@ fn parse_find(rest: &[&str], id: &str) -> Result { message: format!( "Missing action verb for `find {locator}` (got `{flag}` where action was expected).\n\ Valid actions: click, fill, check, hover, text\n\ - Did you mean: agent-browser find {locator} click {flag} ...?", + Did you mean: chrome-use find {locator} click {flag} ...?", locator = locator, flag = s, ), @@ -2549,12 +2549,12 @@ fn parse_find(rest: &[&str], id: &str) -> Result { // with the corrected command using their own value, then the menu. message: format!( "`{loc}` is not a find locator. To match by visible text, name the locator:\n \ - agent-browser find text \"{loc}\" click\n\n\ + chrome-use find text \"{loc}\" click\n\n\ Locators: role, text, label, placeholder, alt, title, testid, first, last, nth\n\ Examples:\n \ - agent-browser find text \"Sign in\" click\n \ - agent-browser find role button --name \"Submit\" click\n \ - agent-browser find label \"Email\" fill you@example.com", + chrome-use find text \"Sign in\" click\n \ + chrome-use find role button --name \"Submit\" click\n \ + chrome-use find label \"Email\" fill you@example.com", loc = locator, ), usage: "find [action] [text]", diff --git a/cli/src/connect.rs b/cli/src/connect.rs index d53c17e..7b43c12 100644 --- a/cli/src/connect.rs +++ b/cli/src/connect.rs @@ -1,4 +1,4 @@ -//! `agent-browser connect` — zero-confirmation control of the user's real, +//! `chrome-use connect` — zero-confirmation control of the user's real, //! logged-in Chrome via the `ab-connect` MV3 extension over Chrome **native //! messaging** (no localhost port, no token; Chrome authenticates the extension //! to this host by id). @@ -17,7 +17,7 @@ use std::path::PathBuf; /// Native-messaging host name; must match `HOST_NAME` in the extension and the /// manifest filename. -pub const HOST_NAME: &str = "com.agent_browser.connect"; +pub const HOST_NAME: &str = "com.leeguoo.chrome_use"; /// Stable id of the `ab-connect` extension, pinned by the `key` in its /// manifest.json (and the signing key of the published `.crx`). Chrome only lets @@ -44,11 +44,11 @@ pub const STORE_URL: &str = /// Stable identifiers for the generated Chrome configuration profile, so a /// re-install replaces (rather than duplicates) it in System Settings. -const PROFILE_ID: &str = "work.pwtk.agent-browser.ab-connect"; +const PROFILE_ID: &str = "work.pwtk.chrome-use.ab-connect"; const PROFILE_UUID: &str = "A1B2C3D4-AB00-4CCE-9E10-AAAABBBBCCCC"; const PROFILE_PAYLOAD_UUID: &str = "A1B2C3D4-AB01-4CCE-9E10-DDDDEEEEFFFF"; -/// `agent-browser extension ` (local; no daemon). +/// `chrome-use extension ` (local; no daemon). /// `args` is the cleaned argv including the leading "extension". pub fn run_connect(args: &[String], json: bool) { let install = args.iter().any(|a| a == "--install" || a == "install"); @@ -66,11 +66,11 @@ pub fn run_connect(args: &[String], json: bool) { } else { println!("✓ removed {removed} native-host manifest(s)."); if profile_removed { - println!("✓ removed ~/.agent-browser/ab-connect.mobileconfig"); + println!("✓ removed ~/.chrome-use/ab-connect.mobileconfig"); } if cfg!(target_os = "macos") { println!( - " To fully remove the extension, delete the \"agent-browser connect\" profile\n\ + " To fully remove the extension, delete the \"chrome-use connect\" profile\n\ in System Settings → Profiles (or run: profiles remove -identifier {PROFILE_ID})." ); } @@ -114,7 +114,7 @@ pub fn run_connect(args: &[String], json: bool) { A) One click: open {STORE_URL}\n and press \"Add to Chrome\".\n\ B) Silent: approve the profile, then restart Chrome —\n \ System Settings → General → Device Management → double-click\n \ - \"agent-browser connect\" → Install. Chrome then force-installs +\n \ + \"chrome-use connect\" → Install. Chrome then force-installs +\n \ auto-updates it (no token, no per-use confirmation).\n\ Both need the extension published to the Web Store; until then use\n \ chrome://extensions → Developer mode → Load unpacked → extensions/ab-connect." @@ -156,23 +156,23 @@ pub fn run_connect(args: &[String], json: bool) { println!("✓ native-messaging host installed ({HOST_NAME})."); println!(" Load the ab-connect extension and it connects automatically."); } else { - println!("✗ not installed. Run: agent-browser connect --install"); + println!("✗ not installed. Run: chrome-use connect --install"); } } /// Write the launcher script + native-messaging host manifest(s). fn install_native_host() -> Result, String> { let home = dirs::home_dir().ok_or("no home dir")?; - let ab_dir = home.join(".agent-browser"); + let ab_dir = home.join(".chrome-use"); std::fs::create_dir_all(&ab_dir).map_err(|e| e.to_string())?; // Chrome execs the manifest `path` directly with the calling extension's // origin as argv[1]; a launcher lets us run the binary in __nm-host mode - // regardless of how/where agent-browser is installed. + // regardless of how/where chrome-use is installed. let exe = std::env::current_exe().map_err(|e| e.to_string())?; let launcher = ab_dir.join("nm-host.sh"); let script = format!( - "#!/bin/sh\n# agent-browser native-messaging host launcher (auto-generated)\nexec \"{}\" __nm-host \"$@\"\n", + "#!/bin/sh\n# chrome-use native-messaging host launcher (auto-generated)\nexec \"{}\" __nm-host \"$@\"\n", exe.display() ); std::fs::write(&launcher, script).map_err(|e| e.to_string())?; @@ -184,7 +184,7 @@ fn install_native_host() -> Result, String> { let manifest = serde_json::json!({ "name": HOST_NAME, - "description": "agent-browser connect — native messaging host", + "description": "chrome-use connect — native messaging host", "path": launcher.display().to_string(), "type": "stdio", "allowed_origins": [ @@ -223,7 +223,7 @@ fn install_force_install_profile(no_open: bool) -> Result { .into()); } let home = dirs::home_dir().ok_or("no home dir")?; - let ab_dir = home.join(".agent-browser"); + let ab_dir = home.join(".chrome-use"); std::fs::create_dir_all(&ab_dir).map_err(|e| e.to_string())?; let path = ab_dir.join("ab-connect.mobileconfig"); std::fs::write(&path, force_install_mobileconfig()).map_err(|e| e.to_string())?; @@ -254,7 +254,7 @@ fn force_install_mobileconfig() -> String { PayloadIdentifier{PROFILE_ID}.chrome PayloadUUID{PROFILE_PAYLOAD_UUID} PayloadEnabled - PayloadDisplayNameagent-browser connect (Chrome) + PayloadDisplayNamechrome-use connect (Chrome) ExtensionInstallForcelist {forcelist} @@ -265,9 +265,9 @@ fn force_install_mobileconfig() -> String { PayloadVersion1 PayloadIdentifier{PROFILE_ID} PayloadUUID{PROFILE_UUID} - PayloadDisplayNameagent-browser connect - PayloadDescriptionForce-installs the agent-browser connect extension so agent-browser can drive your logged-in Chrome. No token, no per-use confirmation. - PayloadOrganizationagent-browser-stealth + PayloadDisplayNamechrome-use connect + PayloadDescriptionForce-installs the chrome-use connect extension so chrome-use can drive your logged-in Chrome. No token, no per-use confirmation. + PayloadOrganizationchrome-use PayloadScopeUser PayloadRemovalDisallowed @@ -280,7 +280,7 @@ fn force_install_mobileconfig() -> String { /// the user from System Settings, or via `profiles remove`). fn remove_force_install_profile() -> bool { dirs::home_dir() - .map(|h| h.join(".agent-browser").join("ab-connect.mobileconfig")) + .map(|h| h.join(".chrome-use").join("ab-connect.mobileconfig")) .filter(|p| p.exists()) .map(|p| std::fs::remove_file(&p).is_ok()) .unwrap_or(false) @@ -378,7 +378,7 @@ fn report(json: bool, ok: bool, msg: &str) { fn nm_log(line: &str) { let path = dirs::home_dir() - .map(|h| h.join(".agent-browser").join("nm-host.log")) + .map(|h| h.join(".chrome-use").join("nm-host.log")) .unwrap_or_else(|| PathBuf::from("/tmp/ab-nm-host.log")); if let Some(p) = path.parent() { let _ = std::fs::create_dir_all(p); @@ -401,13 +401,13 @@ fn random_guid() -> String { /// Where the daemon/CLI reads the relay's CDP WebSocket URL (perms 600). fn relay_url_path() -> PathBuf { dirs::home_dir() - .map(|h| h.join(".agent-browser").join("relay-cdp-url")) + .map(|h| h.join(".chrome-use").join("relay-cdp-url")) .unwrap_or_else(|| PathBuf::from("/tmp/ab-relay-cdp-url")) } /// The live relay CDP WebSocket URL, if the native-messaging host is running /// (it writes the file on connect and removes it on exit). Used by -/// `agent-browser extension connect` to attach without the user copying a URL. +/// `chrome-use extension connect` to attach without the user copying a URL. pub fn relay_url() -> Option { let s = std::fs::read_to_string(relay_url_path()).ok()?; let s = s.trim().to_string(); @@ -421,10 +421,10 @@ pub fn relay_url() -> Option { /// Hidden `__nm-host` mode: launched by Chrome for the ab-connect extension. /// /// Bridges the extension (native-messaging stdio, envelope protocol) to a local -/// **CDP WebSocket endpoint** that agent-browser connects to like any Chrome. +/// **CDP WebSocket endpoint** that chrome-use connects to like any Chrome. /// `relay::RelayState` translates envelope ⇄ raw CDP and emulates browser-level /// Target discovery. The ws URL carries an unguessable guid (written to a 600 -/// file) so only this user's agent-browser — not arbitrary local processes — +/// file) so only this user's chrome-use — not arbitrary local processes — /// can drive the browser. No token, no user interaction. pub fn run_nm_host() { let rt = match tokio::runtime::Builder::new_multi_thread() @@ -496,7 +496,7 @@ async fn nm_host_main() { } }); - // Accept agent-browser CDP clients on the guid-scoped ws endpoint. + // Accept chrome-use CDP clients on the guid-scoped ws endpoint. { let state = state.clone(); let clients = clients.clone(); diff --git a/cli/src/connection.rs b/cli/src/connection.rs index 828735e..59cbe5b 100644 --- a/cli/src/connection.rs +++ b/cli/src/connection.rs @@ -89,7 +89,7 @@ impl Connection { } /// Get the base directory for socket/pid files. -/// Priority: AGENT_BROWSER_SOCKET_DIR > XDG_RUNTIME_DIR > ~/.agent-browser > tmpdir +/// Priority: AGENT_BROWSER_SOCKET_DIR > XDG_RUNTIME_DIR > ~/.chrome-use > tmpdir pub fn get_socket_dir() -> PathBuf { // 1. Explicit override (ignore empty string) if let Ok(dir) = env::var("AGENT_BROWSER_SOCKET_DIR") { @@ -101,17 +101,17 @@ pub fn get_socket_dir() -> PathBuf { // 2. XDG_RUNTIME_DIR (Linux standard, ignore empty string) if let Ok(runtime_dir) = env::var("XDG_RUNTIME_DIR") { if !runtime_dir.is_empty() { - return PathBuf::from(runtime_dir).join("agent-browser"); + return PathBuf::from(runtime_dir).join("chrome-use"); } } // 3. Home directory fallback (like Docker Desktop's ~/.docker/run/) if let Some(home) = dirs::home_dir() { - return home.join(".agent-browser"); + return home.join(".chrome-use"); } // 4. Last resort: temp dir - env::temp_dir().join("agent-browser") + env::temp_dir().join("chrome-use") } #[cfg(unix)] @@ -949,9 +949,7 @@ mod tests { _guard.set("AGENT_BROWSER_SOCKET_DIR", ""); _guard.remove("XDG_RUNTIME_DIR"); - assert!(get_socket_dir() - .to_string_lossy() - .ends_with(".agent-browser")); + assert!(get_socket_dir().to_string_lossy().ends_with(".chrome-use")); } #[test] @@ -961,10 +959,7 @@ mod tests { _guard.remove("AGENT_BROWSER_SOCKET_DIR"); _guard.set("XDG_RUNTIME_DIR", "/run/user/1000"); - assert_eq!( - get_socket_dir(), - PathBuf::from("/run/user/1000/agent-browser") - ); + assert_eq!(get_socket_dir(), PathBuf::from("/run/user/1000/chrome-use")); } #[test] @@ -974,9 +969,7 @@ mod tests { _guard.set("AGENT_BROWSER_SOCKET_DIR", ""); _guard.set("XDG_RUNTIME_DIR", ""); - assert!(get_socket_dir() - .to_string_lossy() - .ends_with(".agent-browser")); + assert!(get_socket_dir().to_string_lossy().ends_with(".chrome-use")); } #[test] @@ -987,7 +980,7 @@ mod tests { _guard.remove("XDG_RUNTIME_DIR"); let result = get_socket_dir(); - assert!(result.to_string_lossy().ends_with(".agent-browser")); + assert!(result.to_string_lossy().ends_with(".chrome-use")); assert!( result.to_string_lossy().contains("home") || result.to_string_lossy().contains("Users") ); diff --git a/cli/src/doctor/chrome.rs b/cli/src/doctor/chrome.rs index 612bd55..ab32f4c 100644 --- a/cli/src/doctor/chrome.rs +++ b/cli/src/doctor/chrome.rs @@ -36,7 +36,7 @@ pub(super) fn check(checks: &mut Vec) { Status::Fail, "No Chrome binary found", ) - .with_fix("agent-browser install"), + .with_fix("chrome-use install"), ), } diff --git a/cli/src/doctor/config.rs b/cli/src/doctor/config.rs index 751c127..090e052 100644 --- a/cli/src/doctor/config.rs +++ b/cli/src/doctor/config.rs @@ -1,5 +1,5 @@ -//! Check user config files: `~/.agent-browser/config.json`, -//! `./agent-browser.json`, and any file referenced by +//! Check user config files: `~/.chrome-use/config.json`, +//! `./chrome-use.json`, and any file referenced by //! `AGENT_BROWSER_CONFIG`. use std::env; @@ -11,7 +11,7 @@ use super::{Check, Status}; pub(super) fn check(checks: &mut Vec) { let category = "Config"; - let user_path = dirs::home_dir().map(|d| d.join(".agent-browser").join("config.json")); + let user_path = dirs::home_dir().map(|d| d.join(".chrome-use").join("config.json")); if let Some(p) = user_path { if p.exists() { match parse_json_file(&p) { @@ -34,7 +34,7 @@ pub(super) fn check(checks: &mut Vec) { } } - let project_path = PathBuf::from("agent-browser.json"); + let project_path = PathBuf::from("chrome-use.json"); if project_path.exists() { match parse_json_file(&project_path) { Ok(_) => checks.push(Check::new( diff --git a/cli/src/doctor/daemon.rs b/cli/src/doctor/daemon.rs index b2f5fc1..3e06e41 100644 --- a/cli/src/doctor/daemon.rs +++ b/cli/src/doctor/daemon.rs @@ -51,7 +51,7 @@ pub(super) fn check(checks: &mut Vec) { format!("Session {} (pid {}){}", session.name, session.pid, suffix), ); if !version_match { - check = check.with_fix(format!("agent-browser --session {} close", session.name)); + check = check.with_fix(format!("chrome-use --session {} close", session.name)); } checks.push(check); } diff --git a/cli/src/doctor/environment.rs b/cli/src/doctor/environment.rs index db6e159..97c0a5f 100644 --- a/cli/src/doctor/environment.rs +++ b/cli/src/doctor/environment.rs @@ -39,7 +39,7 @@ pub(super) fn check(checks: &mut Vec) { let socket_dir = get_socket_dir(); // Under the default setup, state and socket dirs are the same - // (~/.agent-browser). Collapse to a single line when they match; + // (~/.chrome-use). Collapse to a single line when they match; // split when XDG_RUNTIME_DIR or AGENT_BROWSER_SOCKET_DIR diverts // sockets elsewhere. if state_dir == socket_dir { diff --git a/cli/src/doctor/fix.rs b/cli/src/doctor/fix.rs index 233ff70..9996172 100644 --- a/cli/src/doctor/fix.rs +++ b/cli/src/doctor/fix.rs @@ -240,8 +240,8 @@ mod tests { "fixed summary should mention the key generation" ); assert!( - tmp.path().join(".agent-browser/.encryption-key").exists(), - "key file should exist at ~/.agent-browser/.encryption-key" + tmp.path().join(".chrome-use/.encryption-key").exists(), + "key file should exist at ~/.chrome-use/.encryption-key" ); } } diff --git a/cli/src/doctor/helpers.rs b/cli/src/doctor/helpers.rs index 30c168f..05c36bc 100644 --- a/cli/src/doctor/helpers.rs +++ b/cli/src/doctor/helpers.rs @@ -143,7 +143,7 @@ mod tests { }; assert!(which_exists(probe)); assert!(!which_exists( - "agent-browser-this-does-not-exist-please-dont-install-it" + "chrome-use-this-does-not-exist-please-dont-install-it" )); } diff --git a/cli/src/doctor/launch.rs b/cli/src/doctor/launch.rs index 7d8a09c..888f0c3 100644 --- a/cli/src/doctor/launch.rs +++ b/cli/src/doctor/launch.rs @@ -114,7 +114,7 @@ pub(super) fn check(checks: &mut Vec) { Status::Fail, format!("Browser launch failed: {}", e), ) - .with_fix("agent-browser install # or check --debug output"), + .with_fix("chrome-use install # or check --debug output"), ); return; } diff --git a/cli/src/doctor/mod.rs b/cli/src/doctor/mod.rs index 4aa131e..51c4484 100644 --- a/cli/src/doctor/mod.rs +++ b/cli/src/doctor/mod.rs @@ -1,4 +1,4 @@ -//! Diagnose an agent-browser installation. +//! Diagnose an chrome-use installation. //! //! Runs a battery of checks across environment, Chrome install, daemon //! state, config files, encryption, providers, network reachability, and @@ -151,7 +151,7 @@ fn summarize(checks: &[Check]) -> Summary { } fn print_text(checks: &[Check], summary: &Summary, fixed: &[String], fix_ran: bool) { - println!("{}", color::bold("agent-browser doctor")); + println!("{}", color::bold("chrome-use doctor")); let mut current_category = ""; for c in checks { diff --git a/cli/src/doctor/network.rs b/cli/src/doctor/network.rs index 6eedabd..7eb34ee 100644 --- a/cli/src/doctor/network.rs +++ b/cli/src/doctor/network.rs @@ -27,7 +27,7 @@ pub(super) fn check(checks: &mut Vec) { }; let client = match reqwest::Client::builder() - .user_agent(format!("agent-browser/{}", env!("CARGO_PKG_VERSION"))) + .user_agent(format!("chrome-use/{}", env!("CARGO_PKG_VERSION"))) .timeout(Duration::from_secs(3)) .connect_timeout(Duration::from_secs(3)) .build() diff --git a/cli/src/doctor/security.rs b/cli/src/doctor/security.rs index 77618a8..03f0240 100644 --- a/cli/src/doctor/security.rs +++ b/cli/src/doctor/security.rs @@ -115,7 +115,7 @@ pub(super) fn check(checks: &mut Vec) { ), ) .with_fix(format!( - "agent-browser state clean --older-than {}", + "chrome-use state clean --older-than {}", expire_days )), ); diff --git a/cli/src/flags.rs b/cli/src/flags.rs index e21c54f..ea92e6f 100644 --- a/cli/src/flags.rs +++ b/cli/src/flags.rs @@ -4,9 +4,9 @@ use std::env; use std::fs; use std::path::{Path, PathBuf}; -const CONFIG_DIR: &str = ".agent-browser"; +const CONFIG_DIR: &str = ".chrome-use"; const CONFIG_FILENAME: &str = "config.json"; -const PROJECT_CONFIG_FILENAME: &str = "agent-browser.json"; +const PROJECT_CONFIG_FILENAME: &str = "chrome-use.json"; /// Parse idle timeout from user-friendly format. /// Supports: "10s" (seconds), "3m" (minutes), "1h" (hours), or raw milliseconds. @@ -1344,7 +1344,7 @@ mod tests { #[test] fn test_load_config_missing_file_returns_none() { - let result = read_config_file(&PathBuf::from("/nonexistent/agent-browser.json")); + let result = read_config_file(&PathBuf::from("/nonexistent/chrome-use.json")); assert!(result.is_none()); } diff --git a/cli/src/install.rs b/cli/src/install.rs index 467c9c4..fc19b81 100644 --- a/cli/src/install.rs +++ b/cli/src/install.rs @@ -10,7 +10,7 @@ const LAST_KNOWN_GOOD_URL: &str = pub fn get_browsers_dir() -> PathBuf { dirs::home_dir() .unwrap_or_else(|| PathBuf::from(".")) - .join(".agent-browser") + .join(".chrome-use") .join("browsers") } @@ -238,7 +238,7 @@ fn format_reqwest_error(e: &reqwest::Error) -> String { fn http_client() -> Result { reqwest::Client::builder() - .user_agent(format!("agent-browser/{}", env!("CARGO_PKG_VERSION"))) + .user_agent(format!("chrome-use/{}", env!("CARGO_PKG_VERSION"))) .timeout(std::time::Duration::from_secs(120)) .connect_timeout(std::time::Duration::from_secs(30)) .build() @@ -406,7 +406,7 @@ pub fn run_install(with_deps: bool) { eprintln!(" Install Chromium from your system package manager instead:"); eprintln!(" sudo apt install chromium-browser # Debian/Ubuntu"); eprintln!(" sudo dnf install chromium # Fedora"); - eprintln!(" Then use: agent-browser --executable-path /usr/bin/chromium"); + eprintln!(" Then use: chrome-use --executable-path /usr/bin/chromium"); exit(1); } @@ -420,7 +420,7 @@ pub fn run_install(with_deps: bool) { "{} Linux detected. If browser fails to launch, run:", color::warning_indicator() ); - println!(" agent-browser install --with-deps"); + println!(" chrome-use install --with-deps"); println!(); } } @@ -486,7 +486,7 @@ pub fn run_install(with_deps: bool) { "{} If you see \"shared library\" errors when running, use:", color::yellow("Note:") ); - println!(" agent-browser install --with-deps"); + println!(" chrome-use install --with-deps"); } } Err(e) => { @@ -930,7 +930,7 @@ mod tests { let url = format!("http://127.0.0.1:{}/test", port); let _ = client.get(&url).send().await; let request_text = server.await.unwrap(); - let expected_ua = format!("agent-browser/{}", env!("CARGO_PKG_VERSION")); + let expected_ua = format!("chrome-use/{}", env!("CARGO_PKG_VERSION")); assert!( request_text.contains(&expected_ua), "expected User-Agent '{}' in request:\n{}", diff --git a/cli/src/main.rs b/cli/src/main.rs index a9d5cda..000c972 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -504,7 +504,7 @@ fn main() { env::set_var("MSYS2_ARG_CONV_EXCL", "*"); } - // Native-messaging host mode: Chrome launches `agent-browser __nm-host + // Native-messaging host mode: Chrome launches `chrome-use __nm-host // [...]` for the ab-connect extension. Must run before // ANY stdout write — stdout is the Chrome native-messaging channel. if env::args().nth(1).as_deref() == Some("__nm-host") { @@ -548,7 +548,7 @@ fn main() { if flags.force_launch && flags.profile.is_none() && env::var("CI").is_err() { eprintln!( "⚠ --launch opens a fresh, isolated test profile (no cookies, no login, no \ - extensions). The window is labelled `agent-browser ()` in Chrome's \ + extensions). The window is labelled `chrome-use ()` in Chrome's \ profile menu so you can tell it apart from your real browser.\n \ • reuse your real Chrome (cookies/login/extensions): `--profile auto` \ (or set AGENT_BROWSER_PROFILE=auto once)\n \ @@ -674,7 +674,7 @@ fn main() { } None => { eprintln!( - "{} extension not connected. Run `agent-browser extension install`, load the\n ab-connect extension in Chrome (chrome://extensions → Developer mode →\n Load unpacked → extensions/ab-connect), then retry.", + "{} extension not connected. Run `chrome-use extension install`, load the\n ab-connect extension in Chrome (chrome://extensions → Developer mode →\n Load unpacked → extensions/ab-connect), then retry.", color::error_indicator() ); exit(1); @@ -910,7 +910,7 @@ fn main() { if !ignored_flags.is_empty() && !flags.json { // Special case: --headed is irrelevant in CDP-attach mode // (your existing Chrome is always already visible). The - // "agent-browser close + reopen" advice doesn't help because + // "chrome-use close + reopen" advice doesn't help because // the new daemon will attach right back to the same Chrome. // Don't suggest a useless workaround. if ignored_flags == ["--headed"] { @@ -921,7 +921,7 @@ fn main() { ); } else { eprintln!( - "{} {} ignored: daemon already running. Use 'agent-browser close' first to restart with new options.", + "{} {} ignored: daemon already running. Use 'chrome-use close' first to restart with new options.", color::warning_indicator(), ignored_flags.join(", ") ); @@ -1315,7 +1315,7 @@ fn main() { .and_then(|v| v.as_str()) .unwrap_or(""); - eprintln!("[agent-browser] Action requires confirmation:"); + eprintln!("[chrome-use] Action requires confirmation:"); eprintln!(" {}: {}", category, desc); eprint!(" Allow? [y/N]: "); diff --git a/cli/src/native/actions.rs b/cli/src/native/actions.rs index 74d3c31..1e0dd90 100644 --- a/cli/src/native/actions.rs +++ b/cli/src/native/actions.rs @@ -1124,7 +1124,7 @@ impl DaemonState { } Err(broadcast::error::TryRecvError::Empty) => break, Err(broadcast::error::TryRecvError::Lagged(n)) => { - eprintln!("[agent-browser] Warning: CDP event buffer overflowed, {} events dropped. Network requests may be missing from HAR output.", n); + eprintln!("[chrome-use] Warning: CDP event buffer overflowed, {} events dropped. Network requests may be missing from HAR output.", n); continue; } Err(broadcast::error::TryRecvError::Closed) => { @@ -1540,7 +1540,7 @@ async fn connect_auto_with_fresh_tab() -> Result { // before returning success. Without this, a zombie CDP socket (process // alive, websocket dead) would let `connect_auto` and `tab_new` succeed, // we'd return Ok, the next user command would silently no-op, and - // `agent-browser open URL` would exit 0 with the browser still on + // `chrome-use open URL` would exit 0 with the browser still on // about:blank. Failing here lets the caller surface the real error. if let Err(e) = mgr .client @@ -1557,7 +1557,7 @@ async fn connect_auto_with_fresh_tab() -> Result { return Err(format!( "CDP session is unresponsive after attaching ({}). \ The browser may have lost its DevTools connection. \ - Try: agent-browser close, then re-run.", + Try: chrome-use close, then re-run.", e )); } @@ -1634,11 +1634,11 @@ async fn auto_launch(state: &mut DaemonState) -> Result<(), String> { return Err(format!( "Could not connect to your Chrome browser.\n\n\ If Chrome showed an \"Allow remote debugging?\" dialog, click \ - Allow and re-run — that consent is what lets agent-browser attach.\n\n\ - Otherwise, to let agent-browser reuse your logged-in Chrome (recommended):\n\ + Allow and re-run — that consent is what lets chrome-use attach.\n\n\ + Otherwise, to let chrome-use reuse your logged-in Chrome (recommended):\n\ {}\n\n\ Or launch a separate browser that KEEPS your login state:\n \ - agent-browser --launch --profile auto open \n\ + chrome-use --launch --profile auto open \n\ (plain `--launch` alone uses a temporary EMPTY profile — no cookies, \ no logged-in sessions.)\n\n\ Note: remote debugging is a startup flag, not a Chrome setting — \ @@ -1777,15 +1777,15 @@ fn chrome_relaunch_hint() -> &'static str { if cfg!(target_os = "macos") { " 1. Quit Chrome completely\n\ 2. Run: open -a \"Google Chrome\" --args --remote-debugging-port=9222\n\ - 3. Then retry your agent-browser command" + 3. Then retry your chrome-use command" } else if cfg!(target_os = "windows") { " 1. Close Chrome completely\n\ 2. Run: start chrome --remote-debugging-port=9222\n\ - 3. Then retry your agent-browser command" + 3. Then retry your chrome-use command" } else { " 1. Close Chrome completely\n\ 2. Run: google-chrome --remote-debugging-port=9222\n\ - 3. Then retry your agent-browser command" + 3. Then retry your chrome-use command" } } @@ -1854,7 +1854,7 @@ async fn apply_stealth_to_browser(state: &DaemonState) { /// If the previous daemon left a `.restore-url` sidecar (because it was killed /// by a version-mismatch restart), navigate the freshly-connected browser to -/// that URL so `agent-browser get url` after `npm i -g` upgrade still reports +/// that URL so `chrome-use get url` after `npm i -g` upgrade still reports /// the page the user was on. Read-and-delete: the file is removed regardless /// of whether navigation succeeds, so a stale sidecar can't haunt later /// auto-launches. @@ -2204,11 +2204,11 @@ async fn handle_launch(cmd: &Value, state: &mut DaemonState) -> Result\n\ + chrome-use --launch --profile auto open \n\ (plain `--launch` alone uses a temporary EMPTY profile — no cookies, \ no logged-in sessions.)\n\n\ Note: remote debugging is a startup flag, not a Chrome setting — \ @@ -4897,7 +4897,7 @@ async fn handle_pdf(cmd: &Value, state: &DaemonState) -> Result { None => { let dir = dirs::home_dir() .unwrap_or_else(std::env::temp_dir) - .join(".agent-browser") + .join(".chrome-use") .join("tmp") .join("pdfs"); let _ = std::fs::create_dir_all(&dir); @@ -6435,7 +6435,7 @@ async fn handle_getbyrole(cmd: &Value, state: &mut DaemonState) -> Result Result Result format!( r#"(() => {{ const el = document.querySelector('input[placeholder={val}], textarea[placeholder={val}]'); - if (el) {{ el.setAttribute('data-agent-browser-located', 'true'); return true; }} + if (el) {{ el.setAttribute('data-chrome-use-located', 'true'); return true; }} return false; }})()"#, val = serde_json::to_string(value).unwrap_or_default(), @@ -6536,7 +6536,7 @@ async fn handle_semantic_locator( "alttext" => format!( r#"(() => {{ const el = document.querySelector('img[alt={val}], [alt={val}]'); - if (el) {{ el.setAttribute('data-agent-browser-located', 'true'); return true; }} + if (el) {{ el.setAttribute('data-chrome-use-located', 'true'); return true; }} return false; }})()"#, val = serde_json::to_string(value).unwrap_or_default(), @@ -6544,7 +6544,7 @@ async fn handle_semantic_locator( "title" => format!( r#"(() => {{ const el = document.querySelector('[title={val}]'); - if (el) {{ el.setAttribute('data-agent-browser-located', 'true'); return true; }} + if (el) {{ el.setAttribute('data-chrome-use-located', 'true'); return true; }} return false; }})()"#, val = serde_json::to_string(value).unwrap_or_default(), @@ -6552,7 +6552,7 @@ async fn handle_semantic_locator( "testid" => format!( r#"(() => {{ const el = document.querySelector('[data-testid={val}]'); - if (el) {{ el.setAttribute('data-agent-browser-located', 'true'); return true; }} + if (el) {{ el.setAttribute('data-chrome-use-located', 'true'); return true; }} return false; }})()"#, val = serde_json::to_string(value).unwrap_or_default(), @@ -6564,7 +6564,7 @@ async fn handle_semantic_locator( const all = document.querySelectorAll('*'); for (const el of all) {{ if (el.children.length === 0 && {match_fn}) {{ - el.setAttribute('data-agent-browser-located', 'true'); + el.setAttribute('data-chrome-use-located', 'true'); return true; }} }} @@ -6598,13 +6598,13 @@ async fn handle_semantic_locator( return Err(format!("No element found by {} '{}'", strategy, value)); } - let selector = "[data-agent-browser-located='true']"; + let selector = "[data-chrome-use-located='true']"; let action_result = execute_subaction(cmd, state, selector).await; if let Some(ref browser) = state.browser { let _ = browser .evaluate( - "document.querySelector('[data-agent-browser-located]')?.removeAttribute('data-agent-browser-located')", + "document.querySelector('[data-chrome-use-located]')?.removeAttribute('data-chrome-use-located')", None, ) .await; @@ -6654,7 +6654,7 @@ async fn handle_nth(cmd: &Value, state: &mut DaemonState) -> Result= els.length) return false; - els[idx].setAttribute('data-agent-browser-located', 'true'); + els[idx].setAttribute('data-chrome-use-located', 'true'); return true; }})()"#, sel = serde_json::to_string(selector).unwrap_or_default(), @@ -6687,13 +6687,13 @@ async fn handle_nth(cmd: &Value, state: &mut DaemonState) -> Result Result) -> String { fn get_har_dir() -> PathBuf { if let Some(home) = dirs::home_dir() { - home.join(".agent-browser").join("tmp").join("har") + home.join(".chrome-use").join("tmp").join("har") } else { - std::env::temp_dir().join("agent-browser").join("har") + std::env::temp_dir().join("chrome-use").join("har") } } @@ -8902,10 +8902,7 @@ mod tests { .duration_since(std::time::UNIX_EPOCH) .expect("system clock should be after unix epoch") .as_nanos(); - std::env::temp_dir().join(format!( - "agent-browser-{label}-{}-{nanos}", - std::process::id() - )) + std::env::temp_dir().join(format!("chrome-use-{label}-{}-{nanos}", std::process::id())) } #[tokio::test] @@ -9451,7 +9448,7 @@ mod tests { let har: Value = serde_json::from_str(&fs::read_to_string(path).unwrap()).unwrap(); assert_eq!(har["log"]["version"], "1.2"); - assert_eq!(har["log"]["creator"]["name"], "agent-browser"); + assert_eq!(har["log"]["creator"]["name"], "chrome-use"); assert!(har["log"].get("browser").is_none()); assert_eq!(har["log"]["entries"][0]["response"]["content"]["size"], 128); @@ -9461,7 +9458,7 @@ mod tests { #[tokio::test] async fn test_execute_har_stop_skips_browser_auto_launch() { let path = std::env::temp_dir().join(format!( - "agent-browser-har-stop-{}.har", + "chrome-use-har-stop-{}.har", unix_timestamp_millis() )); let mut state = DaemonState::new(); diff --git a/cli/src/native/auth.rs b/cli/src/native/auth.rs index c157761..31b9504 100644 --- a/cli/src/native/auth.rs +++ b/cli/src/native/auth.rs @@ -44,9 +44,9 @@ fn validate_profile_name(name: &str) -> Result<(), String> { fn get_auth_dir() -> PathBuf { if let Some(home) = dirs::home_dir() { - home.join(".agent-browser").join("auth") + home.join(".chrome-use").join("auth") } else { - std::env::temp_dir().join("agent-browser").join("auth") + std::env::temp_dir().join("chrome-use").join("auth") } } @@ -59,9 +59,9 @@ const KEY_FILE_NAME: &str = ".encryption-key"; fn get_agent_browser_dir() -> PathBuf { if let Some(home) = dirs::home_dir() { - home.join(".agent-browser") + home.join(".chrome-use") } else { - std::env::temp_dir().join("agent-browser") + std::env::temp_dir().join("chrome-use") } } @@ -81,7 +81,7 @@ fn parse_key_hex(hex_str: &str) -> Option> { } /// Read the encryption key from AGENT_BROWSER_ENCRYPTION_KEY env var or -/// ~/.agent-browser/.encryption-key file (matching the Node.js implementation). +/// ~/.chrome-use/.encryption-key file (matching the Node.js implementation). fn get_encryption_key() -> Result, String> { if let Ok(key_hex) = std::env::var(ENCRYPTION_KEY_ENV) { return parse_key_hex(&key_hex).ok_or_else(|| { @@ -140,7 +140,7 @@ fn ensure_encryption_key() -> Result, String> { let _ = writeln!( std::io::stderr(), - "[agent-browser] Auto-generated encryption key at {} -- back up this file or set {}", + "[chrome-use] Auto-generated encryption key at {} -- back up this file or set {}", key_file.display(), ENCRYPTION_KEY_ENV ); diff --git a/cli/src/native/browser.rs b/cli/src/native/browser.rs index 2540a68..e2663c4 100644 --- a/cli/src/native/browser.rs +++ b/cli/src/native/browser.rs @@ -221,7 +221,7 @@ impl TabRef { if input.chars().all(|c| c.is_ascii_digit()) { return Err(format!( "Expected a tab id like `t{}` or a label; positional integers are not accepted \ - (run `agent-browser tab` to list stable tab ids)", + (run `chrome-use tab` to list stable tab ids)", input )); } @@ -1179,7 +1179,7 @@ impl BrowserManager { Ok(*id) } else { Err(format!( - "Tab {} not found; run `agent-browser tab` to list open tabs", + "Tab {} not found; run `chrome-use tab` to list open tabs", format_tab_id(*id) )) } @@ -1191,7 +1191,7 @@ impl BrowserManager { .map(|p| p.tab_id) .ok_or_else(|| { format!( - "No tab with label `{}`; run `agent-browser tab` to list open tabs", + "No tab with label `{}`; run `chrome-use tab` to list open tabs", name ) }), diff --git a/cli/src/native/cdp/chrome.rs b/cli/src/native/cdp/chrome.rs index 7203b9d..84befe4 100644 --- a/cli/src/native/cdp/chrome.rs +++ b/cli/src/native/cdp/chrome.rs @@ -181,14 +181,14 @@ fn webrtc_ip_handling_policy(has_proxy: bool) -> Option<&'static str> { } /// Seed a throwaway `--launch` profile with a human-readable name -/// (`agent-browser ()`) so Chrome's toolbar profile chip identifies the +/// (`chrome-use ()`) so Chrome's toolbar profile chip identifies the /// window as an agent's test profile rather than an anonymous empty profile /// (issue #9). The name lives in `Local State`'s `profile.info_cache..name` /// — the same field `resolve_chrome_profile("auto")` reads. Best-effort: any /// write error is ignored (the profile still works, just unlabeled). fn write_temp_profile_label(dir: &std::path::Path) { let session = std::env::var("AGENT_BROWSER_SESSION").unwrap_or_else(|_| "default".to_string()); - let label = format!("agent-browser ({session})"); + let label = format!("chrome-use ({session})"); let local_state = serde_json::json!({ "profile": { "info_cache": { @@ -246,7 +246,7 @@ fn build_chrome_args(options: &LaunchOptions) -> Result { args.push("--headless=new".to_string()); // Linux paints native scrollbars into viewport screenshots unless // Chrome is launched with this flag. `--hide-scrollbars` is - // presence-based, so agent-browser exposes --hide-scrollbars false + // presence-based, so chrome-use exposes --hide-scrollbars false // as the public opt-out instead of forwarding a fake inverse switch. if options.hide_scrollbars { args.push("--hide-scrollbars".to_string()); @@ -286,8 +286,7 @@ fn build_chrome_args(options: &LaunchOptions) -> Result { args.push(format!("--user-data-dir={}", expanded)); (dir, None) } else { - let dir = - std::env::temp_dir().join(format!("agent-browser-chrome-{}", uuid::Uuid::new_v4())); + let dir = std::env::temp_dir().join(format!("chrome-use-chrome-{}", uuid::Uuid::new_v4())); std::fs::create_dir_all(&dir) .map_err(|e| format!("Failed to create temp profile dir: {}", e))?; // Label the throwaway profile so a human watching the desktop can tell @@ -349,11 +348,11 @@ pub fn launch_chrome(options: &LaunchOptions) -> Result { let cache_dir = crate::install::get_browsers_dir(); format!( "Chrome not found. Checked:\n \ - - agent-browser cache: {}\n \ + - chrome-use cache: {}\n \ - System Chrome installations\n \ - Puppeteer browser cache\n \ - Playwright browser cache\n\ - Run `agent-browser install` to download Chrome, or use --executable-path.", + Run `chrome-use install` to download Chrome, or use --executable-path.", cache_dir.display() ) })?, @@ -639,7 +638,7 @@ fn chrome_launch_error(message: &str, stderr_lines: &[String]) -> String { } pub fn find_chrome() -> Option { - // 1. Check Chrome downloaded by `agent-browser install` + // 1. Check Chrome downloaded by `chrome-use install` if let Some(p) = crate::install::find_installed_chrome() { return Some(p); } @@ -651,7 +650,7 @@ pub fn find_chrome() -> Option { let _ = writeln!( std::io::stderr(), "Warning: Chrome cache directory exists ({}) but no Chrome binary found inside. \ - Falling back to system Chrome. Run `agent-browser install` to re-download.", + Falling back to system Chrome. Run `chrome-use install` to re-download.", cache_dir.display() ); } @@ -762,7 +761,7 @@ pub fn cleanup_orphaned_chrome_profiles() { }; for entry in entries.flatten() { let name = entry.file_name(); - if !name.to_string_lossy().starts_with("agent-browser-chrome-") { + if !name.to_string_lossy().starts_with("chrome-use-chrome-") { continue; } let path = entry.path(); @@ -806,7 +805,7 @@ pub async fn auto_connect_cdp() -> Result { // `chrome.debugger` permission, which — unlike a raw `--remote-debugging-port` // CDP attach — never triggers Chrome 136+'s per-connection // "Allow remote debugging?" consent modal. The native-messaging host writes - // ~/.agent-browser/relay-cdp-url while connected and removes it on exit, so a + // ~/.chrome-use/relay-cdp-url while connected and removes it on exit, so a // present URL means the relay is up. This must win over the DevToolsActivePort // / :9222 probes below: if the user's Chrome happens to also be listening on a // debug port, attaching there would pop the consent dialog and defeat the @@ -842,15 +841,13 @@ pub async fn auto_connect_cdp() -> Result { } if host_installed { - return Err( - "The agent-browser-stealth extension is installed, but its relay \ + return Err("The chrome-use extension is installed, but its relay \ isn't connected right now. Wake it up — click the extension's \ toolbar icon, or reload it at chrome://extensions — then retry. \ - (agent-browser will not attach to a raw --remote-debugging-port \ + (chrome-use will not attach to a raw --remote-debugging-port \ while the extension is set up, because that pops Chrome's \"Allow \ remote debugging?\" dialog. Use --cdp to force the raw path.)" - .to_string(), - ); + .to_string()); } let user_data_dirs = get_chrome_user_data_dirs(); @@ -876,7 +873,7 @@ pub async fn auto_connect_cdp() -> Result { Err( "No running Chrome with remote debugging found. Remote debugging is a \ startup flag, not a setting: fully quit Chrome and relaunch it with \ - --remote-debugging-port=9222 (then agent-browser auto-connects), or pass \ + --remote-debugging-port=9222 (then chrome-use auto-connects), or pass \ --cdp /--launch." .to_string(), ) @@ -1177,7 +1174,7 @@ pub fn copy_chrome_profile( profile_directory: &str, ) -> Result { let temp_dir = - std::env::temp_dir().join(format!("agent-browser-profile-{}", uuid::Uuid::new_v4())); + std::env::temp_dir().join(format!("chrome-use-profile-{}", uuid::Uuid::new_v4())); std::fs::create_dir_all(&temp_dir) .map_err(|e| format!("Failed to create temp profile dir: {}", e))?; @@ -1611,7 +1608,7 @@ mod tests { guard.set("PLAYWRIGHT_BROWSERS_PATH", "/nonexistent/path"); let temp_home = std::env::temp_dir().join(format!( - "agent-browser-test-home-{}-{}", + "chrome-use-test-home-{}-{}", std::process::id(), std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) @@ -1741,7 +1738,7 @@ mod tests { let result = build_chrome_args(&opts).unwrap(); assert!( !result.args.iter().any(|a| a == "--hide-scrollbars"), - "--hide-scrollbars false should suppress agent-browser's default hide switch" + "--hide-scrollbars false should suppress chrome-use's default hide switch" ); if let Some(ref dir) = result.temp_user_data_dir { let _ = std::fs::remove_dir_all(dir); @@ -1858,7 +1855,7 @@ mod tests { #[test] fn test_chrome_process_drop_cleans_temp_dir() { let dir = std::env::temp_dir().join(format!( - "agent-browser-chrome-drop-test-{}", + "chrome-use-chrome-drop-test-{}", uuid::Uuid::new_v4() )); let _ = std::fs::create_dir_all(&dir); @@ -1933,7 +1930,7 @@ mod tests { let name = ls["profile"]["info_cache"]["Default"]["name"] .as_str() .unwrap(); - assert!(name.starts_with("agent-browser ("), "got: {name}"); + assert!(name.starts_with("chrome-use ("), "got: {name}"); assert_eq!( ls["profile"]["info_cache"]["Default"]["is_using_default_name"], serde_json::json!(false) @@ -1946,7 +1943,7 @@ mod tests { assert!(prefs["profile"]["name"] .as_str() .unwrap() - .starts_with("agent-browser (")); + .starts_with("chrome-use (")); let _ = std::fs::remove_dir_all(&tmp); } @@ -1999,7 +1996,7 @@ mod tests { impl TempDir { fn new(name: &str) -> Self { Self(std::env::temp_dir().join(format!( - "agent-browser-test-{}-{}-{}", + "chrome-use-test-{}-{}-{}", name, std::process::id(), std::time::SystemTime::now() diff --git a/cli/src/native/cdp/discovery.rs b/cli/src/native/cdp/discovery.rs index 54ee1ff..35f36be 100644 --- a/cli/src/native/cdp/discovery.rs +++ b/cli/src/native/cdp/discovery.rs @@ -61,7 +61,7 @@ pub async fn discover_cdp_url_with_timeout( "All CDP discovery methods failed for {host}:{port}. \ Note: Chrome 136+ no longer serves the HTTP discovery endpoints \ (/json/version, /json/list), so `--cdp ` cannot find the target — \ - use the default auto-connect (just `agent-browser open `), which reads \ + use the default auto-connect (just `chrome-use open `), which reads \ DevToolsActivePort and attaches over WebSocket. \ (details: /json/version: {version_err}; /json/list: {list_err}; WebSocket: {ws_err})" )), diff --git a/cli/src/native/daemon.rs b/cli/src/native/daemon.rs index 6c399f0..200599f 100644 --- a/cli/src/native/daemon.rs +++ b/cli/src/native/daemon.rs @@ -498,15 +498,15 @@ fn get_daemon_socket_dir() -> PathBuf { if let Ok(xdg) = env::var("XDG_RUNTIME_DIR") { if !xdg.is_empty() { - return PathBuf::from(xdg).join("agent-browser"); + return PathBuf::from(xdg).join("chrome-use"); } } if let Some(home) = dirs::home_dir() { - return home.join(".agent-browser"); + return home.join(".chrome-use"); } - std::env::temp_dir().join("agent-browser") + std::env::temp_dir().join("chrome-use") } #[cfg(windows)] diff --git a/cli/src/native/e2e_tests.rs b/cli/src/native/e2e_tests.rs index 0424920..b8f4342 100644 --- a/cli/src/native/e2e_tests.rs +++ b/cli/src/native/e2e_tests.rs @@ -306,7 +306,7 @@ async fn e2e_lightpanda_auto_launch_can_open_page() { async fn e2e_runtime_stream_enable_before_launch_attaches_and_disables() { let guard = EnvGuard::new(&["AGENT_BROWSER_SOCKET_DIR", "AGENT_BROWSER_SESSION"]); let socket_dir = std::env::temp_dir().join(format!( - "agent-browser-e2e-stream-{}-{}", + "chrome-use-e2e-stream-{}-{}", std::process::id(), std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) @@ -609,7 +609,7 @@ async fn e2e_screenshot() { // Named screenshot let tmp_path = std::env::temp_dir() - .join("agent-browser-e2e-test-screenshot.png") + .join("chrome-use-e2e-test-screenshot.png") .to_string_lossy() .to_string(); let resp = execute_command( @@ -2202,7 +2202,7 @@ async fn e2e_state_management() { // Save state let tmp_state = std::env::temp_dir() - .join("agent-browser-e2e-state.json") + .join("chrome-use-e2e-state.json") .to_string_lossy() .to_string(); let resp = execute_command( @@ -2314,7 +2314,7 @@ async fn e2e_save_state_cross_domain() { // Save state (currently on example.com) let tmp_state = std::env::temp_dir() - .join("agent-browser-e2e-cross-domain-state.json") + .join("chrome-use-e2e-cross-domain-state.json") .to_string_lossy() .to_string(); let resp = execute_command( @@ -2718,10 +2718,8 @@ async fn e2e_error_handling() { #[tokio::test] #[ignore] async fn e2e_profile_cookie_persistence() { - let profile_dir = std::env::temp_dir().join(format!( - "agent-browser-e2e-profile-{}", - uuid::Uuid::new_v4() - )); + let profile_dir = + std::env::temp_dir().join(format!("chrome-use-e2e-profile-{}", uuid::Uuid::new_v4())); // Session 1: launch with profile, set a cookie, close { @@ -4254,7 +4252,7 @@ async fn e2e_headers_case_insensitive_no_duplicates() { // Regression: externally opened tabs must appear in tab_list (#1037) // // When connected to Chrome (launched or via --cdp), a tab opened outside of -// agent-browser (e.g. by the user or another CDP client) should be detected +// chrome-use (e.g. by the user or another CDP client) should be detected // and listed. Previously, chrome://newtab/ was filtered by // is_internal_chrome_target, and Target.targetInfoChanged for untracked // targets was silently ignored. @@ -4280,7 +4278,7 @@ async fn e2e_externally_opened_tab_detected() { // Simulate an external client opening a new tab via the browser-level CDP // session (no sessionId). This mirrors what happens when a user manually - // opens a tab while agent-browser is connected via --cdp. + // opens a tab while chrome-use is connected via --cdp. let browser = state.browser.as_ref().expect("browser should be launched"); let _: Value = browser .client @@ -4373,7 +4371,7 @@ async fn e2e_relaunch_on_options_change() { "id": "3", "action": "launch", "headless": true, - "userAgent": "agent-browser-test/1.0" + "userAgent": "chrome-use-test/1.0" }), &mut state, ) @@ -4397,7 +4395,7 @@ async fn e2e_relaunch_on_options_change() { async fn e2e_stream_frame_metadata_respects_custom_viewport() { let guard = EnvGuard::new(&["AGENT_BROWSER_SOCKET_DIR", "AGENT_BROWSER_SESSION"]); let socket_dir = std::env::temp_dir().join(format!( - "agent-browser-e2e-stream-viewport-{}-{}", + "chrome-use-e2e-stream-viewport-{}-{}", std::process::id(), std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) @@ -4703,7 +4701,7 @@ async fn e2e_recording_inherits_viewport() { /// Verify that launching with `storageState` in the launch command restores /// cookies that were previously saved with `state_save`. /// -/// This is the e2e equivalent of `agent-browser --state ./auth.json open `. +/// This is the e2e equivalent of `chrome-use --state ./auth.json open `. /// The launch command accepts a `storageState` field that should load the /// state file (cookies + localStorage) before the first navigation. #[tokio::test] @@ -4711,7 +4709,7 @@ async fn e2e_recording_inherits_viewport() { async fn e2e_state_flag_restores_cookies() { let state_path = std::env::temp_dir() .join(format!( - "agent-browser-e2e-state-flag-{}.json", + "chrome-use-e2e-state-flag-{}.json", uuid::Uuid::new_v4() )) .to_string_lossy() @@ -4819,7 +4817,7 @@ async fn e2e_state_flag_missing_file_fails_launch() { let missing_path = std::env::temp_dir() .join(format!( - "agent-browser-e2e-missing-state-{}.json", + "chrome-use-e2e-missing-state-{}.json", uuid::Uuid::new_v4() )) .to_string_lossy() @@ -4859,14 +4857,14 @@ async fn e2e_state_flag_missing_file_fails_launch() { async fn e2e_storage_state_launch_restarts_clean_browser() { let state_one = std::env::temp_dir() .join(format!( - "agent-browser-e2e-storage-reuse-1-{}.json", + "chrome-use-e2e-storage-reuse-1-{}.json", uuid::Uuid::new_v4() )) .to_string_lossy() .to_string(); let state_two = std::env::temp_dir() .join(format!( - "agent-browser-e2e-storage-reuse-2-{}.json", + "chrome-use-e2e-storage-reuse-2-{}.json", uuid::Uuid::new_v4() )) .to_string_lossy() @@ -4967,7 +4965,7 @@ async fn e2e_storage_state_launch_restarts_clean_browser() { async fn e2e_state_env_restores_cookies_on_auto_launch() { let state_path = std::env::temp_dir() .join(format!( - "agent-browser-e2e-state-env-{}.json", + "chrome-use-e2e-state-env-{}.json", uuid::Uuid::new_v4() )) .to_string_lossy() @@ -5149,7 +5147,7 @@ async fn e2e_session_name_auto_restores_cookies() { // Clean up auto-saved state files let sessions_dir = dirs::home_dir() .unwrap() - .join(".agent-browser") + .join(".chrome-use") .join("sessions"); if let Ok(entries) = std::fs::read_dir(&sessions_dir) { for entry in entries.flatten() { @@ -5168,7 +5166,7 @@ async fn e2e_session_name_auto_restores_cookies() { async fn e2e_explicit_state_load_restores_cookies() { let state_path = std::env::temp_dir() .join(format!( - "agent-browser-e2e-explicit-load-{}.json", + "chrome-use-e2e-explicit-load-{}.json", uuid::Uuid::new_v4() )) .to_string_lossy() diff --git a/cli/src/native/element.rs b/cli/src/native/element.rs index ae2a284..eda4ae2 100644 --- a/cli/src/native/element.rs +++ b/cli/src/native/element.rs @@ -378,7 +378,7 @@ pub async fn resolve_element_object_id( &DomResolveNodeParams { backend_node_id: Some(active_id), node_id: None, - object_group: Some("agent-browser".to_string()), + object_group: Some("chrome-use".to_string()), }, Some(effective_session_id), ) @@ -419,7 +419,7 @@ pub async fn resolve_element_object_id( &DomResolveNodeParams { backend_node_id: Some(fresh_id), node_id: None, - object_group: Some("agent-browser".to_string()), + object_group: Some("chrome-use".to_string()), }, Some(effective_session_id), ) @@ -591,7 +591,7 @@ async fn verify_click_target( let resolve_params = DomResolveNodeParams { backend_node_id: Some(backend_node_id), node_id: None, - object_group: Some("agent-browser-occlusion".to_string()), + object_group: Some("chrome-use-occlusion".to_string()), }; let resolve_fut = client.send_command_typed::<_, serde_json::Value>( "DOM.resolveNode", diff --git a/cli/src/native/humanize.rs b/cli/src/native/humanize.rs index e09b11b..7977253 100644 --- a/cli/src/native/humanize.rs +++ b/cli/src/native/humanize.rs @@ -1,6 +1,6 @@ //! Human-like input behaviour for stealth. //! -//! When agent-browser drives a real Chrome over CDP, the input events it +//! When chrome-use drives a real Chrome over CDP, the input events it //! dispatches are already `isTrusted` — but a click that teleports the cursor //! straight to an element's exact centre, with no approach path and zero delay //! between move/press/release, is a behavioural tell that advanced anti-bot diff --git a/cli/src/native/inspect_server.rs b/cli/src/native/inspect_server.rs index 1c151a4..696e7fb 100644 --- a/cli/src/native/inspect_server.rs +++ b/cli/src/native/inspect_server.rs @@ -13,7 +13,7 @@ use super::cdp::client::InspectProxyHandle; /// Counter for unique attach IDs so concurrent connections don't collide. static ATTACH_ID: AtomicI64 = AtomicI64::new(-1000); -/// Lightweight HTTP + WebSocket server for `agent-browser inspect`. +/// Lightweight HTTP + WebSocket server for `chrome-use inspect`. /// /// Serves two purposes: /// - `GET /` redirects to Chrome's built-in DevTools frontend with `ws=` pointing to this server diff --git a/cli/src/native/providers.rs b/cli/src/native/providers.rs index 6ae724b..1d33ad1 100644 --- a/cli/src/native/providers.rs +++ b/cli/src/native/providers.rs @@ -425,7 +425,7 @@ mod agentcore { let url = format!("https://{}{}", host, path); // Generate a unique session name - let session_name = format!("agent-browser-{}", &uuid::Uuid::new_v4().to_string()[..8]); + let session_name = format!("chrome-use-{}", &uuid::Uuid::new_v4().to_string()[..8]); let mut body_json = json!({ "name": session_name, diff --git a/cli/src/native/relay.rs b/cli/src/native/relay.rs index 73dc7d6..8536415 100644 --- a/cli/src/native/relay.rs +++ b/cli/src/native/relay.rs @@ -12,7 +12,7 @@ //! //! ## Multiple clients (concurrent agents on one shared browser) //! -//! Several agent-browser daemons (one per `--session`) can connect to the same +//! Several chrome-use daemons (one per `--session`) can connect to the same //! relay/Chrome at once. The extension is a single peer, so the relay must //! demultiplex: every forwarded command is re-keyed to a relay-global id mapped //! back to the originating client, and the extension's reply is routed to **only @@ -31,7 +31,7 @@ use serde_json::{json, Value}; /// Protocol version advertised in the connect handshake (matches the extension). pub const RELAY_PROTOCOL: i64 = 3; -/// Identifies one connected CDP client (agent-browser daemon) for routing. +/// Identifies one connected CDP client (chrome-use daemon) for routing. pub type ClientId = u64; /// One target (tab) the extension has attached, as the relay tracks it. diff --git a/cli/src/native/screenshot.rs b/cli/src/native/screenshot.rs index 0736691..f1b2854 100644 --- a/cli/src/native/screenshot.rs +++ b/cli/src/native/screenshot.rs @@ -260,7 +260,7 @@ async fn collect_annotations( "DOM.resolveNode", Some(serde_json::json!({ "backendNodeId": backend_node_id, - "objectGroup": "agent-browser-annotate" + "objectGroup": "chrome-use-annotate" })), Some(session_id), ) @@ -589,11 +589,9 @@ fn round(value: f64) -> i64 { fn get_screenshot_dir() -> PathBuf { if let Some(home) = dirs::home_dir() { - home.join(".agent-browser").join("tmp").join("screenshots") + home.join(".chrome-use").join("tmp").join("screenshots") } else { - std::env::temp_dir() - .join("agent-browser") - .join("screenshots") + std::env::temp_dir().join("chrome-use").join("screenshots") } } diff --git a/cli/src/native/snapshot.rs b/cli/src/native/snapshot.rs index c5ef6db..142d99a 100644 --- a/cli/src/native/snapshot.rs +++ b/cli/src/native/snapshot.rs @@ -927,7 +927,7 @@ async fn find_cursor_interactive_elements( ) .await { - eprintln!("[agent-browser] Warning: failed to clean up data-__ab-ci attributes: {e}"); + eprintln!("[chrome-use] Warning: failed to clean up data-__ab-ci attributes: {e}"); } // Build the map diff --git a/cli/src/native/state.rs b/cli/src/native/state.rs index 1698cb1..3ece0ca 100644 --- a/cli/src/native/state.rs +++ b/cli/src/native/state.rs @@ -717,14 +717,14 @@ pub fn dispatch_state_command(cmd: &Value) -> Option> { } } -/// Return the agent-browser state root (`~/.agent-browser`, falling back to -/// `/agent-browser` when the home directory can't be resolved). +/// Return the chrome-use state root (`~/.chrome-use`, falling back to +/// `/chrome-use` when the home directory can't be resolved). /// This is the parent of `sessions/`, auth storage, and the encryption key. pub fn get_state_dir() -> PathBuf { if let Some(home) = dirs::home_dir() { - home.join(".agent-browser") + home.join(".chrome-use") } else { - std::env::temp_dir().join("agent-browser") + std::env::temp_dir().join("chrome-use") } } @@ -783,19 +783,19 @@ mod tests { #[test] fn test_state_show_nonexistent_file() { - let result = state_show("/tmp/nonexistent-agent-browser-state-file.json"); + let result = state_show("/tmp/nonexistent-chrome-use-state-file.json"); assert!(result.is_err()); } #[test] fn test_state_clear_nonexistent_file() { - let result = state_clear(Some("/tmp/nonexistent-agent-browser-state-file.json")); + let result = state_clear(Some("/tmp/nonexistent-chrome-use-state-file.json")); assert!(result.is_err()); } #[test] fn test_state_rename_nonexistent() { - let result = state_rename("/tmp/nonexistent-agent-browser-state-file.json", "new-name"); + let result = state_rename("/tmp/nonexistent-chrome-use-state-file.json", "new-name"); assert!(result.is_err()); assert!(result.unwrap_err().contains("not found")); } diff --git a/cli/src/native/stream/chat.rs b/cli/src/native/stream/chat.rs index 884f22a..fda8c81 100644 --- a/cli/src/native/stream/chat.rs +++ b/cli/src/native/stream/chat.rs @@ -76,7 +76,7 @@ pub(super) async fn handle_models_request( let _ = stream.write_all(body.as_bytes()).await; } -const SKILL_NAMES: &[&str] = &["agent-browser", "slack", "electron", "dogfood", "agentcore"]; +const SKILL_NAMES: &[&str] = &["chrome-use", "slack", "electron", "dogfood", "agentcore"]; /// Locate the `skills/` directory by walking up from the executable. /// Works for npm installs (binary in `bin/`, skills at `../skills/`) and @@ -87,7 +87,7 @@ fn find_skills_dir() -> Option { let mut dir = real.parent(); while let Some(d) = dir { let candidate = d.join("skills"); - if candidate.join("agent-browser").join("SKILL.md").exists() { + if candidate.join("chrome-use").join("SKILL.md").exists() { return Some(candidate); } dir = d.parent(); @@ -133,7 +133,7 @@ pub(crate) fn get_system_prompt() -> &'static str { } format!( - r#"You are an AI assistant that controls a browser through agent-browser. You have an active browser session, but you can also create new sessions. + r#"You are an AI assistant that controls a browser through chrome-use. You have an active browser session, but you can also create new sessions. RULES: - You MUST use the agent_browser tool for every browser action. NEVER claim you performed an action without calling the tool. @@ -141,19 +141,19 @@ RULES: - If a request is outside your capabilities (e.g. system operations), say so honestly. Do not improvise or pretend. - One tool call per command. Do not chain with `&&` or `;`. - Do not add `--json`. -- Do not run non-agent-browser programs. +- Do not run non-chrome-use programs. - Keep responses concise. - For screenshots, omit the path argument so they save to the default location (which will be displayed inline). Screenshots from tool calls are ALREADY shown to the user. Do NOT re-display them with markdown image syntax in your text response. Never use `![...]()` to reference screenshots. -- To create a new session: add `--session ` to any command (e.g. `agent-browser --session my-session open https://example.com`). If the session does not exist, it will be created automatically. -- To use a different browser engine: add `--engine ` (e.g. `agent-browser --session lp-session --engine lightpanda open https://example.com`). Supported engines: chrome (default), lightpanda. +- To create a new session: add `--session ` to any command (e.g. `chrome-use --session my-session open https://example.com`). If the session does not exist, it will be created automatically. +- To use a different browser engine: add `--engine ` (e.g. `chrome-use --session lp-session --engine lightpanda open https://example.com`). Supported engines: chrome (default), lightpanda. -The following skill references describe agent-browser capabilities in detail. Use them when deciding which commands to run and how to approach tasks. +The following skill references describe chrome-use capabilities in detail. Use them when deciding which commands to run and how to approach tasks. {sections}"#, ) }) } -pub(crate) const CHAT_TOOLS: &str = r#"[{"type":"function","function":{"name":"agent_browser","description":"Execute an agent-browser command. Runs against the active session by default. Add --session to target or create a different session, and --engine to choose a browser engine.","parameters":{"type":"object","properties":{"command":{"type":"string","description":"The command to execute, e.g. 'agent-browser open https://google.com' or 'agent-browser --session new-session open https://example.com' or 'agent-browser snapshot -i' or 'agent-browser click @e3'"}},"required":["command"]}}}]"#; +pub(crate) const CHAT_TOOLS: &str = r#"[{"type":"function","function":{"name":"agent_browser","description":"Execute an chrome-use command. Runs against the active session by default. Add --session to target or create a different session, and --engine to choose a browser engine.","parameters":{"type":"object","properties":{"command":{"type":"string","description":"The command to execute, e.g. 'chrome-use open https://google.com' or 'chrome-use --session new-session open https://example.com' or 'chrome-use snapshot -i' or 'chrome-use click @e3'"}},"required":["command"]}}}]"#; pub(crate) const COMPACT_THRESHOLD_CHARS: usize = 200_000; pub(crate) const KEEP_RECENT_MESSAGES: usize = 6; @@ -462,7 +462,7 @@ pub(crate) async fn execute_chat_tool(session: &str, command: &str) -> String { let single = command.split("&&").next().unwrap_or(command); let single = single.split(';').next().unwrap_or(single).trim(); - let stripped = single.strip_prefix("agent-browser ").unwrap_or(single); + let stripped = single.strip_prefix("chrome-use ").unwrap_or(single); let words = crate::commands::shell_words_split(stripped); let mut global_flags: Vec = Vec::new(); @@ -490,7 +490,7 @@ pub(crate) async fn execute_chat_tool(session: &str, command: &str) -> String { let first_cmd = cmd_words.first().map(|s| s.as_str()).unwrap_or(""); if !ALLOWED_COMMANDS.contains(&first_cmd) { return format!( - "Blocked: '{}' is not a valid agent-browser command.", + "Blocked: '{}' is not a valid chrome-use command.", first_cmd ); } diff --git a/cli/src/native/stream/dashboard.rs b/cli/src/native/stream/dashboard.rs index 0f04e5e..6bc103e 100644 --- a/cli/src/native/stream/dashboard.rs +++ b/cli/src/native/stream/dashboard.rs @@ -815,21 +815,21 @@ mod tests { #[test] fn test_same_origin_ws_request_proxied() { - let req = "GET /api/session/9222/stream HTTP/1.1\r\nHost: dashboard.agent-browser.localhost\r\nOrigin: https://dashboard.agent-browser.localhost\r\nUpgrade: websocket\r\n\r\n"; + let req = "GET /api/session/9222/stream HTTP/1.1\r\nHost: dashboard.chrome-use.localhost\r\nOrigin: https://dashboard.chrome-use.localhost\r\nUpgrade: websocket\r\n\r\n"; assert!(is_same_origin_ws_request(req)); } #[test] fn test_normalize_origin_authority_https_without_port() { assert_eq!( - normalize_origin_authority("https://dashboard.agent-browser.localhost"), - Some("dashboard.agent-browser.localhost".to_string()) + normalize_origin_authority("https://dashboard.chrome-use.localhost"), + Some("dashboard.chrome-use.localhost".to_string()) ); } #[test] fn test_same_origin_ws_request_default_https_port() { - let req = "GET /api/session/9222/stream HTTP/1.1\r\nHost: dashboard.agent-browser.localhost:443\r\nOrigin: https://dashboard.agent-browser.localhost\r\nUpgrade: websocket\r\n\r\n"; + let req = "GET /api/session/9222/stream HTTP/1.1\r\nHost: dashboard.chrome-use.localhost:443\r\nOrigin: https://dashboard.chrome-use.localhost\r\nUpgrade: websocket\r\n\r\n"; assert!(is_same_origin_ws_request(req)); } @@ -841,7 +841,7 @@ mod tests { #[test] fn test_same_origin_http_request_matching_referer() { - let req = "GET /api/session/9222/tabs HTTP/1.1\r\nHost: dashboard.agent-browser.localhost:443\r\nReferer: https://dashboard.agent-browser.localhost/sessions\r\n\r\n"; + let req = "GET /api/session/9222/tabs HTTP/1.1\r\nHost: dashboard.chrome-use.localhost:443\r\nReferer: https://dashboard.chrome-use.localhost/sessions\r\n\r\n"; assert!(is_same_origin_http_request(req)); } diff --git a/cli/src/native/tracing.rs b/cli/src/native/tracing.rs index 6737c78..75728ac 100644 --- a/cli/src/native/tracing.rs +++ b/cli/src/native/tracing.rs @@ -358,16 +358,16 @@ fn get_clock_domain() -> Option<&'static str> { fn get_traces_dir() -> PathBuf { if let Some(home) = dirs::home_dir() { - home.join(".agent-browser").join("tmp").join("traces") + home.join(".chrome-use").join("tmp").join("traces") } else { - std::env::temp_dir().join("agent-browser").join("traces") + std::env::temp_dir().join("chrome-use").join("traces") } } fn get_profiles_dir() -> PathBuf { if let Some(home) = dirs::home_dir() { - home.join(".agent-browser").join("tmp").join("profiles") + home.join(".chrome-use").join("tmp").join("profiles") } else { - std::env::temp_dir().join("agent-browser").join("profiles") + std::env::temp_dir().join("chrome-use").join("profiles") } } diff --git a/cli/src/output.rs b/cli/src/output.rs index badccc3..78bf567 100644 --- a/cli/src/output.rs +++ b/cli/src/output.rs @@ -1130,8 +1130,8 @@ pub fn print_response_with_opts(resp: &Response, action: Option<&str>, opts: &Ou .unwrap_or(""); println!("Confirmation required:"); println!(" {}: {}", category, description); - println!(" Run: agent-browser confirm {}", cid); - println!(" Or: agent-browser deny {}", cid); + println!(" Run: chrome-use confirm {}", cid); + println!(" Or: chrome-use deny {}", cid); return; } if data @@ -1175,9 +1175,9 @@ pub fn print_command_help(command: &str) -> bool { // === Navigation === "open" | "goto" | "navigate" => { r##" -agent-browser open - Launch the browser, optionally navigate +chrome-use open - Launch the browser, optionally navigate -Usage: agent-browser open [url] +Usage: chrome-use open [url] Without a URL, launches the browser but stays on about:blank. This lets you stage state (network routes, cookies, init scripts) before the first @@ -1198,15 +1198,15 @@ Global Options: --init-script Register a page init script (repeatable) Examples: - agent-browser open # Launch, no nav - agent-browser open example.com - agent-browser open https://github.com - agent-browser open localhost:3000 - agent-browser open api.example.com --headers '{"Authorization": "Bearer token"}' + chrome-use open # Launch, no nav + chrome-use open example.com + chrome-use open https://github.com + chrome-use open localhost:3000 + chrome-use open api.example.com --headers '{"Authorization": "Bearer token"}' # ^ Headers only sent to api.example.com, not other domains # Pre-navigation setup in one turn: - agent-browser batch \ + chrome-use batch \ '["open"]' \ '["network","route","*","--abort","--resource-type","script"]' \ '["navigate","http://localhost:3000/target"]' @@ -1214,9 +1214,9 @@ Examples: } "back" => { r##" -agent-browser back - Navigate back in history +chrome-use back - Navigate back in history -Usage: agent-browser back +Usage: chrome-use back Goes back one page in the browser history, equivalent to clicking the browser's back button. @@ -1226,14 +1226,14 @@ Global Options: --session Use specific session Examples: - agent-browser back + chrome-use back "## } "forward" => { r##" -agent-browser forward - Navigate forward in history +chrome-use forward - Navigate forward in history -Usage: agent-browser forward +Usage: chrome-use forward Goes forward one page in the browser history, equivalent to clicking the browser's forward button. @@ -1243,14 +1243,14 @@ Global Options: --session Use specific session Examples: - agent-browser forward + chrome-use forward "## } "reload" => { r##" -agent-browser reload - Reload the current page +chrome-use reload - Reload the current page -Usage: agent-browser reload +Usage: chrome-use reload Reloads the current page, equivalent to pressing F5 or clicking the browser's reload button. @@ -1260,17 +1260,17 @@ Global Options: --session Use specific session Examples: - agent-browser reload + chrome-use reload "## } // === Core Actions === "click" => { r##" -agent-browser click - Click an element or a coordinate +chrome-use click - Click an element or a coordinate -Usage: agent-browser click [--new-tab] - agent-browser click | , | --coords , +Usage: chrome-use click [--new-tab] + chrome-use click | , | --coords , Clicks on the specified element. The selector can be a CSS selector, XPath, or an element reference from snapshot (e.g., @e1). @@ -1288,18 +1288,18 @@ Global Options: --session Use specific session Examples: - agent-browser click "#submit-button" - agent-browser click @e1 - agent-browser click "button.primary" - agent-browser click "//button[@type='submit']" - agent-browser click @e3 --new-tab + chrome-use click "#submit-button" + chrome-use click @e1 + chrome-use click "button.primary" + chrome-use click "//button[@type='submit']" + chrome-use click @e3 --new-tab "## } "dblclick" => { r##" -agent-browser dblclick - Double-click an element +chrome-use dblclick - Double-click an element -Usage: agent-browser dblclick +Usage: chrome-use dblclick Double-clicks on the specified element. Useful for text selection or triggering double-click handlers. @@ -1309,15 +1309,15 @@ Global Options: --session Use specific session Examples: - agent-browser dblclick "#editable-text" - agent-browser dblclick @e5 + chrome-use dblclick "#editable-text" + chrome-use dblclick @e5 "## } "fill" => { r##" -agent-browser fill - Clear and fill an input field +chrome-use fill - Clear and fill an input field -Usage: agent-browser fill +Usage: chrome-use fill Clears the input field and fills it with the specified text. This replaces any existing content in the field. @@ -1327,16 +1327,16 @@ Global Options: --session Use specific session Examples: - agent-browser fill "#email" "user@example.com" - agent-browser fill @e3 "Hello World" - agent-browser fill "input[name='search']" "query" + chrome-use fill "#email" "user@example.com" + chrome-use fill @e3 "Hello World" + chrome-use fill "input[name='search']" "query" "## } "type" => { r##" -agent-browser type - Type text into an element +chrome-use type - Type text into an element -Usage: agent-browser type +Usage: chrome-use type Types text into the specified element character by character. Unlike fill, this does not clear existing content first. @@ -1346,20 +1346,20 @@ Global Options: --session Use specific session Examples: - agent-browser type "#search" "hello" - agent-browser type @e2 "additional text" + chrome-use type "#search" "hello" + chrome-use type @e2 "additional text" See Also: For typing into contenteditable editors (Lexical, ProseMirror, etc.) without a selector, use 'keyboard type' instead: - agent-browser keyboard type "# My Heading" + chrome-use keyboard type "# My Heading" "## } "hover" => { r##" -agent-browser hover - Hover over an element +chrome-use hover - Hover over an element -Usage: agent-browser hover +Usage: chrome-use hover Moves the mouse to hover over the specified element. Useful for triggering hover states or dropdown menus. @@ -1369,15 +1369,15 @@ Global Options: --session Use specific session Examples: - agent-browser hover "#dropdown-trigger" - agent-browser hover @e4 + chrome-use hover "#dropdown-trigger" + chrome-use hover @e4 "## } "focus" => { r##" -agent-browser focus - Focus an element +chrome-use focus - Focus an element -Usage: agent-browser focus +Usage: chrome-use focus Sets keyboard focus to the specified element. @@ -1386,15 +1386,15 @@ Global Options: --session Use specific session Examples: - agent-browser focus "#input-field" - agent-browser focus @e2 + chrome-use focus "#input-field" + chrome-use focus @e2 "## } "check" => { r##" -agent-browser check - Check a checkbox +chrome-use check - Check a checkbox -Usage: agent-browser check +Usage: chrome-use check Checks a checkbox element. If already checked, no action is taken. @@ -1403,15 +1403,15 @@ Global Options: --session Use specific session Examples: - agent-browser check "#terms-checkbox" - agent-browser check @e7 + chrome-use check "#terms-checkbox" + chrome-use check @e7 "## } "uncheck" => { r##" -agent-browser uncheck - Uncheck a checkbox +chrome-use uncheck - Uncheck a checkbox -Usage: agent-browser uncheck +Usage: chrome-use uncheck Unchecks a checkbox element. If already unchecked, no action is taken. @@ -1420,15 +1420,15 @@ Global Options: --session Use specific session Examples: - agent-browser uncheck "#newsletter-opt-in" - agent-browser uncheck @e8 + chrome-use uncheck "#newsletter-opt-in" + chrome-use uncheck @e8 "## } "select" => { r##" -agent-browser select - Select a dropdown option +chrome-use select - Select a dropdown option -Usage: agent-browser select +Usage: chrome-use select Selects one or more options in a only -agent-browser select @e4 "a" "b" # select multiple -agent-browser pick @e4 --option "Europe" # ANY combobox (react-select / ARIA / +chrome-use click @e1 # click +chrome-use click @e1 --new-tab # open link in new tab instead of navigating +chrome-use dblclick @e1 # double-click +chrome-use hover @e1 # hover +chrome-use focus @e1 # focus (useful before keyboard input) +chrome-use fill @e2 "hello" # clear then type +chrome-use type @e2 " world" # type without clearing +chrome-use press Enter # press a key at current focus +chrome-use press Control+a # key combination +chrome-use check @e3 # check checkbox +chrome-use uncheck @e3 # uncheck +chrome-use select @e4 "option-value" # native "`. +that one with `AGENT_BROWSER_CLICK_MODE=dom chrome-use click ...`, or just +`chrome-use eval "