remote-webview-debugger 0.1.0 → 0.2.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 +20 -0
- package/CHANGELOG.zh-CN.md +20 -0
- package/README.ar.md +185 -0
- package/README.de.md +169 -0
- package/README.es.md +169 -0
- package/README.fr.md +169 -0
- package/README.ja.md +169 -0
- package/README.ko.md +169 -0
- package/README.md +68 -14
- package/README.pt-BR.md +169 -0
- package/README.ru.md +169 -0
- package/README.zh-CN.md +69 -14
- package/README.zh-TW.md +169 -0
- package/dist/index.d.ts +216 -8
- package/dist/index.mjs +2479 -2873
- package/dist/index.mjs.map +1 -1
- package/dist/locales/ar.d.ts +4 -0
- package/dist/locales/ar.js +2 -0
- package/dist/locales/ar.mjs +150 -0
- package/dist/locales/ar.mjs.map +1 -0
- package/dist/locales/de.d.ts +4 -0
- package/dist/locales/de.js +2 -0
- package/dist/locales/de.mjs +150 -0
- package/dist/locales/de.mjs.map +1 -0
- package/dist/locales/es.d.ts +4 -0
- package/dist/locales/es.js +2 -0
- package/dist/locales/es.mjs +150 -0
- package/dist/locales/es.mjs.map +1 -0
- package/dist/locales/fr.d.ts +4 -0
- package/dist/locales/fr.js +2 -0
- package/dist/locales/fr.mjs +150 -0
- package/dist/locales/fr.mjs.map +1 -0
- package/dist/locales/ja.d.ts +4 -0
- package/dist/locales/ja.js +2 -0
- package/dist/locales/ja.mjs +150 -0
- package/dist/locales/ja.mjs.map +1 -0
- package/dist/locales/ko.d.ts +4 -0
- package/dist/locales/ko.js +2 -0
- package/dist/locales/ko.mjs +156 -0
- package/dist/locales/ko.mjs.map +1 -0
- package/dist/locales/pt.d.ts +4 -0
- package/dist/locales/pt.js +2 -0
- package/dist/locales/pt.mjs +150 -0
- package/dist/locales/pt.mjs.map +1 -0
- package/dist/locales/ru.d.ts +4 -0
- package/dist/locales/ru.js +2 -0
- package/dist/locales/ru.mjs +150 -0
- package/dist/locales/ru.mjs.map +1 -0
- package/dist/locales/zh-TW.d.ts +4 -0
- package/dist/locales/zh-TW.js +2 -0
- package/dist/locales/zh-TW.mjs +150 -0
- package/dist/locales/zh-TW.mjs.map +1 -0
- package/dist/sdk.js +24 -326
- package/dist/sdk.js.map +1 -1
- package/package.json +8 -2
package/README.ru.md
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
<!-- i18n-source: d9a31993ea867d3ef7058e7d637886f38a6ee3e1 -->
|
|
2
|
+
[English](README.md) | [简体中文](README.zh-CN.md) | [繁體中文](README.zh-TW.md) | [日本語](README.ja.md) | [한국어](README.ko.md) | [Español](README.es.md) | [Français](README.fr.md) | [Deutsch](README.de.md) | [Português (Brasil)](README.pt-BR.md) | Русский | [العربية](README.ar.md)
|
|
3
|
+
|
|
4
|
+
# SDK страницы RWD (`sdk/`)
|
|
5
|
+
|
|
6
|
+
Один JS-файл, который загружает WebView приложения или H5-страница, чтобы ИИ-агент мог изучать страницу и работать с ней через шлюз RWD (`gateway/`) и MCP. Протокол: [`docs/protocol.ru.md`](../docs/protocol.ru.md); архитектура: [`docs/architecture.ru.md`](../docs/architecture.ru.md).
|
|
7
|
+
|
|
8
|
+
```html
|
|
9
|
+
<script src="https://cdn.jsdelivr.net/npm/remote-webview-debugger@0.1.0/dist/sdk.js"></script>
|
|
10
|
+
<script>
|
|
11
|
+
RemoteWebviewDebugger.run("https://rwd.example.com/v1", "rwd_pk_xxxxxxxx", {
|
|
12
|
+
userId: "u-42", // необязательно
|
|
13
|
+
deviceId: "…", // необязательно; передаёт нативный хост, внедряющий SDK
|
|
14
|
+
helpButton: true, // необязательно; показать кнопку «Запросить поддержку»
|
|
15
|
+
app: { name: "Shop", version: "3.2.0" },
|
|
16
|
+
lang: "en", // необязательно; тег языка, по умолчанию из языков браузера
|
|
17
|
+
}).then(({ deviceCode, status }) => console.log(deviceCode, status))
|
|
18
|
+
</script>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Как получить SDK
|
|
22
|
+
|
|
23
|
+
Опубликован в npm как [`remote-webview-debugger`](https://www.npmjs.com/package/remote-webview-debugger); версии и изменения: [CHANGELOG](CHANGELOG.md).
|
|
24
|
+
|
|
25
|
+
| Способ | Откуда |
|
|
26
|
+
|---|---|
|
|
27
|
+
| `<script>` с CDN | `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`) |
|
|
28
|
+
| Сборщик | `npm install remote-webview-debugger`, затем `import { run, status } from 'remote-webview-debugger'` (ES-модуль с типами). Импортируйте его раньше своего кода, чтобы перехват начался как можно раньше. Не подключайте на той же странице ещё и версию для `<script>` |
|
|
29
|
+
| Ваш шлюз | `https://<gateway>/v1/sdk.js` — копия, встроенная в шлюз (доступна также по адресу, зависящему от содержимого, для SRI; его предлагает веб-консоль) |
|
|
30
|
+
| Нативное приложение | Встройте `dist/sdk.js` из пакета или скачайте его из любого из перечисленных источников; см. [интеграцию с нативным приложением](../docs/native-integration.ru.md) |
|
|
31
|
+
|
|
32
|
+
## API
|
|
33
|
+
|
|
34
|
+
| API | Описание |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `run(gatewayUrl, appKey, opts?)` | Выводит страницу в сеть (код устройства, плашка). Возвращает `Promise<{deviceCode, status}>`; повторный вызов безвреден. Параметры — ниже. |
|
|
37
|
+
| `status()` | `{deviceCode, status, control, help, label, deviceId, pageId, version, lang, deviceToken, level, config, pending, logSession}`; `status` — это `idle / connecting / online / polling / stopped / rejected / quota`, `lang` — язык интерфейса (`null` до вызова `run()`), `logSession` — журнал сеанса, в который пишет эта страница (`null`, если запись не ведётся) |
|
|
38
|
+
| `stop()` | Отключиться и убрать интерфейс (сбор console / сетевых запросов продолжается, в ограниченном объёме) |
|
|
39
|
+
| `setUser(userId)` | Сменить пользователя; один раз переподключается с новым `hello` |
|
|
40
|
+
| `help(on)` | Запросить поддержку / отменить запрос |
|
|
41
|
+
| `nativeControl(nativeKey, action)` | «Перехватить» / «Вернуть ИИ» / «Завершить» из собственного интерфейса нативного хоста |
|
|
42
|
+
| `nativeDecide(nativeKey, requestId, value)` | При `nativeApproval` — ответ на `status().pending`: уровень доступа или `null` для запроса на подключение, `'allow'` или `null` для подтверждения чувствительного действия |
|
|
43
|
+
| `setLang(lang?)` | Сменить язык интерфейса во время работы (собственная настройка языка в приложении); без аргумента — вернуться к языкам браузера. См. [Языки](#языки) |
|
|
44
|
+
| `registerLocale(pack)` | Добавить языковой пакет или заменить пакет с тем же `id` (принимает и массив). См. [Языки](#языки) |
|
|
45
|
+
| `builtinLocales` | Идентификаторы встроенных пакетов |
|
|
46
|
+
|
|
47
|
+
Параметры `run()`:
|
|
48
|
+
|
|
49
|
+
| Параметр | Описание |
|
|
50
|
+
|---|---|
|
|
51
|
+
| `userId` | Вошедший пользователь; отправляется в `hello` |
|
|
52
|
+
| `deviceId` | Стабильный идентификатор устройства от нативного хоста (иначе он хранится в localStorage) |
|
|
53
|
+
| `helpButton` | Показывать кнопку «Запросить поддержку» (объединяется через ИЛИ с политикой проекта) |
|
|
54
|
+
| `app` | `{ name, version }` приложения-хоста |
|
|
55
|
+
| `lang` | Язык всего, что видит пользователь телефона (плашка, плавающее окно, нижняя панель, диалоги, всплывающие уведомления, вкладка eruda и описание чувствительного действия «AI wants to …»): тег языка, например `'ja'` или `'pt-BR'`. По умолчанию — языки браузера. См. [Языки](#языки) |
|
|
56
|
+
| `locales` | Дополнительные языковые пакеты, регистрируются до выбора языка (как `registerLocale`) |
|
|
57
|
+
| `messages` | Сообщения, которые нужно переопределить, по языкам: `{ en: { ui: { help: 'Contact support' } }, zh: { ui: { help: '联系客服' } } }` |
|
|
58
|
+
| `networkExclude` | Запросы, через которые агенту не нужно продираться, — шрифты, которые всегда отдают 404, аналитические маячки: `['.ttf', 'WebTracks', '*beacon*', '/\\/track\\//']`. Каждое правило сопоставляется с URL как подстрока без учёта регистра, как шаблон с подстановочным символом `*` или как регулярное выражение, если записано в виде `/…/`. Добавляется к правилам политики проекта. Подходящие запросы не попадают в список сетевых запросов и в события хронологии `net_fail`; страница всё равно их записывает, поэтому агент может запросить их через `includeExcluded` |
|
|
59
|
+
| `lockScreen` | `false`: не блокировать страницу, пока подключён агент (пользователь всё равно может изменить это в плавающем окне). По умолчанию `true` |
|
|
60
|
+
| `theme` | `'auto'` (по умолчанию, следует фону страницы), `'system'`, `'light'` или `'dark'` |
|
|
61
|
+
| `style` | Цвета, шрифты, радиусы скругления, z-index и положение элементов интерфейса: `{ accent: '#e11d48', radius: 12, zIndex: 1000, pill: { side: 'left' } }`. См. [Оформление](#оформление) |
|
|
62
|
+
| `eruda` | `true` или `{ dock, load, src, name }`: показывать Remote Debugger как вкладку внутри eruda; `load` загружает последнюю версию eruda, если её нет на странице. По умолчанию выключено; политика проекта может включить это удалённо, локальные параметры имеют приоритет для каждого поля отдельно |
|
|
63
|
+
| `record` | `false`: эта страница не пишет журнал сеанса (уже сохранённое не удаляется). По умолчанию `true`; политика проекта может отключить журнал для всех страниц |
|
|
64
|
+
| `launchId` | К какому запуску приложения относится эта страница, чтобы все WebView одного процесса приложения записывали журнал в один сеанс. Нативный хост передаёт сюда собственный идентификатор (или определяет `window.RWDLaunchId` как строку или функцию, которая её возвращает); иначе SDK определяет запуск по сессионной cookie и heartbeat |
|
|
65
|
+
| `nativeKey`, `deviceToken`, `nativeApproval` | Для нативных хостов: секрет для `nativeControl` / `nativeDecide`, токен устройства, который хранит хост, и вопросы о подключении / подтверждении в нативном интерфейсе |
|
|
66
|
+
|
|
67
|
+
## Языки
|
|
68
|
+
|
|
69
|
+
Встроены: английский (`en`), упрощённый китайский (`zh`), традиционный китайский (`zh-TW`, используется также для `zh-HK`, `zh-MO` и `zh-Hant`), французский (`fr`), испанский (`es`), русский (`ru`), арабский (`ar`, с раскладкой справа налево), японский (`ja`), корейский (`ko`), немецкий (`de`) и португальский (`pt`, бразильский; используется также для `pt-PT`).
|
|
70
|
+
|
|
71
|
+
**Выбор языка.** Язык пользователя телефона — это `run({ lang })`, если он задан, затем по порядку каждый из `navigator.languages`. Для каждого тега SDK пробует сам тег, а затем его более короткие формы (`pt-BR` → `pt`, `zh-HK` → `zh-TW` → `zh`), берёт первый язык, для которого у него есть пакет, а в крайнем случае — английский. Сообщения, которых нет в пакете, подставляются по той же цепочке, поэтому неполный пакет — это нормально. `setLang('ja')` переключает язык во время работы; `status().lang` — используемый пакет.
|
|
72
|
+
|
|
73
|
+
**Загрузка.** Английский и упрощённый китайский входят в `sdk.js`. Остальные пакеты загружаются по требованию, когда они впервые понадобятся странице (на это время интерфейс показывает резервный язык):
|
|
74
|
+
|
|
75
|
+
- Сборка для `<script>`: из `locales/<id>.js` рядом с `sdk.js` (путь на CDN, например `…/remote-webview-debugger@0.1.0/dist/locales/ja.js`, или `/v1/locales/ja.js` вашего шлюза), а если не получилось — со шлюза, переданного в `run()`. Чтобы избежать лишнего запроса, загрузите пакет сами, до или после `sdk.js`: `<script src="…/dist/locales/ja.js"></script>`.
|
|
76
|
+
- Сборка ES-модуля: динамический импорт, поэтому сборщик выносит каждый язык в отдельный чанк. Чтобы, наоборот, включить язык в бандл сразу: `import ja from 'remote-webview-debugger/locales/ja'`, затем `run(url, key, { locales: [ja] })`.
|
|
77
|
+
|
|
78
|
+
**Собственные пакеты и формулировки.** Пакет — это `{ id, name, dir?, ui?, action?, agent? }`; всё, что не указано, берётся из резервных языков:
|
|
79
|
+
|
|
80
|
+
```js
|
|
81
|
+
RemoteWebviewDebugger.registerLocale({
|
|
82
|
+
id: 'it',
|
|
83
|
+
name: 'Italiano',
|
|
84
|
+
ui: { takeover: 'Prendi il controllo', end: 'Termina', help: 'Richiedi assistenza' /* … */ },
|
|
85
|
+
})
|
|
86
|
+
RemoteWebviewDebugger.run(url, key, {
|
|
87
|
+
messages: { en: { ui: { help: 'Contact support' } } }, // заменить отдельные сообщения любого языка
|
|
88
|
+
})
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
`ui` — тексты интерфейса, `action` дополняет фразу «AI wants to …» в диалоге подтверждения, а `agent` — то, что возвращается агенту. Типы TypeScript (`LocalePack`, `Messages`, `UiMessages`, `ActionMessages`, `AgentMessages`) перечисляют все ключи; эталон — `src/locales/en.ts`.
|
|
92
|
+
|
|
93
|
+
**Две аудитории.** Пользователь телефона получает язык, выбранный как описано выше. Агент читает сообщения об ошибках RPC и сводки хронологии (нажатия, переходы внутри страницы, неудачные запросы) на языке шлюза — `welcome.agentLang` (`RWD_LANG`), а пакет для него загружается тем же способом. До тех пор, пока `welcome` не укажет иное, используется английский; последнее значение хранится в localStorage `rwd:alang`, поэтому оно применяется и к событиям, произошедшим до `welcome` следующей страницы.
|
|
94
|
+
|
|
95
|
+
Так пользователь, говорящий по-китайски, может работать с агентом, работающим на английском: диалог спрашивает `AI 想要点击「提交订单」,允许吗?`, а при отказе агент читает `The user denied this action`.
|
|
96
|
+
|
|
97
|
+
## Оформление
|
|
98
|
+
|
|
99
|
+
Интерфейс живёт в shadow root на `#__rwd` (вне `<body>`), поэтому CSS страницы до него не доходит — только через эти точки входа:
|
|
100
|
+
|
|
101
|
+
- **Пользовательские CSS-свойства** на `:root` или `#__rwd`, например `:root { --rwd-accent-rgb: 225, 29, 72; --rwd-radius: 12px; }`.
|
|
102
|
+
- **`run({ style })`**, который задаёт те же свойства на `#__rwd`. Цвета здесь принимаются в любом формате CSS.
|
|
103
|
+
- **`::part()`** для всего остального: `#__rwd::part(bar) { … }`. Части: `pill`, `window`, `bar`, `task`, `dialog`, `toast`, `button`, `cursor`, `glow`, `overlay`. Имена классов внутри не являются стабильным API.
|
|
104
|
+
- **`theme`**: светлое или тёмное «стекло» (по умолчанию следует фону страницы).
|
|
105
|
+
|
|
106
|
+
| Свойство | Поле `style` | По умолчанию | Что задаёт |
|
|
107
|
+
|---|---|---|---|
|
|
108
|
+
| `--rwd-accent-rgb` | `accent` | `59, 130, 246` | Кнопки, переключатели, курсор агента, подсветку и свечение по краям |
|
|
109
|
+
| `--rwd-danger-rgb` | `danger` | `239, 68, 68` | Кнопку «Завершить» |
|
|
110
|
+
| `--rwd-warning-rgb` | `warning` | `245, 158, 11` | «Запросить поддержку» и «Идёт поддержка»; состояние, когда управляет пользователь |
|
|
111
|
+
| `--rwd-success-rgb` | `success` | `34, 197, 94` | Индикатор «в сети»; «Скопировано» |
|
|
112
|
+
| `--rwd-font` | `font` | системный шрифт интерфейса (для китайского, японского и корейского — свой для каждого языка) | Весь текст |
|
|
113
|
+
| `--rwd-font-mono` | `monoFont` | `ui-monospace, "SF Mono", …` | Код устройства и теги |
|
|
114
|
+
| `--rwd-radius` | `radius` | `18px` | Плавающее окно; у диалога скругление на 2px больше, у карточки задачи и плашки — на 2px меньше, у кнопок диалога — на 6px меньше |
|
|
115
|
+
| `--rwd-button-radius` | `buttonRadius` | `999px` | Кнопки, нижнюю панель и всплывающие уведомления |
|
|
116
|
+
| `--rwd-z-index` | `zIndex` | `2147483647` | Весь интерфейс |
|
|
117
|
+
| `--rwd-bar-bottom` | `barBottom` | `14px` | Нижнюю панель, над нижней безопасной областью |
|
|
118
|
+
| `--rwd-task-top` | `taskTop` | `10px` | Карточку задачи, под верхней безопасной областью |
|
|
119
|
+
| `--rwd-toast-bottom` | `toastBottom` | `84px` | Всплывающие уведомления, над нижней безопасной областью |
|
|
120
|
+
| — | `pill: { side, top }` | правый край (левый в языках с письмом справа налево), `0.62` | Где находится плашка в состоянии покоя, пока пользователь её не перетащит; `top` — от 0 (верх) до 1 (низ) |
|
|
121
|
+
|
|
122
|
+
Цвета задаются тройками `r, g, b`, чтобы полупрозрачные оттенки менялись вместе с ними (то же соглашение, что у `--bs-*-rgb` в Bootstrap).
|
|
123
|
+
|
|
124
|
+
## Поведение на странице (протокол §6–§8, §11)
|
|
125
|
+
|
|
126
|
+
- Уровни доступа: RPC проверяются по `welcome.level` / `control.level` (`forbidden`, «Requires the … level»). Агент без уровня (от более старого шлюза) получает `interact`; если агент не подключён, никаких дополнительных проверок нет.
|
|
127
|
+
- Токен устройства хранится в localStorage `rwd:dtoken`, токен возобновления — в sessionStorage `rwd:resume` (привязан к pageId). Код закрытия 4401: если токен тем временем изменился (его только что получила другая вкладка устройства), сразу повторить попытку один раз, иначе `rejected` и повтор примерно через 10 минут; 4409: переподключиться с новым pageId; 4429: `quota`, повтор примерно через 5 минут.
|
|
128
|
+
- `rwd_invite` из адресной строки убирается при вызове `run()` (`history.replaceState`) и отправляется в `hello`, пока не придёт `welcome`.
|
|
129
|
+
- `welcome.config`: `helpButton` (объединяется через ИЛИ с параметром `run()`), `maskSelectors`, `maskPatterns` (снимок, console, URL сетевых запросов, значения хранилища, сводки хронологии), `confirmSelectors`, `networkExclude` (добавляется к правилам параметра `run()`), `sessionLog`. Последняя конфигурация хранится в localStorage `rwd:cfg` и используется, пока не придёт `welcome`, — поэтому у проекта, отключившего журнал сеанса, он остаётся выключенным с первого же момента следующей страницы, ещё до какого-либо `welcome`.
|
|
130
|
+
- Снимки заканчиваются разделом `[Overlays]` со списком того, что перекрывает страницу, — подложки всплывающего окна, модального окна, нижней шторки — с тегом, коротким фрагментом текста, долей перекрытой области просмотра и ref. Подложка — это пустой элемент, для которого в aria snapshot нет узла, поэтому она получает ref вида `oN` вместо `eN`; именно это позволяет агенту нажать на неё, чтобы закрыть всплывающее окно.
|
|
131
|
+
- Клик сначала нацелен в видимый центр элемента, затем в остальные точки сетки 3×3 внутри него, поэтому значок поверх кнопки не мешает клику. Если перекрыты все точки, RPC завершается ошибкой `not_interactable` и называет перекрывающий элемент и его ref.
|
|
132
|
+
- Диалоги подключения и чувствительных действий (`[data-rwd-confirm]`, `confirmSelectors`, включая отправку формы клавишей Enter) находятся в Shadow DOM, принимают только доверенные нажатия и становятся активными через 0,4 с после появления. Вкладки одного origin делятся решёнными запросами на подключение через BroadcastChannel.
|
|
133
|
+
|
|
134
|
+
Загрузка файла запускает сбор console, необработанных ошибок и сетевых запросов (XHR, fetch, WebSocket, sendBeacon, EventSource) — не более 500 записей каждого вида; при переполнении первыми удаляются обычные логи и успешные запросы. Тела ответов кешируются в пределах 5 МБ суммарно и 64 КБ на каждое. Поэтому загружайте файл синхронно и как можно раньше в `<head>`.
|
|
135
|
+
|
|
136
|
+
## Журнал сеанса (протокол §5.4)
|
|
137
|
+
|
|
138
|
+
Эти буферы — память одной страницы. Параллельно SDK пишет на устройстве **журнал сеанса** в IndexedDB (`rwd-log`), чтобы агент, подключившийся после бага, всё равно мог прочитать весь путь к нему:
|
|
139
|
+
|
|
140
|
+
- Один сеанс — это один запуск приложения, общий для всех страниц origin: `run({ launchId })`, иначе `window.RWDLaunchId`, иначе определяется по сессионной cookie `rwd_launch` и heartbeat (сеанс по cookie, молчавший больше часа, считается новым запуском). `status().logSession` — используемый идентификатор.
|
|
141
|
+
- Записывается: открытие страницы / переходы внутри страницы / переход на передний план и в фон / уход со страницы, нажатия, ввод, отправка форм и позиции, на которых остановилась прокрутка, интерактивные вызовы агента и `rwd.evaluate`, смены управления, console всех уровней и каждый завершённый запрос — неудачные также с очищенными от секретов заголовками и телами (по 64 КБ). Запись идёт независимо от того, доступен ли шлюз. Замаскированные поля хранятся как `••••`, `maskPatterns` применяются при записи.
|
|
142
|
+
- Хранится: последние 3 запуска, по 10 МБ каждый; при превышении первыми удаляются обычные логи и успешные запросы. Если квота хранилища меньше 64 МБ — только текущий запуск; без IndexedDB — ничего.
|
|
143
|
+
- Агент читает журнал через `rwd.log` (`observe`) и `rwd.logDetail` (`advanced`, для заголовков и тел). Иначе ничего не покидает устройство.
|
|
144
|
+
- Отключается через `run({ record: false })` для одной страницы или `policy.sessionLog: false` для проекта — что также удаляет уже сохранённое этим origin.
|
|
145
|
+
|
|
146
|
+
## Структура
|
|
147
|
+
|
|
148
|
+
- `src/index.ts`: публичный API, сеанс, диспетчеризация RPC, состояние управления.
|
|
149
|
+
- `src/transport.ts`: WebSocket страницы (нативный конструктор), таймаут heartbeat, экспоненциальная задержка повторов, а после 3 неудачных попыток — `POST /v1/presence` каждые 60 с.
|
|
150
|
+
- `src/ids.ts`: deviceId / pageId / openerId.
|
|
151
|
+
- `src/launch.ts`: к какому запуску приложения относится эта страница (`launchId`, `window.RWDLaunchId`, cookie `rwd_launch`).
|
|
152
|
+
- `src/pageWatch.ts`: сообщения `page` (url, title, visibility) и события хронологии (переходы внутри страницы, нажатия пользователя).
|
|
153
|
+
- `src/ui.ts`, `src/lock.ts`: плашка, кнопка «Запросить поддержку», блокирующий оверлей, свечение, подсветка действий и диалоги в Shadow DOM; блокирует доверенный ввод пользователя, пока подключён агент, и показывает, сколько длится подключение.
|
|
154
|
+
- `src/approval.ts`: одобрение подключения и подтверждение чувствительных действий (диалог на странице или нативный хост через `nativeDecide`).
|
|
155
|
+
- `src/i18n.ts`, `src/locales/`, `src/localeLoader*.ts`: языковые пакеты (тексты интерфейса, действий и агента для каждого языка), выбор языка и резервные языки, загрузка по требованию; `scripts/locales.mjs` собирает `dist/locales/<id>.js`.
|
|
156
|
+
- Движок агента находится рядом с chobitsu в `packages/chobitsu/src/engine/rwd/`: снимок, `rwd.query`, проверки готовности элемента к действию, синтетический ввод, клавиатура, буферы console / сети, скриншоты, `rwd.storage`, журнал сеанса в IndexedDB (`coldlog.ts`), обнаружение перекрывающих слоёв (`overlays.ts`), уровни доступа и правила подтверждения (`policy.ts`), тексты для агента (`i18n.ts`). Исходники Playwright, используемые для снимков, лежат в `packages/chobitsu/src/engine/vendor/playwright/`. Сборка включает их из исходников в `dist/sdk.js`.
|
|
157
|
+
|
|
158
|
+
## Сборка и тесты
|
|
159
|
+
|
|
160
|
+
```sh
|
|
161
|
+
cd sdk
|
|
162
|
+
npm ci
|
|
163
|
+
npm run build # dist/sdk.js (IIFE, глобальная RemoteWebviewDebugger, ES2017), dist/index.mjs + index.d.ts (ES-модуль)
|
|
164
|
+
npm run typecheck
|
|
165
|
+
npm test # Playwright: WebKit (iPhone 15) + Chromium (Pixel 7)
|
|
166
|
+
npm run gateway # локальный mock-шлюз: http://localhost:8787/demo/platform/?key=rwd_pk_demo
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Тесты используют `test/mock-gateway.mjs`, который реализует сторону протокола, обращённую к странице (welcome, состояние управления, RPC, presence; токены, коды закрытия, уровни доступа, удалённая конфигурация, `agentLang`, одобрение подключения), и отдаёт `/v1/sdk.js`, языковые пакеты в `/v1/locales/` и демо-магазин. Сам он уровни доступа не проверяет, поэтому тесты проверяют собственные проверки страницы. Спецификации в `test/e2e/` запускаются с браузером в `zh-CN` и `agentLang: 'zh'`, если в тесте не указано иное; английский покрывает `i18n.spec.ts`, а `locales.spec.ts` — остальные языковые пакеты, резервные языки, собственные пакеты, оформление и раскладку справа налево. Тест совместимости загружает eruda 3.4.3 и PageSpy 2.4.0 с jsdelivr, поэтому ему нужен доступ в интернет.
|
package/README.zh-CN.md
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
<!-- i18n-source: d9a31993ea867d3ef7058e7d637886f38a6ee3e1 -->
|
|
2
|
+
[English](README.md) | 简体中文 | [繁體中文](README.zh-TW.md) | [日本語](README.ja.md) | [한국어](README.ko.md) | [Español](README.es.md) | [Français](README.fr.md) | [Deutsch](README.de.md) | [Português (Brasil)](README.pt-BR.md) | [Русский](README.ru.md) | [العربية](README.ar.md)
|
|
2
3
|
|
|
3
4
|
# RWD 页面 SDK(`sdk/`)
|
|
4
5
|
|
|
5
6
|
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
|
|
|
7
8
|
```html
|
|
8
|
-
<script src="https://cdn.jsdelivr.net/npm/remote-webview-debugger@0.
|
|
9
|
+
<script src="https://cdn.jsdelivr.net/npm/remote-webview-debugger@0.2.0/dist/sdk.js"></script>
|
|
9
10
|
<script>
|
|
10
11
|
RemoteWebviewDebugger.run("https://rwd.example.com/v1", "rwd_pk_xxxxxxxx", {
|
|
11
12
|
userId: "u-42", // 可选
|
|
12
13
|
deviceId: "…", // 可选,原生注入时由原生传入
|
|
13
|
-
helpButton: true, //
|
|
14
|
+
helpButton: true, // 可选,显示「请求协助」按钮
|
|
14
15
|
app: { name: "商城", version: "3.2.0" },
|
|
15
|
-
lang: "zh", //
|
|
16
|
+
lang: "zh", // 可选,语言标签,默认按浏览器语言
|
|
16
17
|
}).then(({ deviceCode, status }) => console.log(deviceCode, status))
|
|
17
18
|
</script>
|
|
18
19
|
```
|
|
@@ -23,7 +24,7 @@ npm 包名 [`remote-webview-debugger`](https://www.npmjs.com/package/remote-webv
|
|
|
23
24
|
|
|
24
25
|
| 用法 | 地址 |
|
|
25
26
|
|---|---|
|
|
26
|
-
| CDN `<script>` | `https://cdn.jsdelivr.net/npm/remote-webview-debugger@0.
|
|
27
|
+
| CDN `<script>` | `https://cdn.jsdelivr.net/npm/remote-webview-debugger@0.2.0/dist/sdk.js` 或 `https://unpkg.com/remote-webview-debugger@0.2.0/dist/sdk.js`(压缩后的 IIFE,全局对象 `RemoteWebviewDebugger`;请固定版本号,需要的话加上 `integrity`) |
|
|
27
28
|
| 打包工具 | `npm install remote-webview-debugger`,然后 `import { run, status } from 'remote-webview-debugger'`(ES module,带类型声明)。尽量在你自己的代码之前 import,好让记录尽早开始。同一个页面不要再加载 `<script>` 版本 |
|
|
28
29
|
| 你的网关 | `https://<网关>/v1/sdk.js`,网关内嵌的那一份(控制台还提供带 SRI 的按内容寻址地址) |
|
|
29
30
|
| 原生 App | 把包里的 `dist/sdk.js` 打包进 App,或从以上任一地址下载,见[原生接入](../docs/native-integration.zh-CN.md) |
|
|
@@ -36,9 +37,12 @@ npm 包名 [`remote-webview-debugger`](https://www.npmjs.com/package/remote-webv
|
|
|
36
37
|
| `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
38
|
| `stop()` | 断开并移除界面(console / 网络采集继续,有上限) |
|
|
38
39
|
| `setUser(userId)` | 更新用户;会用新的 `hello` 重连一次 |
|
|
39
|
-
| `help(on)` |
|
|
40
|
+
| `help(on)` | 请求协助 / 取消请求 |
|
|
40
41
|
| `nativeControl(nativeKey, action)` | 原生的接管 / 交还 / 结束 |
|
|
41
42
|
| `nativeDecide(nativeKey, requestId, value)` | `nativeApproval` 时回答 `status().pending`:接入审批传级别或 `null`,敏感操作确认传 `'allow'` 或 `null` |
|
|
43
|
+
| `setLang(lang?)` | 运行时切换界面语言(比如 App 自己的语言设置);不传参数则回到浏览器语言。见[多语言](#多语言) |
|
|
44
|
+
| `registerLocale(pack)` | 添加语言包,或替换同 `id` 的语言包(也可以传数组)。见[多语言](#多语言) |
|
|
45
|
+
| `builtinLocales` | 内置语言包的 id 列表 |
|
|
42
46
|
|
|
43
47
|
`run()` 的选项:
|
|
44
48
|
|
|
@@ -46,12 +50,15 @@ npm 包名 [`remote-webview-debugger`](https://www.npmjs.com/package/remote-webv
|
|
|
46
50
|
|---|---|
|
|
47
51
|
| `userId` | 当前登录用户,随 `hello` 发送 |
|
|
48
52
|
| `deviceId` | 原生宿主传入的固定设备 ID(不传则存在 localStorage) |
|
|
49
|
-
| `helpButton` |
|
|
53
|
+
| `helpButton` | 显示「请求协助」按钮(与项目策略取或) |
|
|
50
54
|
| `app` | 宿主 App 的 `{ name, version }` |
|
|
51
|
-
| `lang` | 手机用户看到的界面语言(胶囊、悬浮窗、底部栏、对话框、提示、eruda 里的 tab,以及敏感操作确认里「AI
|
|
55
|
+
| `lang` | 手机用户看到的界面语言(胶囊、悬浮窗、底部栏、对话框、提示、eruda 里的 tab,以及敏感操作确认里「AI 想要…」的描述):语言标签,如 `'ja'`、`'pt-BR'`。默认按浏览器语言。见[多语言](#多语言) |
|
|
56
|
+
| `locales` | 额外的语言包,在选择语言之前注册(同 `registerLocale`) |
|
|
57
|
+
| `messages` | 按语言覆盖部分文案:`{ zh: { ui: { help: '联系客服' } }, en: { ui: { help: 'Contact support' } } }` |
|
|
52
58
|
| `networkExclude` | 不想让 agent 去翻的请求——一直 404 的字体、埋点上报等:`['.ttf', 'WebTracks', '*beacon*', '/\\/track\\//']`。每条规则按不区分大小写的 URL 子串、带 `*` 的通配符,或写成 `/…/` 的正则来匹配。与项目策略里的规则合并。命中的请求不出现在网络列表里,也不产生 `net_fail` 时间线事件;页面仍然记录,agent 需要时可以用 `includeExcluded` 看到 |
|
|
53
59
|
| `lockScreen` | `false`:AI 接入期间默认不锁屏(用户可在悬浮窗里改)。默认 `true` |
|
|
54
60
|
| `theme` | `'auto'`(默认,跟随页面背景)、`'system'`、`'light'` 或 `'dark'` |
|
|
61
|
+
| `style` | 界面的颜色、字体、圆角、z-index 和位置:`{ accent: '#e11d48', radius: 12, zIndex: 1000, pill: { side: 'left' } }`。见[样式覆盖](#样式覆盖) |
|
|
55
62
|
| `eruda` | `true` 或 `{ dock, load, src, name }`:收进 eruda 的「Remote Debugger」tab,`load` 在页面没有 eruda 时加载最新版。默认关闭,项目策略可远程打开,本地选项逐项优先 |
|
|
56
63
|
| `record` | `false`:本页面不写会话日志(已经存下的也不删)。默认 `true`;项目策略可以对所有页面关掉 |
|
|
57
64
|
| `launchId` | 本页面属于 App 的哪一次启动,让同一个 App 进程里的所有 WebView 把会话日志归到同一次会话下。原生宿主在这里传自己的 id(也可以定义 `window.RWDLaunchId`,字符串或返回字符串的函数);不传时由 SDK 用会话 cookie 加心跳推断 |
|
|
@@ -59,13 +66,61 @@ npm 包名 [`remote-webview-debugger`](https://www.npmjs.com/package/remote-webv
|
|
|
59
66
|
|
|
60
67
|
## 多语言
|
|
61
68
|
|
|
62
|
-
|
|
69
|
+
内置语言:英语(`en`)、简体中文(`zh`)、繁体中文(`zh-TW`,`zh-HK`、`zh-MO`、`zh-Hant` 也用它)、法语(`fr`)、西班牙语(`es`)、俄语(`ru`)、阿拉伯语(`ar`,从右到左排版)、日语(`ja`)、韩语(`ko`)、德语(`de`)和葡萄牙语(`pt`,巴西葡萄牙语,`pt-PT` 也用它)。
|
|
63
70
|
|
|
64
|
-
|
|
65
|
-
|
|
71
|
+
**怎么选语言。** 手机用户的语言先看 `run({ lang })`,再依次看 `navigator.languages`。每个语言标签先试它本身,再试更短的形式(`pt-BR` → `pt`,`zh-HK` → `zh-TW` → `zh`),用第一个有语言包的语言,都没有就用英文。语言包里缺的文案也按同样的顺序回退,所以语言包可以只写一部分。`setLang('ja')` 在运行时切换;`status().lang` 是正在用的语言包。
|
|
72
|
+
|
|
73
|
+
**怎么加载。** 英文和简体中文打包在 `sdk.js` 里。其他语言包在页面第一次用到时按需加载(加载完成前界面先用回退语言):
|
|
74
|
+
|
|
75
|
+
- `<script>` 版:从 `sdk.js` 同目录下的 `locales/<id>.js` 加载(CDN 路径如 `…/remote-webview-debugger@0.2.0/dist/locales/ja.js`,或你的网关的 `/v1/locales/ja.js`),失败时再从传给 `run()` 的网关加载。想省掉这次请求,可以自己在 `sdk.js` 之前或之后引入:`<script src="…/dist/locales/ja.js"></script>`。
|
|
76
|
+
- ES module 版:用动态 import,打包工具会把每种语言拆成单独的 chunk。想直接打进主包:`import ja from 'remote-webview-debugger/locales/ja'`,再 `run(url, key, { locales: [ja] })`。
|
|
77
|
+
|
|
78
|
+
**自定义语言包和文案。** 语言包是 `{ id, name, dir?, ui?, action?, agent? }`,没写的文案从回退语言里取:
|
|
79
|
+
|
|
80
|
+
```js
|
|
81
|
+
RemoteWebviewDebugger.registerLocale({
|
|
82
|
+
id: 'it',
|
|
83
|
+
name: 'Italiano',
|
|
84
|
+
ui: { takeover: 'Prendi il controllo', end: 'Termina', help: 'Richiedi assistenza' /* … */ },
|
|
85
|
+
})
|
|
86
|
+
RemoteWebviewDebugger.run(url, key, {
|
|
87
|
+
messages: { zh: { ui: { help: '联系客服' } } }, // 覆盖任意语言的个别文案
|
|
88
|
+
})
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
`ui` 是界面文案,`action` 补全确认对话框里的「AI 想要…」,`agent` 是发回给 agent 的文案。TypeScript 类型(`LocalePack`、`Messages`、`UiMessages`、`ActionMessages`、`AgentMessages`)列出了全部 key;参照 `src/locales/en.ts`。
|
|
92
|
+
|
|
93
|
+
**两类读者。** 手机用户用上面选出的语言。agent 读到的 RPC 错误信息、时间线摘要(点击、页面内跳转、失败的请求)用网关的语言 `welcome.agentLang`(`RWD_LANG`),语言包同样按需加载。收到 welcome 之前默认英文;最近一次的值记在 localStorage `rwd:alang`,下一个页面在 welcome 之前产生的事件也用它。
|
|
66
94
|
|
|
67
95
|
因此中文用户可以和英文 agent 配合:对话框问的是「AI 想要点击「提交订单」,允许吗?」,用户拒绝时 agent 收到 `The user denied this action`。
|
|
68
96
|
|
|
97
|
+
## 样式覆盖
|
|
98
|
+
|
|
99
|
+
界面在 `#__rwd`(位于 `<body>` 之外)的 Shadow DOM 里,页面的 CSS 只能通过下面这些入口影响它:
|
|
100
|
+
|
|
101
|
+
- **CSS 自定义属性**:写在 `:root` 或 `#__rwd` 上,例如 `:root { --rwd-accent-rgb: 225, 29, 72; --rwd-radius: 12px; }`。
|
|
102
|
+
- **`run({ style })`**:把同样的属性设到 `#__rwd` 上;这里的颜色可以写任意 CSS 颜色。
|
|
103
|
+
- **`::part()`**:其他样式用 `#__rwd::part(bar) { … }`。可用的 part:`pill`、`window`、`bar`、`task`、`dialog`、`toast`、`button`、`cursor`、`glow`、`overlay`。内部的 class 名不是稳定的 API。
|
|
104
|
+
- **`theme`**:浅色或深色玻璃(默认跟随页面背景)。
|
|
105
|
+
|
|
106
|
+
| 属性 | `style` 字段 | 默认值 | 作用 |
|
|
107
|
+
|---|---|---|---|
|
|
108
|
+
| `--rwd-accent-rgb` | `accent` | `59, 130, 246` | 按钮、开关、agent 的指针、高亮和边缘光圈 |
|
|
109
|
+
| `--rwd-danger-rgb` | `danger` | `239, 68, 68` | 「结束」按钮 |
|
|
110
|
+
| `--rwd-warning-rgb` | `warning` | `245, 158, 11` | 「请求协助」按钮和「协助中」状态;用户接管状态 |
|
|
111
|
+
| `--rwd-success-rgb` | `success` | `34, 197, 94` | 在线指示点;「已复制」 |
|
|
112
|
+
| `--rwd-font` | `font` | 系统界面字体(中文、日文、韩文按语言选字体) | 全部文字 |
|
|
113
|
+
| `--rwd-font-mono` | `monoFont` | `ui-monospace, "SF Mono", …` | 设备码和标签 |
|
|
114
|
+
| `--rwd-radius` | `radius` | `18px` | 悬浮窗;对话框比它大 2px,任务卡片和胶囊小 2px,对话框按钮小 6px |
|
|
115
|
+
| `--rwd-button-radius` | `buttonRadius` | `999px` | 按钮、底部栏和提示 |
|
|
116
|
+
| `--rwd-z-index` | `zIndex` | `2147483647` | 整个界面 |
|
|
117
|
+
| `--rwd-bar-bottom` | `barBottom` | `14px` | 底部栏到底部安全区的距离 |
|
|
118
|
+
| `--rwd-task-top` | `taskTop` | `10px` | 任务卡片到顶部安全区的距离 |
|
|
119
|
+
| `--rwd-toast-bottom` | `toastBottom` | `84px` | 提示到底部安全区的距离 |
|
|
120
|
+
| — | `pill: { side, top }` | 右边缘(从右到左的语言在左边缘),`0.62` | 用户拖动之前胶囊的初始位置;`top` 从 0(顶部)到 1(底部) |
|
|
121
|
+
|
|
122
|
+
颜色写成 `r, g, b` 三元组,半透明的颜色才能跟着变(和 Bootstrap 的 `--bs-*-rgb` 一样)。
|
|
123
|
+
|
|
69
124
|
## 页面内的行为(协议 §6–§8、§11)
|
|
70
125
|
|
|
71
126
|
- 权限级别:按 `welcome.level` / `control.level` 拦截 RPC(`forbidden`「需要 … 权限」);有 agent 但网关没给级别时按 `interact`,没有 agent 时不额外限制。
|
|
@@ -95,9 +150,9 @@ npm 包名 [`remote-webview-debugger`](https://www.npmjs.com/package/remote-webv
|
|
|
95
150
|
- `src/ids.ts`:deviceId / pageId / openerId。
|
|
96
151
|
- `src/launch.ts`:判断本页面属于 App 的哪一次启动(`launchId`、`window.RWDLaunchId`、`rwd_launch` cookie)。
|
|
97
152
|
- `src/pageWatch.ts`:`page` 消息(url、标题、可见性)和时间线事件(页面内跳转、用户点击摘要)。
|
|
98
|
-
- `src/ui.ts`、`src/lock.ts`:Shadow DOM
|
|
153
|
+
- `src/ui.ts`、`src/lock.ts`:Shadow DOM 里的胶囊、「请求协助」按钮、遮罩、光圈、操作高亮、确认对话框;agent 接入期间拦截用户的可信输入,并显示已连接时长。
|
|
99
154
|
- `src/approval.ts`:接入审批、敏感操作确认(页面对话框或原生宿主 `nativeDecide`)。
|
|
100
|
-
- `src/i18n.ts
|
|
155
|
+
- `src/i18n.ts`、`src/locales/`、`src/localeLoader*.ts`:语言包(每种语言的界面、操作描述和 agent 文案)、语言选择与回退、按需加载;`scripts/locales.mjs` 生成 `dist/locales/<id>.js`。
|
|
101
156
|
- 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
157
|
|
|
103
158
|
## 构建与测试
|
|
@@ -111,4 +166,4 @@ npm test # Playwright:WebKit(iPhone 15)+ Chromium(Pixel 7)
|
|
|
111
166
|
npm run gateway # 本地 mock 网关:http://localhost:8787/demo/platform/?key=rwd_pk_demo
|
|
112
167
|
```
|
|
113
168
|
|
|
114
|
-
测试用 `test/mock-gateway.mjs`(实现协议的页面侧:welcome、控制状态、RPC、presence,令牌、关闭码、级别、远程配置、`agentLang`、接入审批,并提供 `/v1/sdk.js
|
|
169
|
+
测试用 `test/mock-gateway.mjs`(实现协议的页面侧:welcome、控制状态、RPC、presence,令牌、关闭码、级别、远程配置、`agentLang`、接入审批,并提供 `/v1/sdk.js`、`/v1/locales/` 下的语言包和演示站;它不检查级别,以便测到页面自己的拦截),用例在 `test/e2e/`:除非用例另行指定,浏览器语言为 `zh-CN`、`agentLang` 为 `'zh'`;英文由 `i18n.spec.ts` 覆盖,其他语言包、回退、自定义语言包、样式覆盖和从右到左布局由 `locales.spec.ts` 覆盖。共存测试会从 jsdelivr 加载 eruda 3.4.3 和 PageSpy 2.4.0,需要能访问外网。
|
package/README.zh-TW.md
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
<!-- i18n-source: d9a31993ea867d3ef7058e7d637886f38a6ee3e1 -->
|
|
2
|
+
[English](README.md) | [简体中文](README.zh-CN.md) | 繁體中文 | [日本語](README.ja.md) | [한국어](README.ko.md) | [Español](README.es.md) | [Français](README.fr.md) | [Deutsch](README.de.md) | [Português (Brasil)](README.pt-BR.md) | [Русский](README.ru.md) | [العربية](README.ar.md)
|
|
3
|
+
|
|
4
|
+
# RWD 頁面 SDK(`sdk/`)
|
|
5
|
+
|
|
6
|
+
由 App WebView 或 H5 頁面載入的單一 JS 檔案,讓 AI agent 透過 RWD 閘道(`gateway/`)和 MCP 檢視並操作頁面。協定請見 [`docs/protocol.zh-TW.md`](../docs/protocol.zh-TW.md);架構請見 [`docs/architecture.zh-TW.md`](../docs/architecture.zh-TW.md)。
|
|
7
|
+
|
|
8
|
+
```html
|
|
9
|
+
<script src="https://cdn.jsdelivr.net/npm/remote-webview-debugger@0.1.0/dist/sdk.js"></script>
|
|
10
|
+
<script>
|
|
11
|
+
RemoteWebviewDebugger.run("https://rwd.example.com/v1", "rwd_pk_xxxxxxxx", {
|
|
12
|
+
userId: "u-42", // 選用
|
|
13
|
+
deviceId: "…", // 選用;由注入 SDK 的原生宿主傳入
|
|
14
|
+
helpButton: true, // 選用;顯示「請求協助」按鈕
|
|
15
|
+
app: { name: "Shop", version: "3.2.0" },
|
|
16
|
+
lang: "en", // 選用;語言標記,預設依瀏覽器的語言
|
|
17
|
+
}).then(({ deviceCode, status }) => console.log(deviceCode, status))
|
|
18
|
+
</script>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 取得 SDK
|
|
22
|
+
|
|
23
|
+
以 [`remote-webview-debugger`](https://www.npmjs.com/package/remote-webview-debugger) 發布在 npm 上;版本與變更請見[變更記錄](CHANGELOG.zh-CN.md)。
|
|
24
|
+
|
|
25
|
+
| 用法 | 來源 |
|
|
26
|
+
|---|---|
|
|
27
|
+
| 從 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`) |
|
|
28
|
+
| 打包工具 | `npm install remote-webview-debugger`,然後 `import { run, status } from 'remote-webview-debugger'`(附型別定義的 ES module)。請在你自己的程式碼之前 import,讓記錄盡早開始。同一個頁面不要再載入 `<script>` 版本 |
|
|
29
|
+
| 你的閘道 | `https://<閘道>/v1/sdk.js`,即閘道內嵌的那一份(主控台另外提供依內容定址、可搭配 SRI 的網址) |
|
|
30
|
+
| 原生 App | 把套件中的 `dist/sdk.js` 打包進 App,或從上述任一來源下載;請見[原生整合](../docs/native-integration.zh-TW.md) |
|
|
31
|
+
|
|
32
|
+
## API
|
|
33
|
+
|
|
34
|
+
| API | 說明 |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `run(gatewayUrl, appKey, opts?)` | 讓頁面上線(裝置碼、膠囊)。回傳 `Promise<{deviceCode, status}>`;重複呼叫不會有副作用。選項見下表。 |
|
|
37
|
+
| `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` 是這個頁面正在寫入的工作階段記錄(未記錄時為 `null`) |
|
|
38
|
+
| `stop()` | 斷線並移除介面(console / 網路的收集會繼續,有容量上限) |
|
|
39
|
+
| `setUser(userId)` | 變更使用者;會以新的 `hello` 重新連線一次 |
|
|
40
|
+
| `help(on)` | 請求協助 / 取消請求 |
|
|
41
|
+
| `nativeControl(nativeKey, action)` | 從原生宿主自己的介面接管 / 交還 / 結束 |
|
|
42
|
+
| `nativeDecide(nativeKey, requestId, value)` | 搭配 `nativeApproval` 時回答 `status().pending`:連線請求傳入等級或 `null`,敏感操作確認傳入 `'allow'` 或 `null` |
|
|
43
|
+
| `setLang(lang?)` | 在執行期間切換介面語言(例如 App 自己的語言設定);不帶參數則回到瀏覽器的語言。見[語言](#語言) |
|
|
44
|
+
| `registerLocale(pack)` | 新增語言包,或取代 `id` 相同的語言包(也可以傳入陣列)。見[語言](#語言) |
|
|
45
|
+
| `builtinLocales` | 內建語言包的 id 清單 |
|
|
46
|
+
|
|
47
|
+
`run()` 選項:
|
|
48
|
+
|
|
49
|
+
| 選項 | 說明 |
|
|
50
|
+
|---|---|
|
|
51
|
+
| `userId` | 目前登入的使用者,隨 `hello` 送出 |
|
|
52
|
+
| `deviceId` | 原生宿主提供的固定裝置 id(未提供時會存在 localStorage) |
|
|
53
|
+
| `helpButton` | 顯示「請求協助」按鈕(與專案策略取 OR) |
|
|
54
|
+
| `app` | 宿主 App 的 `{ name, version }` |
|
|
55
|
+
| `lang` | 手機使用者看到的所有內容的語言(膠囊、浮動視窗、底部控制列、對話框、提示訊息、eruda 分頁,以及敏感操作中「AI 想要…」的描述):語言標記,例如 `'ja'` 或 `'pt-BR'`。預設:瀏覽器的語言。見[語言](#語言) |
|
|
56
|
+
| `locales` | 額外的語言包,在決定語言之前註冊(與 `registerLocale` 相同) |
|
|
57
|
+
| `messages` | 依語言覆寫的文字:`{ en: { ui: { help: 'Contact support' } }, 'zh-TW': { ui: { help: '聯絡客服' } } }` |
|
|
58
|
+
| `networkExclude` | 不想讓 agent 費力翻找的請求——老是 404 的字型、分析用的 beacon:`['.ttf', 'WebTracks', '*beacon*', '/\\/track\\//']`。每條規則可以是不分大小寫的 URL 子字串、含 `*` 的萬用字元模式,或寫成 `/…/` 的規則運算式。會與專案策略中的規則合併。符合的請求不會出現在網路清單裡,也不會產生 `net_fail` 時間軸事件;頁面仍會記錄它們,所以 agent 可以用 `includeExcluded` 要求查看 |
|
|
59
|
+
| `lockScreen` | `false`:agent 連線期間不鎖定頁面(使用者仍可在浮動視窗中變更)。預設為 `true` |
|
|
60
|
+
| `theme` | `'auto'`(預設,跟隨頁面背景)、`'system'`、`'light'` 或 `'dark'` |
|
|
61
|
+
| `style` | 介面的顏色、字型、圓角、z-index 和位置:`{ accent: '#e11d48', radius: 12, zIndex: 1000, pill: { side: 'left' } }`。見[樣式](#樣式) |
|
|
62
|
+
| `eruda` | `true` 或 `{ dock, load, src, name }`:把 Remote Debugger 顯示為 eruda 中的一個分頁;頁面沒有 eruda 時,`load` 會載入最新版的 eruda。預設關閉;專案策略可以遠端開啟,本機選項逐欄位優先 |
|
|
63
|
+
| `record` | `false`:這個頁面不寫入工作階段記錄(已儲存的內容不會刪除)。預設為 `true`;專案策略可以對所有頁面關閉記錄 |
|
|
64
|
+
| `launchId` | 這個頁面屬於 App 的哪一次啟動,讓同一個 App 程序中的所有 WebView 把工作階段記錄歸到同一個工作階段。原生宿主在這裡傳入自己的 id(或把 `window.RWDLaunchId` 定義為字串,或回傳字串的函式);否則 SDK 會以工作階段 cookie 加上心跳來推斷這次啟動 |
|
|
65
|
+
| `nativeKey`、`deviceToken`、`nativeApproval` | 給原生宿主用:`nativeControl` / `nativeDecide` 的密鑰、由宿主保存的裝置權杖,以及改用原生介面詢問連線核准 / 操作確認 |
|
|
66
|
+
|
|
67
|
+
## 語言
|
|
68
|
+
|
|
69
|
+
內建語言:英文(`en`)、簡體中文(`zh`)、繁體中文(`zh-TW`,`zh-HK`、`zh-MO` 和 `zh-Hant` 也使用它)、法文(`fr`)、西班牙文(`es`)、俄文(`ru`)、阿拉伯文(`ar`,由右至左排版)、日文(`ja`)、韓文(`ko`)、德文(`de`)和葡萄牙文(`pt`,巴西葡萄牙文;`pt-PT` 也使用它)。
|
|
70
|
+
|
|
71
|
+
**如何決定語言。** 手機使用者的語言先看 `run({ lang })`,再依序看 `navigator.languages` 中的每一個。每個語言標記會先試它本身,再試更短的形式(`pt-BR` → `pt`,`zh-HK` → `zh-TW` → `zh`),採用第一個有語言包的語言,都沒有時使用英文。語言包中缺少的文字也以同樣的順序遞補,所以只寫一部分的語言包也沒問題。`setLang('ja')` 可在執行期間切換;`status().lang` 是目前使用的語言包。
|
|
72
|
+
|
|
73
|
+
**如何載入。** 英文和簡體中文包含在 `sdk.js` 裡。其他語言包會在頁面第一次需要時才載入(在那一刻,介面先顯示遞補語言):
|
|
74
|
+
|
|
75
|
+
- `<script>` 版本:從 `sdk.js` 同一目錄下的 `locales/<id>.js` 載入(CDN 路徑如 `…/remote-webview-debugger@0.1.0/dist/locales/ja.js`,或你的閘道的 `/v1/locales/ja.js`),失敗時再從傳給 `run()` 的閘道載入。想省下這次請求,可以自己在 `sdk.js` 之前或之後載入語言包:`<script src="…/dist/locales/ja.js"></script>`。
|
|
76
|
+
- ES module 版本:使用動態 import,所以打包工具會把每種語言拆成獨立的 chunk。若要一開始就打包進去:`import ja from 'remote-webview-debugger/locales/ja'`,然後 `run(url, key, { locales: [ja] })`。
|
|
77
|
+
|
|
78
|
+
**自訂語言包和文字。** 語言包的格式是 `{ id, name, dir?, ui?, action?, agent? }`;沒寫的部分取自它的遞補語言:
|
|
79
|
+
|
|
80
|
+
```js
|
|
81
|
+
RemoteWebviewDebugger.registerLocale({
|
|
82
|
+
id: 'it',
|
|
83
|
+
name: 'Italiano',
|
|
84
|
+
ui: { takeover: 'Prendi il controllo', end: 'Termina', help: 'Richiedi assistenza' /* … */ },
|
|
85
|
+
})
|
|
86
|
+
RemoteWebviewDebugger.run(url, key, {
|
|
87
|
+
messages: { 'zh-TW': { ui: { help: '聯絡客服' } } }, // 覆寫任何語言中的個別文字
|
|
88
|
+
})
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
`ui` 是介面文字,`action` 用來補完確認對話框中的「AI 想要…」,`agent` 是回傳給 agent 的文字。TypeScript 型別(`LocalePack`、`Messages`、`UiMessages`、`ActionMessages`、`AgentMessages`)列出了所有鍵;以 `src/locales/en.ts` 為準。
|
|
92
|
+
|
|
93
|
+
**兩種讀者。** 手機使用者使用上面決定的語言。agent 讀到的 RPC 錯誤訊息和時間軸摘要(點擊、頁面內跳轉、失敗的請求)使用閘道的語言 `welcome.agentLang`(`RWD_LANG`),語言包也以同樣方式載入。在 welcome 指定之前一律使用英文;最近一次的值會存在 localStorage `rwd:alang`,因此下一個頁面在收到 welcome 之前產生的事件也會沿用它。
|
|
94
|
+
|
|
95
|
+
因此說中文的使用者可以和說英文的 agent 搭配:對話框問的是 `AI 想要点击「提交订单」,允许吗?`,使用者拒絕時,agent 讀到的是 `The user denied this action`。
|
|
96
|
+
|
|
97
|
+
## 樣式
|
|
98
|
+
|
|
99
|
+
介面位於 `#__rwd`(在 `<body>` 之外)的 Shadow DOM 中,頁面的 CSS 只能透過下列入口影響它:
|
|
100
|
+
|
|
101
|
+
- **CSS 自訂屬性**:寫在 `:root` 或 `#__rwd` 上,例如 `:root { --rwd-accent-rgb: 225, 29, 72; --rwd-radius: 12px; }`。
|
|
102
|
+
- **`run({ style })`**:把同樣的屬性設定在 `#__rwd` 上;這裡的顏色可以使用任何 CSS 顏色寫法。
|
|
103
|
+
- **`::part()`**:其他樣式用 `#__rwd::part(bar) { … }`。可用的 part:`pill`、`window`、`bar`、`task`、`dialog`、`toast`、`button`、`cursor`、`glow`、`overlay`。內部的 class 名稱不是穩定的 API。
|
|
104
|
+
- **`theme`**:淺色或深色玻璃(預設跟隨頁面背景)。
|
|
105
|
+
|
|
106
|
+
| 屬性 | `style` 欄位 | 預設值 | 作用對象 |
|
|
107
|
+
|---|---|---|---|
|
|
108
|
+
| `--rwd-accent-rgb` | `accent` | `59, 130, 246` | 按鈕、開關、agent 的指標、醒目標示和邊緣光暈 |
|
|
109
|
+
| `--rwd-danger-rgb` | `danger` | `239, 68, 68` | 「結束」按鈕 |
|
|
110
|
+
| `--rwd-warning-rgb` | `warning` | `245, 158, 11` | 「請求協助」按鈕和「協助中」狀態;使用者接管的狀態 |
|
|
111
|
+
| `--rwd-success-rgb` | `success` | `34, 197, 94` | 線上指示點;「已複製」 |
|
|
112
|
+
| `--rwd-font` | `font` | 系統介面字型(中文、日文、韓文依語言選擇字型) | 所有文字 |
|
|
113
|
+
| `--rwd-font-mono` | `monoFont` | `ui-monospace, "SF Mono", …` | 裝置碼和標籤 |
|
|
114
|
+
| `--rwd-radius` | `radius` | `18px` | 浮動視窗;對話框比它大 2px,任務卡片和膠囊小 2px,對話框按鈕小 6px |
|
|
115
|
+
| `--rwd-button-radius` | `buttonRadius` | `999px` | 按鈕、底部控制列和提示訊息 |
|
|
116
|
+
| `--rwd-z-index` | `zIndex` | `2147483647` | 整個介面 |
|
|
117
|
+
| `--rwd-bar-bottom` | `barBottom` | `14px` | 底部控制列與底部安全區域的距離 |
|
|
118
|
+
| `--rwd-task-top` | `taskTop` | `10px` | 任務卡片與頂部安全區域的距離 |
|
|
119
|
+
| `--rwd-toast-bottom` | `toastBottom` | `84px` | 提示訊息與底部安全區域的距離 |
|
|
120
|
+
| — | `pill: { side, top }` | 右側邊緣(由右至左的語言為左側),`0.62` | 閒置膠囊在使用者拖動之前的起始位置;`top` 從 0(頂部)到 1(底部) |
|
|
121
|
+
|
|
122
|
+
顏色寫成 `r, g, b` 三元組,半透明的色調才會跟著改變(與 Bootstrap 的 `--bs-*-rgb` 慣例相同)。
|
|
123
|
+
|
|
124
|
+
## 頁面內的行為(協定 §6–§8、§11)
|
|
125
|
+
|
|
126
|
+
- 等級:RPC 會依 `welcome.level` / `control.level` 檢查(`forbidden`,「需要 … 等級」)。沒有等級的 agent(較舊的閘道)以 `interact` 處理;沒有 agent 連線時,不額外施加限制。
|
|
127
|
+
- 裝置權杖存在 localStorage `rwd:dtoken`,恢復權杖存在 sessionStorage `rwd:resume`(綁定 pageId)。關閉碼 4401:若權杖在此期間已改變(同一台裝置的另一個分頁剛取得它),立即重試一次,否則進入 `rejected`,約 10 分鐘後再試;4409:換新的 pageId 重新連線;4429:`quota`,約 5 分鐘後再試。
|
|
128
|
+
- 網址列中的 `rwd_invite` 會在 `run()` 時移除(`history.replaceState`),並隨 hello 一起送出,直到收到 welcome。
|
|
129
|
+
- `welcome.config`:`helpButton`(與 `run()` 選項取 OR)、`maskSelectors`、`maskPatterns`(快照、console、網路 URL、儲存空間的值、時間軸摘要)、`confirmSelectors`、`networkExclude`(與 `run()` 選項中的規則合併)、`sessionLog`。最近一次的設定會存在 localStorage `rwd:cfg`,在收到 welcome 之前先沿用——所以關閉了工作階段記錄的專案,下一個頁面從第一刻起就不會記錄,不必等 welcome。
|
|
130
|
+
- 快照最後會附上一段 `[Overlays]`,列出浮在頁面上的東西——彈出視窗的遮罩、強制回應視窗、底部滑出面板——包含它的標籤、一小段文字、覆蓋了多少可視區域,以及一個 ref。遮罩是 aria snapshot 中沒有對應節點的空元素,所以它拿到的是 `oN` 而不是 `eN` 形式的 ref;agent 正是靠它點擊遮罩來關閉彈出視窗。
|
|
131
|
+
- 點擊會先瞄準元素可見區域的中心,再嘗試其內部 3×3 網格上的其他點,所以按鈕上蓋了一個徽章也不會擋住點擊。所有點都被遮住時,RPC 會以 `not_interactable` 失敗,並指出遮擋的元素及其 ref。
|
|
132
|
+
- 連線核准和敏感操作的對話框(`[data-rwd-confirm]`、`confirmSelectors`,包括在表單中按 Enter 送出)位於 Shadow DOM 中,只接受受信任的點擊,並在出現 0.4 秒後才可操作。同源的多個分頁之間透過 BroadcastChannel 共享已處理的連線請求。
|
|
133
|
+
|
|
134
|
+
載入檔案時就會開始收集 console、未攔截的錯誤和網路(XHR、fetch、WebSocket、sendBeacon、EventSource),每類最多 500 筆;滿了之後先丟棄一般記錄和成功的請求。回應內容最多快取 5 MB,每筆 64 KB。所以請在 `<head>` 中盡早以同步方式載入。
|
|
135
|
+
|
|
136
|
+
## 工作階段記錄(協定 §5.4)
|
|
137
|
+
|
|
138
|
+
上述緩衝區只存在單一頁面的記憶體中。與此同時,SDK 還會把**工作階段記錄**寫進裝置上的 IndexedDB(`rwd-log`),讓 agent 即使在 bug 發生之後才連線,也能讀到完整的來龍去脈:
|
|
139
|
+
|
|
140
|
+
- 一個工作階段就是 App 的一次啟動,由同一來源的所有頁面共用:`run({ launchId })`,否則 `window.RWDLaunchId`,否則由工作階段 cookie `rwd_launch` 加上心跳推斷(cookie 工作階段超過一小時沒有動靜,就視為新的一次啟動)。`status().logSession` 是目前使用的 id。
|
|
141
|
+
- 記錄內容:頁面開啟 / 頁面內跳轉 / 前景與背景切換 / 離開,使用者的點擊、輸入、送出和捲動停止的位置,agent 的互動類呼叫和 `rwd.evaluate`,控制狀態變化,所有等級的 console,以及每個完成的請求——失敗的請求還會附上遮蔽過的標頭和內容(各 64 KB)。不論閘道連不連得上都會記錄。被遮蔽的欄位存成 `••••`,`maskPatterns` 在寫入記錄時就會套用。
|
|
142
|
+
- 保留量:最近 3 次啟動,每次 10 MB;超過時先丟棄一般記錄和成功的請求。儲存空間配額不足 64 MB 時只保留目前這次啟動;沒有 IndexedDB 時完全不記錄。
|
|
143
|
+
- agent 以 `rwd.log`(`observe`)和 `rwd.logDetail`(`advanced`,用於標頭和內容)讀取。除此之外,記錄不會離開裝置。
|
|
144
|
+
- 關閉方式:單一頁面用 `run({ record: false })`,整個專案用 `policy.sessionLog: false`——後者還會刪除這個來源已經儲存的內容。
|
|
145
|
+
|
|
146
|
+
## 結構
|
|
147
|
+
|
|
148
|
+
- `src/index.ts`:公開 API、工作階段、RPC 分派、控制狀態。
|
|
149
|
+
- `src/transport.ts`:頁面 WebSocket(原生建構函式)、心跳逾時、指數退避,以及連續 3 次失敗後每 60 秒 `POST /v1/presence`。
|
|
150
|
+
- `src/ids.ts`:deviceId / pageId / openerId。
|
|
151
|
+
- `src/launch.ts`:判斷這個頁面屬於 App 的哪一次啟動(`launchId`、`window.RWDLaunchId`、`rwd_launch` cookie)。
|
|
152
|
+
- `src/pageWatch.ts`:`page` 訊息(url、標題、可見性)和時間軸事件(頁面內跳轉、使用者的點擊)。
|
|
153
|
+
- `src/ui.ts`、`src/lock.ts`:Shadow DOM 中的膠囊、「請求協助」按鈕、鎖定覆蓋層、光暈、操作醒目標示和對話框;agent 連線期間擋下使用者受信任的輸入,並顯示已連線的時間。
|
|
154
|
+
- `src/approval.ts`:連線核准和敏感操作確認(頁面對話框,或透過 `nativeDecide` 交給原生宿主)。
|
|
155
|
+
- `src/i18n.ts`、`src/locales/`、`src/localeLoader*.ts`:語言包(每種語言的介面、操作描述和 agent 文字)、語言的決定與遞補、按需載入;`scripts/locales.mjs` 產生 `dist/locales/<id>.js`。
|
|
156
|
+
- 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`。
|
|
157
|
+
|
|
158
|
+
## 建置與測試
|
|
159
|
+
|
|
160
|
+
```sh
|
|
161
|
+
cd sdk
|
|
162
|
+
npm ci
|
|
163
|
+
npm run build # dist/sdk.js(IIFE,全域 RemoteWebviewDebugger,ES2017),dist/index.mjs + index.d.ts(ES module)
|
|
164
|
+
npm run typecheck
|
|
165
|
+
npm test # Playwright:WebKit(iPhone 15)+ Chromium(Pixel 7)
|
|
166
|
+
npm run gateway # 本機模擬閘道:http://localhost:8787/demo/platform/?key=rwd_pk_demo
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
測試使用 `test/mock-gateway.mjs`,它實作了協定的頁面端(welcome、控制狀態、RPC、presence;權杖、關閉碼、等級、遠端設定、`agentLang`、連線核准),並提供 `/v1/sdk.js`、`/v1/locales/` 下的語言包和展示商店。它本身不檢查等級,好讓測試驗證頁面自己的強制檢查。`test/e2e/` 中的測試規格預設以 `zh-CN` 瀏覽器和 `agentLang: 'zh'` 執行,除非個別測試另有指定;英文由 `i18n.spec.ts` 涵蓋,其他語言包、遞補、自訂語言包、樣式和由右至左的版面由 `locales.spec.ts` 涵蓋。共存測試會從 jsdelivr 載入 eruda 3.4.3 和 PageSpy 2.4.0,因此需要能連上網際網路。
|