Skip to main content
Glama

Daedalus

Chrome拡張機能によるリモートブラウザ制御。Evalブリッジ+永続ホットフィックス+タブごとの制御+スクリーンショット、CDP、クッキー、Chromeが拡張機能を実行しているページでのネットワークキャプチャ。

インストール

インストール前に知っておくべきこと: 通常のevalはバナーなしのMAINワールド注入を使用します。ソースフリーのCSPプローブが動的コンパイルが利用可能であることを確認できない場合、DaedalusはCDPフォールバックを試みます。アタッチに成功すると、そのフォールバックが実行されている間、Chromeの「Daedalus started debugging this browser」バナーが表示されます。CDPセッションやネットワークキャプチャを維持すると、アタッチが長く保持されることがあります。バナーはデバッガのアタッチを示すものであり、値の整合性の保証ではありません。

Chromeで展開済み拡張機能(extension/)を読み込みます:

  1. chrome://extensions にアクセス

  2. デベロッパーモードを有効化

  3. パッケージ化されていない拡張機能を読み込むをクリックし、extension/ を選択

初回インストール時に一意のトークンが自動生成され、chrome.storage.local に保存されます。拡張機能のオプションページ(パズルアイコン → Daedalus → Options)で表示・変更できます。

マルチタブ並列スクレイピングの場合は、Chromeのバックグラウンドタブのスロットリングを無効にします:

chrome --disable-background-timer-throttling --disable-backgrounding-occluded-windows --disable-renderer-backgrounding

ブリッジ自体(server.py)は標準ライブラリのみで、インストールは不要です。CLIとインプロセスMCPエンドポイントは、このリポジトリのPythonパッケージです:

pip install .            # the `daedalus` CLI
pip install ".[mcp]"     # ... and the MCP endpoint's dependencies

mcp エクストラがない場合でも、ブリッジは起動し、他のすべてを提供します。起動時にMCPブートストラップの失敗を報告し、/mcp は提供されません。

Related MCP server: Real Browser MCP

仕組み

  1. トークン: インストール時に crypto.randomUUID() で一度生成され、chrome.storage.local に保存されます

  2. タブID: Chromeのネイティブ tabs API — 各タブはChromeの tabId で識別され、作成/更新時に30秒の chrome.alarms ハートビートで登録されます

  3. 単一のSSEストリーム: background.js は1つの永続的な fetch SSE接続(tab=extension)を開き、受信したコマンドを適切なタブにディスパッチします

  4. ページブリッジ: content.js(ISOLATEDワールド)は、window.GM メッセージをbackgroundと page.js(MAINワールド)の間で中継します。Evalは最初に chrome.scripting MAINワールド注入を使用します。ソースフリーの動的コンパイルプローブは、CSPでブロックされたページをCDPにルーティングします。そこでのアタッチに失敗すると、ページリレーに到達します。すべてのチャネルはページの MAIN ワールドセマンティクスで実行されます。

  5. ホットフィックス: 拡張機能全体の chrome.storage.local キー daedalus-hotfixes(トークンごとではない)に保存され、backgroundが各対象のトップレベルページの読み込み時に再生します — MAINワールド注入、またはページCSPが動的コンパイルを禁止している場合はCDP — 非永続的な修正はバージョンゲートされます。トークンをローテーションしても、このストアは分離もクリアもされません。

コマンドの送信

Daedalusは、拡張機能のコマンドサーフェスを <your-bridge>/mcp(ストリーミング可能なHTTPトランスポート)のMCPサーバーとして公開します。リクエストをディスパッチする前に、Bearer値がCLIの既存の設定パスを通じて解決されたブリッジトークンと正確に一致することを要求します: TOKEN は DAEDALUS_TOKEN を上書きし、オプションの _settings プロバイダーを含みます。トークンが設定されていない場合、MCPサーフェスは 401 でフェイルクローズします。エンドポイントは、mcp エクストラがインストールされている場合にのみ存在します(上記「インストール」を参照)。Claude Code(または任意のMCPクライアント)に追加するには:

{
  "mcpServers": {
    "daedalus": {
      "url": "https://daedalus.example.com/mcp",
      "headers": { "Authorization": "Bearer <your-bridge-token>" }
    }
  }
}

ブリッジトークンは、拡張機能がインストール時に生成するものです — 拡張機能のオプションページ(パズルアイコン → Daedalus → Options)に表示されます。

その例ではMCPサーバーをパブリックホスト名の背後に置いていますが、トランスポートのデフォルトの許可ホストはループバックのみです(127.0.0.1:*,localhost:*): DAEDALUS_MCP_ALLOWED_HOSTS にパブリックホスト名を指定しないと、プロキシされたリクエストは拒否されます。3つのMCP設定すべてについては、下記の「サーバー」を参照してください。

7つのグループに40のツール — タブ、eval/デバッグ、メディア、クッキー、CSS/ブロッキング、ホットフィックス、ネットワーク/CDP。完全なリストは CLAUDE.md の <mcp> セクションを参照するか、MCPエンドポイントで tools/list を呼び出してください。

手動検証ヘルパー(MCPクライアント不要):

TOKEN=<tok> python3 scripts/mcp_probe.py list
TOKEN=<tok> python3 scripts/mcp_probe.py call title '{"tab_id":"<tabId>"}'
TOKEN=<tok> python3 scripts/mcp_probe.py call screenshot '{"include_image":true}'

CLI

シェルCLIは、このリポジトリの daedalus-cli ホイール(daedalus_cli/)として提供され、daedalus コマンドとしてインストールされます。環境から DAEDALUS_URL と DAEDALUS_TOKEN を読み取り、TOKEN は一回限りの上書き、ID=<tabId> はタブごとのターゲティング用です(省略するとブロードキャスト):

DAEDALUS_TOKEN=<tok> daedalus tabs
DAEDALUS_TOKEN=<tok> ID=<tabId> daedalus title
DAEDALUS_TOKEN=<tok> daedalus exec myid 'document.title'

daedalus --help はすべてのサブコマンドを一覧表示し、各サブコマンドには独自の --help があります。送信する exec コードは式または関数本体であり、その戻り値が結果として返されます — 契約については上記の「コマンドの送信」を参照してください。

オプションのインポートシームも利用可能です: sys.path に _settings という名前のモジュールがインポート可能な場合、CLIは組み込みの環境フォールバックの代わりに、その setting(name, default) および required(name) 関数を DAEDALUS_URL と DAEDALUS_TOKEN に使用します。TOKEN 環境変数は一回限りのトークン上書きのままで優先されます。_settings がない場合、DAEDALUS_URL はデフォルトを使用し、DAEDALUS_TOKEN は必須です。ID はタブごとのターゲティングの環境変数のままです。

または、生のコマンドファイルを原子的に公開します(MCPもCLIも不要)。最終的な .json 名への直接リダイレクトは、ストリームがライターの完了前にファイルを観測する可能性があるため、サポートされていません。.tmp で終わる兄弟名を書き、同じディレクトリ内で名前を変更します:

# Broadcast to all tabs
commands_dir="$DAEDALUS_DIR/commands"
final="$commands_dir/<token>.json"
tmp="$(mktemp "$commands_dir/.<token>.XXXXXX.tmp")"
printf '%s\n' '{"id":"test1","code":"document.title"}' > "$tmp" &&
  mv "$tmp" "$final"

# Target a specific tab
final="$commands_dir/<token>_<tabId>.json"
tmp="$(mktemp "$commands_dir/.<token>_<tabId>.XXXXXX.tmp")"
printf '%s\n' '{"id":"test1","code":"document.title"}' > "$tmp" &&
  mv "$tmp" "$final"

リーダーは兄弟の .tmp 名を無視します。古いライターが最終名に不正なJSONを公開した場合、リーダーはそれを変更せずに残し、進行中の書き込みを削除する代わりに再試行します。原子的な名前変更の後、SSEストリームがコマンドを配信して消費します。結果は $DAEDALUS_DIR/results/<token>_<tabId>.json(タブごと)と $DAEDALUS_DIR/results/<token>.json(最後の書き込みが優先)に配置されます。page-main 注入とページリレーevalの完了には、exec_ms フィールド(ページコンテキストの実行時間(ミリ秒))が含まれる場合があります。ページは、どちらのチャネルでもこのページタイミングフィールドを偽造または省略できます。CDPの完了には exec_ms は含まれません。キューに入れられた配信に関連する結果には、roundtrip_ms(コマンドのエンキュー(PUT /command)から結果の到着(POST /result)までの、サーバーが観測した完全な往復時間(ミリ秒))も含まれます。これは、キュー待機+SSE配信+クライアントリレー+実行+復路にまたがるため、両方のフィールドが存在する場合、roundtrip_ms − exec_ms はトランスポート/キューのオーバーヘッドの近似値になります。これらの測定は異なるクロックを使用します: exec_ms はページの performance.now() を使用し、roundtrip_ms はサーバーの壁時計ミリ秒を使用するため、それらの差は正確な減算ではなく近似値です。_did のないレガシーフレームには roundtrip_ms はありません。

非同期サポート

デフォルトの page-main チャネルは、式をページ所有の eval で、関数本体をページ所有の Function で実行します。非同期ラッパーがトップレベルの await を提供します。ソースを送信する前に、backgroundは定数の Function プローブのみを注入します。そのプローブが true を返す場合、ソース注入は一度だけ試行され、すべての結果は終端です: 値、例外、または注入トランスポートエラーは、CDPでソースを再試行せずに報告されます。ページは Function を所有しており、このルーティングヒントに影響を与えることができますが、プローブには送信されたソースが含まれていないため、別のチャネルを選択してもその副作用が複製されることはありません。

ソースフリープローブが true を返さない場合(最も一般的にはページCSPが動的コンパイルをブロックするため)、DaedalusはCDPを試みます。アタッチに成功すると、Chromeのデバッガバナーが表示されます。Runtime.evaluate はトップレベルの await にREPLモードを使用します。return を含むソースは、ラッパープローブが式として解析しない限り、関数本体として扱われます。そのラッパープローブはパーサーヒューリスティックであり、非実行のセキュリティ境界ではありません。演算子が作成したソースはそれを逃れて実行される可能性があります。最終的なCDP評価がディスパッチされた後、すべての結果は終端です。送信されたソースが実行される前のアタッチまたは形状の失敗のみがページリレーに到達します。CDPのプロミス解決は10秒に制限されており、結果と例外オブジェクトのハンドルは、維持されたセッションやキャプチャがデバッガのアタッチを保持している場合でも解放されます。

制御していないページで評価されたJavaScriptは、どのチャネルで実行されたかに関係なく、そのページが選択できる値を返します。world フィールドは、送信されたソースを実行した実行チャネルのみを記録します。これはCSPとデバッガの動作に関する診断メタデータであり、信頼シグナルではありません。そのeval値は、通常の注入では page-main、インスペクタフォールバックでは cdp、最終リレーでは page:<hostname> です。ここで <hostname> はコンテンツスクリプトの location.hostname です。backgroundは page: プレフィックスを追加するため、リレーホスト名は cdp や page-main と衝突しません。この名前空間の事実は、返された値について何も述べていません。CLIはフィールドを channel=... としてレンダリングし、ダッシュボードも同様に、MCPの exec、put、result、ping は正確な world 値を保持します。どれも信頼クラスを割り当てません。

デフォルトの注入とCDPフォールバックは、文書化されたスロッピーモードのケースでクラシックスクリプトの動作を保持します: with はコンパイルされ、レガシー8進リテラルは受け入れられ、未宣言の代入はグローバルを作成します。CDP REPLモードでは、繰り返しの let または const 宣言も許可されます。CLIとMCPの結果待機はデフォルトで15秒です。呼び出し元のタイムアウトは、ページで既に実行されているコードをキャンセルしません。Blobリレーは、await を含むコードでは最大10秒、それ以外では3秒待機してから、フォールバックタイムアウトを報告します。

リレー相関は記述的で制限されたままです: 送信元タブが一致する必要があり、受け入れられたメッセージはランダムなリレーIDを消費し、ソースフリーの注入プローブとディスパッチ前のCDPフォールバックが完了するまでリレーIDは登録されません。これらの制御は、クロスタブまたは重複したリレー完了を防ぎます。値の整合性を確立するものではありません。

ページリレーエントリは最大1,000件までライブにできます。容量に達した新しいフォールバックは、ライブの作業を退避させることなく、1つの終端容量エラーを受け取ります。各登録エントリは300,000ミリ秒後に期限切れになり、同じタブの完了または以前の送信失敗がそれを削除しない場合、1つの終端タイムアウトエラーを受け取ります。

ダッシュボード

<your-bridge>/dashboard のブラウザベースの制御サーフェスで、CLIなしで拡張機能のコマンドセット全体を操作できます — ライブタブリスト、eval REPL、スクリーンショット、クッキー、ホットフィックス、ブロックルール、ネットワークキャプチャ、CDP、CSS注入、フェッチタイミング、アップロードブラウザ。

  1. 拡張機能が制御するタブでURLを開きます。

  2. §12 Settingsまでスクロールし、拡張機能のオプションページ(パズルアイコン → Daedalus → Options)からトークンを貼り付けます。保存します。

  3. ダッシュボードがライブイベントにサブスクライブすると、トップバーのSSEステータスドットがシアンに変わります。

server.py がリポジトリの dashboard/ ディレクトリから直接提供します(バニラJS+ESモジュール、ビルド不要)。ライブ更新は既存の /stream エンドポイントを通じて行われます: server.py は、/register が既存のタブを更新したとき、および /sync-tabs、/unregister、/result の成功パスで、イベントを commands/<token>_dashboard/<ts>_<uuid>.json にエンキューします。/unregister はタブが存在しない場合でも発行します。ダッシュボードはそれらを kind:'event' フレームとしてドレインします。

注意: 拡張機能のコンテンツ+ページスクリプトは、ダッシュボードのタブを含む一致するページに注入されます。ブロードキャストevalコマンド(exec -b)はダッシュボード内でも実行されます — 破壊的なコードをブロードキャストする前に、タブごとのターゲティングを優先するか、ダッシュボードを閉じてください。

セルフパッチ

ホットフィックス

各対象のトップレベルページの読み込み時に再生される小さなパッチを永続化します。MCPツール経由: store_hotfix、list_hotfixes、clear_hotfix、clear_hotfixes、set_permanent。例(プローブスクリプト経由):

TOKEN=<tok> python3 scripts/mcp_probe.py call store_hotfix '{"fix_id":"my-fix","code":"console.log(\"patched\")"}'
TOKEN=<tok> python3 scripts/mcp_probe.py call store_hotfix '{"fix_id":"always-on","code":"console.log(\"baseline\")","permanent":true}'
TOKEN=<tok> python3 scripts/mcp_probe.py call set_permanent '{"fix_id":"my-fix","permanent":true}'
TOKEN=<tok> python3 scripts/mcp_probe.py call list_hotfixes
TOKEN=<tok> python3 scripts/mcp_probe.py call clear_hotfix '{"fix_id":"my-fix"}'
TOKEN=<tok> python3 scripts/mcp_probe.py call clear_hotfixes
TOKEN=<tok> python3 scripts/mcp_probe.py call clear_hotfixes '{"include_permanent":true}'

ホットフィックスはデフォルトでバージョンゲートされます。拡張機能のバージョン変更後、保持された非永続的な修正は削除されずにスキップされます。修正を保存すると、レコードが現在のバージョンに更新され、保持された非永続的な修正が再び対象になります。修正を永続的としてマークすると(store_hotfix の permanent: true、または set_permanent)、バージョン変更をまたいで再生されます。clear_hotfixes はデフォルトで非永続的な修正を削除し、永続的な修正を保持します。include_permanent: true を渡すと、ストア全体が削除されます。

拡張機能コマンド

バックグラウンドサービスワーカーは、型付きコマンド(MCPツール経由で発行するか、$DAEDALUS_DIR/commands/ に "type": "..." を含むJSONを書き込むことで発行)を受け付けます:

コマンド

目的

screenshot

表示中のタブをPNGとしてキャプチャ

cdp

生のChrome DevTools Protocol呼び出しを発行

net-capture / net-capture-stop / net-capture-get

CDPによる完全なリクエスト/レスポンス傍受

cookies / set-cookie / remove-cookie / clear-cookies

Cookie jarへのアクセス

open-tab / open-tabs / focus-tab / close-tab / navigate / reload

タブ制御

inject-css / remove-css

タブ単位のCSSインジェクション

block-requests / unblock-requests / list-block-rules

declarativeNetRequestによるブロッキング

store-hotfix / clear-hotfix / clear-all-hotfixes / list-hotfixes / set-permanent

ホットフィックス管理(永続フィックスはバージョン更新後も存続)

ext-reload

拡張機能自体をディスクから再読み込み

fetch-timings

fetchリレー用の診断リングバッファ

GMブリッジ

window.GM(page.js内、MAINワールド)は、以下のTampermonkeyスタイルのサブセットを提供します:

メソッド

説明

GM.getValue(key, default)

拡張機能全体のchrome.storage.localから予約外の文字列キーを読み取る(トークン単位ではない)

GM.setValue(key, value)

予約外の文字列キーを拡張機能全体のストレージに書き込む

GM.deleteValue(key)

予約外の文字列キーを拡張機能全体のストレージから削除

GM.listValues()

予約外のストレージキーを一覧表示

GM.xmlhttpRequest(opts)

バックグラウンド中継によるHTTPリクエスト(CSP非依存)

GM.addStyle(css)

CSSを注入

GM.setClipboard(text, type)

クリップボードに書き込む

GM.notification(opts)

デスクトップ通知

GM.openInTab(url, opts)

新しいタブを開く

GM.download(opts)

ダウンロードをトリガー

GM.info

スクリプトのメタデータ

Cookieアクセスはオペレーターの機能であり、ページの機能ではありません。これはトークン認証付きの上記cookies / set-cookie / remove-cookie / clear-cookiesコマンドを経由し、ページコンテキストには意図的に公開されません。page.jsは一致する各トップレベルページで実行されるため、公開するとページ自身のdocument.cookieでは見えないCookieを読み取れてしまうからです。

アーキテクチャ

Browser (matching tab)                           Server (your bridge host)
┌────────────────────────────────────────────┐   ┌──────────────────────┐
│ MAIN world                                 │   │ bridge (server.py)   │
│  ├─ page-main: default injection channel   │   │ /stream?token        │
│  └─ page.js: GM + relay channel            │   │ watches commands/    │
│            ▲                               │   │                      │
│            │ window.postMessage            │   │ /result writes       │
│ content.js (ISOLATED)                      │   │    results/          │
│            ▲                               │   │                      │
│            │ chrome.runtime                │   │                      │
│ background.js (service worker)             │   │                      │
│  ├─ CDP: CSP fallback channel              │   │                      │
│  ├─ single SSE stream ◄────────────────────┼───┤                      │
│  └─ POST result, fetch ────────────────────┼──►│                      │
└────────────────────────────────────────────┘   └──────────────────────┘
  • 単一のSSEストリーム: バックグラウンドは単一のfetch SSE接続(tab=extension)を開き、chrome.tabs.sendMessageを介してコマンドを対象タブにルーティングします。30秒のウォッチドッグが古くなったストリームで強制再接続を行います。

  • タブ単位のルーティング: コマンドは特定のChrome tabIdを指定するか、全タブにブロードキャストします。

  • CSPの動作: GM.xmlhttpRequestはHTTP処理をバックグラウンドサービスワーカーのfetchに送ります。Evalは通常、バナーなしのMAINワールド注入を使用します。ソースフリーのプローブが動的コンパイルが利用不可と報告した場合、CDPがCSPフォールバックを提供し、アタッチした場合はChromeのデバッガーバナーを表示します。アタッチに失敗した場合はページ/Blobリレーに到達します。結果のworld値はそのチャネルを説明するものであり、完全性を主張するものではありません。

サーバー

ブリッジを3つの必須設定で起動します。DAEDALUS_DIRまたはDAEDALUS_PORTがないと起動時に終了し、ブリッジ制御ルートはすべて、トークンが設定されていないとフェイルクローズします:

DAEDALUS_DIR=<data-dir> DAEDALUS_PORT=<port> DAEDALUS_TOKEN=<bridge-token> python3 server.py

server.pyは、使用している任意のスーパーバイザー(ここではsystemdユニット)の下で実行されます。localhostを超えて公開する場合は、その前にTLS終端リバースプロキシを配置してください。ブリッジ自体はプレーンなHTTPを話します。ブリッジ制御ルートとストレージルートは、そのトークンをCLI設定パスを通じて解決される1つの設定済みシークレットと比較します。TOKENは一回限りのオーバーライドで、それ以外の場合はDAEDALUS_TOKENが必要です(埋め込みの_settingsモジュールがそれを提供することもあります)。設定の欠落と不一致はフェイルクローズします。ページ向けのPOST /segmentとGET /segment-statusルートのみが、代わりにジョブスコープのケイパビリティを使用します。

プロセス内MCPフロントエンドは3つのオプション設定を受け取ります。これらは1つのリスナーを説明するものであるため、まとめて文書化します:DAEDALUS_MCP_PORT(デフォルト8086)はバインドするループバックポートです。そのツールハンドラーは、DAEDALUS_PORT=0の場合も含め、ブリッジの実際にバインドされたループバックURLを使用します。DAEDALUS_LOCAL_URLは、別の場所で実行されているブリッジを前面に置くスタンドアロンMCPデプロイメントのために、そのURLを明示的にオーバーライドします。DAEDALUS_MCP_ALLOWED_HOSTS(デフォルト127.0.0.1:*,localhost:*)は、そのDNSリバインディング保護が受け入れるカンマ区切りのホスト許可リストであり、公開ホスト名で/mcpを前面に置く場合は、そのホスト名をそこに指定することを意味します。

エンドポイント:GET /stream、GET /tabs、GET /health、GET /dashboard[/<asset>]、POST /register、POST /sync-tabs、POST /unregister、POST /poll、POST /result、PUT /command、GET /result、POST/GET/DELETE /upload、GET /screenshot、POST /segment-job、POST /segment + GET /segment-status。POST /segment-jobは設定済みのブリッジトークンを必要とします。ジョブスコープのsigを受け取るのはPOST /segmentとGET /segment-statusのみです。ペイロードとエンドポイントの注意事項についてはCLAUDE.mdを参照してください。

POST /pollは、レガシーのブロードキャストコマンドファイルが存在する場合にそれを消費して削除します。

PUT /commandはターゲットごとのFIFOディレクトリキューにエンキューし(1つのタブへの連続コマンドが上書きされなくなる)、各コマンドに配信ID(_did)をスタンプして、拡張機能が再配信されたフレームを重複排除できるようにし、DAEDALUS_CMD_TTL秒(デフォルト90)後に未請求のコマンドをTTLで破棄します。バックグラウンドコレクターがそのTTLを適用し、SSEコンシューマーが一度も接続しない場合でも空のキューディレクトリを削除します。GET /healthは、ストリーム/レジストリ/最終配信の生存性を報告し、サイレントに死んだブリッジを検出します。

ブリッジは、権限キャリアの繰り返しを1つの値を選択する代わりに拒否します。これには、クエリ文字列やJSONボディ内のtoken、およびセグメントケイパビリティルート上のjob / sigが含まれ、繰り返された値が等しいか空白であっても同様です。MCPトランスポートも同様に、繰り返しのAuthorization、Mcp-Session-Id、Host、Originヘッダー、およびセグメントツールの繰り返しのjob引数を拒否します。

拡張機能が結果を投稿すると、サーバーはコマンドの_didをdeliveryIdとして公開し、新しいresultGenerationを割り当てます。CLIとMCPのウェイターはまず共有結果スロットを覗き、コマンドIDとdeliveryIdの両方を照合し、その後GET /result?...&consume=1&expected=<resultGeneration>で条件付きでその世代を消費します。そのリクエストの間に別の結果がスロットを置き換えた場合、条件付き消費は新しい結果をそのまま残し、消費されなかったことを報告します。expectedなしの単純なconsume=1は、現在のスロットの破壊的な互換性読み取りのままです。

GET /streamはSSE接続を無期限に保持し、タイマーで接続を循環させる代わりに、DAEDALUS_STREAM_KEEPALIVE秒(デフォルト15)ごとにキープアライブコメントで生存性を証明します。DAEDALUS_STREAM_MAX_AGE(デフォルト3600)は最後の手段の上限のみです。クローズパスはtests/test_stream_lifecycle.pyによって固定されています。

DAEDALUS_MAX_BODY_SIZE(デフォルト64 * 1024 * 1024バイト、64 MiB)は、POST、PUT、DELETEハンドラーが読み取るリクエストボディを制限します。宣言されたボディが制限を超えると413を受け取り、Content-Lengthを宣言しないリクエストは空のボディとして読み取られる代わりに411を受け取ります。より大きなセグメントや他のペイロードを中継する場合は増やしてください。

ファイルシステムバックアップの呼び出し元の値は、1つのパスコンポーネントポリシーを使用します:..、C0/C1制御文字とサロゲート文字、Windowsで無効なパス文字とデバイス名、末尾のドットまたはスペース、および240バイトを超えるUTF-8エンコーディングを拒否します。ブリッジトークンはより厳格で、ドットとアンダースコアも拒否します。他のUTF-8ジョブ名は受け入れられるため、クライアントはクエリ文字列でURLエンコードする必要があります。

POST /segment-jobは、3つの固定クォータを各新しいジョブレコードにコピーします:DAEDALUS_MAX_SEGMENT_INDEX(デフォルト99999)、DAEDALUS_MAX_SEGMENTS_PER_JOB(デフォルト10000)、DAEDALUS_MAX_SEGMENT_JOB_SIZE(デフォルト4 * 1024 * 1024 * 1024バイト、4 GiB)。これらの設定を変更すると、その後作成されるジョブに影響します。保存されたクォータを持つジョブは、記録された値を使い続けます。DAEDALUS_MAX_BODY_SIZEは、各個別セグメントリクエストも制限します。

セキュリティ

拡張機能をインストールする前にこれを読んでください。

これはwindow.GMシムを一致するすべてのトップレベルページに注入します(<all_urls>、MAINワールド)。これにより、putで送信されたスクリプトがクロスオリジンリクエストを行えるようになります。その結果は意図的に考慮すべき部分です:訪問する一致するサイトはそのシムを呼び出せるため、そのサイトは拡張機能の権限を通じてクロスオリジンリクエストを発行できます。これは、ワイルドカード@matchを持つユーザースクリプトマネージャーとまったく同じです。それがあなたのブラウジングにとって許容できない場合は、extension/manifest.jsonのmatchesを実際に操作するホストに絞り込み、ブリッジが他のページでは何もしないことを受け入れてください。

ブリッジトークンとサーバーURLは、ページのGMストレージメソッドからは到達できません。 これらはchrome.storage.localのdaedalus-プレフィックスの下にあり、リレーはページスクリプトに対してその名前空間の読み取り、書き込み、列挙を拒否します。このルールがなければ、訪問したサイトはGM.getValue('daedalus-token')でブリッジトークンを読み取ったり、GM.setValue('daedalus-server', ...)でブリッジを静かに再指定したりできてしまいます。tests/test_repo_contract.pyがこのルールを固定しています。

ホットフィックスソースは意図的にページから観測可能な状態であり、機密の拡張機能状態ではありません。バックグラウンドは永続フィックスと拡張機能バージョンに一致する非永続フィックスを選択し、それぞれを通常のevalが使用するのと同じチャネルを通じてページで実行します。ページが動的コンパイルを許可する場合はMAINワールド注入、許可しない場合はCDPです。したがって、ページはどちらのチャネルでも実行するソースを観測できます。

MCPベアラーは、MCPプロセスが読み取るサーバーホストファイルを指定できません。 MCPのput、CSS注入/削除、およびホットフィックスストレージツールは、インラインソースのみを受け入れます。ローカルCLIはファイルパスの利便性を維持しますが、CLIプロセスがオペレーターが選択したファイルを読み取り、その内容をインラインで送信します。したがって、ブリッジトークンを保持すると、文書化されたブラウザおよび拡張機能制御の権限(保存されたホットフィックスソースの読み取りを含む)が付与されますが、これらのツールを通じて任意のホストファイルシステムの読み取りが追加されるわけではありません。

ブリッジ制御ルートとストレージルートには設定済みのブリッジトークンが必要です。/segment と /segment-status だけが機能例外です。 サーバーは、リクエストトークンを TOKEN または DAEDALUS_TOKEN から解決したシークレットと比較し、シークレットが設定されていない場合はリクエストを拒否します。POST /segment-job はジョブスコープの機能を発行するため、そのブリッジトークンが必要です。信頼できないページの JavaScript は、その機能を使ってジョブの投稿と照会のみを行います。ブリッジトークンを保持する者は誰でもブラウザを操作できます。TLS を終端するリバースプロキシなしでブリッジポートをループバックの外に公開しないでください。また、トークンは資格情報として扱ってください。

評価結果には値の整合性の保証はありません。 あなたが制御していないページで評価された JavaScript は、どのチャネルが実行したかに関係なく、そのページが選択した値を返します。world フィールドは、送信されたソースがどのように実行されたかを示すものであり、その値を信頼すべきかどうかを示すものではありません。page-main は通常の MAIN ワールド注入、cdp はインスペクタの CSP フォールバック、page:<hostname> はリレーです。必須の page: プレフィックスはページコンテキストの外で追加されるため、どのホスト名も cdp や page-main を生成できません。この衝突耐性は記述的なものにすぎません。page-main では、ページ所有の eval と Function バインディングが、送信されたソースを読み取るだけでなく、その戻り値に影響を与えることができます。

CDP コンパイルはページの eval と Function バインディングの解決を回避し、実装はシリアライズ前に直接ハンドルを参照で取得します。これらの転送メカニズムは信頼境界を変更しません。送信されたソースは依然としてページ制御の状態を読み取り、プリミティブを含む任意の値を、CDP が受け取る前にページの promise メカニズムを通じてルーティングできます。送信されたソースがそれらのページ制御パスを使用する場合、ページは cdp でも返される値を選択できます。

リレーは、そのランダム ID に関連付けられた保存済み呼び出しのみを受け入れ、送信者のタブが一致することを要求し、エントリを一度だけ消費します。ダウングレードはブリッジトークン、サーバー URL、配信 ID、結果ルートを開示せず、追加の拡張機能やブラウザの権限を付与せず、別のタブの呼び出しに影響を与えることはできません。これらのプロパティはルーティングとブラウザの権限を保護します。リレーマーカーやその返された JavaScript 値を信頼シグナルに変えるものではありません。

デプロイ

ブリッジはループバック上で平文 HTTP を話します。ブリッジ制御ルートとストレージルートは設定済みのトークンを必要とし、トークンがない場合はフェイルクローズします。/segment と /segment-status のみがジョブスコープの機能を使用します。意図的に行わないことが2つあります。それらは前面に配置されるものの役割だからです。

  • TLS と CORS。 server.py は CORS ヘッダーを送信しません。ブリッジがクロスオリジンの場合、サンプルの HLS リレーのページ側 fetch 呼び出しは、GET /segment-status と POST /segment の両方にアクセスする必要があります。プロキシは、両方のルートでページオリジンを許可し、POST のプリフライトに必要なメソッド/ヘッダーを処理する必要があります。ステータス GET がブロックされたり、利用不可、非 2xx、または無効な場合、サンプルはジョブを新しいものとして扱い、すべてのセグメントを再 POST します。これらの書き込みは同じインデックスごとのファイルを置き換えますが、実行は再開/スキップの節約を失います。デプロイがその CORS ポリシーを提供できない場合は、代わりに拡張機能の GM ブリッジを介して両方のリクエストをルーティングしてください。

  • 保存されたアップロードの提供。 ダッシュボードは /uploads/<path> でダウンロードにリンクしますが、ブリッジにはそのようなルートはありません。呼び出し元が指定したアップロード名とバイトをダッシュボードオリジンで直接提供しないでください。実行可能コンテンツがダッシュボードのトークン保持ストレージとオリジンを共有することになるからです。それらのリンクを利用不可にするか、/uploads/ を別のダウンロード専用オリジンにリダイレクトし、Content-Disposition: attachment を強制し、application/octet-stream を使用し、X-Content-Type-Options: nosniff を送信するようにします。GET /upload(リスト)と GET /screenshot はブリッジ自体が提供するため、プロキシの支援は不要です。

設定済みトークンで直接実行すると、これらは両方とも単に存在しません。他のすべては機能します。

ファイル

ファイル

説明

extension/manifest.json

MV3 マニフェスト

extension/background.js

サービスワーカー — SSE、コマンドディスパッチ、fetch リレー、スクリーンショット、CDP、クッキー、ダウンロード

extension/content.js

ページとバックグラウンド間のメッセージリレー

extension/page.js

MAIN ワールドブリッジ — window.GM + eval ハンドラー

extension/options.html / options.js

トークン/サーバー設定 UI

server.py

デバッグサーバー(127.0.0.1:8086 で MCP デーモンスレッドもホスト)

mcp_server.py

MCP サーバー — 拡張機能のコマンドサーフェスを HTTP 経由で server.py にブリッジ

scripts/mcp_probe.py

手動検証用の最小限の MCP クライアントヘルパー

scripts/check_versions.py

すべてのバージョンサイトにわたるバージョン整合性チェック / バンプ

.githooks/

pre-commit + pre-push バージョン整合性ゲート

daedalus_cli/

シェル CLI。daedalus-cli ホイールとして公開

dashboard/

server.py が /dashboard で提供するブラウザ制御サーフェス

examples/

put でページ内で実行するスクリプト — 下記参照

tests/, run_tests.py

スイート群。python3 run_tests.py で全部実行

例

examples/ には、put でページ内で実行することを意図したスクリプトが含まれています。6つのうち5つは特定のサイトではなくブリッジの一部を示しています。Discord のものは意図的にサイト固有です。仮想化リストをスクロールバックするのは、実際の仮想化リストなしでは示せないテクニックだからです。

例

示す内容

request-log-hotfix.js

document_start での永続的な MAIN ワールドホットフィックスと、その3つの単語すべてが重要な理由

hls-segment-relay.js

/segment + /segment-status リレー: 再開可能、冪等、制限付き再試行

kill-hls.js

再試行を止めないプレイヤーインスタンスを見つけて破壊する方法

react-fill-input.js

React 制御の入力に値を入力し、React 自身の状態を実際に変更する方法

scrape-discord-messages.js

仮想化されたメッセージリストをスクロールバックして抽出する方法

open-tabs.js

最小限の GM.openInTab 呼び出し

これらは送信前に __PLACEHOLDER__ 置換で設定を受け取ります。put は関数を呼び出すのではなくスクリプトを送信し、引数を渡す方法がないためです。

それぞれは非同期関数の BODY であり、スタンドアロンスクリプトではありません。ブリッジは送信したものをラップします。そのため、ほとんどがトップレベルの return で終わります。そのうちの1つ — scrape-discord-messages.js — はトップレベルの await も使用します。どちらも実際に実行される場所では有効です。node --check は各ファイルを CommonJS ラッパーで解析します。これはトップレベルの return を許可しますが、トップレベルの await は拒否します。したがって、その1つのファイルだけが構文チェックに失敗します。

開発

python3 run_tests.py はすべてのスイートを実行し、検証した内容を1行の集約行で出力します。実行したスイート数、合格したテスト数、スキップしたテスト数を示します。すべてのテストがスキップされたスイートは何も検証していないため、合格ではなく OVERALL: INCOMPLETE として非ゼロ終了コードで報告されます。通常の原因は依存関係の欠落であり、その上の NO COVERAGE 行がスイート名を示します。MCP スイートが必要とするのは、mcp エクストラのインストールです(「インストール」を参照)。

バージョン文字列は、拡張機能、ダッシュボード、CLI パッケージの複数の場所に存在します。python3 scripts/check_versions.py がそのリストです。すべてのサイトを列挙し、見つかった数を報告します。そのため、この段落では、次に追加されたときに古くなる可能性のある数を再掲しません。それらはまとめてバンプしてください。手動では決して行わないでください。

python3 scripts/check_versions.py --set 0.18.0   # rewrite every site
python3 scripts/check_versions.py                # verify the working tree

.githooks/pre-commit はインデックスをチェックし、.githooks/pre-push はプッシュされた各コミットをチェックするため、中途半端なバンプは入りません。すべてのクローンは一度オプトインする必要があります — そうしないと git は追跡されたディレクトリからフックを実行しません。

git config core.hooksPath .githooks

ブラウザが編集しているチェックアウトとは異なるチェックアウトから拡張機能を読み込む場合は、そこにもフックをインストールしてください。また、拡張機能をリロードするとブラウザ側のファイルが再読み取りされることを忘れないでください。あなたの側ではありません。

Maintenance

ActivityActive
ResponsivenessResponsive

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables MCP clients to drive a real, logged-in Chrome browser for web automation tasks like navigation, clicking, typing, and screenshotting.
    2 npm
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables browser automation over MCP using a real Chrome browser with existing profile, supporting real tabs, downloads, cookies, and RPA workflows.
    104 npm
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables MCP clients to control real Chrome tabs in existing profiles, supporting navigation, interaction, screenshots, and console/network inspection across multiple Chrome instances.
    -
  • A
    license
    B
    quality
    C
    maintenance
    Enables MCP clients to drive a real Chromium browser for automation, including navigation, JavaScript execution, CDP commands, network capture, and multi-tab control.
    7
    GPL 3.0