@weavetab/mcp 2.5.0-beta.0

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 (281) hide show
  1. package/AGENTS.md +48 -0
  2. package/ARCHITECTURE.md +590 -0
  3. package/LICENSE +631 -0
  4. package/README.md +909 -0
  5. package/SECURITY.md +196 -0
  6. package/TROUBLESHOOTING.md +365 -0
  7. package/dist/anticaptcha/detector.d.ts +49 -0
  8. package/dist/anticaptcha/detector.js +465 -0
  9. package/dist/anticaptcha/integration.d.ts +36 -0
  10. package/dist/anticaptcha/integration.js +123 -0
  11. package/dist/anticaptcha/solver.d.ts +27 -0
  12. package/dist/anticaptcha/solver.js +552 -0
  13. package/dist/audit/logger.d.ts +13 -0
  14. package/dist/audit/logger.js +177 -0
  15. package/dist/audit/telemetry.d.ts +36 -0
  16. package/dist/audit/telemetry.js +162 -0
  17. package/dist/cdp/bridge.d.ts +16 -0
  18. package/dist/cdp/bridge.js +826 -0
  19. package/dist/cdp/confirm.d.ts +26 -0
  20. package/dist/cdp/confirm.js +79 -0
  21. package/dist/cdp/connector.d.ts +37 -0
  22. package/dist/cdp/connector.js +661 -0
  23. package/dist/cdp/extension.d.ts +36 -0
  24. package/dist/cdp/extension.js +118 -0
  25. package/dist/cdp/finder.d.ts +1 -0
  26. package/dist/cdp/finder.js +2 -0
  27. package/dist/cdp/helpers.d.ts +11 -0
  28. package/dist/cdp/helpers.js +195 -0
  29. package/dist/cdp/label_cache.d.ts +12 -0
  30. package/dist/cdp/label_cache.js +51 -0
  31. package/dist/cdp/lock.d.ts +33 -0
  32. package/dist/cdp/lock.js +206 -0
  33. package/dist/cdp/omni_broker.d.ts +17 -0
  34. package/dist/cdp/omni_broker.js +119 -0
  35. package/dist/cdp/profiles.d.ts +1 -0
  36. package/dist/cdp/profiles.js +87 -0
  37. package/dist/cdp/pruner.d.ts +41 -0
  38. package/dist/cdp/pruner.js +263 -0
  39. package/dist/cdp/ref_cache.d.ts +101 -0
  40. package/dist/cdp/ref_cache.js +234 -0
  41. package/dist/cdp/retry.d.ts +5 -0
  42. package/dist/cdp/retry.js +39 -0
  43. package/dist/cdp/retry_enhanced.d.ts +44 -0
  44. package/dist/cdp/retry_enhanced.js +102 -0
  45. package/dist/cdp/security.d.ts +50 -0
  46. package/dist/cdp/security.js +237 -0
  47. package/dist/cdp/verify.d.ts +39 -0
  48. package/dist/cdp/verify.js +143 -0
  49. package/dist/cdp/walker.d.ts +91 -0
  50. package/dist/cdp/walker.js +896 -0
  51. package/dist/cdp/walker.test.d.ts +2 -0
  52. package/dist/cdp/walker.test.js +172 -0
  53. package/dist/cdp/websocket_transport.d.ts +15 -0
  54. package/dist/cdp/websocket_transport.js +118 -0
  55. package/dist/cli/plugin.d.ts +9 -0
  56. package/dist/cli/plugin.js +1134 -0
  57. package/dist/cli/updater.d.ts +1 -0
  58. package/dist/cli/updater.js +72 -0
  59. package/dist/config/loader.d.ts +120 -0
  60. package/dist/config/loader.js +704 -0
  61. package/dist/index.d.ts +2 -0
  62. package/dist/index.js +1248 -0
  63. package/dist/intelligence/hints.d.ts +11 -0
  64. package/dist/intelligence/hints.js +39 -0
  65. package/dist/intelligence/intent.d.ts +33 -0
  66. package/dist/intelligence/intent.js +171 -0
  67. package/dist/intelligence/sessionMemory.d.ts +66 -0
  68. package/dist/intelligence/sessionMemory.js +284 -0
  69. package/dist/intelligence/sessionMemory.test.d.ts +2 -0
  70. package/dist/intelligence/sessionMemory.test.js +62 -0
  71. package/dist/intelligence/trail.d.ts +64 -0
  72. package/dist/intelligence/trail.js +233 -0
  73. package/dist/intelligence/trail.test.d.ts +2 -0
  74. package/dist/intelligence/trail.test.js +110 -0
  75. package/dist/intelligence/vision.d.ts +24 -0
  76. package/dist/intelligence/vision.js +151 -0
  77. package/dist/lifecycle/mission_manager.d.ts +16 -0
  78. package/dist/lifecycle/mission_manager.js +44 -0
  79. package/dist/overlay/bubble.d.ts +16 -0
  80. package/dist/overlay/bubble.js +37 -0
  81. package/dist/overlay/ghost.d.ts +35 -0
  82. package/dist/overlay/ghost.js +508 -0
  83. package/dist/overlay/injector.d.ts +22 -0
  84. package/dist/overlay/injector.js +198 -0
  85. package/dist/plugin/interface.d.ts +134 -0
  86. package/dist/plugin/interface.js +1 -0
  87. package/dist/plugin/loader.d.ts +18 -0
  88. package/dist/plugin/loader.js +155 -0
  89. package/dist/plugin/registry.d.ts +50 -0
  90. package/dist/plugin/registry.js +234 -0
  91. package/dist/security/blacklist.d.ts +24 -0
  92. package/dist/security/blacklist.js +80 -0
  93. package/dist/security/filter.d.ts +2 -0
  94. package/dist/security/filter.js +20 -0
  95. package/dist/security/guard.d.ts +8 -0
  96. package/dist/security/guard.js +35 -0
  97. package/dist/security/ratelimit.d.ts +11 -0
  98. package/dist/security/ratelimit.js +57 -0
  99. package/dist/security/secrets.d.ts +75 -0
  100. package/dist/security/secrets.js +365 -0
  101. package/dist/sensors/actionability.d.ts +81 -0
  102. package/dist/sensors/actionability.js +303 -0
  103. package/dist/sensors/guard.d.ts +25 -0
  104. package/dist/sensors/guard.js +168 -0
  105. package/dist/sensors/mutations.d.ts +23 -0
  106. package/dist/sensors/mutations.js +93 -0
  107. package/dist/sensors/network.d.ts +67 -0
  108. package/dist/sensors/network.js +193 -0
  109. package/dist/sensors/operation_monitor.d.ts +80 -0
  110. package/dist/sensors/operation_monitor.js +203 -0
  111. package/dist/sensors/pacing.d.ts +44 -0
  112. package/dist/sensors/pacing.js +111 -0
  113. package/dist/sensors/thoughts.d.ts +2 -0
  114. package/dist/sensors/thoughts.js +59 -0
  115. package/dist/sensors/wait.d.ts +12 -0
  116. package/dist/sensors/wait.js +44 -0
  117. package/dist/server.d.ts +7 -0
  118. package/dist/server.js +1687 -0
  119. package/dist/state/agent.d.ts +36 -0
  120. package/dist/state/agent.js +101 -0
  121. package/dist/state/cache.d.ts +16 -0
  122. package/dist/state/cache.js +92 -0
  123. package/dist/state/cache.test.d.ts +2 -0
  124. package/dist/state/cache.test.js +76 -0
  125. package/dist/state/delta.d.ts +71 -0
  126. package/dist/state/delta.js +234 -0
  127. package/dist/state/delta.test.d.ts +2 -0
  128. package/dist/state/delta.test.js +279 -0
  129. package/dist/state/groups.d.ts +15 -0
  130. package/dist/state/groups.js +80 -0
  131. package/dist/state/session.d.ts +30 -0
  132. package/dist/state/session.js +155 -0
  133. package/dist/tools/accessibility.d.ts +14 -0
  134. package/dist/tools/accessibility.js +32 -0
  135. package/dist/tools/ask.d.ts +14 -0
  136. package/dist/tools/ask.js +49 -0
  137. package/dist/tools/automation.d.ts +36 -0
  138. package/dist/tools/automation.js +218 -0
  139. package/dist/tools/browser_detect.d.ts +13 -0
  140. package/dist/tools/browser_detect.js +73 -0
  141. package/dist/tools/burst.d.ts +35 -0
  142. package/dist/tools/burst.js +344 -0
  143. package/dist/tools/canvas.d.ts +25 -0
  144. package/dist/tools/canvas.js +132 -0
  145. package/dist/tools/captcha.d.ts +38 -0
  146. package/dist/tools/captcha.js +209 -0
  147. package/dist/tools/click.d.ts +36 -0
  148. package/dist/tools/click.js +561 -0
  149. package/dist/tools/click_and_wait.d.ts +33 -0
  150. package/dist/tools/click_and_wait.js +149 -0
  151. package/dist/tools/clipboard.d.ts +29 -0
  152. package/dist/tools/clipboard.js +279 -0
  153. package/dist/tools/console.d.ts +21 -0
  154. package/dist/tools/console.js +107 -0
  155. package/dist/tools/cookies.d.ts +67 -0
  156. package/dist/tools/cookies.js +121 -0
  157. package/dist/tools/design_extract.d.ts +109 -0
  158. package/dist/tools/design_extract.js +641 -0
  159. package/dist/tools/dialog.d.ts +24 -0
  160. package/dist/tools/dialog.js +109 -0
  161. package/dist/tools/drag.d.ts +15 -0
  162. package/dist/tools/drag.js +29 -0
  163. package/dist/tools/emulation.d.ts +33 -0
  164. package/dist/tools/emulation.js +69 -0
  165. package/dist/tools/eval.d.ts +20 -0
  166. package/dist/tools/eval.js +92 -0
  167. package/dist/tools/fill.d.ts +41 -0
  168. package/dist/tools/fill.js +667 -0
  169. package/dist/tools/find.d.ts +30 -0
  170. package/dist/tools/find.js +60 -0
  171. package/dist/tools/geo.d.ts +19 -0
  172. package/dist/tools/geo.js +29 -0
  173. package/dist/tools/github/analyze.d.ts +4 -0
  174. package/dist/tools/github/analyze.js +113 -0
  175. package/dist/tools/github/cache.d.ts +21 -0
  176. package/dist/tools/github/cache.js +28 -0
  177. package/dist/tools/github/index.d.ts +4 -0
  178. package/dist/tools/github/index.js +4 -0
  179. package/dist/tools/github/issues.d.ts +9 -0
  180. package/dist/tools/github/issues.js +88 -0
  181. package/dist/tools/github/pr.d.ts +5 -0
  182. package/dist/tools/github/pr.js +88 -0
  183. package/dist/tools/github/rateLimiter.d.ts +4 -0
  184. package/dist/tools/github/rateLimiter.js +46 -0
  185. package/dist/tools/github/read.d.ts +34 -0
  186. package/dist/tools/github/read.js +74 -0
  187. package/dist/tools/github/repoContext.d.ts +20 -0
  188. package/dist/tools/github/repoContext.js +129 -0
  189. package/dist/tools/highlight.d.ts +20 -0
  190. package/dist/tools/highlight.js +140 -0
  191. package/dist/tools/hover.d.ts +19 -0
  192. package/dist/tools/hover.js +57 -0
  193. package/dist/tools/inspect.d.ts +17 -0
  194. package/dist/tools/inspect.js +125 -0
  195. package/dist/tools/integration.test.d.ts +2 -0
  196. package/dist/tools/integration.test.js +96 -0
  197. package/dist/tools/key.d.ts +25 -0
  198. package/dist/tools/key.js +173 -0
  199. package/dist/tools/macro_compiler.d.ts +7 -0
  200. package/dist/tools/macro_compiler.js +82 -0
  201. package/dist/tools/mouse.d.ts +21 -0
  202. package/dist/tools/mouse.js +62 -0
  203. package/dist/tools/navigate.d.ts +16 -0
  204. package/dist/tools/navigate.js +188 -0
  205. package/dist/tools/network_intercept.d.ts +17 -0
  206. package/dist/tools/network_intercept.js +151 -0
  207. package/dist/tools/office/index.d.ts +9 -0
  208. package/dist/tools/office/index.js +7 -0
  209. package/dist/tools/office/list.d.ts +15 -0
  210. package/dist/tools/office/list.js +41 -0
  211. package/dist/tools/office/parse.d.ts +20 -0
  212. package/dist/tools/office/parse.js +172 -0
  213. package/dist/tools/pdf.d.ts +22 -0
  214. package/dist/tools/pdf.js +42 -0
  215. package/dist/tools/performance.d.ts +12 -0
  216. package/dist/tools/performance.js +47 -0
  217. package/dist/tools/performance_test.d.ts +70 -0
  218. package/dist/tools/performance_test.js +128 -0
  219. package/dist/tools/plan.d.ts +39 -0
  220. package/dist/tools/plan.js +161 -0
  221. package/dist/tools/pointer.d.ts +35 -0
  222. package/dist/tools/pointer.js +522 -0
  223. package/dist/tools/read.d.ts +30 -0
  224. package/dist/tools/read.js +488 -0
  225. package/dist/tools/recording.d.ts +45 -0
  226. package/dist/tools/recording.js +124 -0
  227. package/dist/tools/response.d.ts +129 -0
  228. package/dist/tools/response.js +5 -0
  229. package/dist/tools/scrape.d.ts +24 -0
  230. package/dist/tools/scrape.js +140 -0
  231. package/dist/tools/screenshot.d.ts +40 -0
  232. package/dist/tools/screenshot.js +79 -0
  233. package/dist/tools/scroll.d.ts +23 -0
  234. package/dist/tools/scroll.js +196 -0
  235. package/dist/tools/select.d.ts +13 -0
  236. package/dist/tools/select.js +217 -0
  237. package/dist/tools/snapshot.d.ts +27 -0
  238. package/dist/tools/snapshot.js +99 -0
  239. package/dist/tools/storage.d.ts +18 -0
  240. package/dist/tools/storage.js +29 -0
  241. package/dist/tools/tabs.d.ts +77 -0
  242. package/dist/tools/tabs.js +342 -0
  243. package/dist/tools/thought.d.ts +16 -0
  244. package/dist/tools/thought.js +79 -0
  245. package/dist/tools/type.d.ts +65 -0
  246. package/dist/tools/type.js +1061 -0
  247. package/dist/tools/type_secret.d.ts +23 -0
  248. package/dist/tools/type_secret.js +229 -0
  249. package/dist/tools/upload.d.ts +19 -0
  250. package/dist/tools/upload.js +103 -0
  251. package/dist/tools/video_compiler.d.ts +24 -0
  252. package/dist/tools/video_compiler.js +199 -0
  253. package/dist/tools/viewport.d.ts +44 -0
  254. package/dist/tools/viewport.js +230 -0
  255. package/dist/tools/wait.d.ts +19 -0
  256. package/dist/tools/wait.js +202 -0
  257. package/dist/tools/wait_for.d.ts +23 -0
  258. package/dist/tools/wait_for.js +30 -0
  259. package/dist/tools/windows.d.ts +22 -0
  260. package/dist/tools/windows.js +57 -0
  261. package/dist/ui/index.d.ts +1 -0
  262. package/dist/ui/index.js +12 -0
  263. package/docs/DEV_MODE.md +68 -0
  264. package/docs/DOCUMENTATION.md +532 -0
  265. package/docs/PLUGINS.md +538 -0
  266. package/docs/TOOLS.md +104 -0
  267. package/docs/dev_plugin.md +310 -0
  268. package/ext-dist/background.js +467 -0
  269. package/ext-dist/content.js +3089 -0
  270. package/ext-dist/dashboard.html +583 -0
  271. package/ext-dist/hud/hud.js +73 -0
  272. package/ext-dist/hud/index.html +20 -0
  273. package/ext-dist/manifest.json +77 -0
  274. package/ext-dist/sidepanel/sidepanel.js +25 -0
  275. package/ext-dist/sidepanel.html +547 -0
  276. package/ext-dist/styles.css +218 -0
  277. package/icon.png +0 -0
  278. package/mcp.json +796 -0
  279. package/package.json +113 -0
  280. package/server.json +23 -0
  281. package/smithery.yaml +11 -0
@@ -0,0 +1,234 @@
1
+ /**
2
+ * Weavetab — cdp/ref_cache.ts
3
+ * Unified Ref Coordinate Cache (URC)
4
+ *
5
+ * Single in-memory, per-tab store of refId → {selector, x, y, backendNodeId, fingerprint}
6
+ * populated once by buildActionMap/buildAXTreeMap and read by every interaction tool.
7
+ *
8
+ * Design goals:
9
+ * - Zero extra CDP calls on cache hit (< TTL)
10
+ * - One atomic Runtime.evaluate on stale/miss (atomicResolve)
11
+ * - Keyed strictly by targetId (frame-scoped) — iframe contexts never bleed
12
+ * - Auto-invalidated on frameNavigated
13
+ *
14
+ * Weavetab by fy2ne
15
+ */
16
+ // ─── Storage ──────────────────────────────────────────────────────────────────
17
+ /** Per-tab: Map<targetId, Map<refId, RefCoordEntry>> */
18
+ const store = new Map();
19
+ /** Default TTL: 30 seconds. Overridden by config.refCacheTtlMs at call sites. */
20
+ export const DEFAULT_URC_TTL_MS = 30_000;
21
+ // ─── Stale-miss counters (foundation for adaptive TTL — Q3 follow-up) ─────────
22
+ const staleMissCounters = new Map();
23
+ function recordStaleMiss(hostname) {
24
+ const now = Date.now();
25
+ const entry = staleMissCounters.get(hostname) ?? { count: 0, lastDecayAt: now };
26
+ // Decay: halve the counter every 5 minutes
27
+ const decayIntervals = Math.floor((now - entry.lastDecayAt) / (5 * 60_000));
28
+ if (decayIntervals > 0) {
29
+ entry.count = Math.floor(entry.count / Math.pow(2, decayIntervals));
30
+ entry.lastDecayAt = now;
31
+ }
32
+ entry.count += 1;
33
+ staleMissCounters.set(hostname, entry);
34
+ }
35
+ // Sweep staleMissCounters entries that have decayed to 0 so hostnames don't
36
+ // accumulate indefinitely. Runs every 10 minutes via an unref'd interval.
37
+ setInterval(() => {
38
+ const now = Date.now();
39
+ for (const [hostname, entry] of staleMissCounters.entries()) {
40
+ const decayIntervals = Math.floor((now - entry.lastDecayAt) / (5 * 60_000));
41
+ const decayed = decayIntervals > 0
42
+ ? Math.floor(entry.count / Math.pow(2, decayIntervals))
43
+ : entry.count;
44
+ if (decayed <= 0)
45
+ staleMissCounters.delete(hostname);
46
+ }
47
+ }, 10 * 60_000).unref();
48
+ /** Returns stale miss rate [0–1] for the last observation window. */
49
+ export function getStaleMissRate(hostname) {
50
+ const entry = staleMissCounters.get(hostname);
51
+ if (!entry)
52
+ return 0;
53
+ // Simple heuristic: count > 5 within the window → elevated stale rate
54
+ return Math.min(1, entry.count / 20);
55
+ }
56
+ // ─── Public API ───────────────────────────────────────────────────────────────
57
+ /** Write an entry into the URC for a given tab and refId. */
58
+ export function urcSet(targetId, refId, entry) {
59
+ if (!store.has(targetId))
60
+ store.set(targetId, new Map());
61
+ store.get(targetId).set(refId, entry);
62
+ }
63
+ /**
64
+ * Read an entry from the URC.
65
+ * Returns null if: not found, OR entry is older than maxAgeMs.
66
+ */
67
+ export function urcGet(targetId, refId, maxAgeMs = DEFAULT_URC_TTL_MS) {
68
+ const tabMap = store.get(targetId);
69
+ if (!tabMap)
70
+ return null;
71
+ const entry = tabMap.get(refId);
72
+ if (!entry)
73
+ return null;
74
+ if (Date.now() - entry.capturedAt > maxAgeMs)
75
+ return null;
76
+ return entry;
77
+ }
78
+ /** Return all cached entries for a tab (used by health checks and delta validation). */
79
+ export function urcGetAll(targetId) {
80
+ return store.get(targetId) ?? new Map();
81
+ }
82
+ /**
83
+ * Invalidate all URC entries for a tab.
84
+ * Must be called on: frameNavigated, full map rebuild, tab close.
85
+ */
86
+ export function urcInvalidate(targetId) {
87
+ store.delete(targetId);
88
+ }
89
+ /**
90
+ * Bulk-populate the URC from a freshly built ActionElement array.
91
+ * Called at the end of buildActionMap and buildAXTreeMap.
92
+ * Only writes entries that have both x and selector defined.
93
+ */
94
+ export function urcPopulate(targetId, elements) {
95
+ const now = Date.now();
96
+ for (const el of elements) {
97
+ if (!el.selector || el.x === undefined || el.y === undefined)
98
+ continue;
99
+ const fingerprint = el.fingerprint ??
100
+ `${el.role ?? ""}:${el.type ?? ""}:${(el.label ?? "").slice(0, 30)}`;
101
+ urcSet(targetId, el.id, {
102
+ selector: el.selector,
103
+ x: el.x,
104
+ y: el.y,
105
+ capturedAt: now,
106
+ backendNodeId: el.backendNodeId,
107
+ fingerprint,
108
+ visualHash: el.visualHash,
109
+ xpath: el.xpath,
110
+ value: el.value,
111
+ label: el.label,
112
+ });
113
+ }
114
+ }
115
+ // ─── Atomic Resolution ────────────────────────────────────────────────────────
116
+ /**
117
+ * Single CDP Runtime.evaluate that simultaneously:
118
+ * 1. Finds the element via selector (with WeakMap re-hydration fallback)
119
+ * 2. Validates tag/role fingerprint (lightweight string check)
120
+ * 3. Returns fresh coordinates from getBoundingClientRect
121
+ *
122
+ * Cost: 1 CDP call (~2–5ms). On a URC cache hit this is the ONLY extra call made.
123
+ * On a full miss (Stage 3+) it is still cheaper than re-running buildActionMap.
124
+ */
125
+ export async function atomicResolve(session, selector, fingerprint, hostname) {
126
+ try {
127
+ const { result } = await session.Runtime.evaluate({
128
+ expression: `(function(sel, fp) {
129
+ try {
130
+ // Primary: standard querySelector
131
+ var el = document.querySelector(sel);
132
+
133
+ // Shadow DOM fallback
134
+ if (!el && typeof window.__Weavetab_shadowQuery === 'function') {
135
+ el = window.__Weavetab_shadowQuery(sel);
136
+ }
137
+
138
+ // WeakMap registry fallback: re-derive selector from stored data
139
+ if (!el && window.__wt_registry) {
140
+ // Walk the registry to find the element whose stored id matches
141
+ // (only feasible for small pages — skip for large DOMs by checking size)
142
+ try {
143
+ var allEls = document.querySelectorAll('[data-wt-id]');
144
+ for (var i = 0; i < allEls.length; i++) {
145
+ var candidate = allEls[i];
146
+ var entry = window.__wt_registry.get(candidate);
147
+ if (entry && ('[data-wt-id="' + entry.id + '"]' === sel ||
148
+ entry.selector === sel)) {
149
+ el = candidate;
150
+ break;
151
+ }
152
+ }
153
+ } catch (e) {}
154
+ }
155
+
156
+ if (!el) return { status: 'NOT_FOUND' };
157
+
158
+ // Fingerprint check: "role:type:labelPrefix"
159
+ if (fp) {
160
+ var parts = fp.split(':');
161
+ var fpRole = parts[0];
162
+ var fpType = parts[1];
163
+ var elRole = el.getAttribute('role') || el.tagName.toLowerCase();
164
+ var elType = (el.type || '').toLowerCase();
165
+ if (fpRole && fpRole !== '' && elRole !== fpRole && elType !== fpRole) {
166
+ return { status: 'STALE' };
167
+ }
168
+ }
169
+
170
+ var rect = el.getBoundingClientRect();
171
+ if (!rect.width || !rect.height) return { status: 'INVISIBLE' };
172
+
173
+ // Scroll into view if out of viewport
174
+ if (rect.top < 0 || rect.bottom > window.innerHeight ||
175
+ rect.left < 0 || rect.right > window.innerWidth) {
176
+ el.scrollIntoView({ block: 'center', inline: 'center', behavior: 'instant' });
177
+ rect = el.getBoundingClientRect();
178
+ }
179
+
180
+ // Re-hydrate data-wt-id attribute if stripped by SPA reconciliation
181
+ var idMatch = sel.match(/\\[data-wt-id="([^"]+)"\\]/);
182
+ if (idMatch && !el.hasAttribute('data-wt-id')) {
183
+ try { el.setAttribute('data-wt-id', idMatch[1]); } catch(e) {}
184
+ }
185
+
186
+ return {
187
+ status: 'OK',
188
+ x: Math.round(rect.left + rect.width / 2),
189
+ y: Math.round(rect.top + rect.height / 2)
190
+ };
191
+ } catch (e) {
192
+ return { status: 'NOT_FOUND' };
193
+ }
194
+ })(${JSON.stringify(selector)}, ${JSON.stringify(fingerprint ?? null)})`,
195
+ returnByValue: true,
196
+ awaitPromise: false,
197
+ });
198
+ const val = result?.value;
199
+ if (!val)
200
+ return { status: "NOT_FOUND" };
201
+ // Track stale misses for adaptive TTL follow-up
202
+ if (val.status === "STALE" && hostname) {
203
+ recordStaleMiss(hostname);
204
+ }
205
+ return val;
206
+ }
207
+ catch {
208
+ return { status: "NOT_FOUND" };
209
+ }
210
+ }
211
+ /**
212
+ * Attempt coordinate resolution via CDP DOM.getBoxModel using a backendNodeId.
213
+ * Used as Stage 4 in the click fallback chain when the selector path is unavailable.
214
+ */
215
+ export async function resolveViaBackendNodeId(session, backendNodeId) {
216
+ try {
217
+ const { nodeIds } = await session.DOM.pushNodesByBackendIdsToFrontend({
218
+ backendNodeIds: [backendNodeId],
219
+ });
220
+ if (!nodeIds?.[0])
221
+ return null;
222
+ const { model } = await session.DOM.getBoxModel({ nodeId: nodeIds[0] });
223
+ if (!model?.content)
224
+ return null;
225
+ const c = model.content;
226
+ return {
227
+ x: Math.round((c[0] + c[4]) / 2),
228
+ y: Math.round((c[1] + c[5]) / 2),
229
+ };
230
+ }
231
+ catch {
232
+ return null;
233
+ }
234
+ }
@@ -0,0 +1,5 @@
1
+ export interface RetryOptions {
2
+ maxRetries?: number;
3
+ baseDelay?: number;
4
+ }
5
+ export declare function cdpRetry<T>(fn: () => Promise<T>, opts?: RetryOptions): Promise<T>;
@@ -0,0 +1,39 @@
1
+ import { logDebug } from "../audit/logger.js";
2
+ /**
3
+ * Weavetab — cdp/retry.ts
4
+ * Generic CDP retry wrapper for transient connection failures.
5
+ * Retries on: ECONNRESET, WebSocket not open, Target closed, Session not found.
6
+ * Does NOT retry on: element not found, timeout, permission errors.
7
+ * Weavetab by fy2ne
8
+ */
9
+ const RETRIABLE_PATTERNS = [
10
+ /ECONNRESET/i,
11
+ /WebSocket.*not.*open/i,
12
+ /Target closed/i,
13
+ /Session.*not found/i,
14
+ /No target with given id/i,
15
+ /protocol error/i,
16
+ /connection refused/i,
17
+ ];
18
+ function isRetriable(err) {
19
+ const msg = err instanceof Error ? err.message : String(err);
20
+ return RETRIABLE_PATTERNS.some(p => p.test(msg));
21
+ }
22
+ export async function cdpRetry(fn, opts = {}) {
23
+ const { maxRetries = 2, baseDelay = 200 } = opts;
24
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
25
+ try {
26
+ return await fn();
27
+ }
28
+ catch (err) {
29
+ if (attempt >= maxRetries || !isRetriable(err)) {
30
+ throw err;
31
+ }
32
+ const delay = baseDelay * Math.pow(2, attempt);
33
+ logDebug(`[Weavetab CDP] Retrying after transient error (attempt ${attempt + 1}/${maxRetries}): ${err instanceof Error ? err.message : err}`);
34
+ await new Promise(r => setTimeout(r, delay));
35
+ }
36
+ }
37
+ // Unreachable, but TypeScript needs it
38
+ throw new Error("[Weavetab CDP] Exhausted retries");
39
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Adaptive retry framework for interaction tools
3
+ * Provides intelligent retry logic with multiple strategies and attempt tracking
4
+ */
5
+ export interface RetryAttempt {
6
+ strategy: string;
7
+ success: boolean;
8
+ error?: string;
9
+ durationMs: number;
10
+ }
11
+ export interface RetryResult<T> {
12
+ finalResult: T;
13
+ attempts: RetryAttempt[];
14
+ successfulStrategy: string;
15
+ totalDurationMs: number;
16
+ }
17
+ export interface RetryOptions {
18
+ maxAttempts?: number;
19
+ strategies?: string[];
20
+ onAttempt?: (attempt: number, strategy: string) => void;
21
+ strategyTimeoutMs?: number;
22
+ }
23
+ /**
24
+ * Execute an action with adaptive retry using multiple strategies
25
+ * Tries each strategy in sequence until one succeeds or max attempts reached
26
+ */
27
+ export declare function executeWithRetry<T>(strategies: Array<{
28
+ name: string;
29
+ fn: () => Promise<T>;
30
+ }>, options?: RetryOptions): Promise<RetryResult<T>>;
31
+ /**
32
+ * Create a timeout wrapper for any async function
33
+ */
34
+ export declare function withTimeout<T>(fn: () => Promise<T>, timeoutMs: number, timeoutError?: string): Promise<T>;
35
+ /**
36
+ * Execute with exponential backoff retry
37
+ * For operations that might succeed on retry due to transient failures
38
+ */
39
+ export declare function executeWithBackoff<T>(fn: () => Promise<T>, options?: {
40
+ maxAttempts?: number;
41
+ initialDelayMs?: number;
42
+ maxDelayMs?: number;
43
+ backoffMultiplier?: number;
44
+ }): Promise<T>;
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Adaptive retry framework for interaction tools
3
+ * Provides intelligent retry logic with multiple strategies and attempt tracking
4
+ */
5
+ /**
6
+ * Execute an action with adaptive retry using multiple strategies
7
+ * Tries each strategy in sequence until one succeeds or max attempts reached
8
+ */
9
+ export async function executeWithRetry(strategies, options = {}) {
10
+ const maxAttempts = options.maxAttempts ?? strategies.length;
11
+ const strategyTimeoutMs = options.strategyTimeoutMs ?? 3000;
12
+ const attempts = [];
13
+ const startMs = Date.now();
14
+ for (let i = 0; i < Math.min(maxAttempts, strategies.length); i++) {
15
+ const strategy = strategies[i];
16
+ const attemptStartMs = Date.now();
17
+ if (options.onAttempt) {
18
+ options.onAttempt(i + 1, strategy.name);
19
+ }
20
+ try {
21
+ // Clear the timeout timer when strategy.fn() wins the race, otherwise it
22
+ // fires later and creates an unhandled rejected promise.
23
+ let timeoutId;
24
+ const timeoutPromise = new Promise((_, reject) => {
25
+ timeoutId = setTimeout(() => reject(new Error("Strategy timeout after " + strategyTimeoutMs + "ms")), strategyTimeoutMs);
26
+ });
27
+ const result = await Promise.race([
28
+ strategy.fn().finally(() => clearTimeout(timeoutId)),
29
+ timeoutPromise,
30
+ ]);
31
+ const attemptDuration = Date.now() - attemptStartMs;
32
+ attempts.push({
33
+ strategy: strategy.name,
34
+ success: true,
35
+ durationMs: attemptDuration,
36
+ });
37
+ return {
38
+ finalResult: result,
39
+ attempts,
40
+ successfulStrategy: strategy.name,
41
+ totalDurationMs: Date.now() - startMs,
42
+ };
43
+ }
44
+ catch (error) {
45
+ const attemptDuration = Date.now() - attemptStartMs;
46
+ attempts.push({
47
+ strategy: strategy.name,
48
+ success: false,
49
+ error: error instanceof Error ? error.message : String(error),
50
+ durationMs: attemptDuration,
51
+ });
52
+ // If this is the last attempt, throw the error
53
+ if (i === Math.min(maxAttempts, strategies.length) - 1) {
54
+ throw new Error("All " + strategies.length + " strategies failed. Last error: " + error);
55
+ }
56
+ // Otherwise, continue to next strategy
57
+ process.stderr.write("[executeWithRetry] Strategy \"" + strategy.name + "\" failed, trying next strategy. Error: " + error + "\n");
58
+ }
59
+ }
60
+ // This should never be reached, but TypeScript needs it
61
+ throw new Error('No strategies were provided or all failed');
62
+ }
63
+ /**
64
+ * Create a timeout wrapper for any async function
65
+ */
66
+ // Same orphan-timer handling as the strategy timeout above.
67
+ export function withTimeout(fn, timeoutMs, timeoutError = "Operation timed out after " + timeoutMs + "ms") {
68
+ let timeoutId;
69
+ const timeoutPromise = new Promise((_, reject) => {
70
+ timeoutId = setTimeout(() => reject(new Error(timeoutError)), timeoutMs);
71
+ });
72
+ return Promise.race([
73
+ fn().finally(() => clearTimeout(timeoutId)),
74
+ timeoutPromise,
75
+ ]);
76
+ }
77
+ /**
78
+ * Execute with exponential backoff retry
79
+ * For operations that might succeed on retry due to transient failures
80
+ */
81
+ export async function executeWithBackoff(fn, options = {}) {
82
+ const maxAttempts = options.maxAttempts ?? 3;
83
+ const initialDelayMs = options.initialDelayMs ?? 100;
84
+ const maxDelayMs = options.maxDelayMs ?? 2000;
85
+ const backoffMultiplier = options.backoffMultiplier ?? 2;
86
+ let lastError;
87
+ let delay = initialDelayMs;
88
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
89
+ try {
90
+ return await fn();
91
+ }
92
+ catch (error) {
93
+ lastError = error instanceof Error ? error : new Error(String(error));
94
+ if (attempt < maxAttempts - 1) {
95
+ process.stderr.write("[executeWithBackoff] Attempt " + (attempt + 1) + " failed, retrying in " + delay + "ms. Error: " + error + "\n");
96
+ await new Promise(resolve => setTimeout(resolve, delay));
97
+ delay = Math.min(delay * backoffMultiplier, maxDelayMs);
98
+ }
99
+ }
100
+ }
101
+ throw lastError || new Error('Max retry attempts reached');
102
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Weavetab — cdp/security.ts
3
+ * Security verification functions for browser isolation and process identity.
4
+ * - Profile verification: ensures the browser uses an isolated profile
5
+ * - Browser fingerprinting: verifies launched process identity
6
+ * - Process killing safety: whitelisting and verification
7
+ * Weavetab by fy2ne
8
+ */
9
+ import CDP from "chrome-remote-interface";
10
+ import type { Config } from "../config/loader.js";
11
+ /**
12
+ * Verifies that the browser is actually using the isolated profile directory.
13
+ * Uses CDP to check the actual user data directory being used by the browser.
14
+ * Results are cached per session to avoid per-call overhead.
15
+ */
16
+ export declare function verifyProfileIsolation(session: CDP.Client, config: Config, targetId: string): Promise<{
17
+ verified: boolean;
18
+ actualProfile?: string;
19
+ expectedProfile: string;
20
+ }>;
21
+ /**
22
+ * Clears the profile verification cache for a specific session.
23
+ * Called when a session is closed or reconnected.
24
+ */
25
+ export declare function clearProfileVerificationCache(targetId: string): void;
26
+ interface BrowserFingerprint {
27
+ pid: number;
28
+ port: number;
29
+ profileDir: string;
30
+ browserPath: string;
31
+ launchTimestamp: number;
32
+ userAgent: string;
33
+ cdpVersion: string;
34
+ }
35
+ /**
36
+ * Creates a unique fingerprint for the launched browser process.
37
+ * This is stored in the lock file and verified on reconnection.
38
+ */
39
+ export declare function createBrowserFingerprint(pid: number, port: number, profileDir: string, browserPath: string): BrowserFingerprint;
40
+ /**
41
+ * Verifies that the connected browser matches the expected fingerprint.
42
+ * Runs at CDP connect time only.
43
+ */
44
+ export declare function verifyBrowserFingerprint(session: CDP.Client, expectedFingerprint: BrowserFingerprint): Promise<{
45
+ valid: boolean;
46
+ reason?: string;
47
+ }>;
48
+ export declare function getCurrentFingerprint(): BrowserFingerprint | null;
49
+ export declare function safelyKillWeavetabProcesses(profileDir: string, expectedPid?: number): void;
50
+ export {};