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,响应即时且零token消耗。
    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
成本高(消耗视觉token)
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
后、点击触发异步加载的操作后、对动态渲染内容执行
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

示例:提取HN故事

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] a "故事1", [2] a "故事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] 输入框 "客户姓名", [4] 输入框 "电话"
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页面提取前需要
    wait
    — 在单页应用上执行
    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
获取最新索引