dsh-site-bridge 0.6.2 → 0.6.4
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/README.md +8 -1
- package/README.ru.md +8 -1
- package/README.zh-CN.md +7 -1
- package/extension/manifest.json +1 -1
- package/extension/sw.js +34 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -124,7 +124,7 @@ The popup UI follows the browser language: English, Russian and Simplified Chine
|
|
|
124
124
|
|
|
125
125
|
```text
|
|
126
126
|
site_status → bridge state, pairing path, tabs
|
|
127
|
-
site_tabs {action:"list"} → tabs; claim/use/release/open/focus
|
|
127
|
+
site_tabs {action:"list"} → tabs; claim/use/release/open/close/focus
|
|
128
128
|
site_network {action:"start"} → begin recording, then act on the page
|
|
129
129
|
site_capture {screenshot:true} → write the bundle into the workspace
|
|
130
130
|
site_snapshot {limit:40} → interactive elements with stable selectors
|
|
@@ -145,6 +145,13 @@ A typical loop for writing a page-embedded extension:
|
|
|
145
145
|
the browser's cookies.
|
|
146
146
|
5. Iterate on the selectors the capture marked as fragile.
|
|
147
147
|
|
|
148
|
+
### Popup and tools are the same surface
|
|
149
|
+
|
|
150
|
+
The popup is a manual control for the person at the keyboard: attach a tab, detach it, start recording,
|
|
151
|
+
pick an element. The agent can do all of that through the tools, including `site_tabs {action:"claim"}`
|
|
152
|
+
for any tab it sees in `list`. The attach button is a human gesture, not a permission gate — the
|
|
153
|
+
bridge token is the only thing that decides who may drive the browser.
|
|
154
|
+
|
|
148
155
|
## Architecture
|
|
149
156
|
|
|
150
157
|
```
|
package/README.ru.md
CHANGED
|
@@ -124,7 +124,7 @@ Chrome покажет плашку «началась отладка этого
|
|
|
124
124
|
|
|
125
125
|
```text
|
|
126
126
|
site_status → состояние моста, путь к спариванию, вкладки
|
|
127
|
-
site_tabs {action:"list"} → вкладки; claim/use/release/open/focus
|
|
127
|
+
site_tabs {action:"list"} → вкладки; claim/use/release/open/close/focus
|
|
128
128
|
site_network {action:"start"} → начать запись, затем действовать на странице
|
|
129
129
|
site_capture {screenshot:true} → записать пакет снимка в рабочее пространство
|
|
130
130
|
site_snapshot {limit:40} → интерактивные элементы с устойчивыми селекторами
|
|
@@ -145,6 +145,13 @@ site_inject {js:"..."} → выполнить ваш скрипт в с
|
|
|
145
145
|
браузера.
|
|
146
146
|
5. Доработать селекторы, которые снимок пометил как хрупкие.
|
|
147
147
|
|
|
148
|
+
### Попап и инструменты — одна и та же поверхность
|
|
149
|
+
|
|
150
|
+
Попап — ручное управление для человека за клавиатурой: подключить вкладку, отключить, включить
|
|
151
|
+
запись, выбрать элемент. Агент умеет то же самое через инструменты, включая
|
|
152
|
+
`site_tabs {action:"claim"}` для любой вкладки из `list`. Кнопка подключения — жест человека, а не
|
|
153
|
+
разрешение: доступ решает только токен моста.
|
|
154
|
+
|
|
148
155
|
## Архитектура
|
|
149
156
|
|
|
150
157
|
```
|
package/README.zh-CN.md
CHANGED
|
@@ -103,7 +103,7 @@ Chrome 会显示「已开始调试此浏览器」提示条:这正是全部功
|
|
|
103
103
|
|
|
104
104
|
```text
|
|
105
105
|
site_status → 网桥状态、配对路径、标签页
|
|
106
|
-
site_tabs {action:"list"} → 标签页;claim/use/release/open/focus
|
|
106
|
+
site_tabs {action:"list"} → 标签页;claim/use/release/open/close/focus
|
|
107
107
|
site_network {action:"start"} → 开始录制,然后在页面上操作
|
|
108
108
|
site_capture {screenshot:true} → 把快照包写入工作区
|
|
109
109
|
site_snapshot {limit:40} → 带稳定选择器的可交互元素
|
|
@@ -123,6 +123,12 @@ site_inject {js:"..."} → 在页面里执行你的脚本以验证
|
|
|
123
123
|
4. 用 `site_inject` 注入脚本体 → 确认锚点能找到、BFF 调用能带着浏览器 Cookie 通过。
|
|
124
124
|
5. 针对快照标记为脆弱的选择器继续调整。
|
|
125
125
|
|
|
126
|
+
### 弹出窗口与工具是同一条通道
|
|
127
|
+
|
|
128
|
+
弹出窗口是给键盘前的人的双手控制:连接标签页、断开、开始录制、选择元素。智能体通过工具可以做同样
|
|
129
|
+
的事,包括对 `list` 中任意标签页执行 `site_tabs {action:"claim"}`。连接按钮是人的操作,而不是权限
|
|
130
|
+
闸门——能否驱动浏览器只由网桥令牌决定。
|
|
131
|
+
|
|
126
132
|
## 架构
|
|
127
133
|
|
|
128
134
|
```
|
package/extension/manifest.json
CHANGED
package/extension/sw.js
CHANGED
|
@@ -294,6 +294,25 @@ async function claimTab(tabId) {
|
|
|
294
294
|
return attach(id)
|
|
295
295
|
}
|
|
296
296
|
|
|
297
|
+
chrome.tabs.onUpdated.addListener((tabId, info, tab) => {
|
|
298
|
+
const session = state.sessions.get(tabId)
|
|
299
|
+
if (!session) return
|
|
300
|
+
const next = { id: tab.id, url: tab.url, title: tab.title }
|
|
301
|
+
if (next.title === session.target.title && next.url === session.target.url) return
|
|
302
|
+
session.target = next
|
|
303
|
+
void evt('target', { tab: next, tabId })
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
chrome.tabs.onRemoved.addListener((tabId) => {
|
|
307
|
+
if (!state.sessions.has(tabId)) return
|
|
308
|
+
state.sessions.delete(tabId)
|
|
309
|
+
if (state.activeTabId === tabId) {
|
|
310
|
+
state.activeTabId = state.sessions.size > 0 ? [...state.sessions.keys()][0] : null
|
|
311
|
+
}
|
|
312
|
+
void evt('target', { tab: null, tabId })
|
|
313
|
+
void evt('active', { tabId: state.activeTabId })
|
|
314
|
+
})
|
|
315
|
+
|
|
297
316
|
chrome.debugger.onDetach.addListener((source, reason) => {
|
|
298
317
|
const session = state.sessions.get(source.tabId)
|
|
299
318
|
if (!session) return
|
|
@@ -1512,7 +1531,21 @@ async function dispatch(op, args) {
|
|
|
1512
1531
|
case 'tab.open': {
|
|
1513
1532
|
const tab = await chrome.tabs.create({ url: String(args.url ?? 'about:blank'), active: args.active !== false })
|
|
1514
1533
|
await attach(tab.id)
|
|
1515
|
-
|
|
1534
|
+
// A fresh tab has no title yet: wait for the load so the caller gets a
|
|
1535
|
+
// target it can show, instead of an empty string.
|
|
1536
|
+
const deadline = Date.now() + Math.min(Number(args.waitMs ?? 10000), 30000)
|
|
1537
|
+
let info = tab
|
|
1538
|
+
while (Date.now() < deadline) {
|
|
1539
|
+
info = await chrome.tabs.get(tab.id).catch(() => info)
|
|
1540
|
+
if (info.status === 'complete' && info.title) break
|
|
1541
|
+
await sleep(150)
|
|
1542
|
+
}
|
|
1543
|
+
const session = state.sessions.get(tab.id)
|
|
1544
|
+
if (session) {
|
|
1545
|
+
session.target = { id: info.id, url: info.url, title: info.title }
|
|
1546
|
+
await evt('target', { tab: session.target, tabId: tab.id })
|
|
1547
|
+
}
|
|
1548
|
+
return { target: session?.target ?? { id: tab.id, url: tab.url, title: tab.title } }
|
|
1516
1549
|
}
|
|
1517
1550
|
case 'tab.focus': {
|
|
1518
1551
|
const session = requireSession(args.tabId)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-site-bridge",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.4",
|
|
4
4
|
"description": "Chrome/Edge MV3 bridge for DeepSeek Harness: capture page markup (DOM, styles, selectors) and BFF traffic (xhr/fetch with headers and bodies) into a workspace snapshot bundle, drive the page with trusted CDP input, and verify injected scripts. Authorization never leaves the browser and is never written to disk.",
|
|
5
5
|
"icon": "./icon.svg",
|
|
6
6
|
"type": "module",
|