bunwv について
bunwvは、永続化されたWebViewセッションでエージェントがブラウザーを操作するためのコマンドラインスキルです。操作間でページ状態を保ち、構造化された結果やエラー、スクリーンショット、イベントを扱えます。
主な機能
- 01永続化WebViewセッション
- 02ブラウザー操作とページ待機
- 03DOM評価と要素確認
- 04フォーム操作と送信
- 05スクリーンショットと画像変換
- 06コンソールとCDPイベントの取得
活用シーン
- 01複数手順のウェブサイト操作を自動化
- 02フォーム送信とページ遷移のテスト
- 03ページ内容やブラウザーエラーの調査
- 04UI確認時のスクリーンショット取得
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ naticha/ bunwv --skill bunwvインストール後、Claude Code で/bunwvを入力して読み込まれたか確認します。
詳細
bunwv
永続的な WebView セッションを使用したヘッドレスブラウザー自動化です。デーモンは単一の WebView インスタンスを起動したままにするため、ページの状態(DOM、モーダル、フォーム、SPA のルート、スクロール位置)はコマンド間で維持されます。
エージェント向けの使用パターン
bunwv は、個別のツール呼び出しで操作する AI エージェント向けに設計されています。信頼できる契約事項を以下に示します。
- 成功した動詞は stdout に何も出力せず、終了コード 0 を返します。
click、type、navigate、press、scroll、scroll-to、clear、submit、resize、back/forward/reload、close、exists、wait-for、wait-for-gone、cdp-subscribe、cdp-unsubscribeはすべてこれに従います。読み取り動詞(status、evaluate、events、console、cdp、cdp-subscriptions、screenshot、sessions)は結果を出力します。 - エラーは、安定した終了コードとともに stderr に JSON で出力されます。 stderr のテキストではなく、終了コードに基づいて分岐してください。
0正常、1一般、2使用方法、3タイムアウト、4要素が見つからない、5デーモンに到達できない、6バッチの一部失敗(batch --keep-goingの場合のみ)。
- 動詞の実行中に
console.error/console.warnが自動的に通知されます。 動詞の実行中にページがエラーをログに出力すると、bunwv は stderr に{"console":[…]}を出力します。追加の呼び出しをしなくても失敗を確認できます。 - イベントはカーソルで取得します。
events --since <seq>はカーソルより新しいエントリーと新しいカーソルを返します。ターンをまたいでカーソルを保持し、操作後に再取得してください。バッファーから古いエントリーが削除されている場合、応答には"truncated":true,"oldest":<seq>が含まれます。 - バイナリー出力のファイルパス。
bunwv screenshotは、デフォルトでバイトデータを/tmp/bunwv-screenshot-<session>.jpg(JPEG @ q80)に書き込み、パスを stdout に出力します。画像を確認するには、そのパスに対して Read ツールを使用してください。 - 統一されたエンベロープには
--jsonを使用します。--jsonを指定したコマンドは、{ok, data?, error?, exitCode}を単一の JSON 行として返します。簡潔な出力よりも統一された形式を使いたい場合に便利です。 - 柔軟なフラグ構文。
--flag value、--flag=value、およびフラグの繰り返し(例:--mod Shift --mod Control)がすべて使用できます。フラグはコマンドの前後どちらにも指定できます。bunwv --json statusとbunwv status --jsonは同じ意味です。 BUNWV_SESSION環境変数 — 一度設定すれば、--sessionは省略できます。
コマンド
すべてのコマンドは bunwv <command> で実行します(bun install -g @naticha/bunwv でグローバルにインストールします)。
bunwv start [--width N] [--height N] [--data-store PATH] [--idle-timeout ms]
[--backend webkit|chrome] [--chrome-path PATH] [--chrome-argv '[json]']
[--chrome-url <ws-url>] [--url <initial-url>]
bunwv navigate <url>
bunwv click --selector <css> | --text <text> | --at <x,y>
[--text-match exact|contains|regex] # default: contains (trimmed)
[--button left|right|middle] [--count 1|2|3]
[--mod Shift] [--mod Control] [--mod Alt] [--mod Meta]
[--timeout ms]
bunwv exists <selector> # silent; exit 0 if present, 4 if not
bunwv type <text>
bunwv press <key> [--mod Shift] [--mod Control] ...
bunwv clear <selector>
bunwv submit [--form <selector>] [--button <text>]
bunwv scroll <dx> <dy>
bunwv scroll-to <selector> [--block start|center|end|nearest] [--timeout ms]
bunwv screenshot [--format png|jpeg|webp|avif|heic] [--quality 0-100]
[--max-width N] [--max-height N]
[--placeholder | --metadata]
[--encoding blob|buffer|base64|shmem] [--out <path>|-]
bunwv image <input> [--out <path>|-] [--format ...] [--quality N]
[--resize WxH | --max-width N | --max-height N]
[--rotate 90|180|270] [--flip] [--flop] [--metadata] [--placeholder]
bunwv evaluate <expression>
bunwv console [--clear] [--since <seq>] # terse "<seq> [<level>] <message>", cursor-based
bunwv events [--since <seq>]
bunwv cdp <method> [--params '{}']
bunwv cdp-subscribe <CDP.event> [<CDP.event> ...]
bunwv cdp-unsubscribe <CDP.event> [<CDP.event> ...]
bunwv cdp-subscriptions
bunwv wait-for <selector> | --url <substring> | --title <substring>
[--timeout ms]
bunwv wait-for-gone <selector> | --url <substring> | --title <substring>
[--timeout ms]
bunwv batch [--file <path>] [--keep-going] # stdin NDJSON of JSON arrays
bunwv status [--json]
bunwv resize <width> <height>
bunwv back / forward / reload
bunwv sessions
bunwv close [--all]
bunwv help
すべてのコマンドで、--json(エンベロープを有効化)、--session <name>(または BUNWV_SESSION 環境変数)、および上記の柔軟なフラグ構文を使用できます。
デフォルトのビューポートは、スクリーンショットを読みやすくするため 1920x1080 です。
セッション管理
セッションは名前付きで分離されています。各セッションは、個別の Unix ソケット上で独自のデーモンを実行します。デフォルトのセッション名は default です。--session <name> または BUNWV_SESSION 環境変数で変更できます。
bunwv start # starts "default" session
bunwv start --session cmais # separate "cmais" session
BUNWV_SESSION=cmais bunwv navigate http://localhost:3000
bunwv sessions # list all running sessions
bunwv close --session cmais # stop a specific session
bunwv close --all # stop every running session
自動シャットダウン:デーモンは、30 分間操作がないと終了します。--idle-timeout で変更できます。
bunwv start --idle-timeout 3600000 # 1 hour
bunwv start --idle-timeout 0 # never auto-shutdown
再利用の検出:既存のセッションに対して bunwv start を実行すると、現在の URL を出力して終了コード 0 を返します。
ベストプラクティス:会話の開始時に bunwv sessions を実行し、孤立したデーモンがないか確認してください。不要なものは bunwv close --all で終了します。
基本的な操作ループ
確認してから操作し、再度確認します。基本的な単一ターンのループは次のとおりです。
- デーモンを起動します:
bunwv start(すでに起動している場合は何もしません) - 移動します:
bunwv navigate http://localhost:3000 - スクリーンショットを取得します:
bunwv screenshot—/tmp/bunwv-screenshot-<session>.pngを stdout に出力します - Read ツールでスクリーンショットを読み取ります
- 操作します:
bunwv click --selector "button.submit"(または--text) - 次のスクリーンショットを取得する前に、
bunwv wait-for --url "/next"(またはwait-for "<selector>")を実行します - 再度スクリーンショットを取得して確認します
- タスクが完了したら
bunwv closeを実行します
複数ステップのフローでは、bunwv batch(以下を参照)を優先してください。シーケンス全体が 1 つのプロセスで実行され、確認できる NDJSON トランスクリプトが返されます。
要素のクリック
click は多態的です。--selector、--text、--at のいずれか1つだけを使用してください:
bunwv click --selector "button.submit"
bunwv click --text "Sign In" # default: trimmed contains match
bunwv click --text "Sign In" --text-match exact
bunwv click --text "^Sign.+In$" --text-match regex
bunwv click --at 100,200
修飾キー、ボタン、クリック回数はそれぞれ独立しています:
bunwv click --selector "#ctx" --button right # context menu
bunwv click --selector ".item" --count 2 # double-click
bunwv click --selector "a" --mod Shift # shift+click
bunwv click --selector "a" --mod Meta --mod Shift # cmd+shift+click
bunwv click --selector "button" --timeout 60000 # longer actionability wait
--text はデフォルトで前後の空白を除いた部分一致(大文字と小文字を区別)です。厳密な一致には --text-match exact を、正規表現パターンには --text-match regex を使用してください。--selector と --text はどちらも、アクション可能になるまで待機したうえで、ネイティブの isTrusted: true イベントを生成します。--at は待機をスキップします。
入力フィールドのクリアと編集
React の入力欄をクリアするために Cmd+A / Backspace を使用しないでください。React の状態が更新されません。clear を使用してください:
bunwv clear "input[name='email']"
bunwv click --selector "input[name='email']"
bunwv type "[email protected]"
既存の入力値を編集するときは、必ず clear、click、type の順に実行してください。
要素、URL、タイトルの待機
ページの変更を引き起こすアクションの後には wait-for を使用してください:
bunwv click --text "Save Changes"
bunwv wait-for-gone "[role='dialog']" # wait for modal to close
bunwv screenshot
bunwv click --text "Edit"
bunwv wait-for "[role='dialog']" # wait for modal to appear
bunwv wait-for --url "/dashboard" # wait until URL contains substring
bunwv wait-for --title "Home" # wait until <title> contains substring
--url は location.href をポーリングし、--title は document.title をポーリングします。<selector>、--url、--title のいずれか1つが必須です。デフォルトのタイムアウトは10秒です。
存在の確認
手軽な確認には exists を使用してください(出力なし。存在する場合は終了コード0、存在しない場合は4):
bunwv exists "[data-loaded]"
if [ $? -eq 0 ]; then ... fi
evaluate "!!document.querySelector(...)" よりも exists を優先してください。トークン数が少なく、動作も明確です。
ページデータの抽出
evaluate は結果を JSON リテラルとして出力します。文字列は引用符付き、数値は引用符なし、オブジェクトは構造化 JSON として返されます:
bunwv evaluate "document.title" # "Example"
bunwv evaluate "document.querySelectorAll('.error').length" # 3
bunwv evaluate "[...document.querySelectorAll('h2')].map(h => h.textContent)"
文(const、let、if など)は自動的に IIFE でラップされます。
フォームの送信
送信ボタンをクリックする代わりに submit を使用してください。form.requestSubmit() を使うため、React フォームで受け付けられます(JS の .click() は isTrusted:false を生成するため、多くの React ハンドラーで無視されます):
bunwv submit # first form on page
bunwv submit --button "Save Changes" # submit via a specific button
bunwv submit --form "form.edit-quote" # target a specific form
送信後は、結果として DOM が変化するまで待機してください:
bunwv submit --button "Save Changes"
bunwv wait-for-gone "[role='dialog']"
bunwv screenshot
フォームへの入力
まず入力欄をクリックしてから、テキストを入力してください。フィールド間の移動には Tab を使用します:
bunwv click --selector "input[name='email']"
bunwv type "[email protected]"
bunwv press Tab
bunwv type "password123"
bunwv submit --button "Sign In"
認証情報は .env に記述します(Bun が自動的に読み込みます)。シェルは CLI 引数内の $VAR を展開します:
bunwv type "$TEST_EMAIL"
認証の保持
デーモンの再起動後も Cookie と localStorage を保持するには、--data-store を使用してください:
bunwv start --data-store ./bunwv-session
一度ログインすれば、以降のセッションでも認証状態が維持されます。
コンソール出力の取得によるデバッグ
ページのコンソール出力は自動的に取得されます。動詞の実行中に発生した console.error / console.warn のエントリは、動詞の応答とともに stderr に出力されます。バッファ全体を取得するには:
bunwv console # terse: "<seq> [<level>] <message>", one per line
bunwv console --clear # print then clear
bunwv console --since 42 # only entries with seq > 42 (matches events cursor model)
bunwv --json console # {messages:[…], cursor, truncated?, oldest?}
簡潔な出力では、各エントリが1行に収まるよう、メッセージ内の \n と \r がエスケープされます。バッファが空の場合は何も出力されません(終了コード0)。--since には、確認した最大の seq(各行の最初のフィールド)を指定してください。生のメッセージテキスト(複数行のスタックトレースなど)や切り詰めの通知が必要な場合は --json を使用します。
ナビゲーションと CDP イベント
ナビゲーションイベントとサブスクライブ済みの CDP イベントはリングバッファに格納されます。カーソルを使って取得してください:
bunwv events # full buffer, prints {events, cursor}
bunwv events --since 42 # only events with seq > 42
CDP イベントをサブスクライブします(Chrome バックエンドのみ。先にドメインを有効にしてください)。1回の呼び出しで複数のタイプを指定できます:
bunwv cdp Network.enable
bunwv cdp-subscribe Network.responseReceived Network.requestWillBeSent
bunwv navigate https://example.com
bunwv events --since 0 # inspect events
bunwv cdp-unsubscribe Network.responseReceived Network.requestWillBeSent
bunwv cdp-subscriptions # list active subscriptions, one per line
バッファ内で古いエントリが削除されている場合、events は "truncated":true,"oldest":<seq> を返します。
スクリーンショットのオプション
デフォルトでは、セッションごとのファイルに JPEG(品質80)を書き込み、そのパスを出力します。JPEG がデフォルトなのは、通常、同じ表示領域の PNG と比べてサイズが5~15分の1になり、スクリーンショットの反復処理のコストを大幅に抑えられるためです。
bunwv screenshot # /tmp/bunwv-screenshot-<session>.jpg (JPEG @ q80)
bunwv screenshot --format png # opt back into PNG for pixel-exact comparisons
bunwv screenshot --format webp --quality 70 # smaller again at the cost of decode speed
bunwv screenshot --max-width 1024 # cap longest side; aspect preserved; never upscales
bunwv screenshot --max-width 800 --max-height 600 # bound both axes
bunwv screenshot --out shot.jpg # write to a specific path
bunwv screenshot --out - # bytes to stdout
bunwv screenshot --encoding base64 # base64 string to stdout
すべてのピクセルが不要な場合の低コストなプレビュー:
bunwv screenshot --metadata # {"width":1920,"height":1080,"format":"png"} — size-check before Reading
bunwv screenshot --placeholder # data:image/png;base64,... (tiny blur-up preview, no file)
--placeholder と --metadata はファイルを書き込む代わりに、構造化出力を stdout に出力します。また、互いに併用できません。--encoding shmem との併用もできません。
プラットフォームに関する注意: --format avif と --format heic のエンコードがサポートされるのは、Apple Silicon 搭載の macOS / Windows のみです。それ以外のプラットフォームでは、構造化エラーが返されます。ポータブルなコードには jpeg / webp / png を使用してください。
--encoding shmem(Kitty ターミナル)は {name, size} を出力し、呼び出し側がリンク解除できるよう POSIX shm セグメントを残します。この方法では Bun.Image パイプラインを経由しないため、--max-width / --max-height / --placeholder / --metadata は併用すると拒否されます。
任意の画像を変換する:bunwv image
スクリーンショットのループ外にあるローカル画像ファイル(アップロード、ダウンロードしたアセット、フィクスチャ)には、bunwv image が CLI プロセス内で直接 Bun.Image を実行します。デーモンもセッションも必要ありません。
bunwv image input.png # input.jpg (jpeg @ q80, same dir)
bunwv image input.png --out small.webp --max-width 512 # webp, capped at 512 wide, aspect preserved
bunwv image input.png --resize 200x200 # explicit resize (may distort if aspect differs)
bunwv image input.png --rotate 90 # rotate 90/180/270
bunwv image input.png --flip # vertical flip; --flop is horizontal
bunwv image input.png --metadata # {"width","height","format"} JSON on stdout
bunwv image input.png --placeholder # data: URL on stdout
--format が指定されていない場合、出力形式は --out の拡張子から推測されます。それ以外の場合は JPEG がデフォルトです。--out を省略すると、結果は入力ファイルと同じ場所に新しい拡張子で書き込まれます。
Chrome バックエンドと CDP
macOS では WebKit がデフォルトです。Linux/Windows では Chrome が自動的に使用されます。次のように、どこでも上書きできます。
bunwv start --backend chrome
bunwv start --chrome-path /path/to/chromium
bunwv start --chrome-argv '["--headless=new"]'
bunwv start --chrome-url ws://127.0.0.1:9222/devtools/browser/<id> # attach to an existing Chrome
生の CDP 呼び出し(Chrome のみ):
bunwv cdp "Page.getLayoutMetrics"
bunwv cdp "Runtime.evaluate" --params '{"expression": "1+1"}'
bunwv cdp "Emulation.setDeviceMetricsOverride" --params '{"width":375,"height":812,"deviceScaleFactor":2,"mobile":true}'
WebKit バックエンドでは CDP を使用できません。
クラッシュするバックエンドのデバッグ
バックエンドプロセスの標準入出力をデーモンの標準入出力にルーティングします(人間によるデバッグ専用。エージェントには不要です)。
bunwv start --backend chrome --chrome-stderr inherit
bunwv start --webkit-stderr inherit
バッチモード
bunwv batch は、多数のコマンドを単一のプロセスで実行します。動詞ごとにソケットの往復は 1 回で、コマンドごとの Bun 起動はありません。標準入力の各行は引数の JSON 配列で、各応答は標準出力に NDJSON エンベロープとして出力されます。batch のフラグ(例:--session)は、その行で個別に指定されない限り、すべての行に引き継がれます。
$ cat <<'EOF' | bunwv batch --session cmais --keep-going
["navigate","http://localhost:3000/login"]
["click","--selector","input[name='email']"]
["type","[email protected]"]
["press","Tab"]
["type","hunter2"]
["submit","--button","Sign In"]
["wait-for","--url","/dashboard"]
["screenshot"]
EOF
{"argv":[...],"ok":true,"exitCode":0}
{"argv":[...],"ok":true,"exitCode":0}
...
--keep-going を指定すると、いずれかの行が失敗してもリスト全体を実行します。失敗した行があればプロセスは終了コード 6(batch-partial)で終了し、すべて成功した場合は 0 で終了します。--keep-going を指定しない場合は、失敗した行の終了コードで終了します。--file <path> を指定すると、標準入力の代わりにファイルから読み込みます。
stdout フィールドにはコマンドの簡潔な出力が含まれます(例:evaluate の場合は "\"Example Domain\"")。stdoutBytes は、screenshot --out - などのバイナリ出力を Base64 で表したものです。
エラーからの復旧
コマンドが失敗するかタイムアウトした場合:
- スクリーンショットを撮ってページの状態を確認します
bunwv consoleを実行して、キャプチャされたエラーを確認しますbunwv events --since 0を実行して、ナビゲーション/CDP イベントを確認しますbunwv evaluateを実行して DOM を調べます- デーモンに接続できない場合(終了コード 5)、
bunwv startを実行します。データストアには認証情報が保持されています
ソケットの権限
各セッションの Unix ソケット(/tmp/bunwv-<session>.sock)と PID ファイルは chmod 0600 に設定されており、デーモンを起動したユーザーだけが接続できます。共有マシン(コンテナー、ビルド用マシン)では、他のローカルユーザーがブラウザーセッションを操作するのを防げます。
既知の制限事項
- macOS:WebKit がデフォルトです(依存関係なし)。Linux/Windows:Chrome が自動検出されます。
--backendで上書きできます。 click --selector/--textは、操作可能な状態(表示されている、安定している、遮られていない)になるまで自動的に待機します。 WebView のデフォルトは 30 秒です。--timeoutで上書きできます。--textのデフォルトは、前後の空白を除いた部分文字列一致(含む)です。--text-match exact|regexで変更できます。--atは操作可能になるまでの待機をスキップします — 正確な座標を把握している必要があります。CSS やテキストでうまくいかない場合は、evaluateとgetBoundingClientRect()を使用してください。- React の入力欄では
clearが必要です — Cmd+A/Backspace では React の内部状態は更新されません。 - CDP は Chrome のみ対応です。 WebKit では
bunwv cdpとbunwv cdp-subscribeは拒否されます。 - イベントバッファー:1000 件または 10 MB のいずれかに達した時点で上限になります。古いエントリーは通知なしで破棄されます。見落としたイベントがある場合、
events --sinceはtruncatedを報告します。