remote-webview-debugger 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,46 @@
1
+ English | [简体中文](CHANGELOG.zh-CN.md)
2
+
3
+ # Changelog
4
+
5
+ The page SDK, published to npm as [`remote-webview-debugger`](https://www.npmjs.com/package/remote-webview-debugger). Tags: `sdk-vX.Y.Z`.
6
+
7
+ ## v0.1.0 (2026-11-03)
8
+
9
+ First release.
10
+
11
+ ### 🚀 Enhancements
12
+
13
+ - **engine:** bounded event buffer and response-body cache ([6d7ecc2](https://github.com/ZaneL1u/remote-webview-debugger/commit/6d7ecc2))
14
+ - **engine:** aria snapshots from Playwright's injected scripts (Apache-2.0) ([8ff085d](https://github.com/ZaneL1u/remote-webview-debugger/commit/8ff085d))
15
+ - **engine:** rwd RPC core: snapshot with element refs, DOM helpers, errors ([96d5478](https://github.com/ZaneL1u/remote-webview-debugger/commit/96d5478))
16
+ - **engine:** actions: click, fill, select, hover, scroll, drag, navigate, evaluate ([6f3aba2](https://github.com/ZaneL1u/remote-webview-debugger/commit/6f3aba2))
17
+ - **engine:** keyboard: Playwright key names, editing keys, IME-style text insertion ([b059c53](https://github.com/ZaneL1u/remote-webview-debugger/commit/b059c53))
18
+ - **engine:** console and network observation with a cross-page timeline ([9192481](https://github.com/ZaneL1u/remote-webview-debugger/commit/9192481))
19
+ - **engine:** mask passwords, data-rwd-mask and configured patterns everywhere ([4cd262a](https://github.com/ZaneL1u/remote-webview-debugger/commit/4cd262a))
20
+ - **engine:** rwd.storage: localStorage, sessionStorage and cookies ([a61f0c9](https://github.com/ZaneL1u/remote-webview-debugger/commit/a61f0c9))
21
+ - **engine:** screenshots: native bridge first, DOM rendering as fallback ([39ca812](https://github.com/ZaneL1u/remote-webview-debugger/commit/39ca812))
22
+ - **engine:** access levels and sensitive-action confirmation in the page ([f73c2dd](https://github.com/ZaneL1u/remote-webview-debugger/commit/f73c2dd))
23
+ - page SDK: run(), transport and identities ([2dbd4a8](https://github.com/ZaneL1u/remote-webview-debugger/commit/2dbd4a8))
24
+ - in-page UI: device-code pill, floating card, bottom bar, agent cursor ([7a03025](https://github.com/ZaneL1u/remote-webview-debugger/commit/7a03025))
25
+ - attach approval with levels and confirm dialogs ([8cdd24d](https://github.com/ZaneL1u/remote-webview-debugger/commit/8cdd24d))
26
+ - follow navigation, title and visibility changes ([20de798](https://github.com/ZaneL1u/remote-webview-debugger/commit/20de798))
27
+ - JavaScript API for native hosts ([3f4b92c](https://github.com/ZaneL1u/remote-webview-debugger/commit/3f4b92c))
28
+ - optional eruda tab ([aa932ed](https://github.com/ZaneL1u/remote-webview-debugger/commit/aa932ed))
29
+ - **engine:** agent-facing messages follow welcome.agentLang ([7772845](https://github.com/ZaneL1u/remote-webview-debugger/commit/7772845))
30
+ - run({ lang }) for the phone user's UI ([f742810](https://github.com/ZaneL1u/remote-webview-debugger/commit/f742810))
31
+ - **engine:** list overlays in snapshots; name the element that blocks a click ([4ee90b0](https://github.com/ZaneL1u/remote-webview-debugger/commit/4ee90b0))
32
+ - session log: what happened on the device since the app was launched ([0199a39](https://github.com/ZaneL1u/remote-webview-debugger/commit/0199a39))
33
+ - lock the page as soon as an agent attaches; show how long it has been connected ([3b4a34b](https://github.com/ZaneL1u/remote-webview-debugger/commit/3b4a34b))
34
+
35
+ ### 🩹 Fixes
36
+
37
+ - no nav events; the gateway derives navigations from page messages ([d8ed57e](https://github.com/ZaneL1u/remote-webview-debugger/commit/d8ed57e))
38
+
39
+ ### 📦 Build
40
+
41
+ - publish to npm as remote-webview-debugger ([1e361da](https://github.com/ZaneL1u/remote-webview-debugger/commit/1e361da))
42
+
43
+ ### 📖 Documentation
44
+
45
+ - gateway, SDK and deployment READMEs in both languages ([a6fc85f](https://github.com/ZaneL1u/remote-webview-debugger/commit/a6fc85f))
46
+ - load the SDK from jsDelivr / unpkg or npm; release binaries and image ([f2308af](https://github.com/ZaneL1u/remote-webview-debugger/commit/f2308af))
@@ -0,0 +1,46 @@
1
+ [English](CHANGELOG.md) | 简体中文
2
+
3
+ # 更新日志
4
+
5
+ 页面 SDK,npm 包名 [`remote-webview-debugger`](https://www.npmjs.com/package/remote-webview-debugger)。tag 形如 `sdk-vX.Y.Z`。
6
+
7
+ ## v0.1.0 (2026-11-03)
8
+
9
+ 首个版本。
10
+
11
+ ### 🚀 新功能
12
+
13
+ - **engine:** bounded event buffer and response-body cache ([6d7ecc2](https://github.com/ZaneL1u/remote-webview-debugger/commit/6d7ecc2))
14
+ - **engine:** aria snapshots from Playwright's injected scripts (Apache-2.0) ([8ff085d](https://github.com/ZaneL1u/remote-webview-debugger/commit/8ff085d))
15
+ - **engine:** rwd RPC core: snapshot with element refs, DOM helpers, errors ([96d5478](https://github.com/ZaneL1u/remote-webview-debugger/commit/96d5478))
16
+ - **engine:** actions: click, fill, select, hover, scroll, drag, navigate, evaluate ([6f3aba2](https://github.com/ZaneL1u/remote-webview-debugger/commit/6f3aba2))
17
+ - **engine:** keyboard: Playwright key names, editing keys, IME-style text insertion ([b059c53](https://github.com/ZaneL1u/remote-webview-debugger/commit/b059c53))
18
+ - **engine:** console and network observation with a cross-page timeline ([9192481](https://github.com/ZaneL1u/remote-webview-debugger/commit/9192481))
19
+ - **engine:** mask passwords, data-rwd-mask and configured patterns everywhere ([4cd262a](https://github.com/ZaneL1u/remote-webview-debugger/commit/4cd262a))
20
+ - **engine:** rwd.storage: localStorage, sessionStorage and cookies ([a61f0c9](https://github.com/ZaneL1u/remote-webview-debugger/commit/a61f0c9))
21
+ - **engine:** screenshots: native bridge first, DOM rendering as fallback ([39ca812](https://github.com/ZaneL1u/remote-webview-debugger/commit/39ca812))
22
+ - **engine:** access levels and sensitive-action confirmation in the page ([f73c2dd](https://github.com/ZaneL1u/remote-webview-debugger/commit/f73c2dd))
23
+ - page SDK: run(), transport and identities ([2dbd4a8](https://github.com/ZaneL1u/remote-webview-debugger/commit/2dbd4a8))
24
+ - in-page UI: device-code pill, floating card, bottom bar, agent cursor ([7a03025](https://github.com/ZaneL1u/remote-webview-debugger/commit/7a03025))
25
+ - attach approval with levels and confirm dialogs ([8cdd24d](https://github.com/ZaneL1u/remote-webview-debugger/commit/8cdd24d))
26
+ - follow navigation, title and visibility changes ([20de798](https://github.com/ZaneL1u/remote-webview-debugger/commit/20de798))
27
+ - JavaScript API for native hosts ([3f4b92c](https://github.com/ZaneL1u/remote-webview-debugger/commit/3f4b92c))
28
+ - optional eruda tab ([aa932ed](https://github.com/ZaneL1u/remote-webview-debugger/commit/aa932ed))
29
+ - **engine:** agent-facing messages follow welcome.agentLang ([7772845](https://github.com/ZaneL1u/remote-webview-debugger/commit/7772845))
30
+ - run({ lang }) for the phone user's UI ([f742810](https://github.com/ZaneL1u/remote-webview-debugger/commit/f742810))
31
+ - **engine:** list overlays in snapshots; name the element that blocks a click ([4ee90b0](https://github.com/ZaneL1u/remote-webview-debugger/commit/4ee90b0))
32
+ - session log: what happened on the device since the app was launched ([0199a39](https://github.com/ZaneL1u/remote-webview-debugger/commit/0199a39))
33
+ - lock the page as soon as an agent attaches; show how long it has been connected ([3b4a34b](https://github.com/ZaneL1u/remote-webview-debugger/commit/3b4a34b))
34
+
35
+ ### 🩹 修复
36
+
37
+ - no nav events; the gateway derives navigations from page messages ([d8ed57e](https://github.com/ZaneL1u/remote-webview-debugger/commit/d8ed57e))
38
+
39
+ ### 📦 构建
40
+
41
+ - publish to npm as remote-webview-debugger ([1e361da](https://github.com/ZaneL1u/remote-webview-debugger/commit/1e361da))
42
+
43
+ ### 📖 文档
44
+
45
+ - gateway, SDK and deployment READMEs in both languages ([a6fc85f](https://github.com/ZaneL1u/remote-webview-debugger/commit/a6fc85f))
46
+ - load the SDK from jsDelivr / unpkg or npm; release binaries and image ([f2308af](https://github.com/ZaneL1u/remote-webview-debugger/commit/f2308af))
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 zaneliu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/NOTICE ADDED
@@ -0,0 +1,17 @@
1
+ Remote WebView Debugger
2
+ Copyright (c) 2026 zaneliu. Licensed under the MIT License (see LICENSE).
3
+
4
+ This project includes third-party software:
5
+
6
+ - chobitsu (https://github.com/liriliri/chobitsu), MIT License,
7
+ Copyright (c) 2020-present liriliri.
8
+ Vendored in packages/chobitsu (v1.8.6) with small changes to
9
+ src/domains/Network.ts and src/domains/Runtime.ts; see packages/chobitsu/LICENSE.
10
+
11
+ - Playwright (https://github.com/microsoft/playwright), Apache License 2.0,
12
+ Copyright (c) Microsoft Corporation.
13
+ The accessibility snapshot code in packages/chobitsu/src/engine/vendor/playwright
14
+ is taken from Playwright's injected scripts; see the LICENSE and NOTICE files
15
+ in that directory.
16
+
17
+ The page SDK bundle (sdk.js) also includes licia (MIT) and modern-screenshot (MIT).
package/README.md CHANGED
@@ -1,3 +1,114 @@
1
- # Temporary Holding Version
1
+ English | [简体中文](README.zh-CN.md)
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ # RWD page SDK (`sdk/`)
4
+
5
+ One JS file that an App WebView or H5 page loads, so an AI agent can inspect and operate the page through the RWD gateway (`gateway/`) and MCP. Protocol: [`docs/protocol.md`](../docs/protocol.md); architecture: [`docs/architecture.md`](../docs/architecture.md).
6
+
7
+ ```html
8
+ <script src="https://cdn.jsdelivr.net/npm/remote-webview-debugger@0.1.0/dist/sdk.js"></script>
9
+ <script>
10
+ RemoteWebviewDebugger.run("https://rwd.example.com/v1", "rwd_pk_xxxxxxxx", {
11
+ userId: "u-42", // optional
12
+ deviceId: "…", // optional; passed in by a native host that injects the SDK
13
+ helpButton: true, // optional; show the "Get help" button
14
+ app: { name: "Shop", version: "3.2.0" },
15
+ lang: "en", // optional; 'en' | 'zh', default from navigator.language
16
+ }).then(({ deviceCode, status }) => console.log(deviceCode, status))
17
+ </script>
18
+ ```
19
+
20
+ ## Getting the SDK
21
+
22
+ Published to npm as [`remote-webview-debugger`](https://www.npmjs.com/package/remote-webview-debugger); versions and changes: [CHANGELOG](CHANGELOG.md).
23
+
24
+ | Use | Where |
25
+ |---|---|
26
+ | `<script>` from a CDN | `https://cdn.jsdelivr.net/npm/remote-webview-debugger@0.1.0/dist/sdk.js` or `https://unpkg.com/remote-webview-debugger@0.1.0/dist/sdk.js` (minified IIFE, global `RemoteWebviewDebugger`; pin the version, and add `integrity` if you like) |
27
+ | A bundler | `npm install remote-webview-debugger`, then `import { run, status } from 'remote-webview-debugger'` (ES module with types). Import it before your own code so capture starts early. Don't also load the `<script>` version on the same page |
28
+ | Your gateway | `https://<gateway>/v1/sdk.js`, the copy the gateway embeds (also at a content-addressed URL for SRI, offered by the console) |
29
+ | A native app | Bundle `dist/sdk.js` from the package, or download it from one of the above; see [native integration](../docs/native-integration.md) |
30
+
31
+ ## API
32
+
33
+ | API | Description |
34
+ |---|---|
35
+ | `run(gatewayUrl, appKey, opts?)` | Bring the page online (device code, pill). Returns `Promise<{deviceCode, status}>`; calling it again is harmless. Options below. |
36
+ | `status()` | `{deviceCode, status, control, help, label, deviceId, pageId, version, lang, deviceToken, level, config, pending, logSession}`; `status` is `idle / connecting / online / polling / stopped / rejected / quota`, `lang` the UI language (`null` before `run()`), `logSession` the session log this page writes to (`null` when not recording) |
37
+ | `stop()` | Disconnect and remove the UI (console / network collection continues, bounded) |
38
+ | `setUser(userId)` | Change the user; reconnects once with a new `hello` |
39
+ | `help(on)` | Ask for help / cancel |
40
+ | `nativeControl(nativeKey, action)` | Take over / hand back / end from the native host's own UI |
41
+ | `nativeDecide(nativeKey, requestId, value)` | With `nativeApproval`, answer `status().pending`: a level or `null` for an attach request, `'allow'` or `null` for a sensitive-action confirmation |
42
+
43
+ `run()` options:
44
+
45
+ | Option | Description |
46
+ |---|---|
47
+ | `userId` | The signed-in user, sent with the `hello` |
48
+ | `deviceId` | A stable device id from a native host (otherwise one is kept in localStorage) |
49
+ | `helpButton` | Show the "Get help" button (OR-ed with the project policy) |
50
+ | `app` | `{ name, version }` of the host app |
51
+ | `lang` | Language of everything the phone user sees (pill, floating window, bottom bar, dialogs, toasts, the eruda tab, and the "AI wants to …" description of a sensitive action): `'en'` or `'zh'`. Default: `'zh'` when `navigator.language` starts with `zh`, else `'en'`. |
52
+ | `networkExclude` | Requests the agent should not have to wade through — fonts that always 404, analytics beacons: `['.ttf', 'WebTracks', '*beacon*', '/\\/track\\//']`. Each rule matches the URL as a case-insensitive substring, as a `*` wildcard pattern, or as a regex when written `/…/`. Added to the project policy's rules. Matching requests stay out of the network list and out of `net_fail` timeline events; the page still records them, so an agent can ask for them with `includeExcluded` |
53
+ | `lockScreen` | `false`: don't lock the page while an agent is attached (the user can still change it in the floating window). Default `true` |
54
+ | `theme` | `'auto'` (default, follows the page background), `'system'`, `'light'` or `'dark'` |
55
+ | `eruda` | `true` or `{ dock, load, src, name }`: show Remote Debugger as a tab inside eruda; `load` loads the latest eruda when the page has none. Off by default; the project policy can turn it on remotely, local options win field by field |
56
+ | `record` | `false`: this page writes no session log (and nothing already stored is deleted). Default `true`; the project policy can turn the log off for every page |
57
+ | `launchId` | Which launch of the app this page belongs to, so every WebView of one app process files its session log under one session. A native host passes its own id here (or defines `window.RWDLaunchId` as a string or a function returning one); otherwise the SDK infers the launch from a session cookie plus a heartbeat |
58
+ | `nativeKey`, `deviceToken`, `nativeApproval` | For native hosts: the secret for `nativeControl` / `nativeDecide`, the device token the host keeps, and asking the attach / confirm questions natively |
59
+
60
+ ## Languages
61
+
62
+ Two audiences, two settings:
63
+
64
+ - **Phone user**: `run({ lang })`, else the browser's language (see above). Catalog: `src/i18n.ts`.
65
+ - **Agent**: RPC error messages and timeline summaries (taps, in-page navigation, failed requests) follow the gateway's `welcome.agentLang` (`'en'` / `'zh'`). English until a welcome says otherwise; the last value is kept in localStorage `rwd:alang`, so events from before the next page's welcome use it too. Catalog: `packages/chobitsu/src/engine/rwd/i18n.ts`.
66
+
67
+ So a Chinese-speaking user can work with an English-speaking agent: the dialog asks `AI 想要点击「提交订单」,允许吗?` and, if refused, the agent reads `The user denied this action`.
68
+
69
+ ## Behavior in the page (protocol §6–§8, §11)
70
+
71
+ - Levels: RPCs are checked against `welcome.level` / `control.level` (`forbidden`, "Requires the … level"). An agent without a level (an older gateway) gets `interact`; with no agent attached nothing extra is enforced.
72
+ - The device token is kept in localStorage `rwd:dtoken`, the resume token in sessionStorage `rwd:resume` (bound to the pageId). Close code 4401: if the token changed meanwhile (another tab of the device just got it), retry once right away, else `rejected` and retry after about 10 minutes; 4409: reconnect with a new pageId; 4429: `quota`, retry after about 5 minutes.
73
+ - `rwd_invite` in the address bar is taken out at `run()` (`history.replaceState`) and sent with the hello until a welcome arrives.
74
+ - `welcome.config`: `helpButton` (OR-ed with the `run()` option), `maskSelectors`, `maskPatterns` (snapshot, console, network URLs, storage values, timeline summaries), `confirmSelectors`, `networkExclude` (added to the `run()` option's rules), `sessionLog`. The last config is kept in localStorage `rwd:cfg` and used until the welcome arrives — so a project that turned the session log off keeps it off from the first moment of the next page, before any welcome.
75
+ - Snapshots end with an `[Overlays]` section listing what floats over the page — a popup's backdrop, a modal, a bottom sheet — with its tag, a short piece of text, how much of the viewport it covers and a ref. A backdrop is an empty element the aria snapshot has no node for, so it gets an `oN` ref instead of `eN`; that is what lets an agent tap it to close the popup.
76
+ - A click aims at the element's visible centre first, then at the other spots of a 3×3 grid inside it, so a badge over a button doesn't block it. When every spot is covered, the RPC fails with `not_interactable` and names the obscuring element and its ref.
77
+ - The attach and sensitive-action dialogs (`[data-rwd-confirm]`, `confirmSelectors`, including Enter submitting a form) live in a Shadow DOM, accept trusted taps only, and arm 0.4 s after they appear. Tabs of the same origin share decided attach requests over BroadcastChannel.
78
+
79
+ Loading the file starts collecting console, uncaught errors and network (XHR, fetch, WebSocket, sendBeacon, EventSource), at most 500 entries each; when full, plain logs and successful requests go first. Response bodies are cached up to 5 MB in total, 64 KB each. So load it synchronously and early in `<head>`.
80
+
81
+ ## Session log (protocol §5.4)
82
+
83
+ Those buffers are one page's memory. In parallel the SDK writes a **session log** to IndexedDB (`rwd-log`) on the device, so an agent that attaches after a bug can still read the whole way to it:
84
+
85
+ - One session is one launch of the app, shared by every page of the origin: `run({ launchId })`, else `window.RWDLaunchId`, else inferred from the session cookie `rwd_launch` plus a heartbeat (a cookie session silent for over an hour counts as a new launch). `status().logSession` is the id in use.
86
+ - Recorded: page open / in-page navigation / foreground-background / leave, the user's taps, input, submits and scroll rest positions, the agent's interactive calls and `rwd.evaluate`, control changes, console at every level, and every finished request — failed ones also with redacted headers and bodies (64 KB each). It runs whether or not the gateway is reachable. Masked fields are stored as `••••`, `maskPatterns` are applied as records are written.
87
+ - Kept: the last 3 launches, 10 MB each; over that, plain logs and successful requests are dropped first. Only the current launch when the storage quota is under 64 MB; nothing at all without IndexedDB.
88
+ - Read by the agent with `rwd.log` (`observe`) and `rwd.logDetail` (`advanced`, for headers and bodies). Nothing leaves the device otherwise.
89
+ - Off with `run({ record: false })` for one page, or `policy.sessionLog: false` for the project — which also deletes what this origin already stored.
90
+
91
+ ## Layout
92
+
93
+ - `src/index.ts`: public API, session, RPC dispatch, control state.
94
+ - `src/transport.ts`: the page WebSocket (the native constructor), heartbeat timeout, exponential backoff, and after 3 failed attempts `POST /v1/presence` every 60 s.
95
+ - `src/ids.ts`: deviceId / pageId / openerId.
96
+ - `src/launch.ts`: which launch of the app this page belongs to (`launchId`, `window.RWDLaunchId`, the `rwd_launch` cookie).
97
+ - `src/pageWatch.ts`: `page` messages (url, title, visibility) and timeline events (in-page navigation, the user's taps).
98
+ - `src/ui.ts`, `src/lock.ts`: the pill, help button, lock overlay, glow, action highlight and dialogs in a Shadow DOM; blocks the user's trusted input while an agent is attached, and shows how long it has been connected.
99
+ - `src/approval.ts`: attach approval and sensitive-action confirmation (page dialog, or the native host via `nativeDecide`).
100
+ - `src/i18n.ts`: the user's UI text (en / zh).
101
+ - The agent engine lives next to chobitsu in `packages/chobitsu/src/engine/rwd/`: snapshot, `rwd.query`, actionability checks, synthesized input, keyboard, console / network buffers, screenshots, `rwd.storage`, the session log in IndexedDB (`coldlog.ts`), overlay detection (`overlays.ts`), levels and confirmation rules (`policy.ts`), agent-facing text (`i18n.ts`). The Playwright sources used for snapshots are in `packages/chobitsu/src/engine/vendor/playwright/`. The build bundles them from source into `dist/sdk.js`.
102
+
103
+ ## Build and test
104
+
105
+ ```sh
106
+ cd sdk
107
+ npm ci
108
+ npm run build # dist/sdk.js (IIFE, global RemoteWebviewDebugger, ES2017), dist/index.mjs + index.d.ts (ES module)
109
+ npm run typecheck
110
+ npm test # Playwright: WebKit (iPhone 15) + Chromium (Pixel 7)
111
+ npm run gateway # local mock gateway: http://localhost:8787/demo/platform/?key=rwd_pk_demo
112
+ ```
113
+
114
+ The tests use `test/mock-gateway.mjs`, which implements the page side of the protocol (welcome, control state, RPCs, presence; tokens, close codes, levels, remote config, `agentLang`, attach approval) and serves `/v1/sdk.js` and the demo shop. It does not check levels itself, so the tests exercise the page's own enforcement. The specs in `test/e2e/` run with a `zh-CN` browser and `agentLang: 'zh'` unless a test says otherwise; `i18n.spec.ts` covers English. The coexistence test loads eruda 3.4.3 and PageSpy 2.4.0 from jsdelivr, so it needs internet access.
@@ -0,0 +1,114 @@
1
+ [English](README.md) | 简体中文
2
+
3
+ # RWD 页面 SDK(`sdk/`)
4
+
5
+ App WebView / H5 页面加载的一个 JS 文件,让 AI agent 通过 RWD 网关(`gateway/`)和 MCP 查看、操作页面。协议见 [`docs/protocol.zh-CN.md`](../docs/protocol.zh-CN.md),架构见 [`docs/architecture.zh-CN.md`](../docs/architecture.zh-CN.md)。
6
+
7
+ ```html
8
+ <script src="https://cdn.jsdelivr.net/npm/remote-webview-debugger@0.1.0/dist/sdk.js"></script>
9
+ <script>
10
+ RemoteWebviewDebugger.run("https://rwd.example.com/v1", "rwd_pk_xxxxxxxx", {
11
+ userId: "u-42", // 可选
12
+ deviceId: "…", // 可选,原生注入时由原生传入
13
+ helpButton: true, // 可选,显示「求助」按钮
14
+ app: { name: "商城", version: "3.2.0" },
15
+ lang: "zh", // 可选,'en' | 'zh',默认按 navigator.language
16
+ }).then(({ deviceCode, status }) => console.log(deviceCode, status))
17
+ </script>
18
+ ```
19
+
20
+ ## 获取 SDK
21
+
22
+ npm 包名 [`remote-webview-debugger`](https://www.npmjs.com/package/remote-webview-debugger),版本与变更见[更新日志](CHANGELOG.zh-CN.md)。
23
+
24
+ | 用法 | 地址 |
25
+ |---|---|
26
+ | CDN `<script>` | `https://cdn.jsdelivr.net/npm/remote-webview-debugger@0.1.0/dist/sdk.js` 或 `https://unpkg.com/remote-webview-debugger@0.1.0/dist/sdk.js`(压缩后的 IIFE,全局对象 `RemoteWebviewDebugger`;请固定版本号,需要的话加上 `integrity`) |
27
+ | 打包工具 | `npm install remote-webview-debugger`,然后 `import { run, status } from 'remote-webview-debugger'`(ES module,带类型声明)。尽量在你自己的代码之前 import,好让记录尽早开始。同一个页面不要再加载 `<script>` 版本 |
28
+ | 你的网关 | `https://<网关>/v1/sdk.js`,网关内嵌的那一份(控制台还提供带 SRI 的按内容寻址地址) |
29
+ | 原生 App | 把包里的 `dist/sdk.js` 打包进 App,或从以上任一地址下载,见[原生接入](../docs/native-integration.zh-CN.md) |
30
+
31
+ ## API
32
+
33
+ | API | 说明 |
34
+ |---|---|
35
+ | `run(gatewayUrl, appKey, opts?)` | 上线(设备码、胶囊),返回 `Promise<{deviceCode, status}>`;重复调用无副作用。选项见下表 |
36
+ | `status()` | `{deviceCode, status, control, help, label, deviceId, pageId, version, lang, deviceToken, level, config, pending, logSession}`,`status` 为 `idle / connecting / online / polling / stopped / rejected / quota`,`lang` 是界面语言(`run()` 之前为 `null`),`logSession` 是本页面正在写入的会话日志 id(不在记录时为 `null`) |
37
+ | `stop()` | 断开并移除界面(console / 网络采集继续,有上限) |
38
+ | `setUser(userId)` | 更新用户;会用新的 `hello` 重连一次 |
39
+ | `help(on)` | 发起 / 取消求助 |
40
+ | `nativeControl(nativeKey, action)` | 原生的接管 / 交还 / 结束 |
41
+ | `nativeDecide(nativeKey, requestId, value)` | `nativeApproval` 时回答 `status().pending`:接入审批传级别或 `null`,敏感操作确认传 `'allow'` 或 `null` |
42
+
43
+ `run()` 的选项:
44
+
45
+ | 选项 | 说明 |
46
+ |---|---|
47
+ | `userId` | 当前登录用户,随 `hello` 发送 |
48
+ | `deviceId` | 原生宿主传入的固定设备 ID(不传则存在 localStorage) |
49
+ | `helpButton` | 显示「求助」按钮(与项目策略取或) |
50
+ | `app` | 宿主 App 的 `{ name, version }` |
51
+ | `lang` | 手机用户看到的界面语言(胶囊、悬浮窗、底部栏、对话框、提示、eruda 里的 tab,以及敏感操作确认里「AI 想要…」的描述):`'en'` 或 `'zh'`。默认:`navigator.language` 以 `zh` 开头时为 `'zh'`,否则 `'en'` |
52
+ | `networkExclude` | 不想让 agent 去翻的请求——一直 404 的字体、埋点上报等:`['.ttf', 'WebTracks', '*beacon*', '/\\/track\\//']`。每条规则按不区分大小写的 URL 子串、带 `*` 的通配符,或写成 `/…/` 的正则来匹配。与项目策略里的规则合并。命中的请求不出现在网络列表里,也不产生 `net_fail` 时间线事件;页面仍然记录,agent 需要时可以用 `includeExcluded` 看到 |
53
+ | `lockScreen` | `false`:AI 接入期间默认不锁屏(用户可在悬浮窗里改)。默认 `true` |
54
+ | `theme` | `'auto'`(默认,跟随页面背景)、`'system'`、`'light'` 或 `'dark'` |
55
+ | `eruda` | `true` 或 `{ dock, load, src, name }`:收进 eruda 的「Remote Debugger」tab,`load` 在页面没有 eruda 时加载最新版。默认关闭,项目策略可远程打开,本地选项逐项优先 |
56
+ | `record` | `false`:本页面不写会话日志(已经存下的也不删)。默认 `true`;项目策略可以对所有页面关掉 |
57
+ | `launchId` | 本页面属于 App 的哪一次启动,让同一个 App 进程里的所有 WebView 把会话日志归到同一次会话下。原生宿主在这里传自己的 id(也可以定义 `window.RWDLaunchId`,字符串或返回字符串的函数);不传时由 SDK 用会话 cookie 加心跳推断 |
58
+ | `nativeKey`、`deviceToken`、`nativeApproval` | 原生宿主用:`nativeControl` / `nativeDecide` 的密钥、由宿主保管的设备令牌、由原生弹出接入审批和操作确认 |
59
+
60
+ ## 多语言
61
+
62
+ 两类读者,两个设置:
63
+
64
+ - **手机用户**:`run({ lang })`,否则按浏览器语言(见上)。文案在 `src/i18n.ts`。
65
+ - **agent**:RPC 错误信息、时间线摘要(点击、页面内跳转、失败的请求)等跟随网关的 `welcome.agentLang`(`'en'` / `'zh'`)。收到 welcome 之前默认英文;最近一次的值记在 localStorage `rwd:alang`,下一个页面在 welcome 之前产生的事件也用它。文案在 `packages/chobitsu/src/engine/rwd/i18n.ts`。
66
+
67
+ 因此中文用户可以和英文 agent 配合:对话框问的是「AI 想要点击「提交订单」,允许吗?」,用户拒绝时 agent 收到 `The user denied this action`。
68
+
69
+ ## 页面内的行为(协议 §6–§8、§11)
70
+
71
+ - 权限级别:按 `welcome.level` / `control.level` 拦截 RPC(`forbidden`「需要 … 权限」);有 agent 但网关没给级别时按 `interact`,没有 agent 时不额外限制。
72
+ - 设备令牌存 localStorage `rwd:dtoken`,resume 存 sessionStorage `rwd:resume`(绑定 pageId)。关闭码 4401:令牌在此期间变了(同设备另一个标签页刚拿到)就立即重试一次,否则 `rejected`,约 10 分钟后再试;4409:换新 pageId 重连;4429:`quota`,约 5 分钟后再试。
73
+ - 地址栏 `rwd_invite` 在 `run()` 时移出(`history.replaceState`),随 hello 发送直到收到 welcome。
74
+ - `welcome.config`:`helpButton`(与 `run()` 的选项取或)、`maskSelectors`、`maskPatterns`(快照、console、网络 URL、存储值、时间线摘要)、`confirmSelectors`、`networkExclude`(与 `run()` 选项里的规则合并)、`sessionLog`;上一次的配置记在 localStorage `rwd:cfg`,welcome 之前先用——所以项目关掉了会话日志,下一个页面从第一刻起就不会记录,不用等 welcome。
75
+ - 快照末尾会附一段 `[浮层]` 清单,列出盖在页面上的东西——弹窗遮罩、模态框、底部弹出层——带上标签、一小段文字、覆盖视口的比例和 ref。遮罩往往是个空元素,无障碍快照里没有它的节点,所以给的是 `oN` 而不是 `eN` 形式的 ref;agent 正是靠它点遮罩关掉弹窗。
76
+ - 点击先瞄元素可见区域的中心点,被挡住就再试它内部 3×3 网格上的其他点,所以按钮上盖个角标不影响点击。所有点都被挡住时,RPC 以 `not_interactable` 失败,并给出遮挡元素和它的 ref。
77
+ - 接入审批和敏感操作确认(`[data-rwd-confirm]`、`confirmSelectors`,含表单内回车提交)的对话框在 Shadow DOM 里,只认可信点击,按钮出现 0.4 秒后才可点;同源多个标签页之间用 BroadcastChannel 同步已处理的审批。
78
+
79
+ 加载即开始采集 console、未捕获错误和网络(XHR、fetch、WebSocket、sendBeacon、EventSource),各最多 500 条,满了先丢普通日志 / 成功请求;响应体缓存总计不超过 5MB、单条 64KB。所以要在 `<head>` 里尽早同步加载。
80
+
81
+ ## 会话日志(协议 §5.4)
82
+
83
+ 上面那些缓冲只在单个页面的内存里。与此同时,SDK 还会把**会话日志**写进设备本地的 IndexedDB(`rwd-log`),这样 agent 在 bug 发生之后才接入,也能把来路完整读出来:
84
+
85
+ - 一个会话就是 App 的一次启动,同一来源下所有页面共用:`run({ launchId })`,否则 `window.RWDLaunchId`,否则由会话 cookie `rwd_launch` 加心跳推断(cookie 会话超过 1 小时没动静就算新的一次启动)。当前用的 id 见 `status().logSession`。
86
+ - 记录内容:页面打开 / 页面内跳转 / 前后台切换 / 离开,用户的点击、输入、提交和滚动停下的位置,agent 的交互类调用和 `rwd.evaluate`,控制状态变化,所有级别的 console,以及每个结束的请求——失败的还带脱敏后的请求头和正文(各 64KB)。网关连不上也照样记。被遮盖的字段存成 `••••`,`maskPatterns` 在写入时就生效。
87
+ - 保留量:最近 3 次启动,每次 10MB;超出先丢普通日志和成功的请求。存储配额不足 64MB 时只保留当前这次;没有 IndexedDB 时完全不记。
88
+ - agent 用 `rwd.log`(`observe`)和 `rwd.logDetail`(`advanced`,取请求头和正文)读取。除此之外不会离开设备。
89
+ - 关掉:单个页面用 `run({ record: false })`,整个项目用 `policy.sessionLog: false`——后者还会删除这个来源下已经存下的内容。
90
+
91
+ ## 结构
92
+
93
+ - `src/index.ts`:对外 API、会话、RPC 分发、控制状态。
94
+ - `src/transport.ts`:页面 WS(原生 WebSocket 构造器)、心跳超时、指数退避重连、连续 3 次失败后每 60 秒 `POST /v1/presence`。
95
+ - `src/ids.ts`:deviceId / pageId / openerId。
96
+ - `src/launch.ts`:判断本页面属于 App 的哪一次启动(`launchId`、`window.RWDLaunchId`、`rwd_launch` cookie)。
97
+ - `src/pageWatch.ts`:`page` 消息(url、标题、可见性)和时间线事件(页面内跳转、用户点击摘要)。
98
+ - `src/ui.ts`、`src/lock.ts`:Shadow DOM 里的胶囊、求助按钮、遮罩、光圈、操作高亮、确认对话框;agent 接入期间拦截用户的可信输入,并显示已连接时长。
99
+ - `src/approval.ts`:接入审批、敏感操作确认(页面对话框或原生宿主 `nativeDecide`)。
100
+ - `src/i18n.ts`:用户界面文案(中 / 英)。
101
+ - agent 引擎在 chobitsu 旁边:`packages/chobitsu/src/engine/rwd/`(快照、`rwd.query`、可操作性检查、合成输入、键盘、console / 网络缓冲、截图、`rwd.storage`、IndexedDB 里的会话日志 `coldlog.ts`、浮层识别 `overlays.ts`、级别与确认规则 `policy.ts`、给 agent 的文案 `i18n.ts`),快照用的 Playwright 源码在 `packages/chobitsu/src/engine/vendor/playwright/`。构建时从源码打包进 `dist/sdk.js`。
102
+
103
+ ## 构建与测试
104
+
105
+ ```sh
106
+ cd sdk
107
+ npm ci
108
+ npm run build # dist/sdk.js(IIFE,全局 RemoteWebviewDebugger,ES2017),dist/index.mjs + index.d.ts(ES module)
109
+ npm run typecheck
110
+ npm test # Playwright:WebKit(iPhone 15)+ Chromium(Pixel 7)
111
+ npm run gateway # 本地 mock 网关:http://localhost:8787/demo/platform/?key=rwd_pk_demo
112
+ ```
113
+
114
+ 测试用 `test/mock-gateway.mjs`(实现协议的页面侧:welcome、控制状态、RPC、presence,令牌、关闭码、级别、远程配置、`agentLang`、接入审批,并提供 `/v1/sdk.js` 和演示站;它不检查级别,以便测到页面自己的拦截),用例在 `test/e2e/`:除非用例另行指定,浏览器语言为 `zh-CN`、`agentLang` 为 `'zh'`;英文由 `i18n.spec.ts` 覆盖。共存测试会从 jsdelivr 加载 eruda 3.4.3 和 PageSpy 2.4.0,需要能访问外网。
@@ -0,0 +1,160 @@
1
+ type Lang = 'en' | 'zh';
2
+ type Level = 'observe' | 'interact' | 'advanced';
3
+ type ControlState = 'none' | 'observe' | 'agent' | 'user';
4
+ interface AttachPending {
5
+ kind: 'attach';
6
+ requestId: string;
7
+ agent: {
8
+ name: string;
9
+ keyHint: string | null;
10
+ };
11
+ levels: Level[];
12
+ expiresAt: number;
13
+ }
14
+ interface ConfirmPending {
15
+ kind: 'confirm';
16
+ requestId: string;
17
+ action: string;
18
+ expiresAt: number;
19
+ }
20
+ type LinkState = 'connecting' | 'online' | 'polling' | 'stopped' | 'rejected' | 'quota';
21
+ interface ErudaOptions {
22
+ /** Show Remote Debugger as a tab inside eruda instead of the floating window. Default true. */
23
+ dock?: boolean;
24
+ /** Load eruda when the page has none (the latest release by default) and init it. Default false. */
25
+ load?: boolean;
26
+ /** Script URL for `load`. */
27
+ src?: string;
28
+ /** Name of the eruda tab. */
29
+ name?: string;
30
+ }
31
+ interface ErudaRemote {
32
+ dock?: boolean;
33
+ load?: boolean;
34
+ }
35
+ export declare const version: string;
36
+ export interface RunOptions {
37
+ userId?: string | null;
38
+ deviceId?: string;
39
+ helpButton?: boolean;
40
+ app?: {
41
+ name: string;
42
+ version: string;
43
+ } | null;
44
+ /**
45
+ * Secret chosen by a native host (iOS/Android) when it injects the SDK.
46
+ * Enables nativeControl() so the app can offer its own takeover / handback /
47
+ * end buttons. Page scripts never see it.
48
+ */
49
+ nativeKey?: string;
50
+ /**
51
+ * deviceToken the gateway issued to this deviceId (status().deviceToken),
52
+ * kept by the native host: localStorage is per origin.
53
+ */
54
+ deviceToken?: string;
55
+ /**
56
+ * The native host shows the attach / confirm questions itself: no page
57
+ * dialog; it reads status().pending and answers with nativeDecide().
58
+ */
59
+ nativeApproval?: boolean;
60
+ /**
61
+ * Work with eruda: `true` (or `{ dock: true }`) shows Remote Debugger as a
62
+ * tab inside eruda instead of the floating window; `load: true` loads the
63
+ * latest eruda when the page has none. Off by default; the project policy
64
+ * can turn it on remotely, these options win field by field.
65
+ */
66
+ eruda?: boolean | ErudaOptions;
67
+ /**
68
+ * Lock the page while an agent is attached (default true). The user can
69
+ * still change it in the floating window.
70
+ */
71
+ lockScreen?: boolean;
72
+ /** Our UI's look: 'auto' (default) follows the page background, 'system' the OS, or fixed 'light' / 'dark'. */
73
+ theme?: 'auto' | 'system' | 'light' | 'dark';
74
+ /**
75
+ * Language of the UI the phone user sees (pill, bottom bar, dialogs…):
76
+ * 'en' or 'zh'. Default: 'zh' when navigator.language starts with "zh",
77
+ * else 'en'. What the agent reads follows the gateway (welcome.agentLang).
78
+ */
79
+ lang?: Lang;
80
+ /**
81
+ * Requests left out of what the agent sees in the network list and the
82
+ * timeline (still recorded): URL substrings, `*` wildcards or /regex/,
83
+ * e.g. ['.ttf', 'WebTracks', '*beacon*']. Added to the project's rules.
84
+ */
85
+ networkExclude?: string[];
86
+ /**
87
+ * Keep the session log (default true): what happened since the app was
88
+ * launched, in IndexedDB on the device, for an attached agent to read. The
89
+ * project policy can turn it off for every page.
90
+ */
91
+ record?: boolean;
92
+ /**
93
+ * The host app's id for this launch (same for every webview until the app
94
+ * process ends). Without it the SDK infers the launch; a host can also
95
+ * define window.RWDLaunchId.
96
+ */
97
+ launchId?: string;
98
+ }
99
+ export type Status = 'idle' | LinkState;
100
+ /** welcome.config: the project policy the page applies (docs/protocol.md §3.4, §11). */
101
+ export interface RemoteConfig {
102
+ helpButton: boolean;
103
+ maskSelectors: string[];
104
+ maskPatterns: {
105
+ name: string;
106
+ regex: string;
107
+ }[];
108
+ confirmSelectors: string[];
109
+ networkExclude: string[];
110
+ /** Keep the session log (project policy). */
111
+ sessionLog: boolean;
112
+ eruda: ErudaRemote | null;
113
+ }
114
+ export interface RunResult {
115
+ deviceCode: string | null;
116
+ status: Status;
117
+ }
118
+ /**
119
+ * Bring this page online for remote debugging. Resolves with the device code
120
+ * once the gateway has answered (or after a few seconds with what is known).
121
+ * Calling it again is harmless.
122
+ */
123
+ export declare function run(gatewayUrl: string, appKey: string, opts?: RunOptions): Promise<RunResult>;
124
+ /** Current state: device code, link status, control state. */
125
+ export declare function getStatus(): {
126
+ deviceCode: string | null;
127
+ status: Status;
128
+ control: ControlState;
129
+ help: boolean;
130
+ label: string | null;
131
+ deviceId: string | null;
132
+ pageId: string | null;
133
+ version: string;
134
+ lang: Lang | null;
135
+ deviceToken: string | null;
136
+ level: Level | null;
137
+ config: RemoteConfig | null;
138
+ pending: AttachPending | ConfirmPending | null;
139
+ /** Id of the session log this page writes to; null when not recording. */
140
+ logSession: string | null;
141
+ };
142
+ /** Disconnect and remove the UI. Console / network collection continues (bounded). */
143
+ export declare function stop(): void;
144
+ /** Set or change the signed-in user. The gateway learns it through a new hello. */
145
+ export declare function setUser(userId: string | null): void;
146
+ /** Ask for help (the pill shows it; the console and agents see the device asking), or cancel. */
147
+ export declare function help(on?: boolean): void;
148
+ /**
149
+ * Take over, hand back or end from the native host's own UI. Requires the
150
+ * nativeKey given to run(): page scripts, including an agent's
151
+ * evaluate_script, cannot read it, so they cannot hand control to the agent.
152
+ */
153
+ export declare function nativeControl(key: string, action: 'takeover' | 'handback' | 'end'): boolean;
154
+ /**
155
+ * Answer status().pending from the native host's own dialog: a level (or null
156
+ * to refuse) for `attach`, 'allow' (or null) for `confirm`. Requires the
157
+ * nativeKey, like nativeControl(). False when the key, request or value is wrong.
158
+ */
159
+ export declare function nativeDecide(key: string, requestId: string, value: string | null): boolean;
160
+ export { getStatus as status };