Vercel Sandboxを使ったブラウザ自動化
エフェメラルなVercel SandboxマイクロVM内でagent-browser + ヘッドレスChromeを実行します。Linux VMがオンデマンドで起動し、ブラウザコマンドを実行してシャットダウンします。Vercelにデプロイされたあらゆるフレームワーク(Next.js、SvelteKit、Nuxt、Remix、Astroなど)で動作します。
依存関係
pnpm add @agent-browser/sandbox @vercel/sandbox
Sandbox VMには、Chromiumのシステム依存関係とagent-browser自体が必要です。@agent-browser/sandboxヘルパーは、新しいサンドボックスにはデフォルトでこれらをインストールし、サブ秒起動のためにサンドボックススナップショット(下記参照)を利用します。サンドボックスイメージに既にChromiumの必要なライブラリが含まれている場合にのみ、installSystemDependencies: falseを渡してください。
コアパターン
import {
createAgentBrowserSnapshot,
runAgentBrowserCommand,
withAgentBrowserSandbox,
type VercelSandboxSession,
} from "@agent-browser/sandbox/vercel";
async function withBrowser<T>(
fn: (sandbox: VercelSandboxSession) => Promise<T>,
): Promise<T> {
return withAgentBrowserSandbox(fn);
}
スクリーンショット
screenshot --jsonコマンドはファイルに保存し、パスを返します。ファイルをbase64として読み戻します:
export async function screenshotUrl(url: string) {
return withBrowser(async (sandbox) => {
await runAgentBrowserCommand(sandbox, ["open", url]);
const titleResult = await runAgentBrowserCommand<{ data?: { title?: string } }>(sandbox, [
"get", "title",
]);
const title = titleResult.json?.data?.title || url;
const ssResult = await runAgentBrowserCommand<{ data?: { path?: string } }>(sandbox, [
"screenshot",
]);
const ssPath = ssResult.json?.data?.path;
if (!ssPath) throw new Error("スクリーンショットがファイルパスを返しませんでした。");
const b64Result = await sandbox.runCommand("base64", ["-w", "0", ssPath]);
const screenshot = (await b64Result.stdout()).trim();
await runAgentBrowserCommand(sandbox, ["close"], { json: false });
return { title, screenshot };
});
}
アクセシビリティスナップショット
export async function snapshotUrl(url: string) {
return withBrowser(async (sandbox) => {
await runAgentBrowserCommand(sandbox, ["open", url]);
const titleResult = await runAgentBrowserCommand<{ data?: { title?: string } }>(sandbox, [
"get", "title",
]);
const title = titleResult.json?.data?.title || url;
const snapResult = await runAgentBrowserCommand(sandbox, ["snapshot", "-i", "-c"], {
json: false,
});
await runAgentBrowserCommand(sandbox, ["close"], { json: false });
return { title, snapshot: snapResult.stdout };
});
}
マルチステップワークフロー
サンドボックスはコマンド間で永続化されるため、完全な自動化シーケンスを実行できます:
export async function fillAndSubmitForm(
url: string,
data: Record<string, string>,
postSubmitWaitArgs: string[],
) {
return withBrowser(async (sandbox) => {
await runAgentBrowserCommand(sandbox, ["open", url]);
const snapResult = await runAgentBrowserCommand(sandbox, ["snapshot", "-i"], {
json: false,
});
const snapshot = snapResult.stdout;
// スナップショットを解析して要素参照を見つける...
for (const [ref, value] of Object.entries(data)) {
await runAgentBrowserCommand(sandbox, ["fill", ref, value]);
}
await runAgentBrowserCommand(sandbox, ["click", "@e5"]);
// アプリ固有の待機条件を渡す(例: ["--url", "**/checkout/complete"],
// ["--text", "ありがとう"], ["#confirmation"])
await runAgentBrowserCommand(sandbox, ["wait", ...postSubmitWaitArgs]);
const ssResult = await runAgentBrowserCommand<{ data?: { path?: string } }>(sandbox, [
"screenshot",
]);
const ssPath = ssResult.json?.data?.path;
if (!ssPath) throw new Error("スクリーンショットがファイルパスを返しませんでした。");
const b64Result = await sandbox.runCommand("base64", ["-w", "0", ssPath]);
const screenshot = (await b64Result.stdout()).trim();
await runAgentBrowserCommand(sandbox, ["close"], { json: false });
return { screenshot };
});
}
サンドボックススナップショット(高速起動)
サンドボックススナップショットは、システム依存関係 + agent-browser + Chromiumが既にインストールされたVercel Sandboxの保存済みVMイメージです。Dockerイメージのように考えてください: 毎回依存関係をゼロからインストールする代わりに、サンドボックスは事前に構築されたイメージから起動します。
これはagent-browserのアクセシビリティスナップショット機能(agent-browser snapshot)とは無関係であり、ページのアクセシビリティツリーをダンプするものです。サンドボックススナップショットは、高速なVM起動のためのVercelインフラストラクチャの概念です。
サンドボックススナップショットがない場合、各実行はシステム依存関係 + agent-browser + Chromiumをインストールします(約30秒)。スナップショットがある場合、起動はサブ秒になります。
サンドボックススナップショットの作成
スナップショットには、システム依存関係(dnf経由)、agent-browser、Chromiumが含まれている必要があります:
const snapshotId = await createAgentBrowserSnapshot();
これを一度実行し、その後環境変数を設定します:
AGENT_BROWSER_SNAPSHOT_ID=snap_xxxxxxxxxxxx
デモアプリでヘルパースクリプトが利用可能です:
npx tsx examples/environments/scripts/create-snapshot.ts
Sandboxパターンを使用する本番環境デプロイメントに推奨されます。
認証
Vercelデプロイメントでは、Sandbox SDKはOIDCを介して自動的に認証されます。ローカル開発または明示的な制御の場合は、以下を設定してください:
VERCEL_TOKEN=<パーソナルアクセストークン>
VERCEL_TEAM_ID=<チームID>
VERCEL_PROJECT_ID=<プロジェクトID>
これらはSandbox.create()呼び出しに展開されます。存在しない場合、SDKはVERCEL_OIDC_TOKENにフォールバックします(Vercel上では自動)。
スケジュールワークフロー(Cron)
Vercel Cron Jobsと組み合わせて、定期的なブラウザタスクを実行できます:
// app/api/cron/route.ts (またはフレームワークに応じた同等の場所)
export async function GET() {
const result = await withBrowser(async (sandbox) => {
await sandbox.runCommand("agent-browser", ["open", "https://example.com/pricing"]);
const snap = await sandbox.runCommand("agent-browser", ["snapshot", "-i", "-c"]);
await sandbox.runCommand("agent-browser", ["close"]);
return await snap.stdout();
});
// 結果を処理し、アラートを送信し、データを保存...
return Response.json({ ok: true, snapshot: result });
}
// vercel.json
{ "crons": [{ "path": "/api/cron", "schedule": "0 9 * * *" }] }
環境変数
| 変数 | 必須 | 説明 |
|---|---|---|
AGENT_BROWSER_SNAPSHOT_ID | いいえ(推奨) | サブ秒起動のための事前構築済みサンドボックススナップショットID(上記参照) |
VERCEL_TOKEN | いいえ | Vercelパーソナルアクセストークン(ローカル開発用。Vercel上ではOIDCが自動) |
VERCEL_TEAM_ID | いいえ | VercelチームID(ローカル開発用) |
VERCEL_PROJECT_ID | いいえ | VercelプロジェクトID(ローカル開発用) |
フレームワーク例
パターンはフレームワーク間で同一です。唯一の違いはサーバーサイドコードを配置する場所です:
| フレームワーク | サーバーコードの場所 |
|---|---|
| Next.js | サーバーアクション、APIルート、ルートハンドラ |
| SvelteKit | +page.server.ts、+server.ts |
| Nuxt | server/api/、server/routes/ |
| Remix | loader、action関数 |
| Astro | .astro frontmatter、APIルート |
例
agent-browserリポジトリのexamples/environments/を参照してください。Vercel Sandboxパターンを使用した実用的なアプリがあり、サンドボックススナップショット作成スクリプト、ストリーミング進行状況UI、レート制限などが含まれています。