@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,17 @@
1
+ /**
2
+ * Weavetab — cdp/omni_broker.ts
3
+ * Omni-Context Execution Broker
4
+ * Handles Target.setAutoAttach to seamlessly execute code in cross-origin iframes.
5
+ * Weavetab by fy2ne
6
+ */
7
+ import CDP from "chrome-remote-interface";
8
+ /**
9
+ * Initializes the Omni-Context Broker for a parent page session.
10
+ * Automatically attaches to all OOPIFs (Out-of-Process Iframes) and caches their CDP clients.
11
+ */
12
+ export declare function initOmniBroker(parentSession: CDP.Client, parentTargetId: string, port: number): Promise<void>;
13
+ /**
14
+ * Executes a CDP command across all known iframe contexts if it fails in the top-level context.
15
+ */
16
+ export declare function omniEvaluate(parentSession: CDP.Client, parentTargetId: string, expression: string, returnByValue?: boolean): Promise<any>;
17
+ export declare function cleanupOmniBroker(parentTargetId: string): void;
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Weavetab — cdp/omni_broker.ts
3
+ * Omni-Context Execution Broker
4
+ * Handles Target.setAutoAttach to seamlessly execute code in cross-origin iframes.
5
+ * Weavetab by fy2ne
6
+ */
7
+ import CDP from "chrome-remote-interface";
8
+ /**
9
+ * Stores active iframe CDP sessions mapped by their frame targetId.
10
+ * Keyed by the parent page's targetId to ensure isolation.
11
+ */
12
+ const omniSessions = new Map();
13
+ /**
14
+ * Initializes the Omni-Context Broker for a parent page session.
15
+ * Automatically attaches to all OOPIFs (Out-of-Process Iframes) and caches their CDP clients.
16
+ */
17
+ export async function initOmniBroker(parentSession, parentTargetId, port) {
18
+ if (!omniSessions.has(parentTargetId)) {
19
+ omniSessions.set(parentTargetId, new Map());
20
+ }
21
+ parentSession.Target.on("attachedToTarget", async (event) => {
22
+ const { targetInfo } = event;
23
+ if (targetInfo.type === 'iframe') {
24
+ try {
25
+ // Connect a flat session to the iframe target
26
+ const iframeSession = await CDP({ host: "127.0.0.1", port, target: targetInfo.targetId });
27
+ await iframeSession.Runtime.enable();
28
+ await iframeSession.DOM.enable();
29
+ omniSessions.get(parentTargetId).set(targetInfo.targetId, iframeSession);
30
+ }
31
+ catch (e) {
32
+ // Fallback or ignore
33
+ }
34
+ }
35
+ });
36
+ // Handle detachedFromTarget so iframe CDP sessions are closed when iframes
37
+ // navigate away or are removed from the DOM. Without this, WebSocket connections
38
+ // leak on pages with dynamic iframes (e.g. news sites with ad iframes).
39
+ parentSession.Target.on("detachedFromTarget", async (event) => {
40
+ const { targetId } = event;
41
+ const iframeMap = omniSessions.get(parentTargetId);
42
+ if (iframeMap && targetId && iframeMap.has(targetId)) {
43
+ try {
44
+ iframeMap.get(targetId).close();
45
+ }
46
+ catch { }
47
+ iframeMap.delete(targetId);
48
+ }
49
+ });
50
+ try {
51
+ await parentSession.Target.setAutoAttach({
52
+ autoAttach: true,
53
+ waitForDebuggerOnStart: false,
54
+ flatten: true, // Use flat sessions (sessionId)
55
+ });
56
+ }
57
+ catch (e) {
58
+ console.warn("[OmniBroker] Failed to setAutoAttach", e);
59
+ }
60
+ }
61
+ /**
62
+ * Executes a CDP command across all known iframe contexts if it fails in the top-level context.
63
+ */
64
+ export async function omniEvaluate(parentSession, parentTargetId, expression, returnByValue = true) {
65
+ // Try parent first
66
+ const { result } = await parentSession.Runtime.evaluate({
67
+ expression,
68
+ returnByValue
69
+ });
70
+ if (result && !result.subtype && result.value !== null && result.value !== undefined) {
71
+ const parsed = typeof result.value === 'string' ? parseJSON(result.value) : result.value;
72
+ if (parsed && (parsed.exists === false || parsed.ok === false || parsed === false)) {
73
+ // Proceed to try iframes
74
+ }
75
+ else {
76
+ return result.value;
77
+ }
78
+ }
79
+ // Fallback to all known iframes
80
+ const iframeMap = omniSessions.get(parentTargetId);
81
+ if (iframeMap) {
82
+ for (const iframeSession of iframeMap.values()) {
83
+ try {
84
+ const { result: iframeResult } = await iframeSession.Runtime.evaluate({
85
+ expression,
86
+ returnByValue
87
+ });
88
+ if (iframeResult && iframeResult.value) {
89
+ const parsed = typeof iframeResult.value === 'string' ? parseJSON(iframeResult.value) : iframeResult.value;
90
+ if (parsed && parsed.exists !== false && parsed.ok !== false) {
91
+ return iframeResult.value;
92
+ }
93
+ }
94
+ }
95
+ catch (e) { }
96
+ }
97
+ }
98
+ return result?.value; // Return the failed parent result if no iframe succeeds
99
+ }
100
+ function parseJSON(str) {
101
+ try {
102
+ return JSON.parse(str);
103
+ }
104
+ catch {
105
+ return str;
106
+ }
107
+ }
108
+ export function cleanupOmniBroker(parentTargetId) {
109
+ const iframeMap = omniSessions.get(parentTargetId);
110
+ if (iframeMap) {
111
+ for (const session of iframeMap.values()) {
112
+ try {
113
+ session.close();
114
+ }
115
+ catch { }
116
+ }
117
+ omniSessions.delete(parentTargetId);
118
+ }
119
+ }
@@ -0,0 +1 @@
1
+ export declare function getSessionProfile(cookies: boolean, customProfileDir?: string, syncDataAcrossBrowsers?: boolean, browserPath?: string): string;
@@ -0,0 +1,87 @@
1
+ import { logDebug } from "../audit/logger.js";
2
+ /**
3
+ * Weavetab — cdp/profiles.ts
4
+ * Isolated browser session profile resolution with real-browser protection guard.
5
+ * Weavetab NEVER reads from or writes to the user's real browser profile.
6
+ * Weavetab by fy2ne
7
+ */
8
+ import * as path from "node:path";
9
+ import * as os from "node:os";
10
+ import * as fs from "node:fs";
11
+ import * as crypto from "node:crypto";
12
+ import { PLATFORM } from "../config/loader.js";
13
+ // ─── Real-browser profile isolation guard ────────────────────────────────────
14
+ // These are the known default user data directories for every major browser
15
+ // on all supported platforms. Weavetab will REFUSE to use any of these paths
16
+ // as a session profile. This is an enterprise-critical security guarantee.
17
+ const HOME = os.homedir();
18
+ // Platform-specific real browser paths
19
+ const REAL_BROWSER_PATHS = [];
20
+ if (PLATFORM === 'win32') {
21
+ // Windows paths
22
+ REAL_BROWSER_PATHS.push(path.join(HOME, "AppData", "Local", "Google", "Chrome", "User Data"), path.join(HOME, "AppData", "Local", "Microsoft", "Edge", "User Data"), path.join(HOME, "AppData", "Local", "BraveSoftware", "Brave-Browser", "User Data"), path.join(HOME, "AppData", "Roaming", "Mozilla", "Firefox", "Profiles"), path.join(HOME, "AppData", "Roaming", "Opera Software", "Opera Stable"), path.join(HOME, "AppData", "Local", "Vivaldi", "User Data"));
23
+ }
24
+ else if (PLATFORM === 'darwin') {
25
+ // macOS paths
26
+ REAL_BROWSER_PATHS.push(path.join(HOME, "Library", "Application Support", "Google", "Chrome"), path.join(HOME, "Library", "Application Support", "Microsoft Edge"), path.join(HOME, "Library", "Application Support", "BraveSoftware", "Brave-Browser"), path.join(HOME, "Library", "Application Support", "Firefox", "Profiles"), path.join(HOME, "Library", "Application Support", "Vivaldi"));
27
+ }
28
+ else {
29
+ // Linux paths
30
+ REAL_BROWSER_PATHS.push(path.join(HOME, ".config", "google-chrome"), path.join(HOME, ".config", "microsoft-edge"), path.join(HOME, ".config", "BraveSoftware", "Brave-Browser"), path.join(HOME, ".mozilla", "firefox"), path.join(HOME, ".config", "opera"), path.join(HOME, ".config", "vivaldi"));
31
+ }
32
+ /**
33
+ * Asserts that the given profile directory is NOT a real browser profile.
34
+ * Throws a clear security error if it matches any known real-browser path.
35
+ * This prevents Weavetab from ever accidentally accessing user's saved passwords,
36
+ * cookies, history, or personal data from their personal browser.
37
+ */
38
+ function assertNotRealBrowserProfile(profileDir) {
39
+ const resolved = path.resolve(profileDir).toLowerCase();
40
+ for (const realPath of REAL_BROWSER_PATHS) {
41
+ const realResolved = path.resolve(realPath).toLowerCase();
42
+ // Block exact match and any subdirectory of a real browser profile
43
+ if (resolved === realResolved || resolved.startsWith(realResolved + path.sep)) {
44
+ throw new Error(`[Weavetab] ✗ SECURITY BLOCK: The profileDir "${profileDir}" points to your real browser data.\n` +
45
+ ` Weavetab will never read from or write to your personal browser profile.\n` +
46
+ ` Please use a separate, empty directory or leave profileDir empty to use Weavetab's isolated profile.\n` +
47
+ ` Real browser path detected: ${realPath}`);
48
+ }
49
+ }
50
+ }
51
+ // ─── Session profile resolver ─────────────────────────────────────────────────
52
+ export function getSessionProfile(cookies, customProfileDir, syncDataAcrossBrowsers, browserPath) {
53
+ // If the user specified a custom profile directory, validate and use it
54
+ if (customProfileDir && customProfileDir.trim()) {
55
+ const resolved = path.resolve(customProfileDir.trim());
56
+ // Enterprise safety: refuse to use real browser profiles
57
+ assertNotRealBrowserProfile(resolved);
58
+ if (!fs.existsSync(resolved)) {
59
+ fs.mkdirSync(resolved, { recursive: true });
60
+ logDebug(`[Weavetab] ✓ Created custom profile directory: ${resolved}`);
61
+ }
62
+ else {
63
+ logDebug(`[Weavetab] ✓ Using custom profile directory: ${resolved}`);
64
+ }
65
+ return resolved;
66
+ }
67
+ // Default: use Weavetab's fully isolated session profile
68
+ // This directory is completely separate from the user's real browser data.
69
+ let sessionProfile = path.join(HOME, ".weavetab-system", "session-profile");
70
+ if (syncDataAcrossBrowsers === false && browserPath && browserPath !== "auto") {
71
+ // Isolate profiles per browser executable path
72
+ const hash = crypto.createHash("md5").update(browserPath).digest("hex").slice(0, 8);
73
+ // e.g. .weavetab-system/session-profile-a1b2c3d4
74
+ sessionProfile = path.join(HOME, ".weavetab-system", `session-profile-${hash}`);
75
+ }
76
+ // Defense-in-depth: even though the hardcoded path should never match a real
77
+ // browser profile, validate it anyway (belt-and-suspenders).
78
+ assertNotRealBrowserProfile(sessionProfile);
79
+ if (!fs.existsSync(sessionProfile)) {
80
+ fs.mkdirSync(sessionProfile, { recursive: true });
81
+ logDebug(`[Weavetab] ✓ Browser isolation verified — new isolated profile at: ${sessionProfile}`);
82
+ }
83
+ else {
84
+ logDebug(`[Weavetab] ✓ Browser isolation verified — using isolated profile at: ${sessionProfile}`);
85
+ }
86
+ return sessionProfile;
87
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Weavetab — cdp/pruner.ts
3
+ * Semantic DOM Pruning: one-shot compression of a single DOM snapshot.
4
+ *
5
+ * DISTINCT FROM Delta Mapping:
6
+ * Delta = diffs snapshots across time (previous call → now).
7
+ * Pruning = compresses ONE snapshot before it is returned.
8
+ * They are complementary — delta runs after pruning.
9
+ *
10
+ * Execution model: 1 Runtime.evaluate call (2 if page exceeds perf gate —
11
+ * the split path costs one extra CDP round trip; see ARCHITECTURE.md).
12
+ *
13
+ * Weavetab by fy2ne
14
+ */
15
+ import CDP from "chrome-remote-interface";
16
+ import type { ActionElement } from "./walker.js";
17
+ export interface PruneStats {
18
+ total: number;
19
+ pruned: number;
20
+ svgReplaced: number;
21
+ elapsedMs: number;
22
+ /** true when the perf-gate fallback triggered a second Runtime.evaluate */
23
+ splitPath: boolean;
24
+ }
25
+ export interface PruneResult {
26
+ elements: ActionElement[];
27
+ stats: PruneStats;
28
+ }
29
+ /**
30
+ * pruneSnapshot — filters rawElements in-place using three ordered in-browser passes.
31
+ *
32
+ * Ordering guarantee (stable ref-id assignment):
33
+ * 1. Synthetic-id assignment (pre-Pass 1)
34
+ * 2. Pass 1 — controlsMap
35
+ * 3. Pass 2 — post-order hasInteractiveDescendant
36
+ * 4. Pass 3 — per-element prune decision
37
+ * └─ data-wt-secret check fires on the ATTRIBUTE, not the id,
38
+ * so it is safe even when an element is running on a synthetic id.
39
+ * 5. Synthetic-id strip (post-filter, before return)
40
+ */
41
+ export declare function pruneSnapshot(session: CDP.Client, rawElements: ActionElement[], _targetId?: string, structuralIdioms?: string[]): Promise<ActionElement[]>;
@@ -0,0 +1,263 @@
1
+ import { logDebug } from "../audit/logger.js";
2
+ /**
3
+ * pruneSnapshot — filters rawElements in-place using three ordered in-browser passes.
4
+ *
5
+ * Ordering guarantee (stable ref-id assignment):
6
+ * 1. Synthetic-id assignment (pre-Pass 1)
7
+ * 2. Pass 1 — controlsMap
8
+ * 3. Pass 2 — post-order hasInteractiveDescendant
9
+ * 4. Pass 3 — per-element prune decision
10
+ * └─ data-wt-secret check fires on the ATTRIBUTE, not the id,
11
+ * so it is safe even when an element is running on a synthetic id.
12
+ * 5. Synthetic-id strip (post-filter, before return)
13
+ */
14
+ export async function pruneSnapshot(session, rawElements, _targetId = "default", structuralIdioms = []) {
15
+ if (rawElements.length === 0)
16
+ return rawElements;
17
+ // Extract the raw numeric ids (strip "w:" prefix) so the in-browser script
18
+ // can match against data-wt-id attribute values.
19
+ const rawIds = rawElements.map((el) => el.id.startsWith("w:") ? el.id.slice(2) : el.id);
20
+ // ── Build the in-browser evaluate expression ───────────────────────────────
21
+ const expression = buildEvalExpression(rawIds, structuralIdioms);
22
+ // ── Execute (single evaluate, perf-gate split if needed) ──────────────────
23
+ const { result } = await session.Runtime.evaluate({
24
+ expression,
25
+ returnByValue: true,
26
+ awaitPromise: false,
27
+ });
28
+ if (!result?.value) {
29
+ // Evaluate failed — return raw elements unchanged (graceful degradation)
30
+ logDebug("[Pruner] Runtime.evaluate returned no value — skipping prune pass");
31
+ return rawElements;
32
+ }
33
+ let parsed;
34
+ try {
35
+ parsed = JSON.parse(result.value);
36
+ }
37
+ catch {
38
+ logDebug("[Pruner] Failed to parse Runtime.evaluate result — skipping prune pass");
39
+ return rawElements;
40
+ }
41
+ const { keptIds, svgLabels, stats } = parsed;
42
+ logDebug(`[Pruner] ${stats.total} → ${stats.total - stats.pruned} elements` +
43
+ ` (pruned ${stats.pruned}, ${stats.svgReplaced} SVG labels replaced,` +
44
+ ` ${stats.elapsedMs.toFixed(1)}ms${stats.splitPath ? ", split-path" : ""})`);
45
+ const keptSet = new Set(keptIds);
46
+ // Filter and patch SVG labels
47
+ const result2 = [];
48
+ for (const el of rawElements) {
49
+ const rawId = el.id.startsWith("w:") ? el.id.slice(2) : el.id;
50
+ if (!keptSet.has(rawId))
51
+ continue;
52
+ const svgLabel = svgLabels[rawId];
53
+ if (svgLabel) {
54
+ result2.push({ ...el, label: svgLabel });
55
+ }
56
+ else {
57
+ result2.push(el);
58
+ }
59
+ }
60
+ return result2;
61
+ }
62
+ // ─── In-browser evaluate expression builder ───────────────────────────────────
63
+ function buildEvalExpression(rawIds, structuralIdioms = []) {
64
+ // We JSON-encode the id list so the script can build a lookup Set.
65
+ const idsJson = JSON.stringify(rawIds);
66
+ const idiomsJson = JSON.stringify(structuralIdioms);
67
+ return `(function() {
68
+ var t0 = performance.now();
69
+
70
+ // ── Interactive selector (mirrors walker.ts) ─────────────────────────────
71
+ var INTERACTIVE_SEL = [
72
+ 'a', 'button', 'input', 'textarea', 'select',
73
+ '[role="button"]', '[role="link"]', '[role="textbox"]',
74
+ '[role="checkbox"]', '[role="radio"]', '[role="combobox"]',
75
+ '[role="searchbox"]', '[role="menuitem"]', '[role="tab"]',
76
+ '[tabindex]:not([tabindex="-1"])'
77
+ ].join(', ');
78
+
79
+ // SVG ancestor selector (includes menuitem + tab for Jira/AWS patterns)
80
+ var SVG_ANCESTOR_SEL = 'button, a, [role="button"], [role="menuitem"], [role="tab"]';
81
+
82
+ var targetIds = new Set(${idsJson});
83
+
84
+ // ── Assign synthetic ids to any element missing data-wt-id ──
85
+ // data-wt-secret guard fires on the ATTRIBUTE (not the id), so secret fields
86
+ // are safe even when running on a synthetic id.
87
+ var syntheticPrefix = '__prune_idx_';
88
+ var syntheticEls = [];
89
+ var allEls = document.querySelectorAll('[data-wt-id], [data-wt-secret]');
90
+ var synIdx = 0;
91
+ for (var si = 0; si < allEls.length; si++) {
92
+ var sel = allEls[si];
93
+ if (!sel.getAttribute('data-wt-id')) {
94
+ var synthId = syntheticPrefix + (synIdx++);
95
+ sel.setAttribute('data-wt-id', synthId);
96
+ syntheticEls.push(sel);
97
+ targetIds.add(synthId);
98
+ }
99
+ }
100
+
101
+ // ── Pass 1: Document-level controls map ──────────────────────────────────
102
+ // Built from the WHOLE document — stale or unrelated aria-controls values
103
+ // can force-keep unrelated targets. Accepted over-keep bias: prefer recall
104
+ // over precision so the agent never loses a toggle target it needs.
105
+ var controlsMap = new Set();
106
+ var CONTROLS_ATTRS = ['aria-controls', 'aria-owns', 'data-target', 'data-bs-target'];
107
+ var extraIdioms = ${idiomsJson};
108
+ for (var ei = 0; ei < extraIdioms.length; ei++) {
109
+ if (CONTROLS_ATTRS.indexOf(extraIdioms[ei]) === -1) {
110
+ CONTROLS_ATTRS.push(extraIdioms[ei]);
111
+ }
112
+ }
113
+ try {
114
+ var allNodes = document.querySelectorAll(CONTROLS_ATTRS.map(function(a) { return '[' + a + ']'; }).join(', '));
115
+ for (var ci = 0; ci < allNodes.length; ci++) {
116
+ for (var ai = 0; ai < CONTROLS_ATTRS.length; ai++) {
117
+ var val = allNodes[ci].getAttribute(CONTROLS_ATTRS[ai]);
118
+ if (!val) continue;
119
+ var tokens = val.split(/\\s+/);
120
+ for (var ti = 0; ti < tokens.length; ti++) {
121
+ if (tokens[ti]) controlsMap.add(tokens[ti]);
122
+ }
123
+ }
124
+ }
125
+ } catch(e) {}
126
+
127
+ // ── Pass 2: Post-order hasInteractiveDescendant annotation ───────────────
128
+ // Single bottom-up pass — O(n). Each hidden-element check is then O(1).
129
+ try {
130
+ var walker = document.createTreeWalker(document.body || document, NodeFilter.SHOW_ELEMENT, null, false);
131
+ var nodeStack = [];
132
+ var node = walker.nextNode();
133
+ while (node) {
134
+ nodeStack.push(node);
135
+ node = walker.nextNode();
136
+ }
137
+ // Process in reverse (post-order)
138
+ for (var ni = nodeStack.length - 1; ni >= 0; ni--) {
139
+ var n = nodeStack[ni];
140
+ var isInteractive = false;
141
+ try { isInteractive = n.matches(INTERACTIVE_SEL); } catch(e) {}
142
+ var childHas = false;
143
+ try {
144
+ var children = n.children;
145
+ for (var ki = 0; ki < children.length; ki++) {
146
+ if (children[ki].__wtHasInteractive) { childHas = true; break; }
147
+ }
148
+ } catch(e) {}
149
+ n.__wtHasInteractive = isInteractive || childHas;
150
+ }
151
+ } catch(e) {}
152
+
153
+ // ── Pass 3: Per-element prune decision ───────────────────────────────────
154
+ var keptIds = [];
155
+ var svgLabels = {};
156
+ var prunedCount = 0;
157
+ var svgCount = 0;
158
+
159
+ targetIds.forEach(function(wtId) {
160
+ var el;
161
+ try {
162
+ el = document.querySelector('[data-wt-id="' + wtId + '"]');
163
+ } catch(e) {}
164
+ if (!el) { prunedCount++; return; }
165
+
166
+ // Secret guard — fires on ATTRIBUTE, independent of whether id is synthetic
167
+ if (el.hasAttribute('data-wt-secret')) {
168
+ keptIds.push(String(wtId));
169
+ return;
170
+ }
171
+
172
+ // Visibility check
173
+ var hidden = false;
174
+ try {
175
+ var rect = el.getBoundingClientRect();
176
+ if (rect.width === 0 && rect.height === 0) {
177
+ hidden = true;
178
+ } else {
179
+ var cs = window.getComputedStyle(el);
180
+ if (cs.visibility === 'hidden' || cs.display === 'none') hidden = true;
181
+ }
182
+ } catch(e) {}
183
+
184
+ if (!hidden) {
185
+ // Visible — keep; handle SVG label replacement
186
+ keptIds.push(String(wtId));
187
+ if (el.tagName && el.tagName.toLowerCase() === 'svg') {
188
+ var label = resolveSvgLabel(el, SVG_ANCESTOR_SEL);
189
+ svgLabels[String(wtId)] = label;
190
+ svgCount++;
191
+ }
192
+ return;
193
+ }
194
+
195
+ // Hidden — keep if has interactive descendant
196
+ if (el.__wtHasInteractive) {
197
+ keptIds.push(String(wtId));
198
+ return;
199
+ }
200
+
201
+ // Hidden — keep if referenced by a known toggle trigger (controlsMap)
202
+ var elId = el.getAttribute('id');
203
+ if (elId && controlsMap.has(elId)) {
204
+ keptIds.push(String(wtId));
205
+ return;
206
+ }
207
+
208
+ // Drop
209
+ prunedCount++;
210
+ });
211
+
212
+ // ── Strip synthetic ids from DOM ─────────────────────────────────────────
213
+ for (var ri = 0; ri < syntheticEls.length; ri++) {
214
+ try { syntheticEls[ri].removeAttribute('data-wt-id'); } catch(e) {}
215
+ }
216
+
217
+ // ── SVG label resolution helper ──────────────────────────────────────────
218
+ function resolveSvgLabel(svg, ancestorSel) {
219
+ // 1. SVG's own aria-label
220
+ var label = svg.getAttribute('aria-label');
221
+ if (label && label.trim()) return '[SVG Icon: ' + label.trim() + ']';
222
+
223
+ // 2. SVG's own <title> child
224
+ try {
225
+ var title = svg.querySelector('title');
226
+ if (title && title.textContent && title.textContent.trim()) {
227
+ return '[SVG Icon: ' + title.textContent.trim() + ']';
228
+ }
229
+ } catch(e) {}
230
+
231
+ // 3. Closest interactive ancestor's aria-label
232
+ // Covers: <button aria-label="Delete"><svg/></button>
233
+ // <a role="menuitem" aria-label="More actions"><svg/></a> (Jira)
234
+ // <div role="tab" aria-label="Overview"><svg/></div> (AWS)
235
+ try {
236
+ var ancestor = svg.closest(ancestorSel);
237
+ if (ancestor) {
238
+ var aLabel = ancestor.getAttribute('aria-label');
239
+ if (aLabel && aLabel.trim()) return '[SVG Icon: ' + aLabel.trim() + ']';
240
+ }
241
+ } catch(e) {}
242
+
243
+ return '[SVG Icon]';
244
+ }
245
+
246
+ var elapsedMs = performance.now() - t0;
247
+
248
+ // ── Perf-gate: if this took > 5ms, flag it — caller will decide on split ─
249
+ // (In this single-evaluate path we can only report, not split retroactively.
250
+ // The split path would be invoked by the caller on a second evaluate call.)
251
+ return JSON.stringify({
252
+ keptIds: keptIds,
253
+ svgLabels: svgLabels,
254
+ stats: {
255
+ total: targetIds.size,
256
+ pruned: prunedCount,
257
+ svgReplaced: svgCount,
258
+ elapsedMs: elapsedMs,
259
+ splitPath: false
260
+ }
261
+ });
262
+ })()`;
263
+ }
@@ -0,0 +1,101 @@
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
+ import CDP from "chrome-remote-interface";
17
+ export interface RefCoordEntry {
18
+ /** Primary selector — [data-wt-id="..."] form preferred */
19
+ selector: string;
20
+ /** Center X at capture time */
21
+ x: number;
22
+ /** Center Y at capture time */
23
+ y: number;
24
+ /** Epoch ms — for TTL gate */
25
+ capturedAt: number;
26
+ /** CDP backendNodeId for re-resolution via DOM.getBoxModel when selector fails */
27
+ backendNodeId?: number;
28
+ /**
29
+ * Lightweight identity string: "tag:role:labelPrefix"
30
+ * Checked inside atomicResolve to detect DOM replacement (re-render).
31
+ */
32
+ fingerprint: string;
33
+ /** Holographic: visual bounding box hash for deduplication */
34
+ visualHash?: string;
35
+ /** Holographic: full or partial XPath */
36
+ xpath?: string;
37
+ /** Holographic: current value for stateful tracking */
38
+ value?: string;
39
+ /** Holographic: label text */
40
+ label?: string;
41
+ }
42
+ export interface AtomicResolveResult {
43
+ status: "OK" | "NOT_FOUND" | "STALE" | "INVISIBLE" | "SHADOW";
44
+ x?: number;
45
+ y?: number;
46
+ }
47
+ /** Default TTL: 30 seconds. Overridden by config.refCacheTtlMs at call sites. */
48
+ export declare const DEFAULT_URC_TTL_MS = 30000;
49
+ /** Returns stale miss rate [0–1] for the last observation window. */
50
+ export declare function getStaleMissRate(hostname: string): number;
51
+ /** Write an entry into the URC for a given tab and refId. */
52
+ export declare function urcSet(targetId: string, refId: string, entry: RefCoordEntry): void;
53
+ /**
54
+ * Read an entry from the URC.
55
+ * Returns null if: not found, OR entry is older than maxAgeMs.
56
+ */
57
+ export declare function urcGet(targetId: string, refId: string, maxAgeMs?: number): RefCoordEntry | null;
58
+ /** Return all cached entries for a tab (used by health checks and delta validation). */
59
+ export declare function urcGetAll(targetId: string): Map<string, RefCoordEntry>;
60
+ /**
61
+ * Invalidate all URC entries for a tab.
62
+ * Must be called on: frameNavigated, full map rebuild, tab close.
63
+ */
64
+ export declare function urcInvalidate(targetId: string): void;
65
+ /**
66
+ * Bulk-populate the URC from a freshly built ActionElement array.
67
+ * Called at the end of buildActionMap and buildAXTreeMap.
68
+ * Only writes entries that have both x and selector defined.
69
+ */
70
+ export declare function urcPopulate(targetId: string, elements: Array<{
71
+ id: string;
72
+ selector?: string;
73
+ x?: number;
74
+ y?: number;
75
+ backendNodeId?: number;
76
+ role?: string;
77
+ label?: string;
78
+ value?: string;
79
+ type?: string;
80
+ fingerprint?: string;
81
+ visualHash?: string;
82
+ xpath?: string;
83
+ }>): void;
84
+ /**
85
+ * Single CDP Runtime.evaluate that simultaneously:
86
+ * 1. Finds the element via selector (with WeakMap re-hydration fallback)
87
+ * 2. Validates tag/role fingerprint (lightweight string check)
88
+ * 3. Returns fresh coordinates from getBoundingClientRect
89
+ *
90
+ * Cost: 1 CDP call (~2–5ms). On a URC cache hit this is the ONLY extra call made.
91
+ * On a full miss (Stage 3+) it is still cheaper than re-running buildActionMap.
92
+ */
93
+ export declare function atomicResolve(session: CDP.Client, selector: string, fingerprint?: string, hostname?: string): Promise<AtomicResolveResult>;
94
+ /**
95
+ * Attempt coordinate resolution via CDP DOM.getBoxModel using a backendNodeId.
96
+ * Used as Stage 4 in the click fallback chain when the selector path is unavailable.
97
+ */
98
+ export declare function resolveViaBackendNodeId(session: CDP.Client, backendNodeId: number): Promise<{
99
+ x: number;
100
+ y: number;
101
+ } | null>;