dsh-site-bridge 0.7.2 → 0.7.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.
@@ -2,7 +2,7 @@
2
2
  "manifest_version": 3,
3
3
  "default_locale": "en",
4
4
  "name": "__MSG_appName__",
5
- "version": "0.7.2",
5
+ "version": "0.7.4",
6
6
  "description": "__MSG_appDesc__",
7
7
  "minimum_chrome_version": "116",
8
8
  "permissions": [
@@ -206,12 +206,23 @@ async function currentBrowserTab() {
206
206
  { active: true, currentWindow: true }
207
207
  ]
208
208
  for (const scope of scopes) {
209
+ let tabs = []
209
210
  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]
211
+ tabs = await chrome.tabs.query(scope)
213
212
  } catch {
214
- // try the next scope
213
+ continue
214
+ }
215
+ if (!Array.isArray(tabs) || tabs.length === 0) continue
216
+ const ordered = [...tabs.filter((tab) => scheme(tab) === 'web'), ...tabs.filter((tab) => scheme(tab) !== 'web')]
217
+ for (const tab of ordered) {
218
+ // Ask Chrome whether this tab is readable at all: tabs in app windows of
219
+ // other extensions are listed but cannot be touched.
220
+ try {
221
+ await chrome.tabs.get(tab.id)
222
+ return tab
223
+ } catch {
224
+ // not ours to read — try the next candidate
225
+ }
215
226
  }
216
227
  }
217
228
  return null
package/extension/sw.js CHANGED
@@ -15,7 +15,7 @@
15
15
  * host, which redacts and never writes it to disk.
16
16
  */
17
17
 
18
- const EXT_VERSION = '0.1.0'
18
+ const EXT_VERSION = chrome.runtime.getManifest().version
19
19
  const NET_CAP = 600
20
20
  // Bodies are kept up to this size in the worker, then marked as truncated.
21
21
  // Raise it per machine with chrome.storage.local.set({ maxBodyChars }): the
@@ -297,7 +297,11 @@ async function claimTab(tabId, attachedBy = 'agent') {
297
297
  } catch (error) {
298
298
  // Chrome refuses to read pages of other extensions and its own service
299
299
  // pages; say that in words a person can act on.
300
- throw new Error(`эту вкладку нельзя подключить: ${String(error?.message ?? error)}`)
300
+ const detail = String(error?.message ?? error)
301
+ const hint = /chrome-extension:\/\//.test(detail)
302
+ ? ' Похоже, вкладка открыта в окне-приложении другого расширения — откройте её как обычную вкладку.'
303
+ : ''
304
+ throw new Error(`эту вкладку нельзя подключить: ${detail}${hint}`)
301
305
  }
302
306
  if (!/^https?:|^file:/.test(tab.url ?? '')) {
303
307
  const scheme = String(tab.url ?? '').split(':')[0] || 'неизвестная схема'
@@ -1502,17 +1506,26 @@ async function dispatch(op, args) {
1502
1506
  return { ok: true, version: EXT_VERSION }
1503
1507
  case 'tabs.list': {
1504
1508
  const tabs = await chrome.tabs.query({})
1509
+ // Window type explains why a tab can be listed but not read: app windows
1510
+ // belong to another extension, and Chrome refuses access to them.
1511
+ const windows = await chrome.windows.getAll({}).catch(() => [])
1512
+ const windowType = new Map(windows.map((window) => [window.id, window.type ?? 'normal']))
1505
1513
  const list = tabs
1506
1514
  .filter((tab) => /^https?:|^file:/.test(tab.url ?? ''))
1507
- .map((tab) => ({
1508
- id: tab.id,
1509
- title: tab.title,
1510
- url: tab.url,
1511
- active: tab.active,
1512
- windowId: tab.windowId,
1513
- attached: state.sessions.has(tab.id),
1514
- target: tab.id === state.activeTabId
1515
- }))
1515
+ .map((tab) => {
1516
+ const type = windowType.get(tab.windowId) ?? 'unknown'
1517
+ return {
1518
+ id: tab.id,
1519
+ title: tab.title,
1520
+ url: tab.url,
1521
+ active: tab.active,
1522
+ windowId: tab.windowId,
1523
+ windowType: type,
1524
+ readable: type === 'normal',
1525
+ attached: state.sessions.has(tab.id),
1526
+ target: tab.id === state.activeTabId
1527
+ }
1528
+ })
1516
1529
  await evt('tabs', { tabs: list })
1517
1530
  return { tabs: list }
1518
1531
  }
package/lib/index.js CHANGED
@@ -332,10 +332,10 @@ export function apply(ctx, config) {
332
332
  }
333
333
  const result = await instance.enqueue('tabs.list', {}, { timeout: 20000 })
334
334
  const tabs = Array.isArray(result?.tabs) ? result.tabs : []
335
- const lines = tabs.map(
336
- (tab) =>
337
- `${tab.target ? '*' : tab.attached ? '+' : ' '} [${tab.id}] ${tab.title || '(без названия)'} — ${tab.url}`
338
- )
335
+ const lines = tabs.map((tab) => {
336
+ const window = tab.windowType && tab.windowType !== 'normal' ? ` [окно: ${tab.windowType}, недоступно]` : ''
337
+ return `${tab.target ? '*' : tab.attached ? '+' : ' '} [${tab.id}] ${tab.title || '(без названия)'} — ${tab.url}${window}`
338
+ })
339
339
  return ok(
340
340
  tabs.length === 0
341
341
  ? 'Нет доступных вкладок (нужны http(s)/file://).'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-site-bridge",
3
- "version": "0.7.2",
3
+ "version": "0.7.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",