Communitygithub.com

fourth3950/ego-lite

一個輕量級、高效能的瀏覽器,專為 AI 代理自動化網頁任務而設計。

ego-lite 是什麼?

ego-browser 是一個透過 CLI 可存取的 Node.js 運行時,提供了一個真實的 Chromium 瀏覽器,專為 AI 代理自動化網頁任務而設計。它支援類似 Playwright 的 API,包括 `page`、`page.locator(...)`、`browser` 和 `taskSpaces` 等功能,並提供 `taskSpaces`、`site`、`fetch` 和 `cdp` 等獨特能力。使用者透過 Bash 工具以 `ego-browser nodejs <<'EOF' ... EOF` 的方式執行腳本,並將所有可預測的操作(如觀察、動作、等待、提取、驗證)封裝在一個 heredoc 中,以減少命令調用次數。其核心原則包括:在滿足後置條件時直接跳過不必要的操作、凍結時間窗口以確保資料一致性、以及將瀏覽器工作與任務完成(透過 `taskSpaces.complete()`)分離。ego-browser 適用於 Claude Code、Cursor 等 AI 代理平台,透過隔離的瀏覽上下文(task spaces)來管理使用者登入狀態,並支援任務空間的創建、切換、完成和移交。它強調使用穩定的語義選擇器、優先使用狀態式等待而非計時器,並提供詳細的錯誤處理和重試策略,以確保任務的可靠性與效率。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add fourth3950/ego-lite

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

ego-browser

ego-browser 透過 CLI 可存取的 Node.js 運行時,暴露了一個真實的 Chromium 瀏覽器。其預載的 page、page.locator(...)、browser 和 taskSpaces 外觀遵循 Playwright 風格的名稱和調用形狀;taskSpaces、site、fetch 和 cdp 則提供 ego-browser 特有的功能。

關於設定、安裝或連線問題,請參閱 references/install.md。

使用 Bash 工具以 ego-browser nodejs <<'EOF' ... EOF 的方式執行瀏覽器工作。將 JavaScript 直接放在 heredoc 中;不要建立 .js 檔案、匯入 Playwright、啟動另一個瀏覽器或發明輔助名稱。

Heredoc 只是 JavaScript 的容器;Bash 調用才是執行回合。預設為整個瀏覽器任務使用一次 Bash 調用。 每個 await 都是一個內部操作,不是步驟邊界。在啟動前,將每個可預測的觀察、動作、等待、提取、驗證和有界限的替代方案編碼到腳本中。立即在 JavaScript 中使用瀏覽器結果,並持續在進程中進行適應,直到任務完成;不要僅僅為了檢查中間輸出或規劃下一步而退出。僅在需要使用者或外部控制、無法在進程中進行的視覺檢查,或腳本無法恢復的進程級別故障時,才啟動另一個 Bash 命令。

在檢查頁面控制項之前,選擇狀態最少且可靠的路徑。 當任務指定了結果或約束,但未指定所需的互動時,優先選擇已經正確的狀態,或已知的穩定 URL 或站點路由,這些路由直接編碼了這些結果;驗證最終目標狀態,而不是重新播放等效的過濾器、排序或 UI 導航。當使用者要求該互動、互動本身正在測試中,或沒有已知的可靠等效項時,才使用頁面控制項。切勿發明脆弱的 route。

將已滿足的後置條件視為已完成的工作。 在操作一個可能已顯示所需值的控制項之前,僅執行最小的讀取以決定該狀態。如果匹配,請不要打開其編輯器、重播互動或再次讀取它;直接繼續處理其餘未滿足的結果。除非使用者明確要求轉換或互動本身正在測試中,否則諸如「設定」、「選擇」或「確保」等詞彙描述了所需的最終狀態。

將瀏覽器工作與終端機完成分開。 useOrCreate 開始或恢復一個使用者目標;保留其回傳的 task.id,並重複使用相同的 id 或完全相同的名稱,直到該目標變為終端狀態。將每個可預測的觀察、動作、等待、提取和驗證保持緊湊,但不要在仍在確定目標是否已滿足的 Bash 調用中呼叫 taskSpaces.complete(...)。首先完成瀏覽器工作,並列印證明每個請求的結果以及任何所需的範圍或覆蓋邊界已被驗證的證據。只有在審查了先前的輸出之後,才能透過專用的最終 Bash 調用完成原始任務空間;它不執行任何 page 或 browser 工作。此單一生命週期提交是對一次調用預設的例外,而不是瀏覽器步驟或回合。非空或合理的部分結果、停滯的頁面、耗盡的重試或後備嘗試都不是完成的證據。keep: true 為使用者保留一個終端結果;它不會讓未完成的任務保持活躍。

凍結目前或相對日期工作的時間視窗。 在收集記錄之前,從使用者/任務環境或明確驗證的目前頁面狀態中建立一次「今天/目前/最新」。將內容時間戳記視為資料,而不是時鐘。透過滾動、虛擬化、重新載入、快取或更改的結果批次所揭示的較舊記錄不得取代該錨點。繼續根據原始視窗評估記錄;不要默默地將任務重新基準到觀察到的最新內容日期。

快速開始

此技能中的每個範例都是刻意複合的。根據使用者任務調整其 URL、選擇器和資料。

ego-browser nodejs <<'EOF'
const task = await taskSpaces.useOrCreate('inspect example page')
await browser.openOrReuseTab('https://example.com', { wait: true, timeout: 20000 })

const heading = await page.getByRole('heading').first().innerText()
const info = await page.info()
if (!heading || !('url' in info)) throw new Error('Example page was not ready')

const result = { taskSpaceId: task.id, heading, url: info.url }
console.log(JSON.stringify(result, null, 2))
EOF

將所有可預測的工作保留在腳本內,直到任務完成。使用 console.log(...) 輸出最終結果。

複合模式

提取、選擇、導航、驗證

在列表或搜尋頁面上,先提取結構化的候選項再進行選擇。將提取、選擇、動作、等待、驗證和清理保留在一個 Bash 調用中。

ego-browser nodejs <<'EOF'
const task = await taskSpaces.useOrCreate('compare search results')
await browser.openOrReuseTab('https://example.com/search?q=browser+automation', {
  wait: true,
  timeout: 20000,
})

const cards = page.locator('article')
const items = await cards.evaluateAll((nodes) =>
  nodes.map((node) => ({
    title: node.querySelector('h2')?.textContent?.trim(),
    href: node.querySelector('a')?.href,
  })),
)
const chosenIndex = items.findIndex((item) => item.title && item.href)
if (chosenIndex < 0) throw new Error('No usable result: ' + JSON.stringify(items))

const before = await page.url()
const navigation = page.waitForURL((url) => url.href !== before, { timeout: 15000 })
await cards.nth(chosenIndex).getByRole('link').first().click()
if (!(await navigation)) throw new Error('Chosen result did not navigate')

const info = await page.info()
if (!('url' in info) || info.url === before) throw new Error('Navigation was not verified')
const result = { chosen: items[chosenIndex], opened: info.url }
console.log(JSON.stringify(result, null, 2))
EOF

填寫、觸發、等待、回讀

在觸發它們的動作之前註冊請求/回應等待,然後驗證產生的頁面狀態,而不是將點擊視為成功。

ego-browser nodejs <<'EOF'
const task = await taskSpaces.useOrCreate('search orders')
await browser.openOrReuseTab('https://example.com/orders', { wait: true, timeout: 20000 })

const responsePromise = page.waitForResponse(
  (response) => response.url().includes('/api/orders') && response.ok(),
  { timeout: 15000 },
)
await page.getByLabel('Search orders').fill('pending')
await page.getByRole('button', { name: /search/i }).click()
const response = await responsePromise

const rows = await page.locator('table tbody tr').allInnerTexts()
if (!rows.length) throw new Error('Search completed but returned no visible rows')
const result = { status: response.status(), rows }
console.log(JSON.stringify(result, null, 2))
EOF

重新整理分頁句柄、切換、檢查

將 targetId 視為短期句柄。在同一個 Bash 調用中發現、驗證並使用它。

ego-browser nodejs <<'EOF'
const task = await taskSpaces.useOrCreate('review generated report')
const tabs = await browser.listTabs({ includeChrome: false })
const reportTab = tabs.find((tab) => tab.url.includes('/reports/'))
if (!reportTab?.targetId) throw new Error('Report tab not found: ' + JSON.stringify(tabs))

await browser.switchTab(reportTab.targetId)
const info = await page.info()
const heading = await page.getByRole('heading').first().innerText()
if (!('url' in info) || !info.url.includes('/reports/')) throw new Error('Wrong tab selected')
const result = { taskSpaceId: task.id, heading, url: info.url }
console.log(JSON.stringify(result, null, 2))
EOF

執行時期對應

  • page:導航和狀態(goto、reload、url、title、info)、語義定位器、等待、snapshot、screenshot、screencast、evaluate、keyboard、mouse、下載和事件耗盡。
  • page.locator(selector):鏈接和過濾;first / nth / last;點擊、懸停、dragTo、scrollIntoViewIfNeeded、表單、鍵盤、上傳、狀態讀取、集合、元素評估、螢幕截圖和等待方法。
  • browser:listTabs、currentTab、switchTab、openOrReuseTab、closeTab、ensureRealTab、iframeTarget。
  • taskSpaces:list、switch、new、useOrCreate、claim、complete、handOff、takeOver、waitForAgentControl。
  • fetch.server 執行 Node.js 端的請求;fetch.browser 在目前頁面來源中執行請求。僅將 cdp 用作逃生艙。
  • console.log 是輸出通道。當確切簽名不明確時,使用 console.log(help('page'))、console.log(help('locator')) 或其他 help(name) 調用。

執行規則

  • page.url() 在 ego-browser 中是異步的;始終使用 await page.url()。page.waitForURL(...) 謂詞接收一個 URL 物件,因此檢查 url.href、url.pathname 或 url.searchParams。它預設等待 load;僅在有意在載入前繼續時使用 waitUntil: 'commit'。
  • page.waitForURL、page.waitForLoadState、page.waitForSelector、定位器 waitFor 和 page.waitForFunction 在超時時回傳一個 falsy 值。在繼續之前檢查結果或立即驗證所需的狀態。
  • 在觸發它們的動作之前註冊請求、回應或導航等待。優先使用基於狀態的等待;僅在短暫的視覺穩定時使用 page.waitForTimeout(...),並保持在 2000 毫秒或以下。
  • 優先使用穩定的語義定位器。當頁面結構未知時,使用 evaluateAll、allInnerTexts 或其他有界限的讀取一次收集相關的控制項或候選項,在 JavaScript 中推導出後續動作,並在同一個 heredoc 中繼續,而不是跨命令枚舉選擇器猜測。
  • 單元素動作和必要的讀取(包括原始 CSS 和原始 xpath= 定位器)是嚴格的並且會自動等待。對於零匹配,在更正定位器之前確認載入、活動分頁和模態/疊加層狀態。對於多個匹配,檢查 count() / allInnerTexts(),使用 filter(...) 進行語義縮小,並僅在確認重複是合法的之後才使用 first() / nth()。讓成功的動作推動腳本前進;在決定分支時讀取狀態,並為任務所需的最終後置條件讀取一次,而不是在每個動作之後。已經滿足的所需狀態不需要重播。
  • 失敗時,使用一次有針對性的觀察來實質性地改變策略。不要重複近乎相同的定位器或命令;根據證據切換到穩定的語義、DOM 或視覺路徑。
  • 保留明確請求的使用者可見轉換和停止邊界。當所需的點擊可能會導航目前分頁或打開另一個分頁時,點擊一次,並從同一個腳本中的 await page.url() 加上重新整理的 browser.listTabs() 來解析結果;不要僅僅因為知道其目的地就將點擊替換為直接導航。不要吞沒來自必要動作的失敗。

任務空間

任務空間是一個隔離的瀏覽上下文,具有自己的分頁,繼承使用者的登入狀態。在第一個 Bash 腳本開始附近使用 taskSpaces.useOrCreate(nameOrId) 選擇它一次。如果外部依賴使得後續命令不可避免,請在繼續之前選擇相同的回傳數值 task.id 或完全相同的簡短目標名稱;僅為單獨的使用者目標建立一個新空間。保留已經驗證的事實,而不是重新開始設定。

useOrCreate 重用或建立代理擁有的空間。如果匹配的空間是用戶擁有的,它會選擇該空間而不聲明它,因此瀏覽器工作會遇到使用者控制的硬性停止。在使用者明確確認在那裡工作後,使用 taskSpaces.list() → taskSpaces.claim(id) → browser.listTabs() → browser.switchTab(targetId)。

每個空間都有 ownership: 'agent' | 'agentDelegatedToUser' | 'user':

對使用者擁有的空間的操作行為
taskSpaces.switch拋出;它只切換代理擁有的空間
taskSpaces.claim將所有權轉移給代理並選擇該空間
taskSpaces.handOff / complete(..., { keep: true })以 { done: false, skipped: 'user-owned' } 跳過
taskSpaces.complete(..., { keep: false })聲明,然後關閉空間
taskSpaces.takeOver / waitForAgentControl不執行所有權檢查

在聲明成功之前,檢查 handOff 和 complete 的 done 結果。

將完成視為與瀏覽器執行分開的終端提交。在捕獲並列印最終 URL、值和其他證據後,在不完成的情況下結束正在工作的 Bash 調用。審查該輸出:每個請求的後置條件以及任何所需的範圍或覆蓋邊界必須被證明,而不僅僅是可能。如果它們被證明了,執行一個專用的最終 Bash 調用,該調用最多呼叫一次 taskSpaces.complete(nameOrId, { keep })(針對原始 id 或確切名稱),檢查 done,並且不執行任何 page 或 browser 工作。如果有任何未滿足或未證明的內容,請在同一個原始任務空間中繼續;更正、重試或後續階段不是一個新目標。keep 是必需的。預設為 false;僅在使用者要求保留完成的頁面、必須在其中手動操作,或結果無法作為 URL、檔案、工件或摘要傳遞時,才使用 true。邊進行邊關閉臨時分頁,並僅保留使用者需要的分頁。

切勿硬編碼、手動複製或將 targetId 重新命名為 id。在當前的 Bash 調用中獲取並使用它。如果另一個命令確實是必要的,請重新整理 browser.listTabs() 並在切換或關閉之前驗證 find(...) 結果。browser.iframeTarget(...) 回傳一個目標 id 字串或 null,而不是一個物件。

控制權移交

「使用者正在控制」、「不活躍」或「未指派」錯誤是整個任務的硬性停止。不要重試、繞過它,或自動呼叫 taskSpaces.takeOver。詢問使用者並等待。

對於登入、驗證碼或其他手動步驟,請完成

…

[為翻譯而截斷]

相關技能