qiaomu-opencli-browser

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

OpenCLI Browser — Browser Automation for AI Agents

OpenCLI Browser — 面向AI Agent的浏览器自动化工具

Control Chrome step-by-step via CLI. Reuses existing login sessions — no passwords needed.
通过CLI逐步控制Chrome。复用现有登录会话——无需密码。

Prerequisites

前置条件

bash
opencli doctor    # Verify extension + daemon connectivity
Requires: Chrome running + OpenCLI Browser Bridge extension installed.
bash
opencli doctor    # 验证扩展程序与守护进程的连接状态
要求:Chrome已运行 + 已安装OpenCLI Browser Bridge扩展程序。

Critical Rules

核心规则

  1. ALWAYS use
    state
    to inspect the page, NEVER use
    screenshot
    state
    returns structured DOM with
    [N]
    element indices, is instant and costs zero tokens.
    screenshot
    requires vision processing and is slow. Only use
    screenshot
    when the user explicitly asks to save a visual.
  2. ALWAYS use
    click
    /
    type
    /
    select
    for interaction, NEVER use
    eval
    to click or type
    eval "el.click()"
    bypasses scrollIntoView and CDP click pipeline, causing failures on off-screen elements. Use
    state
    to find the
    [N]
    index, then
    click <N>
    .
  3. Verify inputs with
    get value
    , not screenshots
    — after
    type
    , run
    get value <index>
    to confirm.
  4. Run
    state
    after every page change
    — after
    open
    ,
    click
    (on links),
    scroll
    , always run
    state
    to see the new elements and their indices. Never guess indices.
  5. Chain commands aggressively with
    &&
    — combine
    open + state
    , multiple
    type
    calls, and
    type + get value
    into single
    &&
    chains. Each tool call has overhead; chaining cuts it.
  6. eval
    is read-only
    — use
    eval
    ONLY for data extraction (
    JSON.stringify(...)
    ), never for clicking, typing, or navigating. Always wrap in IIFE to avoid variable conflicts:
    eval "(function(){ const x = ...; return JSON.stringify(x); })()"
    .
  7. Minimize total tool calls — plan your sequence before acting. A good task completion uses 3-5 tool calls, not 15-20. Combine
    open + state
    as one call. Combine
    type + type + click
    as one call. Only run
    state
    separately when you need to discover new indices.
  8. Prefer
    network
    to discover APIs
    — most sites have JSON APIs. API-based adapters are more reliable than DOM scraping.
  1. 始终使用
    state
    检查页面,绝不使用
    screenshot
    ——
    state
    返回带有
    [N]
    元素索引的结构化DOM,响应即时且无需消耗任何令牌。
    screenshot
    需要视觉处理,速度较慢。仅当用户明确要求保存可视化内容时才使用
    screenshot
  2. 始终使用
    click
    /
    type
    /
    select
    进行交互,绝不使用
    eval
    来点击或输入
    ——
    eval "el.click()"
    会绕过scrollIntoView和CDP点击流程,导致屏幕外元素操作失败。使用
    state
    查找
    [N]
    索引,然后执行
    click <N>
  3. 使用
    get value
    验证输入,而非截图
    —— 执行
    type
    后,运行
    get value <index>
    确认输入内容。
  4. 每次页面变更后都要运行
    state
    —— 在
    open
    click
    (点击链接时)、
    scroll
    之后,务必运行
    state
    查看新元素及其索引。切勿猜测索引。
  5. &&
    积极串联命令
    —— 将
    open + state
    、多次
    type
    调用、
    type + get value
    合并为单个
    &&
    串联的命令。每次工具调用都会产生开销,串联可减少该开销。
  6. eval
    仅用于读取操作
    ——
    eval
    仅可用于数据提取(
    JSON.stringify(...)
    ),绝不能用于点击、输入或导航。始终用立即执行函数表达式(IIFE)包裹以避免变量冲突:
    eval "(function(){ const x = ...; return JSON.stringify(x); })()"
  7. 尽量减少工具调用总数 —— 操作前规划好流程。完成一项任务的优秀流程仅需3-5次工具调用,而非15-20次。将
    open + state
    合并为一次调用。将
    type + type + click
    合并为一次调用。仅当需要发现新索引时才单独运行
    state
  8. 优先使用
    network
    发现API
    —— 大多数网站都有JSON API。基于API的适配器比DOM爬取更可靠。

Command Cost Guide

命令成本指南

CostCommandsWhen to use
Free & instant
state
,
get *
,
eval
,
network
,
scroll
,
keys
Default — use these
Free but changes page
open
,
click
,
type
,
select
,
back
Interaction — run
state
after
Expensive (vision tokens)
screenshot
ONLY when user needs a saved image
成本命令使用场景
免费且即时
state
,
get *
,
eval
,
network
,
scroll
,
keys
默认使用——优先选择这些命令
免费但会变更页面
open
,
click
,
type
,
select
,
back
交互操作——执行后需运行
state
高成本(视觉令牌)
screenshot
仅在用户需要保存图片时使用

Action Chaining Rules

命令串联规则

Commands can be chained with
&&
. The browser persists via daemon, so chaining is safe.
Always chain when possible — fewer tool calls = faster completion:
bash
undefined
命令可通过
&&
串联。浏览器通过守护进程保持运行状态,因此串联操作是安全的。
尽可能串联命令 —— 工具调用次数越少,完成速度越快:
bash
undefined

GOOD: open + inspect in one call (saves 1 round trip)

推荐:一次调用完成打开+检查(减少1次往返)

opencli browser open https://example.com && opencli browser state
opencli browser open https://example.com && opencli browser state

GOOD: fill form in one call (saves 2 round trips)

推荐:一次调用完成表单填写(减少2次往返)

opencli browser type 3 "hello" && opencli browser type 4 "world" && opencli browser click 7
opencli browser type 3 "hello" && opencli browser type 4 "world" && opencli browser click 7

GOOD: type + verify in one call

推荐:一次调用完成输入+验证

opencli browser type 5 "test@example.com" && opencli browser get value 5
opencli browser type 5 "test@example.com" && opencli browser get value 5

GOOD: click + wait + state in one call (for page-changing clicks)

推荐:一次调用完成点击+等待+检查(针对会跳转页面的点击操作)

opencli browser click 12 && opencli browser wait time 1 && opencli browser state
opencli browser click 12 && opencli browser wait time 1 && opencli browser state

BAD: separate calls for each action (wasteful)

不推荐:每个操作单独调用(浪费资源)

opencli browser type 3 "hello" # Don't do this opencli browser type 4 "world" # when you can chain opencli browser click 7 # all three together

**Page-changing — always put last** in a chain (subsequent commands see stale indices):
- `open <url>`, `back`, `click <link/button that navigates>`

**Rule**: Chain when you already know the indices. Run `state` separately when you need to discover indices first.
opencli browser type 3 "hello" # 不要这样做 opencli browser type 4 "world" # 能串联时就串联 opencli browser click 7 # 将三个操作合并

**页面变更命令——始终放在串联末尾**(后续命令会读取到过期索引):
- `open <url>`、`back`、`click <会跳转的链接/按钮>`

**规则**:已明确索引时使用串联。需要先发现索引时单独运行`state`。

Core Workflow

核心工作流程

  1. Navigate:
    opencli browser open <url>
  2. Inspect:
    opencli browser state
    → elements with
    [N]
    indices
  3. Interact: use indices —
    click
    ,
    type
    ,
    select
    ,
    keys
  4. Wait (if needed):
    opencli browser wait selector ".loaded"
    or
    wait text "Success"
  5. Verify:
    opencli browser state
    or
    opencli browser get value <N>
  6. Repeat: browser stays open between commands
  7. Save: write a TS adapter to
    ~/.opencli/clis/<site>/<command>.ts
  1. 导航
    opencli browser open <url>
  2. 检查
    opencli browser state
    → 获取带有
    [N]
    索引的元素
  3. 交互:使用索引执行
    click
    type
    select
    keys
    操作
  4. 等待(如有需要):
    opencli browser wait selector ".loaded"
    wait text "Success"
  5. 验证
    opencli browser state
    opencli browser get value <N>
  6. 重复:浏览器在命令之间保持打开状态
  7. 保存:编写TS适配器并保存到
    ~/.opencli/clis/<site>/<command>.ts

Commands

命令列表

Navigation

导航

bash
opencli browser open <url>              # Open URL (page-changing)
opencli browser back                    # Go back (page-changing)
opencli browser scroll down             # Scroll (up/down, --amount N)
opencli browser scroll up --amount 1000
bash
opencli browser open <url>              # 打开URL(会变更页面)
opencli browser back                    # 返回上一页(会变更页面)
opencli browser scroll down             # 滚动页面(向上/向下,--amount N指定滚动量)
opencli browser scroll up --amount 1000

Inspect (free & instant)

检查(免费且即时)

bash
opencli browser state                   # Structured DOM with [N] indices — PRIMARY tool
opencli browser screenshot [path.png]   # Save visual to file — ONLY for user deliverables
bash
opencli browser state                   # 获取带[N]索引的结构化DOM——核心工具
opencli browser screenshot [path.png]   # 将可视化内容保存到文件——仅用于交付给用户的场景

Get (free & instant)

获取信息(免费且即时)

bash
opencli browser get title               # Page title
opencli browser get url                 # Current URL
opencli browser get text <index>        # Element text content
opencli browser get value <index>       # Input/textarea value (use to verify after type)
opencli browser get html                # Full page HTML
opencli browser get html --selector "h1" # Scoped HTML
opencli browser get attributes <index>  # Element attributes
bash
opencli browser get title               # 获取页面标题
opencli browser get url                 # 获取当前URL
opencli browser get text <index>        # 获取元素文本内容
opencli browser get value <index>       # 获取输入框/文本域的值(用于输入后验证)
opencli browser get html                # 获取完整页面HTML
opencli browser get html --selector "h1" # 获取指定选择器范围内的HTML
opencli browser get attributes <index>  # 获取元素属性

Interact

交互操作

bash
opencli browser click <index>           # Click element [N]
opencli browser type <index> "text"     # Type into element [N]
opencli browser select <index> "option" # Select dropdown
opencli browser keys "Enter"            # Press key (Enter, Escape, Tab, Control+a)
bash
opencli browser click <index>           # 点击元素[N]
opencli browser type <index> "text"     # 向元素[N]输入文本
opencli browser select <index> "option" # 选择下拉选项
opencli browser keys "Enter"            # 按键(Enter、Escape、Tab、Control+a等)

Wait

等待操作

Three variants — use the right one for the situation:
bash
opencli browser wait time 3                       # Wait N seconds (fixed delay)
opencli browser wait selector ".loaded"            # Wait until element appears in DOM
opencli browser wait selector ".spinner" --timeout 5000  # With timeout (default 30s)
opencli browser wait text "Success"                # Wait until text appears on page
When to wait: After
open
on SPAs, after
click
that triggers async loading, before
eval
on dynamically rendered content.
三种变体——根据场景选择合适的方式:
bash
opencli browser wait time 3                       # 等待N秒(固定延迟)
opencli browser wait selector ".loaded"            # 等待元素出现在DOM中
opencli browser wait selector ".spinner" --timeout 5000  # 带超时时间的等待(默认30秒)
opencli browser wait text "Success"                # 等待文本出现在页面上
等待场景:在SPA页面执行
open
后、触发异步加载的
click
后、对动态渲染内容执行
eval
前。

Extract (free & instant, read-only)

数据提取(免费且即时,仅读取)

Use
eval
ONLY for reading data. Never use it to click, type, or navigate.
bash
opencli browser eval "document.title"
opencli browser eval "JSON.stringify([...document.querySelectorAll('h2')].map(e => e.textContent))"
eval
仅可用于读取数据。绝不能用它执行点击、输入或导航操作。
bash
opencli browser eval "document.title"
opencli browser eval "JSON.stringify([...document.querySelectorAll('h2')].map(e => e.textContent))"

IMPORTANT: wrap complex logic in IIFE to avoid "already declared" errors

重要提示:将复杂逻辑包裹在IIFE中,避免“已声明”错误

opencli browser eval "(function(){ const items = [...document.querySelectorAll('.item')]; return JSON.stringify(items.map(e => e.textContent)); })()"

**Selector safety**: Always use fallback selectors — `querySelector` returns `null` on miss:
```bash
opencli browser eval "(function(){ const items = [...document.querySelectorAll('.item')]; return JSON.stringify(items.map(e => e.textContent)); })()"

**选择器安全**:始终使用备选选择器——`querySelector`在未找到元素时会返回`null`:
```bash

BAD: crashes if selector misses

不推荐:未找到选择器时会崩溃

opencli browser eval "document.querySelector('.title').textContent"
opencli browser eval "document.querySelector('.title').textContent"

GOOD: fallback with || or ?.

推荐:使用||或?.设置备选方案

opencli browser eval "(document.querySelector('.title') || document.querySelector('h1') || {textContent:''}).textContent" opencli browser eval "document.querySelector('.title')?.textContent ?? 'not found'"
undefined
opencli browser eval "(document.querySelector('.title') || document.querySelector('h1') || {textContent:''}).textContent" opencli browser eval "document.querySelector('.title')?.textContent ?? 'not found'"
undefined

Network (API Discovery)

网络监控(API发现)

bash
opencli browser network                  # Show captured API requests (auto-captured since open)
opencli browser network --detail 3       # Show full response body of request #3
opencli browser network --all            # Include static resources
bash
opencli browser network                  # 显示捕获的API请求(从打开页面时自动捕获)
opencli browser network --detail 3       # 显示第3个请求的完整响应体
opencli browser network --all            # 包含静态资源

Sedimentation (Save as CLI)

沉淀为可复用CLI——完整流程

bash
opencli browser init hn/top              # Generate adapter scaffold at ~/.opencli/clis/hn/top.ts
opencli browser verify hn/top            # Test the adapter (adds --limit 3 only if `limit` arg is defined)
  • init
    auto-detects the domain from the active browser session (no need to specify it)
  • init
    creates the file + populates
    site
    ,
    name
    ,
    domain
    , and
    columns
    from current page
  • verify
    runs the adapter end-to-end and prints output; if no
    limit
    arg exists in the adapter, it won't pass
    --limit 3
bash
opencli browser init hn/top              # 在~/.opencli/clis/hn/top.ts生成适配器脚手架
opencli browser verify hn/top            # 测试适配器(仅当适配器定义了`limit`参数时才会传入--limit 3)
  • init
    会从活跃浏览器会话中自动检测域名(无需手动指定)
  • init
    会创建文件并从当前页面填充
    site
    name
    domain
    columns
    字段
  • verify
    会端到端运行适配器并打印输出;如果适配器中没有
    limit
    参数,则不会传入
    --limit 3

Session

会话管理

bash
opencli browser close                   # Close automation window
bash
opencli browser close                   # 关闭自动化窗口

Example: Extract HN Stories

示例:提取Hacker News文章

bash
opencli browser open https://news.ycombinator.com
opencli browser state                   # See [1] a "Story 1", [2] a "Story 2"...
opencli browser eval "JSON.stringify([...document.querySelectorAll('.titleline a')].slice(0,5).map(a => ({title: a.textContent, url: a.href})))"
opencli browser close
bash
opencli browser open https://news.ycombinator.com
opencli browser state                   # 查看[1]“Story 1”、[2]“Story 2”...
opencli browser eval "JSON.stringify([...document.querySelectorAll('.titleline a')].slice(0,5).map(a => ({title: a.textContent, url: a.href})))"
opencli browser close

Example: Fill a Form

示例:填写表单

bash
opencli browser open https://httpbin.org/forms/post
opencli browser state                   # See [3] input "Customer Name", [4] input "Telephone"
opencli browser type 3 "OpenCLI" && opencli browser type 4 "555-0100"
opencli browser get value 3             # Verify: "OpenCLI"
opencli browser close
bash
opencli browser open https://httpbin.org/forms/post
opencli browser state                   # 查看[3]输入框“Customer Name”、[4]输入框“Telephone”
opencli browser type 3 "OpenCLI" && opencli browser type 4 "555-0100"
opencli browser get value 3             # 验证:返回“OpenCLI”
opencli browser close

Saving as Reusable CLI — Complete Workflow

保存为可复用CLI——完整工作流程

Step-by-step sedimentation flow:

分步沉淀流程:

bash
undefined
bash
undefined

1. Explore the website

1. 探索网站

opencli browser open https://news.ycombinator.com opencli browser state # Understand DOM structure
opencli browser open https://news.ycombinator.com opencli browser state # 了解DOM结构

2. Discover APIs (crucial for high-quality adapters)

2. 发现API(对高质量适配器至关重要)

opencli browser eval "fetch('/api/...').then(r=>r.json())" # Trigger API calls opencli browser network # See captured API requests opencli browser network --detail 0 # Inspect response body
opencli browser eval "fetch('/api/...').then(r=>r.json())" # 触发API调用 opencli browser network # 查看捕获的API请求 opencli browser network --detail 0 # 检查响应体

3. Generate scaffold

3. 生成脚手架

opencli browser init hn/top # Creates ~/.opencli/clis/hn/top.ts
opencli browser init hn/top # 创建~/.opencli/clis/hn/top.ts

4. Edit the adapter (fill in func logic)

4. 编辑适配器(填充函数逻辑)

- If API found: use fetch() directly (Strategy.PUBLIC or COOKIE)

- 如果找到API:直接使用fetch()(Strategy.PUBLIC或COOKIE策略)

- If no API: use page.evaluate() for DOM extraction (Strategy.UI)

- 如果没有API:使用page.evaluate()进行DOM提取(Strategy.UI策略)

5. Verify

5. 验证

opencli browser verify hn/top # Runs the adapter and shows output
opencli browser verify hn/top # 运行适配器并显示输出

6. If verify fails, edit and retry

6. 如果验证失败,编辑后重试

7. Close when done

7. 完成后关闭

opencli browser close
undefined
opencli browser close
undefined

Example adapter:

适配器示例:

typescript
// ~/.opencli/clis/hn/top.ts
import { cli, Strategy } from '@jackwener/opencli/registry';

cli({
  site: 'hn',
  name: 'top',
  description: 'Top Hacker News stories',
  domain: 'news.ycombinator.com',
  strategy: Strategy.PUBLIC,
  browser: false,
  args: [{ name: 'limit', type: 'int', default: 5 }],
  columns: ['rank', 'title', 'score', 'url'],
  func: async (_page, kwargs) => {
    const limit = Math.min(Math.max(1, kwargs.limit ?? 5), 50);
    const resp = await fetch('https://hacker-news.firebaseio.com/v0/topstories.json');
    const ids = await resp.json();
    return Promise.all(
      ids.slice(0, limit).map(async (id: number, i: number) => {
        const item = await (await fetch(`https://hacker-news.firebaseio.com/v0/item/${id}.json`)).json();
        return { rank: i + 1, title: item.title, score: item.score, url: item.url ?? '' };
      })
    );
  },
});
Save to
~/.opencli/clis/<site>/<command>.ts
→ immediately available as
opencli <site> <command>
.
typescript
// ~/.opencli/clis/hn/top.ts
import { cli, Strategy } from '@jackwener/opencli/registry';

cli({
  site: 'hn',
  name: 'top',
  description: 'Top Hacker News stories',
  domain: 'news.ycombinator.com',
  strategy: Strategy.PUBLIC,
  browser: false,
  args: [{ name: 'limit', type: 'int', default: 5 }],
  columns: ['rank', 'title', 'score', 'url'],
  func: async (_page, kwargs) => {
    const limit = Math.min(Math.max(1, kwargs.limit ?? 5), 50);
    const resp = await fetch('https://hacker-news.firebaseio.com/v0/topstories.json');
    const ids = await resp.json();
    return Promise.all(
      ids.slice(0, limit).map(async (id: number, i: number) => {
        const item = await (await fetch(`https://hacker-news.firebaseio.com/v0/item/${id}.json`)).json();
        return { rank: i + 1, title: item.title, score: item.score, url: item.url ?? '' };
      })
    );
  },
});
保存到
~/.opencli/clis/<site>/<command>.ts
→ 即可直接通过
opencli <site> <command>
调用。

Strategy Guide

策略指南

StrategyWhenbrowser:
Strategy.PUBLIC
Public API, no auth
false
Strategy.COOKIE
Needs login cookies
true
Strategy.UI
Direct DOM interaction
true
Always prefer API over UI — if you discovered an API during browsing, use
fetch()
directly.
策略使用场景browser配置:
Strategy.PUBLIC
公开API,无需认证
false
Strategy.COOKIE
需要登录Cookie
true
Strategy.UI
直接DOM交互
true
优先选择API而非UI —— 如果在浏览过程中发现API,直接使用
fetch()

Tips

技巧

  1. Always
    state
    first
    — never guess element indices, always inspect first
  2. Sessions persist — browser stays open between commands, no need to re-open
  3. Use
    eval
    for data extraction
    eval "JSON.stringify(...)"
    is faster than multiple
    get
    calls
  4. Use
    network
    to find APIs
    — JSON APIs are more reliable than DOM scraping
  5. Alias:
    opencli op
    is shorthand for
    opencli browser
  1. 始终先运行
    state
    —— 绝不猜测元素索引,始终先检查
  2. 会话保持持久 —— 浏览器在命令之间保持打开状态,无需重新打开
  3. 使用
    eval
    进行数据提取
    ——
    eval "JSON.stringify(...)"
    比多次
    get
    调用更快
  4. 使用
    network
    查找API
    —— JSON API比DOM爬取更可靠
  5. 别名
    opencli op
    opencli browser
    的简写

Common Pitfalls

常见陷阱

  1. form.submit()
    fails in automation
    — Don't use
    form.submit()
    or
    eval
    to submit forms. Navigate directly to the search URL instead:
    bash
    # BAD: form.submit() often silently fails
    opencli browser eval "document.querySelector('form').submit()"
    # GOOD: construct the URL and navigate
    opencli browser open "https://github.com/search?q=opencli&type=repositories"
  2. GitHub DOM changes frequently — Prefer
    data-testid
    attributes when available; they are more stable than class names or tag structure.
  3. SPA pages need
    wait
    before extraction
    — After
    open
    or
    click
    on single-page apps, the DOM isn't ready immediately. Always
    wait selector
    or
    wait text
    before
    eval
    .
  4. Use
    state
    before clicking
    — Run
    opencli browser state
    to inspect available interactive elements and their indices. Never guess indices from memory.
  5. evaluate
    runs in browser context
    page.evaluate()
    in adapters executes inside the browser. Node.js APIs (
    fs
    ,
    path
    ,
    process
    ) are NOT available. Use
    fetch()
    for network calls, DOM APIs for page data.
  6. Backticks in
    page.evaluate
    break JSON storage
    — When writing adapters that will be stored/transported as JSON, avoid template literals inside
    page.evaluate
    . Use string concatenation or function-style evaluate:
    typescript
    // BAD: template literal backticks break when adapter is in JSON
    page.evaluate(`document.querySelector("${selector}")`)
    // GOOD: function-style evaluate
    page.evaluate((sel) => document.querySelector(sel), selector)
  1. form.submit()
    在自动化中易失败
    —— 不要使用
    form.submit()
    eval
    提交表单。直接导航到搜索URL:
    bash
    # 不推荐:form.submit()经常静默失败
    opencli browser eval "document.querySelector('form').submit()"
    # 推荐:构造URL并导航
    opencli browser open "https://github.com/search?q=opencli&type=repositories"
  2. GitHub的DOM频繁变更 —— 优先使用
    data-testid
    属性;它们比类名或标签结构更稳定。
  3. SPA页面提取前需要等待 —— 在SPA页面执行
    open
    click
    后,DOM不会立即就绪。执行
    eval
    前务必使用
    wait selector
    wait text
    等待。
  4. 点击前先运行
    state
    —— 运行
    opencli browser state
    检查可用交互元素及其索引。绝不凭记忆猜测索引。
  5. evaluate
    在浏览器上下文运行
    —— 适配器中的
    page.evaluate()
    在浏览器内部执行。Node.js API(
    fs
    path
    process
    )不可用。使用
    fetch()
    进行网络调用,使用DOM API获取页面数据。
  6. page.evaluate
    中的反引号会破坏JSON存储
    —— 编写将以JSON格式存储/传输的适配器时,避免在
    page.evaluate
    中使用模板字符串。使用字符串拼接或函数式evaluate:
    typescript
    // 不推荐:模板字符串的反引号在JSON适配器中会失效
    page.evaluate(`document.querySelector("${selector}")`)
    // 推荐:函数式evaluate
    page.evaluate((sel) => document.querySelector(sel), selector)

Troubleshooting

故障排除

ErrorFix
"Browser not connected"Run
opencli doctor
"attach failed: chrome-extension://"Disable 1Password temporarily
Element not found
opencli browser scroll down && opencli browser state
Stale indices after page changeRun
opencli browser state
again to get fresh indices
错误修复方案
"Browser not connected"运行
opencli doctor
"attach failed: chrome-extension://"临时禁用1Password
元素未找到
opencli browser scroll down && opencli browser state
页面变更后索引过期重新运行
opencli browser state
获取新索引