ego (lite) 只是一個瀏覽器,ego 則是你跨裝置的個人 Agent。
加入候補名單

ego-browser

面向 AI Agent 的瀏覽器自動化執行環境,連接 ego (lite) 的真實 Chromium 工作階段。

llms.txt

ego-browser 是 ego (lite) 為 AI Agent 提供的瀏覽器自動化執行環境。它透過 Chrome DevTools Protocol 連接到 ego (lite) 的真實 Chromium 工作階段,以 Node.js heredoc 腳本為入口:Agent 在一次 stdin 傳送中寫入完整的 JS 流程,所有 helper 都已預先注入腳本作用域,瀏覽器狀態則持續保留在 Space 中。

ego-browser 不是給人手動操作瀏覽器用的,也不是 Playwright / Puppeteer 的替代品,目標讀者是 LLM Agent。

適合誰用

  • 需要自動操作瀏覽器的 AI 編程 Agent:Claude Code、Codex、Cursor、自定義 SDK Agent。
  • 構建垂直 Agent 的團隊:自動操作飛書、Google Docs、Salesforce 等後臺系統。
  • 反覆執行固定網頁流程:登入、填寫表單、匯出、搜尋、讀取表格。
  • 曾經把完整 DOM 或 HTML 傳入 LLM、受 Token 限制的人。

安裝

隨 ego (lite) 一起安裝,參見 快速開始。安裝後即可在任意目錄執行 ego-browser 命令。

也可以單獨安裝 Skill:

npx skills add github:CitroLabs/ego-lite/skills/ego-browser

核心循環

Agent 操作網頁的典型節奏,所有命令都在一次 heredoc 內完成:

ego-browser nodejs <<'EOF'
const task = await useOrCreateTaskSpace('search github issues')

await openOrReuseTab('https://github.com/issues', { wait: true, timeout: 20 })

cliLog(await snapshotText())

EOF
  1. 複用或建立 Task Space(每次 heredoc 都要聲明,詳見 Space)。
  2. 打開目標頁面。
  3. 讀取快照(snapshotText())拿到帶 [ref=N, loc=..., url=...] 的語義樹。
  4. 基於 @N ref 或 CSS selector 執行動作。
  5. cliLog(...) 輸出最終結果。

heredoc 內是 Node.js 程序;js(...) 內才是瀏覽器頁面上下文。兩者不要混用。

Helper 參考

所有 helper 在腳本作用域內以 camelCase 直接可用,無需 import

Task Space

await listTaskSpaces()
const task = await useOrCreateTaskSpace('describe task')   // 複用或建立
await completeTaskSpace(task.name)                         // 任務完成、保留分頁
await closeTaskSpace(task.name)                            // 不再需要展示,關閉空間

name 用 3-6 詞自然語言描述當前任務,不要用佔位符。

導航與狀態

await listTabs()
await openOrReuseTab(url, { wait: true, timeout: 20 })
await gotoAndWait(url, { timeout: 20, settle: 1 })
await newTab(url)
await switchTab(tabId)
await currentTab()
await pageInfo()
await ensureRealTab()        // 新建的 Task Space 可能還沒有分頁

觀察

await snapshotText()                              // 整頁語義 Snapshot(預設)
await snapshotText({ scope: 'only_within_viewport' })
await captureScreenshot('result.png')
await drainEvents()                               // 讀取導覽與網路事件佇列

鼠標與滾動

click / doubleClick / hover / dragMouse 接受統一的 target 格式(CSS 像素):

  • 'string':CSS selector 或 @ref,點擊元素中心。
  • [x, y]{x, y}:viewport 座標。
  • {selector, x, y}:以元素左上角為基準,疊加偏移。
  • options.label:3-6 詞描述,傳入後觸發視覺高亮動畫。
await click('@21', { label: '查看登入狀態' })
await click('button.primary', { label: '點擊提交按鈕' })
await click([420, 260])
await hover('@5', { label: '懸停查看菜單' })
await dragMouse([from, to], { label: '拖拽卡片' })

await scrollBy(900)
await scroll({ dy: 900 })
await scrollToBottomUntil(
  async () => await js(String.raw`document.querySelectorAll('article').length`) >= 20,
  { step: 900, wait: 1, maxSteps: 20 },
)

鍵盤與輸入

await typeText('hello world')
await fillInput('@2', 'user@test.com')
await pressKey('Enter')
await dispatchKey({ ... })

檔案與網路

await uploadFile('input[type="file"]', '/absolute/path/to/file.pdf')
await httpGet('https://api.example.com/data')   // 瀏覽器上下文發起的 GET

等待

await wait(1)                                    // 秒
await waitForLoad()
await waitForElement('@1')
await waitForNetworkIdle()

wait()timeout 單位是秒;只有以 Ms 結尾的參數才是毫秒。

瀏覽器執行

js(source) 本質是 Runtime.evaluate,接受字符串。不要像 Puppeteer 那樣傳函數加參數,會觸發 warning 並被 .toString() 包裹,閉包變量與參數通道都會丟失。

多步邏輯封裝成一個 IIFE 一次傳回:

const data = await js(String.raw`(() => {
  const items = [...document.querySelectorAll('article')]
  return items.map(el => ({
    text: el.innerText,
    links: [...el.querySelectorAll('a')].map(a => a.href),
  }))
})()`)

await elementEval('@1', el => el.getBoundingClientRect())
await cdp('Page.captureScreenshot', { format: 'png' })

輸出與自查

cliLog(value)                  // heredoc 中唯一的輸出方式
cliLog(help('click'))          // 查看 helper 用法

推薦工作流

通常先從 snapshotText + ref / loc 入手,因為它保留語義結構,並顯著降低座標脆弱性:

  1. 複用或建立 Task Space。
  2. 打開或切換頁面(openOrReuseTab / gotoAndWait)。
  3. snapshotText() 獲取 [ref=N, loc=..., url=...] 語義樹,ref 自動註冊到 refMap。
  4. @N 配合 click / fillInput / elementEval 執行動作,或在 js(...) 內一次性完成 DOM 抽取。
  5. cliLog(...) 輸出最終結果。

可組合的其他路徑:

  • captureScreenshot + click([x, y]):視覺版面、canvas 類介面、虛擬清單、accessibility 不完整的頁面。
  • js / elementEval / cdp:直接抽取 DOM、查看瀏覽器狀態,或常規 helper 不夠直接的場景。

優先把導航、觀察、滾動、抽取、過濾、聚合、輸出寫在一段 ego-browser nodejs heredoc 裡完成,不要再用第二個本機 node 腳本處理同一批資料。

ref 的有效範圍

@N 僅對最近一次 snapshotText 的 refMap 有效。每次 snapshotText() 都會重建 refMap。ref 編號來自元素的 CDP backendNodeId,同一元素在多次 Snapshot 中編號通常一致;但要操作 @N,N 必須出現在最近一次 Snapshot 輸出中。

觸發 Unknown ref 的常見原因:

  • 元素被滾出 viewport。
  • DOM 重渲染。
  • 上一輪 scope: 'only_within_viewport' 而下一輪未覆蓋該元素。

需要跨多輪穩定引用同一個元素時,使用 Snapshot 輸出裡的 loc=... 作為穩定 selector,或直接寫 CSS selector。這也是 Experience 累積的基礎(見 Skills)。

Skill Workspace

ego-browser 不自帶可修改的 Agent Experience。預設會從儲存庫的 Skill 套件載入 helper 擴充功能和已累積的網站 Experience:

../../skills/ego-browser

可透過環境變數覆寫:

EGO_BROWSER_AGENT_WORKSPACE=/path/to/ego-browser ego-browser nodejs <<'EOF'
cliLog(await siteSkills())
EOF

learnings/ 目錄下的網站 Experience 會持續生效,每次呼叫 helper 時都會重新讀取。Experience 的寫入與探索機制請見 Skills

校驗已學經驗:

npm run validate:learnings

目錄結構

package/ego-browser/
├── src/                      # browser-runtime / helpers / run.js
│   ├── browser-runtime.js    # 瀏覽器側 ego runtime 橋接
│   ├── helpers.js            # 暴露給 Agent 腳本的 helper
│   ├── run.js                # CLI 入口(執行 stdin)
│   └── learning/             # 經驗索引、域名校驗、格式校驗
├── artifacts/ego-browser/    # 構建產物,npm bin 指向這裡
└── test/                     # 單元測試

skills/ego-browser/
├── SKILL.md / SKILL.zh.md    # Agent 呼叫入口
└── learnings/                # 網站 Experience 目錄

注意事項

  • snapshotText() 預設使用 scope: 'full_page',涵蓋整頁。只有在僅需可見範圍時才傳入 'only_within_viewport'
  • js() 傳回的是表達式求值結果,不要再 JSON.parse(...)
  • js() 模板字符串裡寫正則時,反斜槓要寫兩次(\\d\\s),或改用 String.raw
  • 頂層 return 會被自動包成 IIFE;嵌套回調中的 return 也可能誤觸發,複雜表達式優先寫成 (() => { ... })()
  • 使用者明確要求使用 ego-browser 時,預設執行環境已就緒。除非首次執行發生錯誤,否則不要預先執行 which ego-browsernode -v 或查看 help。