@opentabs-dev/browser-extension 0.0.34

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.
Files changed (305) hide show
  1. package/dist/background-log-state.d.ts +10 -0
  2. package/dist/background-log-state.d.ts.map +1 -0
  3. package/dist/background-log-state.js +11 -0
  4. package/dist/background-log-state.js.map +1 -0
  5. package/dist/background-message-handlers.d.ts +41 -0
  6. package/dist/background-message-handlers.d.ts.map +1 -0
  7. package/dist/background-message-handlers.js +214 -0
  8. package/dist/background-message-handlers.js.map +1 -0
  9. package/dist/background.d.ts +2 -0
  10. package/dist/background.d.ts.map +1 -0
  11. package/dist/background.js +3780 -0
  12. package/dist/background.js.map +1 -0
  13. package/dist/bg-log-state.d.ts +10 -0
  14. package/dist/bg-log-state.d.ts.map +1 -0
  15. package/dist/bg-log-state.js +11 -0
  16. package/dist/bg-log-state.js.map +1 -0
  17. package/dist/browser-commands/content-commands.d.ts +25 -0
  18. package/dist/browser-commands/content-commands.d.ts.map +1 -0
  19. package/dist/browser-commands/content-commands.js +166 -0
  20. package/dist/browser-commands/content-commands.js.map +1 -0
  21. package/dist/browser-commands/cookie-commands.d.ts +14 -0
  22. package/dist/browser-commands/cookie-commands.d.ts.map +1 -0
  23. package/dist/browser-commands/cookie-commands.js +99 -0
  24. package/dist/browser-commands/cookie-commands.js.map +1 -0
  25. package/dist/browser-commands/extension-commands.d.ts +12 -0
  26. package/dist/browser-commands/extension-commands.d.ts.map +1 -0
  27. package/dist/browser-commands/extension-commands.js +386 -0
  28. package/dist/browser-commands/extension-commands.js.map +1 -0
  29. package/dist/browser-commands/helpers.d.ts +35 -0
  30. package/dist/browser-commands/helpers.d.ts.map +1 -0
  31. package/dist/browser-commands/helpers.js +121 -0
  32. package/dist/browser-commands/helpers.js.map +1 -0
  33. package/dist/browser-commands/index.d.ts +11 -0
  34. package/dist/browser-commands/index.d.ts.map +1 -0
  35. package/dist/browser-commands/index.js +10 -0
  36. package/dist/browser-commands/index.js.map +1 -0
  37. package/dist/browser-commands/interaction-commands.d.ts +23 -0
  38. package/dist/browser-commands/interaction-commands.d.ts.map +1 -0
  39. package/dist/browser-commands/interaction-commands.js +353 -0
  40. package/dist/browser-commands/interaction-commands.js.map +1 -0
  41. package/dist/browser-commands/key-press-command.d.ts +2 -0
  42. package/dist/browser-commands/key-press-command.d.ts.map +1 -0
  43. package/dist/browser-commands/key-press-command.js +144 -0
  44. package/dist/browser-commands/key-press-command.js.map +1 -0
  45. package/dist/browser-commands/network-commands.d.ts +6 -0
  46. package/dist/browser-commands/network-commands.d.ts.map +1 -0
  47. package/dist/browser-commands/network-commands.js +69 -0
  48. package/dist/browser-commands/network-commands.js.map +1 -0
  49. package/dist/browser-commands/resource-commands.d.ts +37 -0
  50. package/dist/browser-commands/resource-commands.d.ts.map +1 -0
  51. package/dist/browser-commands/resource-commands.js +153 -0
  52. package/dist/browser-commands/resource-commands.js.map +1 -0
  53. package/dist/browser-commands/scroll-command.d.ts +2 -0
  54. package/dist/browser-commands/scroll-command.d.ts.map +1 -0
  55. package/dist/browser-commands/scroll-command.js +133 -0
  56. package/dist/browser-commands/scroll-command.js.map +1 -0
  57. package/dist/browser-commands/tab-commands.d.ts +33 -0
  58. package/dist/browser-commands/tab-commands.d.ts.map +1 -0
  59. package/dist/browser-commands/tab-commands.js +121 -0
  60. package/dist/browser-commands/tab-commands.js.map +1 -0
  61. package/dist/browser-commands.d.ts +36 -0
  62. package/dist/browser-commands.d.ts.map +1 -0
  63. package/dist/browser-commands.js +1931 -0
  64. package/dist/browser-commands.js.map +1 -0
  65. package/dist/confirmation-badge.d.ts +17 -0
  66. package/dist/confirmation-badge.d.ts.map +1 -0
  67. package/dist/confirmation-badge.js +64 -0
  68. package/dist/confirmation-badge.js.map +1 -0
  69. package/dist/constants.d.ts +79 -0
  70. package/dist/constants.d.ts.map +1 -0
  71. package/dist/constants.js +79 -0
  72. package/dist/constants.js.map +1 -0
  73. package/dist/dispatch-helpers.d.ts +61 -0
  74. package/dist/dispatch-helpers.d.ts.map +1 -0
  75. package/dist/dispatch-helpers.js +149 -0
  76. package/dist/dispatch-helpers.js.map +1 -0
  77. package/dist/extension-messages.d.ts +146 -0
  78. package/dist/extension-messages.d.ts.map +1 -0
  79. package/dist/extension-messages.js +2 -0
  80. package/dist/extension-messages.js.map +1 -0
  81. package/dist/iife-injection.d.ts +55 -0
  82. package/dist/iife-injection.d.ts.map +1 -0
  83. package/dist/iife-injection.js +474 -0
  84. package/dist/iife-injection.js.map +1 -0
  85. package/dist/json-rpc-errors.d.ts +8 -0
  86. package/dist/json-rpc-errors.d.ts.map +1 -0
  87. package/dist/json-rpc-errors.js +8 -0
  88. package/dist/json-rpc-errors.js.map +1 -0
  89. package/dist/known-methods.d.ts +19 -0
  90. package/dist/known-methods.d.ts.map +1 -0
  91. package/dist/known-methods.js +68 -0
  92. package/dist/known-methods.js.map +1 -0
  93. package/dist/log-collector.d.ts +45 -0
  94. package/dist/log-collector.d.ts.map +1 -0
  95. package/dist/log-collector.js +99 -0
  96. package/dist/log-collector.js.map +1 -0
  97. package/dist/message-router.d.ts +28 -0
  98. package/dist/message-router.d.ts.map +1 -0
  99. package/dist/message-router.js +367 -0
  100. package/dist/message-router.js.map +1 -0
  101. package/dist/messaging.d.ts +15 -0
  102. package/dist/messaging.d.ts.map +1 -0
  103. package/dist/messaging.js +41 -0
  104. package/dist/messaging.js.map +1 -0
  105. package/dist/network-capture.d.ts +56 -0
  106. package/dist/network-capture.d.ts.map +1 -0
  107. package/dist/network-capture.js +374 -0
  108. package/dist/network-capture.js.map +1 -0
  109. package/dist/offscreen/index.d.ts +16 -0
  110. package/dist/offscreen/index.d.ts.map +1 -0
  111. package/dist/offscreen/index.js +549 -0
  112. package/dist/offscreen/index.js.map +1 -0
  113. package/dist/plugin-storage.d.ts +19 -0
  114. package/dist/plugin-storage.d.ts.map +1 -0
  115. package/dist/plugin-storage.js +100 -0
  116. package/dist/plugin-storage.js.map +1 -0
  117. package/dist/rate-limiter.d.ts +18 -0
  118. package/dist/rate-limiter.d.ts.map +1 -0
  119. package/dist/rate-limiter.js +53 -0
  120. package/dist/rate-limiter.js.map +1 -0
  121. package/dist/resource-prompt-dispatch.d.ts +14 -0
  122. package/dist/resource-prompt-dispatch.d.ts.map +1 -0
  123. package/dist/resource-prompt-dispatch.js +195 -0
  124. package/dist/resource-prompt-dispatch.js.map +1 -0
  125. package/dist/sanitize-error.d.ts +8 -0
  126. package/dist/sanitize-error.d.ts.map +1 -0
  127. package/dist/sanitize-error.js +25 -0
  128. package/dist/sanitize-error.js.map +1 -0
  129. package/dist/sanitize-svg.d.ts +20 -0
  130. package/dist/sanitize-svg.d.ts.map +1 -0
  131. package/dist/sanitize-svg.js +296 -0
  132. package/dist/sanitize-svg.js.map +1 -0
  133. package/dist/side-panel/App.d.ts +3 -0
  134. package/dist/side-panel/App.d.ts.map +1 -0
  135. package/dist/side-panel/App.js +147 -0
  136. package/dist/side-panel/App.js.map +1 -0
  137. package/dist/side-panel/bridge.d.ts +50 -0
  138. package/dist/side-panel/bridge.d.ts.map +1 -0
  139. package/dist/side-panel/bridge.js +113 -0
  140. package/dist/side-panel/bridge.js.map +1 -0
  141. package/dist/side-panel/components/ConfirmationDialog.d.ts +16 -0
  142. package/dist/side-panel/components/ConfirmationDialog.d.ts.map +1 -0
  143. package/dist/side-panel/components/ConfirmationDialog.js +39 -0
  144. package/dist/side-panel/components/ConfirmationDialog.js.map +1 -0
  145. package/dist/side-panel/components/EmptyStates.d.ts +8 -0
  146. package/dist/side-panel/components/EmptyStates.d.ts.map +1 -0
  147. package/dist/side-panel/components/EmptyStates.js +40 -0
  148. package/dist/side-panel/components/EmptyStates.js.map +1 -0
  149. package/dist/side-panel/components/ErrorBoundary.d.ts +23 -0
  150. package/dist/side-panel/components/ErrorBoundary.d.ts.map +1 -0
  151. package/dist/side-panel/components/ErrorBoundary.js +28 -0
  152. package/dist/side-panel/components/ErrorBoundary.js.map +1 -0
  153. package/dist/side-panel/components/FailedPluginCard.d.ts +6 -0
  154. package/dist/side-panel/components/FailedPluginCard.d.ts.map +1 -0
  155. package/dist/side-panel/components/FailedPluginCard.js +9 -0
  156. package/dist/side-panel/components/FailedPluginCard.js.map +1 -0
  157. package/dist/side-panel/components/Footer.d.ts +3 -0
  158. package/dist/side-panel/components/Footer.d.ts.map +1 -0
  159. package/dist/side-panel/components/Footer.js +32 -0
  160. package/dist/side-panel/components/Footer.js.map +1 -0
  161. package/dist/side-panel/components/Header.d.ts +5 -0
  162. package/dist/side-panel/components/Header.d.ts.map +1 -0
  163. package/dist/side-panel/components/Header.js +6 -0
  164. package/dist/side-panel/components/Header.js.map +1 -0
  165. package/dist/side-panel/components/PluginCard.d.ts +10 -0
  166. package/dist/side-panel/components/PluginCard.d.ts.map +1 -0
  167. package/dist/side-panel/components/PluginCard.js +50 -0
  168. package/dist/side-panel/components/PluginCard.js.map +1 -0
  169. package/dist/side-panel/components/PluginIcon.d.ts +21 -0
  170. package/dist/side-panel/components/PluginIcon.d.ts.map +1 -0
  171. package/dist/side-panel/components/PluginIcon.js +48 -0
  172. package/dist/side-panel/components/PluginIcon.js.map +1 -0
  173. package/dist/side-panel/components/PluginList.d.ts +11 -0
  174. package/dist/side-panel/components/PluginList.d.ts.map +1 -0
  175. package/dist/side-panel/components/PluginList.js +17 -0
  176. package/dist/side-panel/components/PluginList.js.map +1 -0
  177. package/dist/side-panel/components/ToolIcon.d.ts +7 -0
  178. package/dist/side-panel/components/ToolIcon.d.ts.map +1 -0
  179. package/dist/side-panel/components/ToolIcon.js +8 -0
  180. package/dist/side-panel/components/ToolIcon.js.map +1 -0
  181. package/dist/side-panel/components/ToolRow.d.ts +11 -0
  182. package/dist/side-panel/components/ToolRow.d.ts.map +1 -0
  183. package/dist/side-panel/components/ToolRow.js +8 -0
  184. package/dist/side-panel/components/ToolRow.js.map +1 -0
  185. package/dist/side-panel/components/VersionMismatchBanner.d.ts +3 -0
  186. package/dist/side-panel/components/VersionMismatchBanner.d.ts.map +1 -0
  187. package/dist/side-panel/components/VersionMismatchBanner.js +5 -0
  188. package/dist/side-panel/components/VersionMismatchBanner.js.map +1 -0
  189. package/dist/side-panel/components/retro/Accordion.d.ts +8 -0
  190. package/dist/side-panel/components/retro/Accordion.d.ts.map +1 -0
  191. package/dist/side-panel/components/retro/Accordion.js +15 -0
  192. package/dist/side-panel/components/retro/Accordion.js.map +1 -0
  193. package/dist/side-panel/components/retro/Alert.d.ts +25 -0
  194. package/dist/side-panel/components/retro/Alert.d.ts.map +1 -0
  195. package/dist/side-panel/components/retro/Alert.js +33 -0
  196. package/dist/side-panel/components/retro/Alert.js.map +1 -0
  197. package/dist/side-panel/components/retro/Badge.d.ts +15 -0
  198. package/dist/side-panel/components/retro/Badge.d.ts.map +1 -0
  199. package/dist/side-panel/components/retro/Badge.js +23 -0
  200. package/dist/side-panel/components/retro/Badge.js.map +1 -0
  201. package/dist/side-panel/components/retro/Button.d.ts +13 -0
  202. package/dist/side-panel/components/retro/Button.d.ts.map +1 -0
  203. package/dist/side-panel/components/retro/Button.js +33 -0
  204. package/dist/side-panel/components/retro/Button.js.map +1 -0
  205. package/dist/side-panel/components/retro/Empty.d.ts +31 -0
  206. package/dist/side-panel/components/retro/Empty.d.ts.map +1 -0
  207. package/dist/side-panel/components/retro/Empty.js +25 -0
  208. package/dist/side-panel/components/retro/Empty.js.map +1 -0
  209. package/dist/side-panel/components/retro/Input.d.ts +8 -0
  210. package/dist/side-panel/components/retro/Input.d.ts.map +1 -0
  211. package/dist/side-panel/components/retro/Input.js +7 -0
  212. package/dist/side-panel/components/retro/Input.js.map +1 -0
  213. package/dist/side-panel/components/retro/Loader.d.ts +14 -0
  214. package/dist/side-panel/components/retro/Loader.d.ts.map +1 -0
  215. package/dist/side-panel/components/retro/Loader.js +30 -0
  216. package/dist/side-panel/components/retro/Loader.js.map +1 -0
  217. package/dist/side-panel/components/retro/Menu.d.ts +9 -0
  218. package/dist/side-panel/components/retro/Menu.d.ts.map +1 -0
  219. package/dist/side-panel/components/retro/Menu.js +17 -0
  220. package/dist/side-panel/components/retro/Menu.js.map +1 -0
  221. package/dist/side-panel/components/retro/NumberStepper.d.ts +18 -0
  222. package/dist/side-panel/components/retro/NumberStepper.d.ts.map +1 -0
  223. package/dist/side-panel/components/retro/NumberStepper.js +38 -0
  224. package/dist/side-panel/components/retro/NumberStepper.js.map +1 -0
  225. package/dist/side-panel/components/retro/Progress.d.ts +9 -0
  226. package/dist/side-panel/components/retro/Progress.d.ts.map +1 -0
  227. package/dist/side-panel/components/retro/Progress.js +6 -0
  228. package/dist/side-panel/components/retro/Progress.js.map +1 -0
  229. package/dist/side-panel/components/retro/Switch.d.ts +4 -0
  230. package/dist/side-panel/components/retro/Switch.d.ts.map +1 -0
  231. package/dist/side-panel/components/retro/Switch.js +6 -0
  232. package/dist/side-panel/components/retro/Switch.js.map +1 -0
  233. package/dist/side-panel/components/retro/Text.d.ts +11 -0
  234. package/dist/side-panel/components/retro/Text.d.ts.map +1 -0
  235. package/dist/side-panel/components/retro/Text.js +28 -0
  236. package/dist/side-panel/components/retro/Text.js.map +1 -0
  237. package/dist/side-panel/components/retro/Tooltip.d.ts +12 -0
  238. package/dist/side-panel/components/retro/Tooltip.d.ts.map +1 -0
  239. package/dist/side-panel/components/retro/Tooltip.js +32 -0
  240. package/dist/side-panel/components/retro/Tooltip.js.map +1 -0
  241. package/dist/side-panel/constants.d.ts +5 -0
  242. package/dist/side-panel/constants.d.ts.map +1 -0
  243. package/dist/side-panel/constants.js +5 -0
  244. package/dist/side-panel/constants.js.map +1 -0
  245. package/dist/side-panel/hooks/useServerNotifications.d.ts +21 -0
  246. package/dist/side-panel/hooks/useServerNotifications.d.ts.map +1 -0
  247. package/dist/side-panel/hooks/useServerNotifications.js +93 -0
  248. package/dist/side-panel/hooks/useServerNotifications.js.map +1 -0
  249. package/dist/side-panel/hooks/useTheme.d.ts +8 -0
  250. package/dist/side-panel/hooks/useTheme.d.ts.map +1 -0
  251. package/dist/side-panel/hooks/useTheme.js +30 -0
  252. package/dist/side-panel/hooks/useTheme.js.map +1 -0
  253. package/dist/side-panel/index.d.ts +2 -0
  254. package/dist/side-panel/index.d.ts.map +1 -0
  255. package/dist/side-panel/index.js +12 -0
  256. package/dist/side-panel/index.js.map +1 -0
  257. package/dist/side-panel/lib/cn.d.ts +3 -0
  258. package/dist/side-panel/lib/cn.d.ts.map +1 -0
  259. package/dist/side-panel/lib/cn.js +4 -0
  260. package/dist/side-panel/lib/cn.js.map +1 -0
  261. package/dist/side-panel/lib/utils.d.ts +3 -0
  262. package/dist/side-panel/lib/utils.d.ts.map +1 -0
  263. package/dist/side-panel/lib/utils.js +4 -0
  264. package/dist/side-panel/lib/utils.js.map +1 -0
  265. package/dist/side-panel/side-panel.js +78034 -0
  266. package/dist/side-panel/styles.css +2 -0
  267. package/dist/side-panel-state.d.ts +11 -0
  268. package/dist/side-panel-state.d.ts.map +1 -0
  269. package/dist/side-panel-state.js +38 -0
  270. package/dist/side-panel-state.js.map +1 -0
  271. package/dist/side-panel-toggle.d.ts +3 -0
  272. package/dist/side-panel-toggle.d.ts.map +1 -0
  273. package/dist/side-panel-toggle.js +31 -0
  274. package/dist/side-panel-toggle.js.map +1 -0
  275. package/dist/tab-matching.d.ts +51 -0
  276. package/dist/tab-matching.d.ts.map +1 -0
  277. package/dist/tab-matching.js +160 -0
  278. package/dist/tab-matching.js.map +1 -0
  279. package/dist/tab-state.d.ts +56 -0
  280. package/dist/tab-state.d.ts.map +1 -0
  281. package/dist/tab-state.js +282 -0
  282. package/dist/tab-state.js.map +1 -0
  283. package/dist/tool-dispatch.d.ts +19 -0
  284. package/dist/tool-dispatch.d.ts.map +1 -0
  285. package/dist/tool-dispatch.js +336 -0
  286. package/dist/tool-dispatch.js.map +1 -0
  287. package/dist/types.d.ts +82 -0
  288. package/dist/types.d.ts.map +1 -0
  289. package/dist/types.js +2 -0
  290. package/dist/types.js.map +1 -0
  291. package/icons/icon-128.png +0 -0
  292. package/icons/icon-16.png +0 -0
  293. package/icons/icon-32.png +0 -0
  294. package/icons/icon-48.png +0 -0
  295. package/icons/icon.svg +6 -0
  296. package/manifest.json +38 -0
  297. package/offscreen/offscreen.html +10 -0
  298. package/package.json +49 -0
  299. package/side-panel/dark-mode.js +29 -0
  300. package/side-panel/fonts/archivo-black-latin.woff2 +0 -0
  301. package/side-panel/fonts/space-grotesk-latin.woff2 +0 -0
  302. package/side-panel/fonts/space-mono-400-latin.woff2 +0 -0
  303. package/side-panel/fonts/space-mono-700-latin.woff2 +0 -0
  304. package/side-panel/fonts.css +34 -0
  305. package/side-panel/side-panel.html +16 -0
@@ -0,0 +1,282 @@
1
+ import { IS_READY_TIMEOUT_MS } from './constants.js';
2
+ import { forwardToSidePanel, sendTabStateNotification, sendToServer } from './messaging.js';
3
+ import { getAllPluginMeta } from './plugin-storage.js';
4
+ import { findAllMatchingTabs, urlMatchesPatterns } from './tab-matching.js';
5
+ /**
6
+ * Last-known tab state cache per plugin. Used by checkTabChanged and
7
+ * checkTabRemoved to suppress redundant tab.stateChanged notifications
8
+ * when a tab event fires but the plugin's effective state hasn't actually
9
+ * changed (e.g., a page reload where the plugin was "ready" before and
10
+ * is still "ready" after).
11
+ *
12
+ * The cache is populated by sendTabSyncAll (called after sync.full) and
13
+ * updated on every state change notification sent to the server.
14
+ * It is cleared on disconnect and repopulated when sync.full arrives on
15
+ * the next connection.
16
+ */
17
+ const lastKnownState = new Map();
18
+ /**
19
+ * Per-plugin promise chain for serializing state computations. Concurrent
20
+ * calls for the same plugin are chained sequentially so lastKnownState reads
21
+ * and writes are atomic within each plugin. Different plugins run in parallel.
22
+ */
23
+ const pluginLocks = new Map();
24
+ /**
25
+ * Chain an async operation onto a plugin's lock so it runs sequentially
26
+ * with any other pending operations for the same plugin. Returns the
27
+ * promise for the operation itself (rejections are logged on the lock
28
+ * chain but propagated to the caller via the returned promise).
29
+ */
30
+ const withPluginLock = (pluginName, fn) => {
31
+ const prev = pluginLocks.get(pluginName) ?? Promise.resolve();
32
+ const next = prev.then(fn);
33
+ pluginLocks.set(pluginName, next.catch((err) => {
34
+ console.warn('[opentabs] tab state operation failed for plugin', pluginName, ':', err);
35
+ }));
36
+ return next;
37
+ };
38
+ /**
39
+ * Probe a single tab for adapter readiness. Returns true if the adapter's
40
+ * isReady() returns true within the timeout, false otherwise.
41
+ */
42
+ const probeTabReadiness = async (tabId, pluginName) => {
43
+ let timerId;
44
+ try {
45
+ const results = await Promise.race([
46
+ chrome.scripting.executeScript({
47
+ target: { tabId },
48
+ world: 'MAIN',
49
+ func: async (pName) => {
50
+ const ot = globalThis.__openTabs;
51
+ const adapter = ot?.adapters?.[pName];
52
+ if (!adapter || typeof adapter !== 'object')
53
+ return false;
54
+ if (typeof adapter.isReady !== 'function')
55
+ return false;
56
+ return await adapter.isReady();
57
+ },
58
+ args: [pluginName],
59
+ }),
60
+ new Promise(resolve => {
61
+ timerId = setTimeout(() => resolve(null), IS_READY_TIMEOUT_MS);
62
+ }),
63
+ ]);
64
+ if (results === null) {
65
+ console.warn(`[opentabs] isReady() timed out for plugin "${pluginName}" in tab ${tabId}`);
66
+ return false;
67
+ }
68
+ const readyResult = results[0];
69
+ return readyResult?.result === true;
70
+ }
71
+ finally {
72
+ if (timerId !== undefined)
73
+ clearTimeout(timerId);
74
+ }
75
+ };
76
+ /**
77
+ * Compute the tab state for a single plugin by checking all matching tabs
78
+ * for adapter readiness. Reports 'ready' if ANY matching tab is ready,
79
+ * 'unavailable' if tabs exist but none are ready, 'closed' if no tabs match.
80
+ */
81
+ const computePluginTabState = async (plugin) => {
82
+ const tabs = await findAllMatchingTabs(plugin);
83
+ if (tabs.length === 0) {
84
+ return { state: 'closed', tabId: null, url: null };
85
+ }
86
+ // Track the first unavailable tab for fallback reporting
87
+ let firstUnavailable;
88
+ for (const tab of tabs) {
89
+ if (tab.id === undefined)
90
+ continue;
91
+ try {
92
+ const ready = await probeTabReadiness(tab.id, plugin.name);
93
+ if (ready) {
94
+ return { state: 'ready', tabId: tab.id, url: tab.url ?? null };
95
+ }
96
+ firstUnavailable ??= tab;
97
+ }
98
+ catch (err) {
99
+ console.warn(`[opentabs] computePluginTabState failed for plugin ${plugin.name} in tab ${tab.id}:`, err);
100
+ firstUnavailable ??= tab;
101
+ }
102
+ }
103
+ // All matching tabs exist but none are ready
104
+ const fallbackTab = firstUnavailable ?? tabs[0];
105
+ return {
106
+ state: 'unavailable',
107
+ tabId: fallbackTab?.id ?? null,
108
+ url: fallbackTab?.url ?? null,
109
+ };
110
+ };
111
+ /**
112
+ * Scan all open tabs and send tab.syncAll to MCP server with current state
113
+ * of all known plugins. Called after sync.full is processed so the extension
114
+ * has up-to-date plugin metadata before reporting tab states.
115
+ *
116
+ * Also populates the lastKnownState cache so subsequent checkTabChanged /
117
+ * checkTabRemoved calls can suppress redundant notifications.
118
+ */
119
+ const sendTabSyncAll = async () => {
120
+ const index = await getAllPluginMeta();
121
+ const plugins = Object.values(index);
122
+ if (plugins.length === 0)
123
+ return;
124
+ const settled = await Promise.allSettled(plugins.map(async (plugin) => [plugin.name, await computePluginTabState(plugin)]));
125
+ const entries = [];
126
+ for (const result of settled) {
127
+ if (result.status === 'fulfilled') {
128
+ entries.push(result.value);
129
+ }
130
+ else {
131
+ console.warn('[opentabs] Tab state computation failed during syncAll:', result.reason);
132
+ }
133
+ }
134
+ if (entries.length === 0)
135
+ return;
136
+ const tabSyncPayload = Object.fromEntries(entries);
137
+ // Write each plugin's state through the per-plugin lock so concurrent
138
+ // checkTabChanged / checkTabRemoved calls are properly serialized.
139
+ const pluginNamesInSync = new Set();
140
+ await Promise.all(entries.map(([pluginName, stateInfo]) => {
141
+ pluginNamesInSync.add(pluginName);
142
+ return withPluginLock(pluginName, () => {
143
+ lastKnownState.set(pluginName, stateInfo.state);
144
+ return Promise.resolve();
145
+ });
146
+ }));
147
+ // Remove entries for plugins no longer in the index
148
+ for (const key of lastKnownState.keys()) {
149
+ if (!pluginNamesInSync.has(key)) {
150
+ lastKnownState.delete(key);
151
+ pluginLocks.delete(key);
152
+ }
153
+ }
154
+ sendToServer({
155
+ jsonrpc: '2.0',
156
+ method: 'tab.syncAll',
157
+ params: { tabs: tabSyncPayload },
158
+ });
159
+ // Forward individual tab.stateChanged messages to the side panel so it
160
+ // gets initial tab states on connect without a separate fetch round-trip.
161
+ for (const [pluginName, stateInfo] of entries) {
162
+ forwardToSidePanel({
163
+ type: 'sp:serverMessage',
164
+ data: {
165
+ jsonrpc: '2.0',
166
+ method: 'tab.stateChanged',
167
+ params: { plugin: pluginName, state: stateInfo.state, tabId: stateInfo.tabId, url: stateInfo.url },
168
+ },
169
+ });
170
+ }
171
+ };
172
+ /**
173
+ * Clear the last-known state cache. Called on WebSocket disconnect so the
174
+ * next connect triggers a full sync without stale cache interference.
175
+ */
176
+ const clearTabStateCache = () => {
177
+ lastKnownState.clear();
178
+ pluginLocks.clear();
179
+ };
180
+ /**
181
+ * Remove tab-state tracking entries for a single plugin. Called when a plugin
182
+ * is uninstalled or removed during sync.full so the maps do not grow
183
+ * unboundedly during long-running sessions.
184
+ */
185
+ const clearPluginTabState = (pluginName) => {
186
+ lastKnownState.delete(pluginName);
187
+ pluginLocks.delete(pluginName);
188
+ };
189
+ /**
190
+ * Update the last-known state for a single plugin, serialized through the
191
+ * plugin lock so it cannot interleave with checkTabChanged / checkTabRemoved
192
+ * reads and writes for the same plugin. Called by handlePluginUpdate in
193
+ * message-router.ts after computing the new state via computePluginTabState.
194
+ */
195
+ const updateLastKnownState = (pluginName, state) => withPluginLock(pluginName, () => {
196
+ lastKnownState.set(pluginName, state);
197
+ return Promise.resolve();
198
+ });
199
+ /** Return a snapshot of last-known tab states for all plugins. */
200
+ const getLastKnownStates = () => lastKnownState;
201
+ /**
202
+ * Compute state for each affected plugin, diff against the lastKnownState
203
+ * cache, and send tab.stateChanged only when the state actually changed.
204
+ * Each plugin's computation is serialized via withPluginLock to prevent
205
+ * interleaving with concurrent calls or updateLastKnownState writes.
206
+ */
207
+ const notifyAffectedPlugins = async (affectedPlugins) => {
208
+ await Promise.all(affectedPlugins.map(plugin => withPluginLock(plugin.name, async () => {
209
+ const newState = await computePluginTabState(plugin);
210
+ // Suppress redundant notifications: only send if state actually changed
211
+ const previous = lastKnownState.get(plugin.name);
212
+ if (previous === newState.state)
213
+ return;
214
+ // Update the cache before sending so rapid sequential events see the
215
+ // latest state and don't produce duplicate notifications.
216
+ lastKnownState.set(plugin.name, newState.state);
217
+ sendTabStateNotification(plugin.name, newState);
218
+ })));
219
+ };
220
+ /**
221
+ * Check if a tab removal affects any plugin's tab state. All plugins are
222
+ * checked because chrome.tabs.get fails for removed tabs and onRemoved
223
+ * provides no URL, so pattern matching is not possible.
224
+ */
225
+ const checkTabRemoved = async (_removedTabId) => {
226
+ const index = await getAllPluginMeta();
227
+ const plugins = Object.values(index);
228
+ if (plugins.length === 0)
229
+ return;
230
+ await notifyAffectedPlugins(plugins);
231
+ };
232
+ /**
233
+ * Check if a tab URL change or page load affects any plugin's tab state.
234
+ * Only plugins whose patterns match the changed URL or that have an active
235
+ * (non-closed) state are checked, avoiding O(n × scripting calls) per event.
236
+ *
237
+ * Optimized paths:
238
+ * - URL change: plugins matching the new URL OR plugins with active state
239
+ * - status=complete: the tab's URL is fetched once and matched against all
240
+ * plugin patterns, avoiding per-plugin chrome.tabs queries
241
+ */
242
+ const checkTabChanged = async (changedTabId, changeInfo) => {
243
+ const index = await getAllPluginMeta();
244
+ const plugins = Object.values(index);
245
+ if (plugins.length === 0)
246
+ return;
247
+ let affectedPlugins;
248
+ if (changeInfo.url) {
249
+ // URL changed — check plugins matching the new URL plus plugins with
250
+ // active state (not 'closed'). Active plugins may have been on this tab
251
+ // before navigation, so recomputing their state discovers they no longer
252
+ // have a matching tab and transitions them to 'closed'.
253
+ const changedUrl = changeInfo.url;
254
+ affectedPlugins = plugins.filter(p => urlMatchesPatterns(changedUrl, p.urlPatterns) ||
255
+ (lastKnownState.has(p.name) && lastKnownState.get(p.name) !== 'closed'));
256
+ }
257
+ else if (changeInfo.status === 'complete') {
258
+ // Page finished loading — fetch the tab's current URL once and filter
259
+ // plugins by pattern match instead of calling findMatchingTab per plugin.
260
+ let tabUrl;
261
+ try {
262
+ const tab = await chrome.tabs.get(changedTabId);
263
+ tabUrl = tab.url;
264
+ }
265
+ catch {
266
+ // Tab may have been closed between event and handler — nothing to do
267
+ return;
268
+ }
269
+ if (!tabUrl)
270
+ return;
271
+ affectedPlugins = plugins.filter(p => urlMatchesPatterns(tabUrl, p.urlPatterns) ||
272
+ (lastKnownState.has(p.name) && lastKnownState.get(p.name) !== 'closed'));
273
+ }
274
+ else {
275
+ return;
276
+ }
277
+ if (affectedPlugins.length === 0)
278
+ return;
279
+ await notifyAffectedPlugins(affectedPlugins);
280
+ };
281
+ export { checkTabChanged, checkTabRemoved, clearPluginTabState, clearTabStateCache, computePluginTabState, getLastKnownStates, sendTabSyncAll, updateLastKnownState, };
282
+ //# sourceMappingURL=tab-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tab-state.js","sourceRoot":"","sources":["../src/tab-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC5F,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAI5E;;;;;;;;;;;GAWG;AACH,MAAM,cAAc,GAAG,IAAI,GAAG,EAAoB,CAAC;AAEnD;;;;GAIG;AACH,MAAM,WAAW,GAAG,IAAI,GAAG,EAAyB,CAAC;AAErD;;;;;GAKG;AACH,MAAM,cAAc,GAAG,CAAC,UAAkB,EAAE,EAAuB,EAAiB,EAAE;IACpF,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IAC9D,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC3B,WAAW,CAAC,GAAG,CACb,UAAU,EACV,IAAI,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;QAC1B,OAAO,CAAC,IAAI,CAAC,kDAAkD,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IACzF,CAAC,CAAC,CACH,CAAC;IACF,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,iBAAiB,GAAG,KAAK,EAAE,KAAa,EAAE,UAAkB,EAAoB,EAAE;IACtF,IAAI,OAAkD,CAAC;IACvD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;YACjC,MAAM,CAAC,SAAS,CAAC,aAAa,CAAC;gBAC7B,MAAM,EAAE,EAAE,KAAK,EAAE;gBACjB,KAAK,EAAE,MAAM;gBACb,IAAI,EAAE,KAAK,EAAE,KAAa,EAAE,EAAE;oBAC5B,MAAM,EAAE,GAAI,UAAsC,CAAC,UAEtC,CAAC;oBACd,MAAM,OAAO,GAAG,EAAE,EAAE,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC;oBACtC,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ;wBAAE,OAAO,KAAK,CAAC;oBAC1D,IAAI,OAAO,OAAO,CAAC,OAAO,KAAK,UAAU;wBAAE,OAAO,KAAK,CAAC;oBACxD,OAAO,MAAO,OAAO,CAAC,OAAkC,EAAE,CAAC;gBAC7D,CAAC;gBACD,IAAI,EAAE,CAAC,UAAU,CAAC;aACnB,CAAC;YACF,IAAI,OAAO,CAAO,OAAO,CAAC,EAAE;gBAC1B,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,mBAAmB,CAAC,CAAC;YACjE,CAAC,CAAC;SACH,CAAC,CAAC;QAEH,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,OAAO,CAAC,IAAI,CAAC,8CAA8C,UAAU,YAAY,KAAK,EAAE,CAAC,CAAC;YAC1F,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,WAAW,GAAG,OAAO,CAAC,CAAC,CAAqC,CAAC;QACnE,OAAO,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,CAAC;YAAS,CAAC;QACT,IAAI,OAAO,KAAK,SAAS;YAAE,YAAY,CAAC,OAAO,CAAC,CAAC;IACnD,CAAC;AACH,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,qBAAqB,GAAG,KAAK,EAAE,MAAkB,EAA+B,EAAE;IACtF,MAAM,IAAI,GAAG,MAAM,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC/C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;IACrD,CAAC;IAED,yDAAyD;IACzD,IAAI,gBAA6C,CAAC;IAElD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,GAAG,CAAC,EAAE,KAAK,SAAS;YAAE,SAAS;QACnC,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,iBAAiB,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;YAC3D,IAAI,KAAK,EAAE,CAAC;gBACV,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC;YACjE,CAAC;YACD,gBAAgB,KAAK,GAAG,CAAC;QAC3B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,sDAAsD,MAAM,CAAC,IAAI,WAAW,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;YACzG,gBAAgB,KAAK,GAAG,CAAC;QAC3B,CAAC;IACH,CAAC;IAED,6CAA6C;IAC7C,MAAM,WAAW,GAAG,gBAAgB,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;IAChD,OAAO;QACL,KAAK,EAAE,aAAa;QACpB,KAAK,EAAE,WAAW,EAAE,EAAE,IAAI,IAAI;QAC9B,GAAG,EAAE,WAAW,EAAE,GAAG,IAAI,IAAI;KAC9B,CAAC;AACJ,CAAC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,cAAc,GAAG,KAAK,IAAmB,EAAE;IAC/C,MAAM,KAAK,GAAG,MAAM,gBAAgB,EAAE,CAAC;IACvC,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAEjC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CACtC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAC,MAAM,EAAC,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,qBAAqB,CAAC,MAAM,CAAC,CAAU,CAAC,CACzF,CAAC;IACF,MAAM,OAAO,GAA8C,EAAE,CAAC;IAC9D,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;YAClC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,CAAC,yDAAyD,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;QACzF,CAAC;IACH,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IACjC,MAAM,cAAc,GAAuC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IAEvF,sEAAsE;IACtE,mEAAmE;IACnE,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAU,CAAC;IAC5C,MAAM,OAAO,CAAC,GAAG,CACf,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,EAAE,SAAS,CAAC,EAAE,EAAE;QACtC,iBAAiB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAClC,OAAO,cAAc,CAAC,UAAU,EAAE,GAAG,EAAE;YACrC,cAAc,CAAC,GAAG,CAAC,UAAU,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC;YAChD,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3B,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CACH,CAAC;IACF,oDAAoD;IACpD,KAAK,MAAM,GAAG,IAAI,cAAc,CAAC,IAAI,EAAE,EAAE,CAAC;QACxC,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAChC,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC3B,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,YAAY,CAAC;QACX,OAAO,EAAE,KAAK;QACd,MAAM,EAAE,aAAa;QACrB,MAAM,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE;KACjC,CAAC,CAAC;IAEH,uEAAuE;IACvE,0EAA0E;IAC1E,KAAK,MAAM,CAAC,UAAU,EAAE,SAAS,CAAC,IAAI,OAAO,EAAE,CAAC;QAC9C,kBAAkB,CAAC;YACjB,IAAI,EAAE,kBAAkB;YACxB,IAAI,EAAE;gBACJ,OAAO,EAAE,KAAK;gBACd,MAAM,EAAE,kBAAkB;gBAC1B,MAAM,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,CAAC,GAAG,EAAE;aACnG;SACF,CAAC,CAAC;IACL,CAAC;AACH,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,kBAAkB,GAAG,GAAS,EAAE;IACpC,cAAc,CAAC,KAAK,EAAE,CAAC;IACvB,WAAW,CAAC,KAAK,EAAE,CAAC;AACtB,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,mBAAmB,GAAG,CAAC,UAAkB,EAAQ,EAAE;IACvD,cAAc,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAClC,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;AACjC,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,oBAAoB,GAAG,CAAC,UAAkB,EAAE,KAAe,EAAiB,EAAE,CAClF,cAAc,CAAC,UAAU,EAAE,GAAG,EAAE;IAC9B,cAAc,CAAC,GAAG,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IACtC,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;AAC3B,CAAC,CAAC,CAAC;AAEL,kEAAkE;AAClE,MAAM,kBAAkB,GAAG,GAAkC,EAAE,CAAC,cAAc,CAAC;AAE/E;;;;;GAKG;AACH,MAAM,qBAAqB,GAAG,KAAK,EAAE,eAA6B,EAAiB,EAAE;IACnF,MAAM,OAAO,CAAC,GAAG,CACf,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAC3B,cAAc,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;QACrC,MAAM,QAAQ,GAAG,MAAM,qBAAqB,CAAC,MAAM,CAAC,CAAC;QAErD,wEAAwE;QACxE,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACjD,IAAI,QAAQ,KAAK,QAAQ,CAAC,KAAK;YAAE,OAAO;QAExC,qEAAqE;QACrE,0DAA0D;QAC1D,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;QAEhD,wBAAwB,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAClD,CAAC,CAAC,CACH,CACF,CAAC;AACJ,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,eAAe,GAAG,KAAK,EAAE,aAAqB,EAAiB,EAAE;IACrE,MAAM,KAAK,GAAG,MAAM,gBAAgB,EAAE,CAAC;IACvC,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAEjC,MAAM,qBAAqB,CAAC,OAAO,CAAC,CAAC;AACvC,CAAC,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,eAAe,GAAG,KAAK,EAAE,YAAoB,EAAE,UAAqC,EAAiB,EAAE;IAC3G,MAAM,KAAK,GAAG,MAAM,gBAAgB,EAAE,CAAC;IACvC,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAEjC,IAAI,eAA6B,CAAC;IAElC,IAAI,UAAU,CAAC,GAAG,EAAE,CAAC;QACnB,qEAAqE;QACrE,wEAAwE;QACxE,yEAAyE;QACzE,wDAAwD;QACxD,MAAM,UAAU,GAAG,UAAU,CAAC,GAAG,CAAC;QAClC,eAAe,GAAG,OAAO,CAAC,MAAM,CAC9B,CAAC,CAAC,EAAE,CACF,kBAAkB,CAAC,UAAU,EAAE,CAAC,CAAC,WAAW,CAAC;YAC7C,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,QAAQ,CAAC,CAC1E,CAAC;IACJ,CAAC;SAAM,IAAI,UAAU,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QAC5C,sEAAsE;QACtE,0EAA0E;QAC1E,IAAI,MAA0B,CAAC;QAC/B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;YAChD,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC;QACnB,CAAC;QAAC,MAAM,CAAC;YACP,qEAAqE;YACrE,OAAO;QACT,CAAC;QACD,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,eAAe,GAAG,OAAO,CAAC,MAAM,CAC9B,CAAC,CAAC,EAAE,CACF,kBAAkB,CAAC,MAAM,EAAE,CAAC,CAAC,WAAW,CAAC;YACzC,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,QAAQ,CAAC,CAC1E,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,OAAO;IACT,CAAC;IAED,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAEzC,MAAM,qBAAqB,CAAC,eAAe,CAAC,CAAC;AAC/C,CAAC,CAAC;AAEF,OAAO,EACL,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAClB,cAAc,EACd,oBAAoB,GACrB,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { PluginMeta } from './extension-messages.js';
2
+ /**
3
+ * Notify the extension-side dispatch that a progress event arrived.
4
+ * Called from the background message handler (tool:progress case).
5
+ */
6
+ declare const notifyDispatchProgress: (dispatchId: string) => void;
7
+ /**
8
+ * Get the link for console.warn logging: npm URL for published plugins, filesystem path for local.
9
+ */
10
+ declare const getPluginLink: (plugin: PluginMeta) => string;
11
+ /**
12
+ * Handle tool.dispatch request from MCP server.
13
+ * Finds matching tabs, checks adapter readiness (with fallback to other
14
+ * matching tabs when the best-ranked tab is not ready), executes the tool,
15
+ * and returns the result.
16
+ */
17
+ declare const handleToolDispatch: (params: Record<string, unknown>, id: string | number) => Promise<void>;
18
+ export { getPluginLink, handleToolDispatch, notifyDispatchProgress };
19
+ //# sourceMappingURL=tool-dispatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-dispatch.d.ts","sourceRoot":"","sources":["../src/tool-dispatch.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAS1D;;;GAGG;AACH,QAAA,MAAM,sBAAsB,GAAI,YAAY,MAAM,KAAG,IAGpD,CAAC;AAEF;;GAEG;AACH,QAAA,MAAM,aAAa,GAAI,QAAQ,UAAU,KAAG,MAQ3C,CAAC;AA6RF;;;;;GAKG;AACH,QAAA,MAAM,kBAAkB,GAAU,QAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,MAAM,GAAG,MAAM,KAAG,OAAO,CAAC,IAAI,CAmEpG,CAAC;AAEF,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,CAAC"}
@@ -0,0 +1,336 @@
1
+ import { requireStringParam } from './browser-commands/helpers.js';
2
+ import { MAX_INPUT_SIZE, MAX_SCRIPT_TIMEOUT_MS, SCRIPT_TIMEOUT_MS } from './constants.js';
3
+ import { dispatchWithTabFallback, resolvePlugin } from './dispatch-helpers.js';
4
+ import { JSONRPC_INTERNAL_ERROR, JSONRPC_INVALID_PARAMS } from './json-rpc-errors.js';
5
+ import { sendToServer } from './messaging.js';
6
+ import { toErrorMessage } from '@opentabs-dev/shared';
7
+ /**
8
+ * Per-dispatch progress callbacks — keyed by dispatchId, called by background.ts
9
+ * when a tool:progress message arrives. Each callback resets the extension-side
10
+ * script timeout for the corresponding dispatch.
11
+ */
12
+ const progressCallbacks = new Map();
13
+ /**
14
+ * Notify the extension-side dispatch that a progress event arrived.
15
+ * Called from the background message handler (tool:progress case).
16
+ */
17
+ const notifyDispatchProgress = (dispatchId) => {
18
+ const cb = progressCallbacks.get(dispatchId);
19
+ if (cb)
20
+ cb();
21
+ };
22
+ /**
23
+ * Get the link for console.warn logging: npm URL for published plugins, filesystem path for local.
24
+ */
25
+ const getPluginLink = (plugin) => {
26
+ if (plugin.trustTier === 'local' && plugin.sourcePath) {
27
+ return plugin.sourcePath;
28
+ }
29
+ if (plugin.trustTier === 'official') {
30
+ return `https://npmjs.com/package/@opentabs-dev/plugin-${plugin.name}`;
31
+ }
32
+ return `https://npmjs.com/package/opentabs-plugin-${plugin.name}`;
33
+ };
34
+ /**
35
+ * Inject a console.warn into the target tab before tool execution for transparency.
36
+ */
37
+ const injectToolInvocationLog = async (tabId, pluginName, toolName, link) => {
38
+ try {
39
+ await chrome.scripting.executeScript({
40
+ target: { tabId },
41
+ world: 'MAIN',
42
+ func: (pName, tName, lnk) => {
43
+ console.warn(`[opentabs] ${pName}.${tName} invoked — ${lnk}`);
44
+ },
45
+ args: [pluginName, toolName, link],
46
+ });
47
+ }
48
+ catch {
49
+ // Tab may not be injectable — logging is best-effort
50
+ }
51
+ };
52
+ /**
53
+ * Inject an ISOLATED world content script that listens for opentabs:progress
54
+ * CustomEvents from the MAIN world and relays them to the background service
55
+ * worker via chrome.runtime.sendMessage. Returns after the listener is installed.
56
+ *
57
+ * CustomEvents fired in MAIN world are visible in ISOLATED world because they
58
+ * share the same DOM — this is the correct, CSP-safe pattern for cross-world
59
+ * communication in Chrome extensions.
60
+ */
61
+ const injectProgressListener = async (tabId, dispatchId) => {
62
+ try {
63
+ await chrome.scripting.executeScript({
64
+ target: { tabId },
65
+ world: 'ISOLATED',
66
+ func: (dId) => {
67
+ const eventName = `opentabs:progress:${dId}`;
68
+ const handler = (e) => {
69
+ const detail = e.detail;
70
+ if (!detail)
71
+ return;
72
+ void chrome.runtime.sendMessage({
73
+ type: 'tool:progress',
74
+ dispatchId: detail.dispatchId,
75
+ progress: detail.progress,
76
+ total: detail.total,
77
+ message: detail.message,
78
+ });
79
+ };
80
+ document.addEventListener(eventName, handler);
81
+ // Store a cleanup function on the document so we can remove the listener later
82
+ const cleanupKey = `__opentabs_progress_cleanup_${dId}`;
83
+ const doc = document;
84
+ doc[cleanupKey] = () => {
85
+ document.removeEventListener(eventName, handler);
86
+ doc[cleanupKey] = undefined;
87
+ };
88
+ },
89
+ args: [dispatchId],
90
+ });
91
+ }
92
+ catch {
93
+ // Tab may not be injectable — progress is best-effort
94
+ }
95
+ };
96
+ /**
97
+ * Remove the ISOLATED world progress listener installed by injectProgressListener.
98
+ * Fire-and-forget — errors are silently ignored since the dispatch is already complete.
99
+ */
100
+ const removeProgressListener = (tabId, dispatchId) => {
101
+ chrome.scripting
102
+ .executeScript({
103
+ target: { tabId },
104
+ world: 'ISOLATED',
105
+ func: (dId) => {
106
+ const cleanupKey = `__opentabs_progress_cleanup_${dId}`;
107
+ const cleanup = document[cleanupKey];
108
+ if (cleanup)
109
+ cleanup();
110
+ },
111
+ args: [dispatchId],
112
+ })
113
+ .catch(() => {
114
+ // Best-effort cleanup
115
+ });
116
+ };
117
+ /**
118
+ * Execute a tool on a specific tab. Returns the structured result from the
119
+ * adapter script, or throws if the tab is inaccessible (e.g., closed).
120
+ *
121
+ * The extension-side timeout starts at SCRIPT_TIMEOUT_MS (25s). When the tool
122
+ * reports progress, the timeout is reset via the progressCallbacks registry.
123
+ * The absolute upper bound is MAX_SCRIPT_TIMEOUT_MS (295s).
124
+ *
125
+ * @param dispatchId - Correlation ID for progress reporting. The injected MAIN
126
+ * world function creates a ToolHandlerContext with a reportProgress callback
127
+ * that fires CustomEvents keyed by this ID.
128
+ */
129
+ const executeToolOnTab = async (tabId, pluginName, toolName, input, dispatchId) => {
130
+ let timeoutId;
131
+ const startTs = Date.now();
132
+ const scriptPromise = chrome.scripting.executeScript({
133
+ target: { tabId },
134
+ world: 'MAIN',
135
+ func: async (pName, tName, tInput, dId) => {
136
+ const ot = globalThis.__openTabs;
137
+ const adapter = ot?.adapters?.[pName];
138
+ if (!adapter || typeof adapter !== 'object') {
139
+ return { type: 'error', code: -32002, message: `Adapter "${pName}" not injected or not ready` };
140
+ }
141
+ // Defense-in-depth: reject adapters that are not frozen. Legitimate
142
+ // adapters are always frozen by the hashAndFreeze snippet appended to
143
+ // the IIFE. An unfrozen adapter indicates tampering by a page script.
144
+ if (!Object.isFrozen(adapter)) {
145
+ return {
146
+ type: 'error',
147
+ code: -32002,
148
+ message: `Adapter "${pName}" failed integrity check (not frozen)`,
149
+ };
150
+ }
151
+ if (typeof adapter.isReady !== 'function') {
152
+ return { type: 'error', code: -32002, message: `Adapter "${pName}" has no isReady function` };
153
+ }
154
+ if (!Array.isArray(adapter.tools)) {
155
+ return { type: 'error', code: -32002, message: `Adapter "${pName}" has no tools array` };
156
+ }
157
+ let ready;
158
+ try {
159
+ ready = await adapter.isReady();
160
+ }
161
+ catch {
162
+ return { type: 'error', code: -32002, message: `Adapter "${pName}" isReady() threw an error` };
163
+ }
164
+ if (!ready) {
165
+ return {
166
+ type: 'error',
167
+ code: -32002,
168
+ message: `Plugin "${pName}" is not ready (state: unavailable)`,
169
+ };
170
+ }
171
+ const tool = adapter.tools.find((t) => t.name === tName);
172
+ if (!tool || typeof tool.handle !== 'function') {
173
+ return { type: 'error', code: -32603, message: `Tool "${tName}" not found in adapter "${pName}"` };
174
+ }
175
+ // Create ToolHandlerContext with reportProgress that fires a CustomEvent
176
+ // on the document. The ISOLATED world content script listens for this event
177
+ // and relays it to the background service worker. Missing progress/total
178
+ // default to 0 for indeterminate progress reporting.
179
+ const context = {
180
+ reportProgress(opts) {
181
+ try {
182
+ document.dispatchEvent(new CustomEvent(`opentabs:progress:${dId}`, {
183
+ detail: {
184
+ dispatchId: dId,
185
+ progress: opts.progress ?? 0,
186
+ total: opts.total ?? 0,
187
+ message: opts.message,
188
+ },
189
+ }));
190
+ }
191
+ catch {
192
+ // Fire-and-forget — progress reporting errors must not affect tool execution
193
+ }
194
+ },
195
+ };
196
+ try {
197
+ const output = await tool.handle(tInput, context);
198
+ return { type: 'success', output };
199
+ }
200
+ catch (err) {
201
+ const caughtError = err;
202
+ if (typeof caughtError.code !== 'string') {
203
+ return {
204
+ type: 'error',
205
+ code: -32603,
206
+ message: caughtError.message ?? 'Tool execution failed',
207
+ };
208
+ }
209
+ const data = { code: caughtError.code };
210
+ if (typeof caughtError.retryable === 'boolean')
211
+ data.retryable = caughtError.retryable;
212
+ if (typeof caughtError.retryAfterMs === 'number')
213
+ data.retryAfterMs = caughtError.retryAfterMs;
214
+ if (typeof caughtError.category === 'string')
215
+ data.category = caughtError.category;
216
+ return {
217
+ type: 'error',
218
+ code: -32603,
219
+ message: caughtError.message ?? 'Tool execution failed',
220
+ data,
221
+ };
222
+ }
223
+ },
224
+ args: [pluginName, toolName, input, dispatchId],
225
+ });
226
+ let timeoutReject;
227
+ const timeoutPromise = new Promise((_resolve, reject) => {
228
+ timeoutReject = reject;
229
+ timeoutId = setTimeout(() => {
230
+ reject(new Error(`Script execution timed out after ${SCRIPT_TIMEOUT_MS}ms`));
231
+ }, SCRIPT_TIMEOUT_MS);
232
+ });
233
+ // Register a progress callback that resets the extension-side timeout.
234
+ // Called by background.ts when a tool:progress message arrives.
235
+ progressCallbacks.set(dispatchId, () => {
236
+ clearTimeout(timeoutId);
237
+ const elapsed = Date.now() - startTs;
238
+ const remainingMax = MAX_SCRIPT_TIMEOUT_MS - elapsed;
239
+ if (remainingMax <= 0) {
240
+ timeoutReject?.(new Error(`Script execution exceeded absolute max timeout of ${MAX_SCRIPT_TIMEOUT_MS}ms`));
241
+ return;
242
+ }
243
+ const nextTimeout = Math.min(SCRIPT_TIMEOUT_MS, remainingMax);
244
+ timeoutId = setTimeout(() => {
245
+ timeoutReject?.(new Error(`Script execution timed out after ${SCRIPT_TIMEOUT_MS}ms`));
246
+ }, nextTimeout);
247
+ });
248
+ let results;
249
+ try {
250
+ results = await Promise.race([scriptPromise, timeoutPromise]);
251
+ }
252
+ finally {
253
+ clearTimeout(timeoutId);
254
+ progressCallbacks.delete(dispatchId);
255
+ }
256
+ const firstResult = results[0];
257
+ const result = firstResult?.result;
258
+ if (!result || typeof result !== 'object' || !('type' in result)) {
259
+ return { type: 'error', code: JSONRPC_INTERNAL_ERROR, message: 'No result from tool execution' };
260
+ }
261
+ return result;
262
+ };
263
+ /**
264
+ * Handle tool.dispatch request from MCP server.
265
+ * Finds matching tabs, checks adapter readiness (with fallback to other
266
+ * matching tabs when the best-ranked tab is not ready), executes the tool,
267
+ * and returns the result.
268
+ */
269
+ const handleToolDispatch = async (params, id) => {
270
+ // dispatchId is the correlation key for progress reporting, injected by the MCP server
271
+ const dispatchId = typeof params.dispatchId === 'string' ? params.dispatchId : String(id);
272
+ const pluginName = requireStringParam(params, 'plugin', id);
273
+ if (!pluginName)
274
+ return;
275
+ const toolName = requireStringParam(params, 'tool', id);
276
+ if (!toolName)
277
+ return;
278
+ const rawInput = params.input;
279
+ if (rawInput !== undefined && rawInput !== null && (typeof rawInput !== 'object' || Array.isArray(rawInput))) {
280
+ sendToServer({
281
+ jsonrpc: '2.0',
282
+ error: { code: JSONRPC_INVALID_PARAMS, message: 'Invalid "input" param (expected object)' },
283
+ id,
284
+ });
285
+ return;
286
+ }
287
+ const input = (rawInput ?? {});
288
+ let inputJson;
289
+ try {
290
+ inputJson = JSON.stringify(input);
291
+ }
292
+ catch (err) {
293
+ sendToServer({
294
+ jsonrpc: '2.0',
295
+ error: {
296
+ code: JSONRPC_INVALID_PARAMS,
297
+ message: `Failed to serialize tool input: ${toErrorMessage(err)}`,
298
+ },
299
+ id,
300
+ });
301
+ return;
302
+ }
303
+ if (inputJson.length > MAX_INPUT_SIZE) {
304
+ sendToServer({
305
+ jsonrpc: '2.0',
306
+ error: {
307
+ code: JSONRPC_INVALID_PARAMS,
308
+ message: `Tool input too large: ${(inputJson.length / 1024 / 1024).toFixed(1)}MB (limit: 10MB)`,
309
+ },
310
+ id,
311
+ });
312
+ return;
313
+ }
314
+ const plugin = await resolvePlugin(pluginName, id);
315
+ if (!plugin)
316
+ return;
317
+ const link = getPluginLink(plugin);
318
+ await dispatchWithTabFallback({
319
+ id,
320
+ pluginName,
321
+ plugin,
322
+ operationName: 'tool execution',
323
+ executeOnTab: async (tabId) => {
324
+ await injectToolInvocationLog(tabId, pluginName, toolName, link);
325
+ await injectProgressListener(tabId, dispatchId);
326
+ try {
327
+ return await executeToolOnTab(tabId, pluginName, toolName, input, dispatchId);
328
+ }
329
+ finally {
330
+ removeProgressListener(tabId, dispatchId);
331
+ }
332
+ },
333
+ });
334
+ };
335
+ export { getPluginLink, handleToolDispatch, notifyDispatchProgress };
336
+ //# sourceMappingURL=tool-dispatch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-dispatch.js","sourceRoot":"","sources":["../src/tool-dispatch.ts"],"names":[],"mappings":"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"}