dsh-site-bridge 0.7.0 → 0.7.2
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/extension/manifest.json
CHANGED
package/extension/popup.js
CHANGED
|
@@ -138,10 +138,17 @@ function render(status) {
|
|
|
138
138
|
|
|
139
139
|
// Nothing to claim once a tab is bound: the action would be a no-op.
|
|
140
140
|
const selfAttached = selfTabId != null && targets.some((item) => item.id === selfTabId)
|
|
141
|
+
// Service pages (chrome://, other extensions, the Web Store) cannot be driven;
|
|
142
|
+
// do not offer a button that Chrome will refuse.
|
|
143
|
+
const claimable = selfTabUrl == null || /^(https?|file):/.test(selfTabUrl)
|
|
141
144
|
claim.hidden = targets.length > 0
|
|
142
|
-
claim.disabled = !connected || targets.length > 0
|
|
145
|
+
claim.disabled = !connected || !claimable || targets.length > 0
|
|
146
|
+
claim.title = claimable ? '' : t('notClaimable')
|
|
143
147
|
claim.textContent = t('connectTab')
|
|
144
|
-
$('claim-more')
|
|
148
|
+
const claimMore = $('claim-more')
|
|
149
|
+
claimMore.hidden = targets.length === 0 || selfAttached
|
|
150
|
+
claimMore.disabled = !connected || !claimable
|
|
151
|
+
claimMore.title = claimable ? t('attachAnother') : t('notClaimable')
|
|
145
152
|
|
|
146
153
|
const pick = $('pick')
|
|
147
154
|
pick.classList.toggle('active', status.picking === true)
|
|
@@ -170,12 +177,14 @@ function render(status) {
|
|
|
170
177
|
// Which tab this popup is open on: attaching that tab again is a no-op, so the
|
|
171
178
|
// attach button is only offered when the current tab is not attached yet.
|
|
172
179
|
let selfTabId = null
|
|
180
|
+
let selfTabUrl = null
|
|
173
181
|
|
|
174
182
|
async function refresh() {
|
|
175
183
|
try {
|
|
176
184
|
if (chrome.tabs?.query) {
|
|
177
|
-
const
|
|
185
|
+
const tab = await currentBrowserTab()
|
|
178
186
|
selfTabId = tab?.id ?? null
|
|
187
|
+
selfTabUrl = tab?.url ?? null
|
|
179
188
|
}
|
|
180
189
|
render(await send({ type: 'status' }))
|
|
181
190
|
} catch {
|
|
@@ -183,6 +192,31 @@ async function refresh() {
|
|
|
183
192
|
}
|
|
184
193
|
}
|
|
185
194
|
|
|
195
|
+
/**
|
|
196
|
+
* The browser tab this popup belongs to. `currentWindow` from a popup can point
|
|
197
|
+
* at the popup's own extension page, and Chrome refuses to read other
|
|
198
|
+
* extensions' pages, so prefer a real http(s)/file tab from the focused window.
|
|
199
|
+
*/
|
|
200
|
+
async function currentBrowserTab() {
|
|
201
|
+
const scheme = (tab) => (/^(https?|file):/.test(String(tab?.url ?? '')) ? 'web' : 'other')
|
|
202
|
+
const scopes = [
|
|
203
|
+
{ active: true, lastFocusedWindow: true, windowType: 'normal' },
|
|
204
|
+
{ active: true, windowType: 'normal' },
|
|
205
|
+
{ active: true, lastFocusedWindow: true },
|
|
206
|
+
{ active: true, currentWindow: true }
|
|
207
|
+
]
|
|
208
|
+
for (const scope of scopes) {
|
|
209
|
+
try {
|
|
210
|
+
const tabs = await chrome.tabs.query(scope)
|
|
211
|
+
if (!Array.isArray(tabs) || tabs.length === 0) continue
|
|
212
|
+
return tabs.find((tab) => scheme(tab) === 'web') ?? tabs[0]
|
|
213
|
+
} catch {
|
|
214
|
+
// try the next scope
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return null
|
|
218
|
+
}
|
|
219
|
+
|
|
186
220
|
/* Autosave: a complete pair is stored as soon as it appears, no button. */
|
|
187
221
|
let saveTimer = null
|
|
188
222
|
|
|
@@ -262,7 +296,7 @@ $('import-file').addEventListener('change', async (event) => {
|
|
|
262
296
|
async function attachCurrentTab() {
|
|
263
297
|
let tabId = null
|
|
264
298
|
try {
|
|
265
|
-
const
|
|
299
|
+
const tab = await currentBrowserTab()
|
|
266
300
|
tabId = tab?.id ?? null
|
|
267
301
|
} catch {
|
|
268
302
|
tabId = null
|
package/extension/sw.js
CHANGED
|
@@ -284,13 +284,26 @@ async function claimTab(tabId, attachedBy = 'agent') {
|
|
|
284
284
|
// caller that only knows "this tab" cannot silently pass NaN.
|
|
285
285
|
let id = Number(tabId)
|
|
286
286
|
if (!Number.isFinite(id)) {
|
|
287
|
-
const
|
|
287
|
+
const candidates = await chrome.tabs
|
|
288
|
+
.query({ active: true, lastFocusedWindow: true, windowType: 'normal' })
|
|
289
|
+
.catch(() => [])
|
|
290
|
+
const active = candidates.find((tab) => /^(https?|file):/.test(tab.url ?? '')) ?? candidates[0]
|
|
288
291
|
id = active?.id
|
|
289
292
|
}
|
|
290
293
|
if (!Number.isFinite(id)) throw new Error('no tab to claim: pass tabId or open a page first')
|
|
291
|
-
|
|
294
|
+
let tab = null
|
|
295
|
+
try {
|
|
296
|
+
tab = await chrome.tabs.get(id)
|
|
297
|
+
} catch (error) {
|
|
298
|
+
// Chrome refuses to read pages of other extensions and its own service
|
|
299
|
+
// pages; say that in words a person can act on.
|
|
300
|
+
throw new Error(`эту вкладку нельзя подключить: ${String(error?.message ?? error)}`)
|
|
301
|
+
}
|
|
292
302
|
if (!/^https?:|^file:/.test(tab.url ?? '')) {
|
|
293
|
-
|
|
303
|
+
const scheme = String(tab.url ?? '').split(':')[0] || 'неизвестная схема'
|
|
304
|
+
throw new Error(
|
|
305
|
+
`эту страницу нельзя подключить (${scheme}:): Chrome не даёт доступ к служебным страницам и страницам других расширений. Откройте обычный сайт http(s) или file://`
|
|
306
|
+
)
|
|
294
307
|
}
|
|
295
308
|
return attach(id, attachedBy)
|
|
296
309
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-site-bridge",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
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",
|