dsh-site-bridge 0.0.0-stage → 0.4.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/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,242 @@
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
+ ### How this differs from browser automation plugins
63
+
64
+ There are plugins that hand an agent `browser_*` tools: navigate, click, type, read the page. This is
65
+ not one of them, even though it can do those things — input here is the instrument that verifies a
66
+ capture, not the product.
67
+
68
+ The product is the capture: the DOM with boxes and computed styles, theme CSS variables, selector
69
+ candidates checked for uniqueness, and the site's own xhr/fetch calls with headers and bodies, all
70
+ written to files an agent can read. Automation exists so that "this selector works" and "this BFF call
71
+ passes with my cookies" are checked against the live page instead of assumed.
72
+
73
+ The practical consequence: for generic web browsing, a dedicated automation plugin will have more
74
+ tools than this one, and that is fine — it is solving a different problem. For writing an extension
75
+ that embeds into one specific site, you need the capture, and the input comes with it.
76
+
77
+ ## Requirements
78
+
79
+ - DeepSeek Harness with the `dsh` CLI and a profile (Desktop or Web).
80
+ - Chrome or Edge 125+ (the extension uses `chrome.debugger` and MV3 service workers).
81
+ - Node.js 22+ for the tooling in `tools/` and for the tests.
82
+
83
+ ## Install
84
+
85
+ ### 1. Host half
86
+
87
+ ```powershell
88
+ dsh plugin --profile desktop add dsh-site-bridge
89
+ ```
90
+
91
+ That pulls the published package. From a local checkout instead:
92
+
93
+ ```powershell
94
+ # from the repository root
95
+ powershell -NoProfile -ExecutionPolicy Bypass -File scripts\install.ps1
96
+ ```
97
+
98
+ The script links the plugin into your profile, adds it to `dsh.profile.bundles`, and stages the
99
+ extension into `%USERPROFILE%\.dsh\site-bridge\extension`. Manual equivalent:
100
+
101
+ ```powershell
102
+ dsh plugin --profile desktop add <path-to-this-repo>
103
+ ```
104
+
105
+ Add `"dsh-site-bridge"` to `dsh.profile.bundles` in `%USERPROFILE%\.dsh\profiles\desktop\package.json`
106
+ if the CLI did not, then restart DSH — the host half loads at profile start.
107
+
108
+ ### 2. Browser half
109
+
110
+ 1. `chrome://extensions` → enable **Developer mode** → **Load unpacked** →
111
+ `%USERPROFILE%\.dsh\site-bridge\extension`.
112
+ 2. Open the popup. It shows the port and the path to `bridge.json` (the same is printed by
113
+ `site_status`).
114
+ 3. Click **Import bridge.json** (`Импорт bridge.json` / `导入 bridge.json`) and pick that file, or
115
+ paste the token — it saves itself, no button.
116
+ 4. Open the page you want to work on and click **Connect this tab**.
117
+
118
+ Chrome shows the "started debugging this browser" banner: that is the CDP session everything runs
119
+ on. It disappears when you press **Disconnect**.
120
+
121
+ The popup UI follows the browser language: English, Russian and Simplified Chinese ship in
122
+ `extension/_locales`.
123
+
124
+ ## Usage
125
+
126
+ ```text
127
+ site_status → bridge state, pairing path, current tab
128
+ site_tabs {action:"list"} → tabs; {action:"claim", tabId} binds one
129
+ site_network {action:"start"} → begin recording, then act on the page
130
+ site_capture {screenshot:true} → write the bundle into the workspace
131
+ site_act {steps:"[...]"} → click/fill/type/select/press/scroll/upload/wait/eval
132
+ site_cursor {x,y,click:true} → virtual cursor, real mouse event
133
+ site_shot {fullPage:true} → screenshot to captures/shots/*.png
134
+ site_inject {js:"..."} → run your script in the page to verify it
135
+ ```
136
+
137
+ A typical loop for writing a page-embedded extension:
138
+
139
+ 1. `site_tabs` → bind the page.
140
+ 2. `site_capture` → read `selectors.md`, `styles.json`, `api.md`.
141
+ 3. Write the userscript / content script in your own repository.
142
+ 4. `site_inject` with the script body → check that the anchor is found and the BFF call passes with
143
+ the browser's cookies.
144
+ 5. Iterate on the selectors the capture marked as fragile.
145
+
146
+ ## Architecture
147
+
148
+ ```
149
+ ┌──────────────────────────── DSH host ────────────────────────────┐
150
+ │ tools (site_*) ──► bridge: 127.0.0.1, token, long-poll queue │
151
+ │ redaction on ingest ──► in-memory buffer ──► artifacts writer │
152
+ └───────────────────────────────┬───────────────────────────────────┘
153
+ │ HTTP long-poll (no websocket)
154
+ ┌───────────────────────────────┴───────────────────────────────────┐
155
+ │ MV3 service worker: chrome.debugger ──► CDP │
156
+ │ Network domain → requests with headers and bodies │
157
+ │ Input domain → trusted mouse/keyboard, file uploads │
158
+ │ Runtime domain → DOM snapshot, selectors, injected scripts │
159
+ │ popup: pairing, target tab, capture toggle (ru / en / zh) │
160
+ └────────────────────────────────────────────────────────────────────┘
161
+ ```
162
+
163
+ The wire protocol is small and documented in [docs/protocol.md](docs/protocol.md).
164
+
165
+ ## Security
166
+
167
+ - **Credentials never leave the browser.** The extension does not read cookies or tokens: the page
168
+ makes its own requests and the browser attaches them. The bridge sees them in flight, but values of
169
+ `cookie`, `authorization`, `x-csrf-token`, `x-api-key`, `set-cookie` and similar headers,
170
+ credential-shaped query parameters and credential-shaped JSON/form fields are replaced with
171
+ `«redacted»` **on ingest** — before anything is stored.
172
+ - **Nothing credential-shaped is written to disk.** Header *names* are kept, because "this endpoint
173
+ needs `x-csrf-token`" is part of the contract; the value is not.
174
+ - **Capture is site-scoped.** Only the inspected site's own `xhr`/`fetch` (same host, same
175
+ registrable domain, or third-party JSON) is recorded. Telemetry and security agents that POST a full
176
+ page dump to their servers are filtered out — otherwise page source with inline tokens would land in
177
+ the bundle.
178
+ - **Loopback only.** The bridge binds `127.0.0.1` and rejects any request without the 32-byte pairing
179
+ token. `site_status` prints the path to the token file, not the token.
180
+ - **Residual risk.** Header values and bodies still pass through the DSH process in memory. If a site
181
+ puts a secret under an unusual key, redaction will not recognise it. Treat a capture bundle as
182
+ sensitive, exactly like any traffic dump.
183
+
184
+ ### Position on trust
185
+
186
+ This is a deliberate trade-off, not a claim of superiority. The bridge requires a token and the
187
+ extension asks for `debugger` rather than `<all_urls>`, which costs convenience — a pairing step and
188
+ the Chrome debugging banner — and buys a smaller blast radius. Plugins that skip the token or take
189
+ host permissions are not doing it wrong; they optimise for a different thing.
190
+
191
+ ## Limitations
192
+
193
+ Cross-origin iframes, closed shadow roots and body size caps are analysed in
194
+ [docs/limitations.md](docs/limitations.md) — with the exact code location, the plan to remove each and
195
+ the expected cost.
196
+
197
+ ## Tests
198
+
199
+ ```powershell
200
+ node test\redact.test.mjs # 8 — redaction rules
201
+ node test\bridge.test.mjs # 8 — wire protocol, token, liveness
202
+ node test\artifacts.test.mjs # 5 — bundle layout and large-body spill
203
+ node test\plugin.test.mjs # 2 — tool registration and the full tool path
204
+ node test\e2e.mjs # 31 — real Chrome through the wire protocol
205
+ node test\tools-chrome.mjs # 19 — real Chrome through the tools
206
+ ```
207
+
208
+ `e2e.mjs` and `tools-chrome.mjs` launch a throwaway Chrome, load the extension with
209
+ `Extensions.loadUnpacked`, and exercise capture, trusted input, the virtual cursor, upload, injection
210
+ and screenshots against a local page with a working BFF endpoint.
211
+
212
+ ## Repository layout
213
+
214
+ ```
215
+ lib/ DSH plugin: tools, bridge, redaction, artifact writer
216
+ extension/ MV3: service worker (CDP), popup, icons, locales
217
+ docs/ protocol, limitations, detailed guide
218
+ scripts/ install.ps1
219
+ tools/ icon rasteriser, popup/icon previews, asar reader (dev only)
220
+ test/ unit, integration, end-to-end
221
+ ```
222
+
223
+
224
+
225
+ MIT — see [LICENSE](LICENSE).
226
+
227
+ ## Versioning
228
+
229
+ The version lives in `package.json` and is mirrored into `extension/manifest.json`; `CHANGELOG.md`
230
+ holds the history. Every commit that changes shipped code bumps the version, docs-only commits do not:
231
+
232
+ ```powershell
233
+ node tools\version.mjs check # fail if the three files disagree
234
+ node tools\version.mjs bump minor # bump package.json + manifest.json + CHANGELOG stub
235
+ ```
236
+
237
+ Releases are tagged `vX.Y.Z`. Versions below 0.3.0 were assigned retroactively from the commit
238
+ history, so an old tag may pin commit content that still carried the 0.1.0 stamp.
239
+
240
+ ## License
241
+
242
+ MIT — see [LICENSE](LICENSE).
package/README.ru.md ADDED
@@ -0,0 +1,241 @@
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
+ Есть плагины, которые дают агенту инструменты `browser_*`: навигация, клик, ввод, чтение страницы.
65
+ Это не один из них, хотя всё перечисленное умеет: ввод здесь — инструмент проверки снимка, а не
66
+ продукт.
67
+
68
+ Продукт — снимок: DOM с боксами и computed-стилями, CSS-переменные темы, кандидаты селекторов с
69
+ проверкой уникальности и собственные xhr/fetch сайта с заголовками и телами, разложенные по файлам,
70
+ которые агент читает. Автоматизация нужна, чтобы «этот селектор работает» и «этот вызов BFF проходит
71
+ с моими куками» проверялись на живой странице, а не предполагались.
72
+
73
+ Практическое следствие: для обычного браузинга специализированный плагин автоматизации даст больше
74
+ инструментов, и это нормально — он решает другую задачу. Для написания расширения, встраивающегося в
75
+ конкретный сайт, нужен снимок, а ввод идёт в комплекте.
76
+
77
+ ## Требования
78
+
79
+ - DeepSeek Harness с CLI `dsh` и профилем (Desktop или Web).
80
+ - Chrome или Edge 125+ (расширение использует `chrome.debugger` и MV3 service worker).
81
+ - Node.js 22+ для инструментов в `tools/` и для тестов.
82
+
83
+ ## Установка
84
+
85
+ ### 1. Host-половина
86
+
87
+ ```powershell
88
+ dsh plugin --profile desktop add dsh-site-bridge
89
+ ```
90
+
91
+ Это ставит опубликованный пакет. Из локального клона вместо этого:
92
+
93
+ ```powershell
94
+ # из корня репозитория
95
+ powershell -NoProfile -ExecutionPolicy Bypass -File scripts\install.ps1
96
+ ```
97
+
98
+ Скрипт линкует плагин в ваш профиль, добавляет его в `dsh.profile.bundles` и раскладывает расширение
99
+ в `%USERPROFILE%\.dsh\site-bridge\extension`. Вручную то же самое:
100
+
101
+ ```powershell
102
+ dsh plugin --profile desktop add <путь-к-этому-репозиторию>
103
+ ```
104
+
105
+ Если CLI не сделал этого сам, добавьте `"dsh-site-bridge"` в `dsh.profile.bundles` в
106
+ `%USERPROFILE%\.dsh\profiles\desktop\package.json`, затем перезапустите DSH — host-половина
107
+ загружается при старте профиля.
108
+
109
+ ### 2. Браузерная половина
110
+
111
+ 1. `chrome://extensions` → включите **Режим разработчика** → **Загрузить распакованное** →
112
+ `%USERPROFILE%\.dsh\site-bridge\extension`.
113
+ 2. Откройте попап. Он покажет порт и путь к `bridge.json` (то же печатает `site_status`).
114
+ 3. Нажмите **Импорт bridge.json** и выберите этот файл — или вставьте токен: он сохранится сам,
115
+ кнопки нет.
116
+ 4. Откройте нужную страницу и нажмите **Подключить эту вкладку**.
117
+
118
+ Chrome покажет плашку «началась отладка этого браузера»: это и есть CDP-сессия, на которой всё
119
+ работает. Она исчезнет после **Отключить**.
120
+
121
+ Интерфейс попапа следует языку браузера: английский, русский и упрощённый китайский лежат в
122
+ `extension/_locales`.
123
+
124
+ ## Использование
125
+
126
+ ```text
127
+ site_status → состояние моста, путь к токену, текущая вкладка
128
+ site_tabs {action:"list"} → вкладки; {action:"claim", tabId} привязывает одну
129
+ site_network {action:"start"} → начать запись, затем действуйте на странице
130
+ site_capture {screenshot:true} → записать пакет в рабочее пространство
131
+ site_act {steps:"[...]"} → click/fill/type/select/press/scroll/upload/wait/eval
132
+ site_cursor {x,y,click:true} → виртуальный курсор, настоящее событие мыши
133
+ site_shot {fullPage:true} → скриншот в captures/shots/*.png
134
+ site_inject {js:"..."} → выполнить ваш скрипт в странице для проверки
135
+ ```
136
+
137
+ Типичный цикл написания встраиваемого расширения:
138
+
139
+ 1. `site_tabs` → привязать страницу.
140
+ 2. `site_capture` → прочитать `selectors.md`, `styles.json`, `api.md`.
141
+ 3. Написать userscript / content script в своём репозитории.
142
+ 4. `site_inject` с телом скрипта → проверить, что якорь найден и вызов BFF проходит с куками
143
+ браузера.
144
+ 5. Доработать селекторы, которые снимок пометил как хрупкие.
145
+
146
+ ## Архитектура
147
+
148
+ ```
149
+ ┌──────────────────────────── DSH host ────────────────────────────┐
150
+ │ инструменты (site_*) ──► мост: 127.0.0.1, токен, очередь long-poll│
151
+ │ редакция на приёме ──► буфер в памяти ──► запись артефактов │
152
+ └───────────────────────────────┬───────────────────────────────────┘
153
+ │ HTTP long-poll (без websocket)
154
+ ┌───────────────────────────────┴───────────────────────────────────┐
155
+ │ MV3 service worker: chrome.debugger ──► CDP │
156
+ │ Network domain → запросы с заголовками и телами │
157
+ │ Input domain → доверенные мышь/клавиатура, загрузка файлов │
158
+ │ Runtime domain → снимок DOM, селекторы, внедрённые скрипты │
159
+ │ popup: подключение, вкладка, запись (ru / en / zh) │
160
+ └────────────────────────────────────────────────────────────────────┘
161
+ ```
162
+
163
+ Протокол небольшой и описан в [docs/protocol.md](docs/protocol.md).
164
+
165
+ ## Безопасность
166
+
167
+ - **Авторизация не покидает браузер.** Расширение не читает куки и токены: запросы делает сама
168
+ страница, браузер прикладывает их. Мост видит их в полёте, но значения `cookie`, `authorization`,
169
+ `x-csrf-token`, `x-api-key`, `set-cookie` и подобных заголовков, credential-подобные query-параметры
170
+ и поля JSON/форм заменяются на `«redacted»` **на приёме** — до того, как что-то сохранено.
171
+ - **На диск не попадает ничего авторизационного.** Имена заголовков сохраняются, потому что «этому
172
+ эндпоинту нужен `x-csrf-token`» — часть контракта; значения — нет.
173
+ - **Захват ограничен сайтом.** Пишутся только свои `xhr`/`fetch` проверяемого сайта (тот же хост, тот
174
+ же домен второго уровня или сторонний JSON). Телеметрия и агенты безопасности, которые отправляют
175
+ дамп HTML страницы на свой сервер, отфильтровываются — иначе исходник с инлайн-токенами попадал бы
176
+ в пакет.
177
+ - **Только loopback.** Мост слушает `127.0.0.1` и отклоняет любой запрос без 32-байтного токена
178
+ сопряжения. `site_status` печатает путь к файлу с токеном, а не сам токен.
179
+ - **Остаточный риск.** Значения заголовков и тела всё равно проходят через процесс DSH в памяти. Если
180
+ сайт кладёт секрет под нестандартным ключом, редакция его не узнает. Считайте пакет снимка
181
+ чувствительным, как и любой дамп трафика.
182
+
183
+ ### Позиция по доверию
184
+
185
+ Это осознанный трейд-офф, а не заявка на превосходство. Мост требует токен, а расширение просит
186
+ `debugger` вместо `<all_urls>`: теряется удобство — шаг сопряжения и плашка отладки Chrome — и
187
+ уменьшается радиус поражения. Плагины, которые обходятся без токена или берут права на все сайты, не
188
+ делают что-то неправильно; они оптимизируют другое.
189
+
190
+ ## Ограничения
191
+
192
+ Кросс-доменные iframe, закрытые shadow-корни и лимиты тела разобраны в
193
+ [docs/limitations.md](docs/limitations.md) — с точным местом в коде, планом снятия каждого и оценкой
194
+ стоимости.
195
+
196
+ ## Тесты
197
+
198
+ ```powershell
199
+ node test\redact.test.mjs # 8 — правила редакции
200
+ node test\bridge.test.mjs # 8 — протокол, токен, живость
201
+ node test\artifacts.test.mjs # 5 — раскладка пакета и вынос больших тел
202
+ node test\plugin.test.mjs # 2 — регистрация инструментов и сквозной путь
203
+ node test\e2e.mjs # 31 — реальный Chrome через протокол
204
+ node test\tools-chrome.mjs # 19 — реальный Chrome через инструменты
205
+ ```
206
+
207
+ `e2e.mjs` и `tools-chrome.mjs` поднимают одноразовый Chrome, загружают расширение через
208
+ `Extensions.loadUnpacked` и проверяют захват, доверенный ввод, виртуальный курсор, загрузку файла,
209
+ инъекцию и скриншоты на локальной странице с рабочим BFF-эндпоинтом.
210
+
211
+ ## Структура репозитория
212
+
213
+ ```
214
+ lib/ плагин DSH: инструменты, мост, редакция, запись артефактов
215
+ extension/ MV3: service worker (CDP), popup, иконки, локали
216
+ docs/ протокол, ограничения, подробное руководство
217
+ scripts/ install.ps1
218
+ tools/ растеризация иконок, превью попапа/иконок, чтение asar (только для разработки)
219
+ test/ юнит, интеграционные, end-to-end
220
+ ```
221
+
222
+
223
+
224
+ MIT — см. [LICENSE](LICENSE).
225
+
226
+ ## Версионирование
227
+
228
+ Версия живёт в `package.json` и зеркалится в `extension/manifest.json`; история — в `CHANGELOG.md`.
229
+ Каждый коммит, меняющий поставляемый код, поднимает версию; коммиты только с документацией — нет:
230
+
231
+ ```powershell
232
+ node tools\version.mjs check # упадёт, если три файла расходятся
233
+ node tools\version.mjs bump minor # поднимет package.json + manifest.json + заготовку в CHANGELOG
234
+ ```
235
+
236
+ Релизы помечаются тегами `vX.Y.Z`. Версии ниже 0.3.0 проставлены задним числом по истории коммитов,
237
+ поэтому старый тег может указывать на содержимое, где ещё стояла 0.1.0.
238
+
239
+ ## Лицензия
240
+
241
+ MIT — см. [LICENSE](LICENSE).