bug 已經發生過了?記錄還在手機裡。
從 App 啟動那一刻起,SDK 就把工作階段記錄寫進裝置的 IndexedDB:造訪過的頁面、使用者的點擊和輸入、console 輸出、每一個請求——失敗的請求連標頭和內容一起留下。這些資料不會流向任何地方;它們按同樣的遮蔽規則留在手機上,直到使用者核准的 agent 用 get_session_overview 和 get_session_log 來讀。保留最近 3 次啟動,每次最多 10 MB。
開源 · MIT 授權 · MCP
在 H5 頁面或 App 的 WebView 裡加一段指令碼。使用者報出一個短短的裝置碼,Claude Code、Codex、Cursor 或任何 MCP 用戶端就能看頁面、查 console 和網路請求,親手點一遍把問題重現出來——整個過程使用者看得見,也隨時可以接管。
K7Q2-9M4X01線上示範親手體驗
示範裡的手機上,商城頁面載入的就是真正的頁面 SDK。閘道在你的瀏覽器裡模擬,任何資料都不會離開這個分頁;但下面每一次快照、點擊、console 輸出和即時修補都是真實的。
這裡是真的:demo.debugger.site 上有一個公開的示範閘道,和其他服務完全隔離,每天清空。手機裡的商城就是從那裡載入的——取得一把金鑰,把 agent 接上去,就在這裡看它做事。
AI agent
已連線 rwd MCP 伺服器
看 agent 接入手機、重現結帳頁的 bug、找出根本原因,並在手機上當場驗證修復。
點一下就好,不用註冊。這把金鑰只能用於示範閘道,示範每天清空時失效。
取得後會自動填進下面的指令裡。
在終端機裡執行一次指令,或者寫進 MCP 用戶端的設定。金鑰已經填好了。
claude mcp add --transport http rwd-demo https://demo.debugger.site/v1/mcp \
--header "Authorization: Bearer rwd_sk_live_…"codex mcp add rwd-demo --url https://demo.debugger.site/v1/mcp \
--bearer-token-env-var RWD_DEMO_KEY
export RWD_DEMO_KEY=rwd_sk_live_…{
"mcpServers": {
"rwd-demo": {
"url": "https://demo.debugger.site/v1/mcp",
"headers": {
"Authorization": "Bearer rwd_sk_live_…"
}
}
}
}手機裡商城的裝置碼已經填進指令裡。agent 要求接入時,在商城裡點 允許,然後看它操作;隨時可以 接管 或 結束。
用 rwd-demo 這個 MCP 伺服器連接裝置 XXXX-XXXX。在商城裡給一件商品選上禮盒包裝,去結帳,找出總價顯示「--」的原因。把 XXXX-XXXX 換成商城裡顯示的裝置碼。
使用者的手機K7Q2-9M4X
示範商城,真實線上····-····
這就是 demo.debugger.site 本身,跑著真實的 SDK。也可以掃描 QR 碼,在手機上開啟。
完全在瀏覽器裡執行:iframe 裡是真實的 SDK(sdk.js),WebSocket 墊片把它的連線透過 postMessage 轉給頁面裡的模擬閘道,再由一個照腳本執行的 agent 發出真實的 RPC。agent 的話跟隨頁面語言;工具結果使用閘道語言(英文或中文)。
02運作原理
在 <head> 裡盡早加一段指令碼,console 和網路從第一行就開始記錄。App 裡則在頁面開始載入時注入同一段指令碼。
每台手機都有一個固定的裝置碼,例如 K7Q2-9M4X,顯示在一個小膠囊裡。使用者點一下 請求協助,這台裝置就會排到 agent 清單的最前面。
用 API 金鑰把閘道的託管 MCP 位址加進你的 agent,然後直接描述問題。agent 會自己找到裝置、請求使用者同意,然後開始追查。
<!-- 放在 <head> 里,越早越好;生产环境请锁定版本号 -->
<script src="https://cdn.jsdelivr.net/npm/remote-webview-debugger/dist/sdk.js"></script>
<script>
RemoteWebviewDebugger.run("https://rwd.example.com/v1", "rwd_pk_…", {
userId: currentUser?.id ?? null, // 可选:按用户查找设备
helpButton: true, // 显示「请求协助」按钮
})
</script>正式環境請鎖定版本號,或者直接用你閘道自帶的 /v1/sdk.js(主控台提供 SRI 校驗值)。
claude mcp add --transport http rwd https://rwd.example.com/v1/mcp \
--header "Authorization: Bearer rwd_sk_live_…"codex mcp add rwd --url https://rwd.example.com/v1/mcp \
--bearer-token-env-var RWD_API_KEY
export RWD_API_KEY=rwd_sk_live_…{
"mcpServers": {
"rwd": {
"url": "https://rwd.example.com/v1/mcp",
"headers": {
"Authorization": "Bearer rwd_sk_live_…"
}
}
}
}# 只支持 stdio 的客户端:本地桥接到同一个托管地址
claude mcp add rwd -e RWD_API_KEY=rwd_sk_live_… \
-- rwd mcp --gateway https://rwd.example.com/v103agent 能做什麼
Playwright 風格的 YAML,元素帶 ref。agent 依結構理解頁面,而不是盯著像素,再用 ref 精準操作。
點擊、填入、輸入、按鍵、下拉選擇、游標停留、捲動、拖曳,都帶可操作性檢查,React、Vue 的受控元件也認。
從頁面載入起就記錄在有上限的緩衝區裡,agent 接入之前的錯誤也查得到;需要時還能看請求和回應內容。
列表頁 → 商品頁 → 結帳頁,或者新開的 WebView,同一台裝置的所有頁面組成一個工作階段,附跨頁面時間軸。
每個工具結果最後都會附上發生了什麼:新頁面、跳轉、錯誤、使用者接管。agent 不用輪詢。
agent 把計畫和進度顯示在裝置頂端,使用者隨時知道 AI 在做什麼、做到哪一步。
App 提供原生截圖橋接時是真實畫面,否則用 DOM 重新繪製。兩種方式都會遮蔽敏感資訊。
從 App 啟動起,頁面、點擊、console 和每個請求都冷存在手機的 IndexedDB 裡,事後也能回看。
advanced 等級下,agent 可以用 evaluate_script 為執行中的頁面打上修補,當場驗證修復。
輕量心跳加上指數退避;連續 3 次失敗後改為每分鐘輪詢一次,需要時由閘道喚醒頁面。
手機端介面、主控台和 agent 看到的文字都有 11 種語言,含由右至左的阿拉伯文。也支援自訂語言包和用詞。
/v1/mcp 是 Streamable HTTP,Claude Code、Codex、Cursor 直接可用;只支援 stdio 的用戶端用一個很小的橋接指令。
list_devicesconnect_devicewait_for_devicelist_pagesselect_pagewait_for_pagetake_snapshotfind_elementstake_screenshotclickfilltype_textpress_keyselect_optionhoverscrolldragbatch_actionsnavigate_pagego_backreload_pagewait_forlist_console_messageslist_network_requestsget_network_requestget_storageevaluate_scriptset_taskget_session_timelineget_session_overviewget_session_logwait_for_eventwait_for_controldisconnect_device04亮點
傳輸線、遠端除錯連接埠和螢幕錄影都給不了的三樣東西。
從 App 啟動那一刻起,SDK 就把工作階段記錄寫進裝置的 IndexedDB:造訪過的頁面、使用者的點擊和輸入、console 輸出、每一個請求——失敗的請求連標頭和內容一起留下。這些資料不會流向任何地方;它們按同樣的遮蔽規則留在手機上,直到使用者核准的 agent 用 get_session_overview 和 get_session_log 來讀。保留最近 3 次啟動,每次最多 10 MB。
使用者授予 advanced 等級後,evaluate_script 直接在頁面裡執行——agent 可以修正錯誤的狀態、覆寫一個函式、重新繪製畫面,然後在真實裝置上看結果:一個用來證明診斷的即時修補。它只存在於那個頁面的記憶體裡,不會留存;敏感點擊仍然需要使用者點頭。
每個工具結果最後都附上上次呼叫以來裝置上的變化,agent 永遠不用輪詢。頁面內容作為不可信的資料交給模型。手機上的任務卡片展示 agent 的計畫,閘道也說 agent 的語言——工具說明和結果都有 11 種語言。
05隱私與控制
遠端除錯的前提是信任。每一次接入都看得見、要同意、能隨時打斷。
agent 要求接入時,使用者在手機上看到是誰、要什麼權限,自己選擇等級。只認真實的點擊,指令碼沒辦法替使用者點「允許」。
AI 操作時頁面邊緣亮起光暈,使用者的輸入被鎖定。點一下 接管 AI 立刻暫停,點 結束 直接斷線。
密碼欄位和帶 data-rwd-mask 的內容,在快照、螢幕截圖、console、網路資料和工作階段記錄裡都顯示為 ••••。還可以按專案設定選擇器和規則運算式。
給「送出訂單」「刪除帳號」這類按鈕加上 data-rwd-confirm,agent 每次點擊前都要使用者確認。
observe 只看,interact 可操作,advanced 還能執行指令碼、讀請求內容和儲存資料。按 API 金鑰和專案分別設上限。
每次接入、核准、RPC 呼叫和接管都會記下誰、做了什麼、什麼時候——但不記錄頁面內容和參數值。
06App 原生整合
下載一次 SDK,在頁面開始載入時注入:iOS 用 WKUserScript,Android 用 addDocumentStartJavaScript。網頁本身一行都不用改。
傳入固定的 deviceId(並保存 deviceToken),裝置碼跨 WebView、跨網域都不變。
用 nativeControl() 做自己的接管 / 交還 / 結束按鈕,頁面指令碼呼叫不了。
nativeApproval: true 把接入核准和敏感操作確認交給 App 自己的介面。
一個很小的橋接(WKWebView 快照、Android 位圖),agent 拿到的是真實畫面而不是 DOM 重新繪製。
import WebKit
/// 在 webView.load(…) 之前调用:下载一次 SDK,在 documentStart 注入。
func installRemoteDebugger(into controller: WKUserContentController,
userId: String?, done: @escaping () -> Void) {
let gateway = "https://rwd.example.com/v1"
URLSession.shared.dataTask(with: URL(string: gateway + "/sdk.js")!) { data, _, _ in
DispatchQueue.main.async {
defer { done() }
guard let data, let sdk = String(data: data, encoding: .utf8) else { return }
let options: [String: Any] = [
"deviceId": stableDeviceId, // 存在 UserDefaults 里的 UUID
"userId": userId ?? NSNull(),
"helpButton": true,
]
let json = String(data: try! JSONSerialization.data(withJSONObject: options), encoding: .utf8)!
let boot = sdk + "\n;RemoteWebviewDebugger.run(\"\(gateway)\", \"rwd_pk_…\", \(json));"
controller.addUserScript(WKUserScript(source: boot,
injectionTime: .atDocumentStart,
forMainFrameOnly: true))
}
}.resume()
}// 依赖:androidx.webkit:webkit:1.12.0;需开启 javaScriptEnabled 和 domStorageEnabled
// 在 webView.loadUrl(…) 之前调用
fun installRemoteDebugger(webView: WebView, userId: String?, onReady: () -> Unit) = thread {
val gateway = "https://rwd.example.com/v1"
val sdk = runCatching { URL("$gateway/sdk.js").readText() }.getOrNull()
webView.post {
if (sdk != null && WebViewFeature.isFeatureSupported(WebViewFeature.DOCUMENT_START_SCRIPT)) {
val options = JSONObject()
.put("deviceId", stableDeviceId) // 存在 SharedPreferences 里的 UUID
.put("userId", userId ?: JSONObject.NULL)
.put("helpButton", true)
val boot = "$sdk\n;RemoteWebviewDebugger.run(${JSONObject.quote(gateway)}, \"rwd_pk_…\", $options);"
WebViewCompat.addDocumentStartJavaScript(webView, boot, setOf("https://m.example.com"))
}
onReady()
}
}<!-- 放在 <head> 里,越早越好;生产环境请锁定版本号 -->
<script src="https://cdn.jsdelivr.net/npm/remote-webview-debugger/dist/sdk.js"></script>
<script>
RemoteWebviewDebugger.run("https://rwd.example.com/v1", "rwd_pk_…", {
userId: currentUser?.id ?? null, // 可选:按用户查找设备
helpButton: true, // 显示「请求协助」按钮
})
</script>07自行架設
閘道內嵌了 Web 主控台和頁面 SDK。預設用 SQLite,需要時換成 Postgres;Docker Compose 加 Caddy 負責 HTTPS。
裝置、API 金鑰、成員與角色、QR 碼邀請、安全原則、稽核記錄,還有一份填好你自己金鑰的整合指南。
預設是一個 SQLite 檔案;設定 RWD_DATABASE_URL=postgres://… 即可改用 Postgres。
/v1/metrics 提供 Prometheus 指標,SDK 有帶版本號和 SRI 校驗的位址,/v1/healthz 用於健康檢查。
# 一個容器:主控台 + 閘道 + SDK
docker run -d --name rwd -p 8080:8080 -v rwd-data:/data \
-e RWD_ADMIN_PASSWORD='change-me' \
ghcr.io/zanel1u/remote-webview-debugger:latest
# 或者自己建置、上傳,由 Caddy 提供 HTTPS
git clone https://github.com/ZaneL1u/remote-webview-debugger
RWD_DOMAIN=rwd.example.com \
remote-webview-debugger/deploy/platform/deploy.sh you@your-server amd64
# → 主控台 https://rwd.example.com
# → 閘道 https://rwd.example.com/v1(MCP 在 /v1/mcp)伺服器上只需要 Docker;從原始碼建置需要 Go 和 Node。
08常見問題
不需要。H5 頁面裡 SDK 就是頁面的一部分;App 裡由原生程式碼注入。使用者只會看到一個顯示裝置碼的小膠囊,以及 agent 要求接入時的確認對話框。
只限使用者核准的範圍:這台裝置上的 WebView 頁面,依授予的權限等級。WebView 之外的 App 介面和系統一概碰不到。遮蔽的欄位始終是遮蔽的,頁面內容也會作為不可信的資料交給模型。
查得到。SDK 從 App 啟動起就在裝置上寫工作階段記錄——頁面、點擊、console 和請求——之後接入的 agent 用 get_session_log 就能把通往 bug 的整條路徑讀出來。除此之外,記錄不會離開手機。
它能驗證修復:在 advanced 等級下,evaluate_script 在記憶體裡為執行中的頁面打上修補——修正狀態、覆寫函式、重新繪製——agent 就能看到問題是否消失。真正的修復仍然走你正常的發布流程;裝置上不會留下任何東西。
任何 MCP 用戶端。Claude Code、Codex、Cursor 直接連託管的 Streamable HTTP 位址;只支援 stdio 的用戶端用 rwd mcp,它只是把 stdio 轉送到同一個位址。
主流的行動裝置 WebView 和瀏覽器都可以:iOS 的 WKWebView、Android System WebView、各類 App 內建瀏覽器,以及手機上的 Safari、Chrome。桌面瀏覽器也能用。
頁面只維持一條輕量的心跳 WebSocket,斷線後以指數退避重連。連續 3 次連不上,就改成每分鐘輪詢一次;agent 要接入時,閘道會通知它立即重連。
只是一段指令碼。console 和網路記錄放在有上限的環形緩衝區裡(各 500 筆,回應內容總共不超過 5MB),工作階段記錄在背景寫入,閒置時連線上只有心跳。
可以。快照和命中測試會跳過它們的節點,SDK 自己的流量也不會出現在它們的網路面板裡,甚至可以作為一個分頁收進 eruda。
SDK 是真實的,沒有任何改動;只有閘道是在你瀏覽器裡模擬的。一個 WebSocket 墊片透過 postMessage 把 SDK 的連線轉給它,再由照腳本執行的 agent 發出和真實閘道一樣的 RPC。想完整體驗真實鏈路,可以用你自己的 agent 試一次。