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.
@@ -149,5 +149,8 @@
149
149
  },
150
150
  "disconnectTab": {
151
151
  "message": "Disconnect this tab"
152
+ },
153
+ "notClaimable": {
154
+ "message": "This page cannot be attached: Chrome does not allow service pages or other extensions' pages"
152
155
  }
153
156
  }
@@ -149,5 +149,8 @@
149
149
  },
150
150
  "disconnectTab": {
151
151
  "message": "Отключить эту вкладку"
152
+ },
153
+ "notClaimable": {
154
+ "message": "Эту страницу нельзя подключить: Chrome не даёт доступ к служебным страницам и страницам других расширений"
152
155
  }
153
156
  }
@@ -149,5 +149,8 @@
149
149
  },
150
150
  "disconnectTab": {
151
151
  "message": "断开此标签页"
152
+ },
153
+ "notClaimable": {
154
+ "message": "无法连接此页面:Chrome 不允许访问服务页面或其他扩展的页面"
152
155
  }
153
156
  }
@@ -2,7 +2,7 @@
2
2
  "manifest_version": 3,
3
3
  "default_locale": "en",
4
4
  "name": "__MSG_appName__",
5
- "version": "0.7.0",
5
+ "version": "0.7.2",
6
6
  "description": "__MSG_appDesc__",
7
7
  "minimum_chrome_version": "116",
8
8
  "permissions": [
@@ -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').hidden = targets.length === 0 || selfAttached
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 [tab] = await chrome.tabs.query({ active: true, currentWindow: true })
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 [tab] = await chrome.tabs.query({ active: true, currentWindow: true })
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 [active] = await chrome.tabs.query({ active: true, lastFocusedWindow: true })
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
- const tab = await chrome.tabs.get(id)
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
- throw new Error(`cannot drive ${tab.url || 'this tab'}: only http(s) and file:// pages are supported`)
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.0",
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",