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
获取最新索引