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はヒアドキュメントに直接記述し、.jsファイルを作成したり、Playwrightをインポートしたり、別のブラウザを起動したり、ヘルパー名を発明したりしないでください。
ヒアドキュメントは単なるJavaScriptのコンテナであり、Bash呼び出しが実行ラウンドです。デフォルトでは、ブラウザタスク全体に対して1回のBash呼び出しを使用します。 各awaitは内部操作であり、ステップの境界ではありません。起動前に、予測可能なすべての観察、アクション、待機、抽出、検証、および限定された代替案をスクリプト内にエンコードします。ブラウザの結果をJavaScript内で即座に使用し、タスクが完了するまでプロセス内で適応し続けます。中間出力を検査したり、次のアクションを計画したりするためだけに終了しないでください。必要なユーザーまたは外部制御、プロセス内で実行できない視覚的検査、またはスクリプトが回復できないプロセスレベルの障害に対してのみ、別のBashコマンドを開始します。
ページコントロールを検査する前に、最もステートフルでない信頼性の高いルートを選択してください。 タスクが結果や制約を指定しているが、必要なインタラクションを指定していない場合、それらを直接エンコードする、すでに正しい状態、または既知の安定したURLやサイトルートを優先してください。同等のフィルター、並べ替え、またはUIを介したナビゲーションを再生する代わりに、結果の目標状態を検証します。ユーザーがそのインタラクションを要求した場合、インタラクション自体がテスト対象である場合、または信頼性の高い同等物が不明な場合にのみ、ページコントロールを使用します。脆弱なルートを決して発明しないでください。
すでに満たされた事後条件は完了済みとして扱います。 必要な値がすでに表示されている可能性のあるコントロールを操作する前に、その状態を判断するために必要な最小限の読み取りのみを実行します。一致する場合は、そのエディターを開いたり、インタラクションを再生したり、再度読み取ったりしないでください。残りの未達の結果に直接進みます。「設定」、「選択」、または「確認」などの言葉は、ユーザーが明示的に遷移を要求するか、インタラクション自体がテスト対象でない限り、必要な最終状態を説明します。
ブラウザ作業をターミナル完了から分離します。 useOrCreateは1つのユーザー目標を開始または再開します。返されたtask.idを保持し、その目標が終了するまで同じIDまたはまったく同じ名前を再利用します。予測可能なすべての観察、アクション、待機、抽出、検証をコンパクトに保ちますが、目標が満たされているかどうかをまだ判断しているBash呼び出し内でtaskSpaces.complete(...)を呼び出さないでください。最初にブラウザ作業を完了し、要求されたすべての結果と、必要な範囲またはカバレッジ境界が証明されたことを示す証拠を出力します。その事前出力を確認した後でのみ、専用の最終Bash呼び出しが元のタスクスペースを完了できます。この呼び出しはpageやbrowserの作業を実行しません。この単一のライフサイクルコミットは、1回の呼び出しのデフォルトに対する例外であり、ブラウザのステップやラウンドではありません。空でない、またはもっともらしい部分的な結果、停止したページ、使い果たされた再試行、またはフォールバックの試行は、完了の証拠にはなりません。keep: trueは、終了した結果をユーザーが保持できるようにしますが、未完了のタスクを存続させるわけではありません。
現在または相対日付の作業の時間枠を固定します。 ユーザー/タスク環境から、または明示的に検証された現在のページ状態から、「今日/現在/最新」を1回確立してから、レコードを収集します。コンテンツのタイムスタンプはデータとして扱い、時計としては扱いません。スクロール、仮想化、リロード、キャッシュ、または変更された結果バッチによって明らかになった古いレコードは、そのアンカーを置き換えてはなりません。元のウィンドウに対してレコードの評価を続けます。タスクをサイレントに最新のコンテンツ日付にリベースしないでください。
クイックスタート
このスキルのすべての例は、意図的に複合されています。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(...)で最終結果を出力します。
複合パターン
抽出、選択、移動、検証
リストページや検索ページで、選択する前に構造化された候補を抽出します。抽出、選択、アクション、待機、検証、およびクリーンアップを1回の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側のリクエストを実行します。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は、タイムアウト時に偽値を返します。続行する前に結果を確認するか、必要な状態をすぐに検証します。- リクエスト、レスポンス、またはナビゲーションの待機は、それらをトリガーするアクションの前に登録します。状態ベースの待機を優先します。
page.waitForTimeout(...)は、短時間の視覚的安定のためだけに使用し、2000ミリ秒以下に抑えます。 - 安定したセマンティックロケーターを優先します。ページ構造が不明な場合は、
evaluateAll、allInnerTexts、または別の限定された読み取りで関連するコントロールまたは候補を1回収集し、JavaScriptで次のアクションを導出し、セレクターの推測をコマンド間で列挙する代わりに、同じヒアドキュメント内で続行します。 - 単一要素のアクションと必要な読み取り(生のCSSおよび生の
xpath=ロケーターを含む)は厳格であり、自動待機します。一致がゼロの場合は、ロケーターを修正する前に、ロード、アクティブタブ、およびモーダル/オーバーレイの状態を確認します。複数の一致の場合は、count()/allInnerTexts()を検査し、セマンティックに絞り込むか、filter(...)を使用し、重複が正当であることを確認した後にのみfirst()/nth()を使用します。成功したアクションにスクリプトを進めさせます。ブランチを決定するときと、タスクに必要な最終的な事後条件のために1回だけ状態を読み取ります。すべてのアクションの後ではありません。すでに満たされた必要な状態は、再生を必要としません。 - 失敗した場合は、1つのターゲットを絞った観察を使用して、戦略を大幅に変更します。ほぼ同一のロケーターやコマンドを繰り返さないでください。証拠に基づいて、安定したセマンティック、DOM、またはビジュアルパスに切り替えます。
- ユーザーから明示的に要求された表示遷移と停止境界を保持します。必要なクリックが現在のタブを移動したり、別のタブを開いたりする可能性がある場合は、1回クリックし、同じスクリプト内で
await page.url()と更新されたbrowser.listTabs()から結果を解決します。宛先がわかっているという理由だけで、クリックを直接ナビゲーションに置き換えないでください。必要なアクションからの失敗を飲み込まないでください。
タスクスペース
タスクスペースは、ユーザーのログイン状態を継承する、独自のタブを持つ分離されたブラウジングコンテキストです。最初のBashスクリプトの開始近くで、taskSpaces.useOrCreate(nameOrId)を使用して1回選択します。外部依存関係により後のコマンドが避けられない場合は、続行する前に、同じ返された数値の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呼び出しを終了します。その出力を確認します。要求されたすべての事後条件と、必要な範囲またはカバレッジ境界は、単なる可能性ではなく、証明される必要があります。証明されている場合は、元のIDまたは正確な名前に対して最大1回taskSpaces.complete(nameOrId, { keep })を呼び出し、doneをチェックし、pageまたはbrowserの作業を実行しない、専用の最終Bash呼び出しを1回実行します。何かが未達または未証明の場合は、代わりに同じ元のタスクスペース内で続行します。修正、再試行、または後のフェーズは新しい目標ではありません。keepは必須です。デフォルトはfalseです。ユーザーが完了したページを保持するように要求した場合、ユーザーがその中で手動で行動する必要がある場合、または結果をURL、ファイル、アーティファクト、またはサマリーとして配信できない場合にのみtrueを使用します。スクラッチタブは随時閉じ、ユーザーが必要とするタブのみを保持します。
targetIdをハードコードしたり、手動でコピーしたり、idに名前を変更したりしないでください。現在のBash呼び出し内で取得して使用します。別のコマンドが本当に必要な場合は、browser.listTabs()を更新し、切り替えたり閉じたりする前にfind(...)の結果を検証します。browser.iframeTarget(...)は、オブジェクトではなく、ターゲットID文字列またはnullを返します。
制御ハンドオフ
「ユーザーが制御中」、「非アクティブ」、または「割り当てられていません」というエラーは、タスク全体のハードストップです。再試行したり、回避策を講じたり、taskSpaces.takeOverを自動的に呼び出したりしないでください。ユーザーに問い合わせて待機します。
ログイン、キャプチャ、または別の手動ステップについては、ブラウザ作業を終了し、ユーザーが実行できるように結果を出力し、エージェントが再開できるようにtaskSpaces.handOff(nameOrId)を呼び出します。ユーザーが終了したことを示した後、taskSpaces.takeOver(nameOrId)で再開します。その呼び出しでpage.url()などが失敗した場合は、browser.listTabs()を使用してアクティブなタブをリフレッシュして見つけます。taskSpaces.waitForAgentControlは、ブロックするシグナルを待機します。タイムアウト(デフォルトはなし)が必要な場合にのみこれを使用します。
このスキルに期待できること
pageのinfo()メソッドは、url、title、viewport、cookies、localStorage、sessionStorage、hasModals、isReady、readyState、contentLength、mimeType、fullPageImage、domCount、resourceCount、isMobile、isDarkMode、language、userAgent、performance、accessibilityを含むオブジェクトを返します。メソッドの後に()を必ず含めてください。browser.listTabs({ includeChrome: false })は、chrome://URLを除外します。ユーザーに表示されるコンテキストでは、chrome://newtabを空のタブとして扱います。事後条件の空のタブは無視または閉じて構いません。アクティブなターゲットIDが別のコンテキストで期限切れになった場合(switchTab呼び出しを参照)、それを介して行われる後続の操作は失敗します。console.log(help('page'))、console.log(help('locator'))、console.log(help('browser'))、console.log(help('taskSpaces'))、console.log(help('fetch'))、またはconsole.log(help('cdp'))は、このスキルの完全なAPIシグネチャと戻り値の型を出力します。正確な詳細については、これらの出力を参照してください。