Loading...
Loading...
Compare original and translation side by side
opencli doctor # Verify extension + daemon connectivityopencli doctor # 验证扩展 + 守护进程连通性statescreenshotstate[N]screenshotscreenshotclicktypeselectevaleval "el.click()"state[N]click <N>get valuetypeget value <index>stateopenclickscrollstate&&open + statetypetype + get value&&evalevalJSON.stringify(...)eval "(function(){ const x = ...; return JSON.stringify(x); })()"open + statetype + type + clickstatenetworkstatescreenshotstate[N]screenshotscreenshotclicktypeselectevaleval "el.click()"state[N]click <N>get valuetypeget value <index>stateopenclickscrollstate&&open + statetypetype + get value&&evalevalJSON.stringify(...)eval "(function(){ const x = ...; return JSON.stringify(x); })()"open + statetype + type + clickstatenetwork| Cost | Commands | When to use |
|---|---|---|
| Free & instant | | Default — use these |
| Free but changes page | | Interaction — run |
| Expensive (vision tokens) | | ONLY when user needs a saved image |
| 成本 | 命令 | 使用场景 |
|---|---|---|
| 免费且即时 | | 默认选项 — 优先使用 |
| 免费但会修改页面 | | 交互场景 — 执行后运行 |
| 成本高(消耗视觉token) | | 仅当用户需要保存图片时使用 |
&&undefined&&undefined
**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.
**页面跳转类命令始终放在链式调用末尾**(后续命令会读取到过期索引):
- `open <url>`、`back`、`click <会跳转的链接/按钮>`
**规则**:当你已经知道索引时使用链式调用。当你需要先发现索引时单独运行`state`。opencli browser open <url>opencli browser state[N]clicktypeselectkeysopencli browser wait selector ".loaded"wait text "Success"opencli browser stateopencli browser get value <N>~/.opencli/clis/<site>/<command>.tsopencli browser open <url>opencli browser state[N]clicktypeselectkeysopencli browser wait selector ".loaded"wait text "Success"opencli browser stateopencli browser get value <N>~/.opencli/clis/<site>/<command>.tsopencli 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 1000opencli browser open <url> # 打开URL(会修改页面)
opencli browser back # 返回上一页(会修改页面)
opencli browser scroll down # 滚动(向上/向下,支持--amount N参数)
opencli browser scroll up --amount 1000opencli browser state # Structured DOM with [N] indices — PRIMARY tool
opencli browser screenshot [path.png] # Save visual to file — ONLY for user deliverablesopencli browser state # 带[N]索引的结构化DOM — 核心工具
opencli browser screenshot [path.png] # 保存视觉内容到文件 — 仅用于用户交付物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 attributesopencli 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> # 元素属性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)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等)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 pageopenclickevalopencli browser wait time 3 # 等待N秒(固定延迟)
opencli browser wait selector ".loaded" # 等待元素出现在DOM中
opencli browser wait selector ".spinner" --timeout 5000 # 带超时(默认30秒)
opencli browser wait text "Success" # 等待文本出现在页面中openevalevalopencli browser eval "document.title"
opencli browser eval "JSON.stringify([...document.querySelectorAll('h2')].map(e => e.textContent))"evalopencli browser eval "document.title"
opencli browser eval "JSON.stringify([...document.querySelectorAll('h2')].map(e => e.textContent))"
**Selector safety**: Always use fallback selectors — `querySelector` returns `null` on miss:
```bash
**选择器安全性**:始终使用降级选择器 — 匹配失败时`querySelector`会返回`null`:
```bashundefinedundefinedopencli 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 resourcesopencli browser network # 展示捕获的API请求(打开页面后自动捕获)
opencli browser network --detail 3 # 展示第3条请求的完整响应体
opencli browser network --all # 包含静态资源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)initinitsitenamedomaincolumnsverifylimit--limit 3opencli browser init hn/top # 在~/.opencli/clis/hn/top.ts生成适配器脚手架
opencli browser verify hn/top # 测试适配器(仅当适配器定义了`limit`参数时才会添加--limit 3)initinitsitenamedomaincolumnsverifylimit--limit 3opencli browser close # Close automation windowopencli browser close # 关闭自动化窗口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 closeopencli 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 closeopencli 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 closeopencli 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 closeundefinedundefinedundefinedundefined// ~/.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>.tsopencli <site> <command>// ~/.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>.tsopencli <site> <command>| Strategy | When | browser: |
|---|---|---|
| Public API, no auth | |
| Needs login cookies | |
| Direct DOM interaction | |
fetch()| 策略 | 场景 | browser: 取值 |
|---|---|---|
| 公开API,无需鉴权 | |
| 需要登录cookie | |
| 直接DOM交互 | |
fetch()stateevaleval "JSON.stringify(...)"getnetworkopencli opopencli browserstateevaleval "JSON.stringify(...)"getnetworkopencli opopencli browserform.submit()form.submit()eval# 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"data-testidwaitopenclickwait selectorwait textevalstateopencli browser stateevaluatepage.evaluate()fspathprocessfetch()page.evaluatepage.evaluate// 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)form.submit()form.submit()eval# 不好的写法:form.submit()经常会静默失败
opencli browser eval "document.querySelector('form').submit()"
# 好的写法:构造URL并跳转
opencli browser open "https://github.com/search?q=opencli&type=repositories"data-testidwaitopenclickevalwait selectorwait textstateopencli browser stateevaluatepage.evaluate()fspathprocessfetch()page.evaluatepage.evaluate// 不好的写法:适配器以JSON存储时模板字面量的反引号会出错
page.evaluate(`document.querySelector("${selector}")`)
// 好的写法:函数式evaluate
page.evaluate((sel) => document.querySelector(sel), selector)| Error | Fix |
|---|---|
| "Browser not connected" | Run |
| "attach failed: chrome-extension://" | Disable 1Password temporarily |
| Element not found | |
| Stale indices after page change | Run |
| 错误 | 修复方案 |
|---|---|
| "Browser not connected" | 运行 |
| "attach failed: chrome-extension://" | 临时禁用1Password |
| 元素未找到 | 执行 |
| 页面变化后索引过期 | 重新运行 |