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 +21 -0
- package/README.md +241 -2
- package/README.ru.md +241 -0
- package/README.zh-CN.md +203 -0
- package/cordis.patch.yml +6 -0
- package/docs/README.md +185 -0
- package/docs/limitations.md +148 -0
- package/docs/listing/awesome-dsh-plugin.yml +21 -0
- package/docs/protocol.md +101 -0
- package/extension/_locales/en/messages.json +147 -0
- package/extension/_locales/ru/messages.json +147 -0
- package/extension/_locales/zh_CN/messages.json +147 -0
- package/extension/icon-small.svg +27 -0
- package/extension/icon.svg +29 -0
- package/extension/icons/icon-128.png +0 -0
- package/extension/icons/icon-16.png +0 -0
- package/extension/icons/icon-32.png +0 -0
- package/extension/icons/icon-48.png +0 -0
- package/extension/manifest.json +37 -0
- package/extension/popup.html +196 -0
- package/extension/popup.js +277 -0
- package/extension/sw.js +1522 -0
- package/lib/artifacts.js +306 -0
- package/lib/bridge.js +376 -0
- package/lib/index.js +668 -0
- package/lib/redact.js +149 -0
- package/package.json +59 -4
- package/scripts/install.ps1 +48 -0
- package/tools/asar-read.mjs +51 -0
- package/tools/make-icons.mjs +127 -0
- package/tools/make-screenshots.mjs +215 -0
- package/tools/preview-icon.mjs +95 -0
- package/tools/preview-popup.mjs +132 -0
- package/tools/preview-sheet.mjs +110 -0
- package/tools/version.mjs +119 -0
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
|
-
#
|
|
1
|
+
# dsh-site-bridge
|
|
2
2
|
|
|
3
|
-
|
|
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)
|
|
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)
|
|
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).
|