dsh-site-bridge 0.0.0-stage → 0.3.3

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 k4 (kesha4)
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/README.md CHANGED
@@ -1,3 +1,214 @@
1
- # Temporary Holding Version
1
+ # dsh-site-bridge
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
+ **English** · [Русский](README.ru.md) · [中文](README.zh-CN.md)
4
+
5
+ **Hands and eyes for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) in your own browser.**
6
+ Capture a page's markup and its BFF traffic into files an agent can read, drive the page with trusted
7
+ input, and verify the extension you just wrote — live, in the same tab.
8
+
9
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
10
+
11
+ Two halves, one bridge:
12
+
13
+ | Half | What it does |
14
+ |---|---|
15
+ | **DSH plugin** (`lib/`) | Owns the loopback bridge and exposes eight tools to the model. Writes the snapshot bundle into the session workspace. |
16
+ | **MV3 extension** (`extension/`) | Lives in your Chrome/Edge profile, works with your cookies, captures markup and network over CDP, drives the page with real input events. Localized: English, Русский, 中文. |
17
+
18
+ Neither half is useful alone: the plugin holds the model-facing surface and the files, the extension
19
+ holds the browser session and the fidelity.
20
+
21
+ ---
22
+
23
+ ## Why
24
+
25
+ Writing an extension that embeds into someone else's site is mostly archaeology: what the markup is
26
+ actually made of, which selector survives the next deploy, and which BFF endpoint this button calls
27
+ with which payload. By hand that means DevTools, copy-paste and guesses.
28
+
29
+ This project turns the archaeology into a capture:
30
+
31
+ ```
32
+ site_capture → captures/<host>/<timestamp>/
33
+ dom.html serialized markup of the captured scope
34
+ dom.json element tree: boxes, attributes, selector candidates
35
+ styles.json computed styles of key nodes + CSS custom properties
36
+ selectors.md shortlist of stable selectors, with why each is stable
37
+ network.jsonl every captured xhr/fetch, one JSON object per line
38
+ api.md endpoints grouped by method+path, with request/response contracts
39
+ screenshot.png optional full-page render
40
+ ```
41
+
42
+ The agent reads those files like any other source and writes the extension. Then `site_inject` runs
43
+ that code in the live page, so "it should work" becomes "it works here".
44
+
45
+ ## Features
46
+
47
+ - **Markup** — element tree with boxes and visibility, computed styles of interactive nodes, CSS
48
+ variables of the theme, and selector candidates ranked by stability (`data-testid` > `id` > `name` >
49
+ `aria-label` > class path > positional path), each checked for uniqueness against the live DOM.
50
+ - **BFF traffic** — `xhr`/`fetch` with method, status, request headers, request body, response
51
+ headers and response body, scoped to the inspected site.
52
+ - **Trusted input** — clicks, typing, select, key presses, scrolling and file uploads through CDP
53
+ `Input.*` / `DOM.setFileInputFiles`: events the page cannot tell apart from a human's.
54
+ - **Virtual cursor** — a DOM overlay that moves to the target plus a real mouse event at those
55
+ coordinates, for controls that ignore synthetic `click()`.
56
+ - **Deep selectors** — `host >>> inner` resolves through open shadow roots and same-origin iframes;
57
+ click coordinates are corrected for frame offsets.
58
+ - **Works in background tabs** — reading, clicking, typing and screenshots work while the tab is not
59
+ focused (Chrome throttles only timers and animation there).
60
+ - **No stored credentials** — see [Security](#security).
61
+
62
+ ## Requirements
63
+
64
+ - DeepSeek Harness with the `dsh` CLI and a profile (Desktop or Web).
65
+ - Chrome or Edge 125+ (the extension uses `chrome.debugger` and MV3 service workers).
66
+ - Node.js 22+ for the tooling in `tools/` and for the tests.
67
+
68
+ ## Install
69
+
70
+ ### 1. Host half
71
+
72
+ ```powershell
73
+ # from the repository root
74
+ powershell -NoProfile -ExecutionPolicy Bypass -File scripts\install.ps1
75
+ ```
76
+
77
+ The script links the plugin into your profile, adds it to `dsh.profile.bundles`, and stages the
78
+ extension into `%USERPROFILE%\.dsh\site-bridge\extension`. Manual equivalent:
79
+
80
+ ```powershell
81
+ dsh plugin --profile desktop add <path-to-this-repo>
82
+ ```
83
+
84
+ Add `"dsh-site-bridge"` to `dsh.profile.bundles` in `%USERPROFILE%\.dsh\profiles\desktop\package.json`
85
+ if the CLI did not, then restart DSH — the host half loads at profile start.
86
+
87
+ ### 2. Browser half
88
+
89
+ 1. `chrome://extensions` → enable **Developer mode** → **Load unpacked** →
90
+ `%USERPROFILE%\.dsh\site-bridge\extension`.
91
+ 2. Open the popup. It shows the port and the path to `bridge.json` (the same is printed by
92
+ `site_status`).
93
+ 3. Click **Import bridge.json** (`Импорт bridge.json` / `导入 bridge.json`) and pick that file, or
94
+ paste the token — it saves itself, no button.
95
+ 4. Open the page you want to work on and click **Connect this tab**.
96
+
97
+ Chrome shows the "started debugging this browser" banner: that is the CDP session everything runs
98
+ on. It disappears when you press **Disconnect**.
99
+
100
+ The popup UI follows the browser language: English, Russian and Simplified Chinese ship in
101
+ `extension/_locales`.
102
+
103
+ ## Usage
104
+
105
+ ```text
106
+ site_status → bridge state, pairing path, current tab
107
+ site_tabs {action:"list"} → tabs; {action:"claim", tabId} binds one
108
+ site_network {action:"start"} → begin recording, then act on the page
109
+ site_capture {screenshot:true} → write the bundle into the workspace
110
+ site_act {steps:"[...]"} → click/fill/type/select/press/scroll/upload/wait/eval
111
+ site_cursor {x,y,click:true} → virtual cursor, real mouse event
112
+ site_shot {fullPage:true} → screenshot to captures/shots/*.png
113
+ site_inject {js:"..."} → run your script in the page to verify it
114
+ ```
115
+
116
+ A typical loop for writing a page-embedded extension:
117
+
118
+ 1. `site_tabs` → bind the page.
119
+ 2. `site_capture` → read `selectors.md`, `styles.json`, `api.md`.
120
+ 3. Write the userscript / content script in your own repository.
121
+ 4. `site_inject` with the script body → check that the anchor is found and the BFF call passes with
122
+ the browser's cookies.
123
+ 5. Iterate on the selectors the capture marked as fragile.
124
+
125
+ ## Architecture
126
+
127
+ ```
128
+ ┌──────────────────────────── DSH host ────────────────────────────┐
129
+ │ tools (site_*) ──► bridge: 127.0.0.1, token, long-poll queue │
130
+ │ redaction on ingest ──► in-memory buffer ──► artifacts writer │
131
+ └───────────────────────────────┬───────────────────────────────────┘
132
+ │ HTTP long-poll (no websocket)
133
+ ┌───────────────────────────────┴───────────────────────────────────┐
134
+ │ MV3 service worker: chrome.debugger ──► CDP │
135
+ │ Network domain → requests with headers and bodies │
136
+ │ Input domain → trusted mouse/keyboard, file uploads │
137
+ │ Runtime domain → DOM snapshot, selectors, injected scripts │
138
+ │ popup: pairing, target tab, capture toggle (ru / en / zh) │
139
+ └────────────────────────────────────────────────────────────────────┘
140
+ ```
141
+
142
+ The wire protocol is small and documented in [docs/protocol.md](docs/protocol.md).
143
+
144
+ ## Security
145
+
146
+ - **Credentials never leave the browser.** The extension does not read cookies or tokens: the page
147
+ makes its own requests and the browser attaches them. The bridge sees them in flight, but values of
148
+ `cookie`, `authorization`, `x-csrf-token`, `x-api-key`, `set-cookie` and similar headers,
149
+ credential-shaped query parameters and credential-shaped JSON/form fields are replaced with
150
+ `«redacted»` **on ingest** — before anything is stored.
151
+ - **Nothing credential-shaped is written to disk.** Header *names* are kept, because "this endpoint
152
+ needs `x-csrf-token`" is part of the contract; the value is not.
153
+ - **Capture is site-scoped.** Only the inspected site's own `xhr`/`fetch` (same host, same
154
+ registrable domain, or third-party JSON) is recorded. Telemetry and security agents that POST a full
155
+ page dump to their servers are filtered out — otherwise page source with inline tokens would land in
156
+ the bundle.
157
+ - **Loopback only.** The bridge binds `127.0.0.1` and rejects any request without the 32-byte pairing
158
+ token. `site_status` prints the path to the token file, not the token.
159
+ - **Residual risk.** Header values and bodies still pass through the DSH process in memory. If a site
160
+ puts a secret under an unusual key, redaction will not recognise it. Treat a capture bundle as
161
+ sensitive, exactly like any traffic dump.
162
+
163
+ ## Limitations
164
+
165
+ Cross-origin iframes, closed shadow roots and body size caps are analysed in
166
+ [docs/limitations.md](docs/limitations.md) — with the exact code location, the plan to remove each and
167
+ the expected cost.
168
+
169
+ ## Tests
170
+
171
+ ```powershell
172
+ node test\redact.test.mjs # 8 — redaction rules
173
+ node test\bridge.test.mjs # 8 — wire protocol, token, liveness
174
+ node test\artifacts.test.mjs # 5 — bundle layout and large-body spill
175
+ node test\plugin.test.mjs # 2 — tool registration and the full tool path
176
+ node test\e2e.mjs # 31 — real Chrome through the wire protocol
177
+ node test\tools-chrome.mjs # 19 — real Chrome through the tools
178
+ ```
179
+
180
+ `e2e.mjs` and `tools-chrome.mjs` launch a throwaway Chrome, load the extension with
181
+ `Extensions.loadUnpacked`, and exercise capture, trusted input, the virtual cursor, upload, injection
182
+ and screenshots against a local page with a working BFF endpoint.
183
+
184
+ ## Repository layout
185
+
186
+ ```
187
+ lib/ DSH plugin: tools, bridge, redaction, artifact writer
188
+ extension/ MV3: service worker (CDP), popup, icons, locales
189
+ docs/ protocol, limitations, detailed guide
190
+ scripts/ install.ps1
191
+ tools/ icon rasteriser, popup/icon previews, asar reader (dev only)
192
+ test/ unit, integration, end-to-end
193
+ ```
194
+
195
+
196
+
197
+ MIT — see [LICENSE](LICENSE).
198
+
199
+ ## Versioning
200
+
201
+ The version lives in `package.json` and is mirrored into `extension/manifest.json`; `CHANGELOG.md`
202
+ holds the history. Every commit that changes shipped code bumps the version, docs-only commits do not:
203
+
204
+ ```powershell
205
+ node tools\version.mjs check # fail if the three files disagree
206
+ node tools\version.mjs bump minor # bump package.json + manifest.json + CHANGELOG stub
207
+ ```
208
+
209
+ Releases are tagged `vX.Y.Z`. Versions below 0.3.0 were assigned retroactively from the commit
210
+ history, so an old tag may pin commit content that still carried the 0.1.0 stamp.
211
+
212
+ ## License
213
+
214
+ MIT — see [LICENSE](LICENSE).
package/README.ru.md ADDED
@@ -0,0 +1,213 @@
1
+ # dsh-site-bridge
2
+
3
+ [English](README.md) · **Русский** · [中文](README.zh-CN.md)
4
+
5
+ **Глаза и руки [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) в вашем браузере.**
6
+ Снимает разметку страницы и её BFF-трафик в файлы, понятные агенту, управляет страницей доверенным
7
+ вводом и проверяет расширение, которое вы только что написали, — сразу в той же вкладке.
8
+
9
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
10
+
11
+ Две половины, один мост:
12
+
13
+ | Половина | Что делает |
14
+ |---|---|
15
+ | **Плагин DSH** (`lib/`) | Держит loopback-мост и даёт модели восемь инструментов. Пишет пакет снимка в рабочее пространство сессии. |
16
+ | **Расширение MV3** (`extension/`) | Живёт в вашем профиле Chrome/Edge, работает с вашими куками, снимает разметку и сеть через CDP, управляет страницей настоящими событиями ввода. Локализовано: English, Русский, 中文. |
17
+
18
+ По отдельности ни одна половина не решает задачу: плагин держит модель и файлы, расширение — сессию
19
+ браузера и точность.
20
+
21
+ ---
22
+
23
+ ## Зачем
24
+
25
+ Писать расширение, которое встраивается в чужой сайт, — это в основном археология: из чего на самом
26
+ деле сделана разметка, какой селектор переживёт следующий деплой и какой BFF-эндпоинт вызывает эта
27
+ кнопка и с каким телом. Вручную это DevTools, копипаст и догадки.
28
+
29
+ Проект превращает археологию в снимок:
30
+
31
+ ```
32
+ site_capture → captures/<host>/<timestamp>/
33
+ dom.html сериализованная разметка снятой области
34
+ dom.json дерево элементов: боксы, атрибуты, кандидаты селекторов
35
+ styles.json computed-стили ключевых узлов + CSS-переменные темы
36
+ selectors.md шорт-лист устойчивых селекторов с объяснением, почему они устойчивы
37
+ network.jsonl каждый снятый xhr/fetch, один JSON на строку
38
+ api.md эндпоинты, сгруппированные по method+path, с контрактами запроса/ответа
39
+ screenshot.png опциональный рендер всей страницы
40
+ ```
41
+
42
+ Агент читает эти файлы как обычный исходник и пишет расширение. Затем `site_inject` выполняет этот
43
+ код в живой странице, и «должно работать» превращается в «работает здесь».
44
+
45
+ ## Возможности
46
+
47
+ - **Разметка** — дерево элементов с боксами и видимостью, computed-стили интерактивных узлов,
48
+ CSS-переменные темы и кандидаты селекторов по устойчивости (`data-testid` > `id` > `name` >
49
+ `aria-label` > путь по классам > позиционный путь), каждый проверен на уникальность по живому DOM.
50
+ - **BFF-трафик** — `xhr`/`fetch` с методом, статусом, заголовками запроса, телом запроса,
51
+ заголовками ответа и телом ответа, только для проверяемого сайта.
52
+ - **Доверенный ввод** — клики, ввод, select, клавиши, прокрутка и загрузка файлов через CDP
53
+ `Input.*` / `DOM.setFileInputFiles`: события, которые страница не отличает от действий человека.
54
+ - **Виртуальный курсор** — DOM-оверлей, который едет к цели, плюс настоящее событие мыши в этих
55
+ координатах — для контролов, игнорирующих программный `click()`.
56
+ - **Глубокие селекторы** — `host >>> inner` проходит сквозь открытые shadow-корни и same-origin
57
+ iframe; координаты клика пересчитываются с учётом смещения фрейма.
58
+ - **Работает в фоновых вкладках** — чтение, клики, ввод и скриншоты работают, когда вкладка не в
59
+ фокусе (Chrome тротлит там только таймеры и анимации).
60
+ - **Ничего не хранится из авторизации** — см. [Безопасность](#безопасность).
61
+
62
+ ## Требования
63
+
64
+ - DeepSeek Harness с CLI `dsh` и профилем (Desktop или Web).
65
+ - Chrome или Edge 125+ (расширение использует `chrome.debugger` и MV3 service worker).
66
+ - Node.js 22+ для инструментов в `tools/` и для тестов.
67
+
68
+ ## Установка
69
+
70
+ ### 1. Host-половина
71
+
72
+ ```powershell
73
+ # из корня репозитория
74
+ powershell -NoProfile -ExecutionPolicy Bypass -File scripts\install.ps1
75
+ ```
76
+
77
+ Скрипт линкует плагин в ваш профиль, добавляет его в `dsh.profile.bundles` и раскладывает расширение
78
+ в `%USERPROFILE%\.dsh\site-bridge\extension`. Вручную то же самое:
79
+
80
+ ```powershell
81
+ dsh plugin --profile desktop add <путь-к-этому-репозиторию>
82
+ ```
83
+
84
+ Если CLI не сделал этого сам, добавьте `"dsh-site-bridge"` в `dsh.profile.bundles` в
85
+ `%USERPROFILE%\.dsh\profiles\desktop\package.json`, затем перезапустите DSH — host-половина
86
+ загружается при старте профиля.
87
+
88
+ ### 2. Браузерная половина
89
+
90
+ 1. `chrome://extensions` → включите **Режим разработчика** → **Загрузить распакованное** →
91
+ `%USERPROFILE%\.dsh\site-bridge\extension`.
92
+ 2. Откройте попап. Он покажет порт и путь к `bridge.json` (то же печатает `site_status`).
93
+ 3. Нажмите **Импорт bridge.json** и выберите этот файл — или вставьте токен: он сохранится сам,
94
+ кнопки нет.
95
+ 4. Откройте нужную страницу и нажмите **Подключить эту вкладку**.
96
+
97
+ Chrome покажет плашку «началась отладка этого браузера»: это и есть CDP-сессия, на которой всё
98
+ работает. Она исчезнет после **Отключить**.
99
+
100
+ Интерфейс попапа следует языку браузера: английский, русский и упрощённый китайский лежат в
101
+ `extension/_locales`.
102
+
103
+ ## Использование
104
+
105
+ ```text
106
+ site_status → состояние моста, путь к токену, текущая вкладка
107
+ site_tabs {action:"list"} → вкладки; {action:"claim", tabId} привязывает одну
108
+ site_network {action:"start"} → начать запись, затем действуйте на странице
109
+ site_capture {screenshot:true} → записать пакет в рабочее пространство
110
+ site_act {steps:"[...]"} → click/fill/type/select/press/scroll/upload/wait/eval
111
+ site_cursor {x,y,click:true} → виртуальный курсор, настоящее событие мыши
112
+ site_shot {fullPage:true} → скриншот в captures/shots/*.png
113
+ site_inject {js:"..."} → выполнить ваш скрипт в странице для проверки
114
+ ```
115
+
116
+ Типичный цикл написания встраиваемого расширения:
117
+
118
+ 1. `site_tabs` → привязать страницу.
119
+ 2. `site_capture` → прочитать `selectors.md`, `styles.json`, `api.md`.
120
+ 3. Написать userscript / content script в своём репозитории.
121
+ 4. `site_inject` с телом скрипта → проверить, что якорь найден и вызов BFF проходит с куками
122
+ браузера.
123
+ 5. Доработать селекторы, которые снимок пометил как хрупкие.
124
+
125
+ ## Архитектура
126
+
127
+ ```
128
+ ┌──────────────────────────── DSH host ────────────────────────────┐
129
+ │ инструменты (site_*) ──► мост: 127.0.0.1, токен, очередь long-poll│
130
+ │ редакция на приёме ──► буфер в памяти ──► запись артефактов │
131
+ └───────────────────────────────┬───────────────────────────────────┘
132
+ │ HTTP long-poll (без websocket)
133
+ ┌───────────────────────────────┴───────────────────────────────────┐
134
+ │ MV3 service worker: chrome.debugger ──► CDP │
135
+ │ Network domain → запросы с заголовками и телами │
136
+ │ Input domain → доверенные мышь/клавиатура, загрузка файлов │
137
+ │ Runtime domain → снимок DOM, селекторы, внедрённые скрипты │
138
+ │ popup: подключение, вкладка, запись (ru / en / zh) │
139
+ └────────────────────────────────────────────────────────────────────┘
140
+ ```
141
+
142
+ Протокол небольшой и описан в [docs/protocol.md](docs/protocol.md).
143
+
144
+ ## Безопасность
145
+
146
+ - **Авторизация не покидает браузер.** Расширение не читает куки и токены: запросы делает сама
147
+ страница, браузер прикладывает их. Мост видит их в полёте, но значения `cookie`, `authorization`,
148
+ `x-csrf-token`, `x-api-key`, `set-cookie` и подобных заголовков, credential-подобные query-параметры
149
+ и поля JSON/форм заменяются на `«redacted»` **на приёме** — до того, как что-то сохранено.
150
+ - **На диск не попадает ничего авторизационного.** Имена заголовков сохраняются, потому что «этому
151
+ эндпоинту нужен `x-csrf-token`» — часть контракта; значения — нет.
152
+ - **Захват ограничен сайтом.** Пишутся только свои `xhr`/`fetch` проверяемого сайта (тот же хост, тот
153
+ же домен второго уровня или сторонний JSON). Телеметрия и агенты безопасности, которые отправляют
154
+ дамп HTML страницы на свой сервер, отфильтровываются — иначе исходник с инлайн-токенами попадал бы
155
+ в пакет.
156
+ - **Только loopback.** Мост слушает `127.0.0.1` и отклоняет любой запрос без 32-байтного токена
157
+ сопряжения. `site_status` печатает путь к файлу с токеном, а не сам токен.
158
+ - **Остаточный риск.** Значения заголовков и тела всё равно проходят через процесс DSH в памяти. Если
159
+ сайт кладёт секрет под нестандартным ключом, редакция его не узнает. Считайте пакет снимка
160
+ чувствительным, как и любой дамп трафика.
161
+
162
+ ## Ограничения
163
+
164
+ Кросс-доменные iframe, закрытые shadow-корни и лимиты тела разобраны в
165
+ [docs/limitations.md](docs/limitations.md) — с точным местом в коде, планом снятия каждого и оценкой
166
+ стоимости.
167
+
168
+ ## Тесты
169
+
170
+ ```powershell
171
+ node test\redact.test.mjs # 8 — правила редакции
172
+ node test\bridge.test.mjs # 8 — протокол, токен, живость
173
+ node test\artifacts.test.mjs # 5 — раскладка пакета и вынос больших тел
174
+ node test\plugin.test.mjs # 2 — регистрация инструментов и сквозной путь
175
+ node test\e2e.mjs # 31 — реальный Chrome через протокол
176
+ node test\tools-chrome.mjs # 19 — реальный Chrome через инструменты
177
+ ```
178
+
179
+ `e2e.mjs` и `tools-chrome.mjs` поднимают одноразовый Chrome, загружают расширение через
180
+ `Extensions.loadUnpacked` и проверяют захват, доверенный ввод, виртуальный курсор, загрузку файла,
181
+ инъекцию и скриншоты на локальной странице с рабочим BFF-эндпоинтом.
182
+
183
+ ## Структура репозитория
184
+
185
+ ```
186
+ lib/ плагин DSH: инструменты, мост, редакция, запись артефактов
187
+ extension/ MV3: service worker (CDP), popup, иконки, локали
188
+ docs/ протокол, ограничения, подробное руководство
189
+ scripts/ install.ps1
190
+ tools/ растеризация иконок, превью попапа/иконок, чтение asar (только для разработки)
191
+ test/ юнит, интеграционные, end-to-end
192
+ ```
193
+
194
+
195
+
196
+ MIT — см. [LICENSE](LICENSE).
197
+
198
+ ## Версионирование
199
+
200
+ Версия живёт в `package.json` и зеркалится в `extension/manifest.json`; история — в `CHANGELOG.md`.
201
+ Каждый коммит, меняющий поставляемый код, поднимает версию; коммиты только с документацией — нет:
202
+
203
+ ```powershell
204
+ node tools\version.mjs check # упадёт, если три файла расходятся
205
+ node tools\version.mjs bump minor # поднимет package.json + manifest.json + заготовку в CHANGELOG
206
+ ```
207
+
208
+ Релизы помечаются тегами `vX.Y.Z`. Версии ниже 0.3.0 проставлены задним числом по истории коммитов,
209
+ поэтому старый тег может указывать на содержимое, где ещё стояла 0.1.0.
210
+
211
+ ## Лицензия
212
+
213
+ MIT — см. [LICENSE](LICENSE).
@@ -0,0 +1,179 @@
1
+ # dsh-site-bridge
2
+
3
+ [English](README.md) · [Русский](README.ru.md) · **中文**
4
+
5
+ **把 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 的眼睛和双手放进你自己的浏览器。**
6
+ 把页面的结构与其 BFF 流量抓取成智能体可读的文件,用可信输入驱动页面,并在同一个标签页里验证你刚写好的扩展。
7
+
8
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
9
+
10
+ 两部分,一座桥:
11
+
12
+ | 部分 | 作用 |
13
+ |---|---|
14
+ | **DSH 插件**(`lib/`) | 维护本机回环桥接,向模型提供八个工具,并把快照包写入会话工作区。 |
15
+ | **MV3 扩展**(`extension/`) | 运行在你的 Chrome/Edge 配置中,使用你的 Cookie,通过 CDP 抓取结构与网络,用真实输入事件驱动页面。已本地化:English、Русский、中文。 |
16
+
17
+ 两者缺一不可:插件承载面向模型的接口与文件,扩展承载浏览器会话与保真度。
18
+
19
+ ---
20
+
21
+ ## 为什么需要它
22
+
23
+ 写一个嵌入别人网站的扩展,本质上是一场考古:页面结构到底是什么、哪个选择器能活过下次发版、这个按钮调用了哪个 BFF 接口、请求体长什么样。手工做这些就是 DevTools、复制粘贴和猜测。
24
+
25
+ 这个项目把考古变成一次抓取:
26
+
27
+ ```
28
+ site_capture → captures/<host>/<timestamp>/
29
+ dom.html 被抓区域序列化后的结构
30
+ dom.json 元素树:盒子、属性、选择器候选
31
+ styles.json 关键节点的计算样式 + 主题 CSS 变量
32
+ selectors.md 稳定选择器清单,并说明每个为什么稳定
33
+ network.jsonl 每个抓到的 xhr/fetch,每行一个 JSON
34
+ api.md 按 method+path 分组的接口,以及请求/响应契约
35
+ screenshot.png 可选的整页截图
36
+ ```
37
+
38
+ 智能体像读普通源码一样读这些文件,然后写扩展。接着 `site_inject` 把这段代码跑在活页面上,"应该能用" 就变成 "在这里能用"。
39
+
40
+ ## 功能
41
+
42
+ - **结构** —— 带盒子与可见性的元素树、交互节点的计算样式、主题 CSS 变量,以及按稳定性排序的选择器候选(`data-testid` > `id` > `name` > `aria-label` > class 路径 > 位置路径),每个都在活的 DOM 上检查过唯一性。
43
+ - **BFF 流量** —— `xhr`/`fetch` 的方法、状态、请求头、请求体、响应头、响应体,且只针对被检查的站点。
44
+ - **可信输入** —— 通过 CDP `Input.*` / `DOM.setFileInputFiles` 完成点击、输入、下拉、按键、滚动和文件上传:这些事件页面无法与真人操作区分。
45
+ - **虚拟光标** —— 一个移动到目标的 DOM 覆盖层,并在该坐标发出真实鼠标事件,用于忽略合成 `click()` 的控件。
46
+ - **深层选择器** —— `host >>> inner` 可以穿过开放的 shadow root 与同源 iframe;点击坐标会按 iframe 偏移校正。
47
+ - **后台标签页可用** —— 标签页不在前台时,读取、点击、输入和截图仍然可用(Chrome 只在后台限制定时器与动画)。
48
+ - **不保存任何凭据** —— 见[安全](#安全)。
49
+
50
+ ## 环境要求
51
+
52
+ - 带 `dsh` CLI 与配置档(Desktop 或 Web)的 DeepSeek Harness。
53
+ - Chrome 或 Edge 125+(扩展依赖 `chrome.debugger` 与 MV3 service worker)。
54
+ - Node.js 22+,用于 `tools/` 里的工具和测试。
55
+
56
+ ## 安装
57
+
58
+ ### 1. 宿主部分
59
+
60
+ ```powershell
61
+ # 在仓库根目录
62
+ powershell -NoProfile -ExecutionPolicy Bypass -File scripts\install.ps1
63
+ ```
64
+
65
+ 脚本会把插件链接进你的配置档、写入 `dsh.profile.bundles`,并把扩展复制到 `%USERPROFILE%\.dsh\site-bridge\extension`。等价的手工步骤:
66
+
67
+ ```powershell
68
+ dsh plugin --profile desktop add <本仓库路径>
69
+ ```
70
+
71
+ 如果 CLI 没写入,请把 `"dsh-site-bridge"` 加到 `%USERPROFILE%\.dsh\profiles\desktop\package.json` 的 `dsh.profile.bundles` 中,然后重启 DSH —— 宿主部分在配置档启动时加载。
72
+
73
+ ### 2. 浏览器部分
74
+
75
+ 1. `chrome://extensions` → 打开**开发者模式** → **加载已解压的扩展程序** → `%USERPROFILE%\.dsh\site-bridge\extension`。
76
+ 2. 打开扩展弹窗。它会显示端口和 `bridge.json` 的路径(`site_status` 也会打印同样内容)。
77
+ 3. 点击 **导入 bridge.json**(Import bridge.json)并选择该文件,或直接粘贴令牌 —— 会自动保存,无需按钮。
78
+ 4. 打开要操作的页面,点击 **连接此标签页**。
79
+
80
+ Chrome 会显示「已开始调试此浏览器」提示条:这正是全部功能所依赖的 CDP 会话。点击**断开**后消失。
81
+
82
+ 弹窗界面跟随浏览器语言:英语、俄语、简体中文位于 `extension/_locales`。
83
+
84
+ ## 用法
85
+
86
+ ```text
87
+ site_status → 网桥状态、令牌路径、当前标签页
88
+ site_tabs {action:"list"} → 标签页列表;{action:"claim", tabId} 绑定其一
89
+ site_network {action:"start"} → 开始录制,然后在页面上操作
90
+ site_capture {screenshot:true} → 把快照包写入工作区
91
+ site_act {steps:"[...]"} → click/fill/type/select/press/scroll/upload/wait/eval
92
+ site_cursor {x,y,click:true} → 虚拟光标,真实鼠标事件
93
+ site_shot {fullPage:true} → 截图到 captures/shots/*.png
94
+ site_inject {js:"..."} → 在页面中执行你的脚本以验证
95
+ ```
96
+
97
+ 编写页内扩展的典型循环:
98
+
99
+ 1. `site_tabs` → 绑定页面。
100
+ 2. `site_capture` → 阅读 `selectors.md`、`styles.json`、`api.md`。
101
+ 3. 在你自己的仓库里写 userscript / content script。
102
+ 4. 用 `site_inject` 注入脚本体 → 确认锚点能找到、BFF 调用能带着浏览器 Cookie 通过。
103
+ 5. 针对快照标记为脆弱的选择器继续调整。
104
+
105
+ ## 架构
106
+
107
+ ```
108
+ ┌──────────────────────────── DSH 宿主 ────────────────────────────┐
109
+ │ tools (site_*) ──► 网桥:127.0.0.1、令牌、long-poll 队列 │
110
+ │ 写入时脱敏 ──► 内存缓冲 ──► 快照包写入 │
111
+ └───────────────────────────────┬───────────────────────────────────┘
112
+ │ HTTP long-poll(不使用 websocket)
113
+ ┌───────────────────────────────┴───────────────────────────────────┐
114
+ │ MV3 service worker: chrome.debugger ──► CDP │
115
+ │ Network domain → 带请求头与请求体的请求 │
116
+ │ Input domain → 可信鼠标/键盘、文件上传 │
117
+ │ Runtime domain → DOM 快照、选择器、注入脚本 │
118
+ │ popup:配对、目标标签页、录制开关(ru / en / zh) │
119
+ └────────────────────────────────────────────────────────────────────┘
120
+ ```
121
+
122
+ 协议很小,详见 [docs/protocol.md](docs/protocol.md)。
123
+
124
+ ## 安全
125
+
126
+ - **凭据不离开浏览器。** 扩展不读取 Cookie 或令牌:页面自己发请求,浏览器自动附带。网桥能看到传输中的值,但 `cookie`、`authorization`、`x-csrf-token`、`x-api-key`、`set-cookie` 等请求头的值、形似凭据的查询参数与 JSON/表单字段,都会在**接收时**替换为 `«redacted»` —— 早于任何保存动作。
127
+ - **磁盘上不写任何凭据类内容。** 请求头的*名字*会保留,因为「这个接口需要 `x-csrf-token`」属于契约的一部分;值不会。
128
+ - **录制限定站点。** 只记录被检查站点自身的 `xhr`/`fetch`(同主机、同可注册域,或第三方 JSON)。把整页 HTML 转储到自己服务器的遥测与安全代理会被过滤掉 —— 否则含内联令牌的页面源码就会落进快照包。
129
+ - **仅回环。** 网桥只绑定 `127.0.0.1`,任何没有 32 字节配对令牌的请求都会被拒绝。`site_status` 打印的是令牌文件路径,而不是令牌本身。
130
+ - **残余风险。** 请求头与请求体仍会经过 DSH 进程的内存。如果站点把密钥放在非常规字段名下,脱敏无法识别。请像对待任何流量转储一样,把快照包视为敏感数据。
131
+
132
+ ## 限制
133
+
134
+ 跨域 iframe、封闭的 shadow root 与响应体大小上限在 [docs/limitations.md](docs/limitations.md) 中逐条分析 —— 包含代码位置、消除方案与预估成本。
135
+
136
+ ## 测试
137
+
138
+ ```powershell
139
+ node test\redact.test.mjs # 8 —— 脱敏规则
140
+ node test\bridge.test.mjs # 8 —— 协议、令牌、在线判定
141
+ node test\artifacts.test.mjs # 5 —— 快照包布局与大体积响应体外置
142
+ node test\plugin.test.mjs # 2 —— 工具注册与完整工具路径
143
+ node test\e2e.mjs # 31 —— 真实 Chrome,走协议层
144
+ node test\tools-chrome.mjs # 19 —— 真实 Chrome,走工具层
145
+ ```
146
+
147
+ `e2e.mjs` 与 `tools-chrome.mjs` 会启动一次性 Chrome,通过 `Extensions.loadUnpacked` 加载扩展,并针对带真实 BFF 接口的本地页面验证抓取、可信输入、虚拟光标、文件上传、注入与截图。
148
+
149
+ ## 仓库结构
150
+
151
+ ```
152
+ lib/ DSH 插件:工具、网桥、脱敏、快照包写入
153
+ extension/ MV3:service worker(CDP)、弹窗、图标、多语言
154
+ docs/ 协议、限制、详细指南
155
+ scripts/ install.ps1
156
+ tools/ 图标栅格化、弹窗/图标预览、asar 读取(仅开发用)
157
+ test/ 单元、集成、端到端
158
+ ```
159
+
160
+
161
+
162
+ MIT —— 见 [LICENSE](LICENSE)。
163
+
164
+ ## 版本管理
165
+
166
+ 版本号保存在 `package.json`,并同步到 `extension/manifest.json`;历史记录在 `CHANGELOG.md`。
167
+ 任何改动发布代码的提交都要提升版本号,仅文档的提交不需要:
168
+
169
+ ```powershell
170
+ node tools\version.mjs check # 三个文件不一致时报错
171
+ node tools\version.mjs bump minor # 同步更新 package.json、manifest.json 与 CHANGELOG 条目
172
+ ```
173
+
174
+ 发布使用 `vX.Y.Z` 标签。0.3.0 之前的版本是按提交历史回溯标注的,因此旧标签指向的内容里
175
+ 可能仍写着 0.1.0。
176
+
177
+ ## 许可证
178
+
179
+ MIT —— 见 [LICENSE](LICENSE)。
@@ -0,0 +1,6 @@
1
+ # dsh bundle patch: inserts the site-bridge row into a profile's layer stack.
2
+ # The bridge itself starts lazily on the first tool call (or immediately when
3
+ # `port` is set and `autoStart` is true).
4
+ - insert:
5
+ - id: site-bridge
6
+ name: 'dsh-site-bridge'