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 +21 -0
- package/README.md +213 -2
- package/README.ru.md +213 -0
- package/README.zh-CN.md +179 -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 +68 -0
- package/extension/_locales/ru/messages.json +68 -0
- package/extension/_locales/zh_CN/messages.json +68 -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 +184 -0
- package/extension/popup.js +253 -0
- package/extension/sw.js +1215 -0
- package/lib/artifacts.js +306 -0
- package/lib/bridge.js +376 -0
- package/lib/index.js +611 -0
- package/lib/redact.js +149 -0
- package/package.json +58 -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,214 @@
|
|
|
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
|
+
## 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)
|
|
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).
|
package/README.zh-CN.md
ADDED
|
@@ -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)
|
|
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)。
|
package/cordis.patch.yml
ADDED