@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,74 @@
1
+ import { getRepoContext, isGithubError } from "./repoContext.js";
2
+ import { loadRepoCache } from "./cache.js";
3
+ import { githubAnalyze } from "./analyze.js";
4
+ export async function githubRead(session, args, config) {
5
+ const context = await getRepoContext(session, args.repo);
6
+ if (isGithubError(context))
7
+ return context;
8
+ const { owner, repo } = context;
9
+ // Check cache to get default_branch and file_tree
10
+ let cached = await loadRepoCache(owner, repo, config);
11
+ if (!cached) {
12
+ // We need cache to get default_branch. Call analyze first.
13
+ const analyzeResult = await githubAnalyze(session, config, args.repo);
14
+ if (isGithubError(analyzeResult)) {
15
+ return analyzeResult;
16
+ }
17
+ cached = analyzeResult;
18
+ }
19
+ const default_branch = cached.default_branch;
20
+ const file_tree = cached.file_tree;
21
+ const results = [];
22
+ const warnings = [];
23
+ const fetchPromises = args.paths.map(async (rawPath) => {
24
+ // Sanitize path
25
+ const path = rawPath.replace(/\\/g, '/').replace(/^\/+/, '');
26
+ if (path.includes('..') || path.includes('\0')) {
27
+ return { path: rawPath, error: "INVALID_PATH", message: "Path traversal or null bytes detected." };
28
+ }
29
+ // Check if path exists in tree
30
+ const exists = file_tree.some(entry => entry.path === path);
31
+ if (!exists) {
32
+ warnings.push(`Path '${path}' was not found in the cached file tree. Branch contents may differ.`);
33
+ }
34
+ const rawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${default_branch}/${path}`;
35
+ try {
36
+ const controller = new AbortController();
37
+ const timeout = setTimeout(() => controller.abort(), 10000);
38
+ const res = await fetch(rawUrl, { signal: controller.signal });
39
+ clearTimeout(timeout);
40
+ if (!res.ok) {
41
+ return { path, error: "FETCH_FAILED", status: res.status, message: res.statusText };
42
+ }
43
+ const content = await res.text();
44
+ const lines = content.split('\n');
45
+ const total_lines = lines.length;
46
+ let returnedContent = content;
47
+ let truncated = false;
48
+ if (args.line_range && Array.isArray(args.line_range) && args.line_range.length === 2) {
49
+ const start = Math.max(0, args.line_range[0] - 1); // 1-indexed to 0-indexed
50
+ const end = Math.min(total_lines, args.line_range[1]);
51
+ returnedContent = lines.slice(start, end).join('\n');
52
+ truncated = (start > 0 || end < total_lines);
53
+ }
54
+ else if (total_lines > 200) {
55
+ returnedContent = lines.slice(0, 200).join('\n');
56
+ truncated = true;
57
+ }
58
+ return {
59
+ path,
60
+ total_lines,
61
+ truncated,
62
+ content: returnedContent
63
+ };
64
+ }
65
+ catch (err) {
66
+ return { path, error: "FETCH_ERROR", message: err.message };
67
+ }
68
+ });
69
+ const pathResults = await Promise.all(fetchPromises);
70
+ return {
71
+ warnings: warnings.length > 0 ? warnings : undefined,
72
+ files: pathResults
73
+ };
74
+ }
@@ -0,0 +1,20 @@
1
+ import CDP from "chrome-remote-interface";
2
+ export interface RepoContext {
3
+ owner: string;
4
+ repo: string;
5
+ }
6
+ export type GithubToolError = {
7
+ error: string;
8
+ message: string;
9
+ [key: string]: any;
10
+ };
11
+ /**
12
+ * Resolve the current GitHub repo context.
13
+ *
14
+ * @param session - CDP session
15
+ * @param repoSlug - Optional "owner/repo" or full GitHub URL. When provided,
16
+ * the function uses it directly instead of reading the tab URL.
17
+ * If the browser is not already on GitHub it navigates there.
18
+ */
19
+ export declare function getRepoContext(session: CDP.Client, repoSlug?: unknown): Promise<RepoContext | GithubToolError>;
20
+ export declare function isGithubError(result: any): result is GithubToolError;
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Parse a repo slug from a string that can be:
3
+ * - "owner/repo"
4
+ * - "https://github.com/owner/repo[/anything]"
5
+ *
6
+ * Returns null (instead of throwing) for any non-string input so callers
7
+ * never encounter `TypeError: slug.trim is not a function`.
8
+ */
9
+ function parseRepoSlug(slug) {
10
+ // ── Enterprise guard: reject any non-string at runtime ─────────────────────
11
+ // Zod marks `repo` as optional(string), but MCP argument dispatch can
12
+ // forward an object / number / null when the model omits the field in some
13
+ // call patterns. Checking typeof here is the only safe boundary.
14
+ if (typeof slug !== "string")
15
+ return null;
16
+ const trimmed = slug.trim();
17
+ if (!trimmed)
18
+ return null;
19
+ // Full GitHub URL
20
+ try {
21
+ const u = new URL(trimmed);
22
+ if (u.hostname === "github.com") {
23
+ const parts = u.pathname.split("/").filter(Boolean);
24
+ if (parts.length >= 2)
25
+ return { owner: parts[0], repo: parts[1] };
26
+ }
27
+ }
28
+ catch { /* not a URL — fall through to shorthand parse */ }
29
+ // "owner/repo" shorthand
30
+ const parts = trimmed.split("/").filter(Boolean);
31
+ if (parts.length >= 2)
32
+ return { owner: parts[0], repo: parts[1] };
33
+ return null;
34
+ }
35
+ /**
36
+ * Resolve the current GitHub repo context.
37
+ *
38
+ * @param session - CDP session
39
+ * @param repoSlug - Optional "owner/repo" or full GitHub URL. When provided,
40
+ * the function uses it directly instead of reading the tab URL.
41
+ * If the browser is not already on GitHub it navigates there.
42
+ */
43
+ export async function getRepoContext(session, repoSlug) {
44
+ // ── Runtime type normalisation ────────────────────────────────────────────
45
+ // `repoSlug` is declared optional(string) in the Zod schema, but callers
46
+ // such as server.ts cast `(args as any).repo`, meaning any JS value can
47
+ // arrive here. Normalise to string | undefined before any string ops.
48
+ const slugStr = typeof repoSlug === "string" && repoSlug.trim() !== ""
49
+ ? repoSlug.trim()
50
+ : undefined;
51
+ // If a repo slug was explicitly given, prefer it over the current tab URL.
52
+ if (slugStr) {
53
+ const parsed = parseRepoSlug(slugStr);
54
+ if (!parsed) {
55
+ return {
56
+ error: "INVALID_REPO_SLUG",
57
+ message: `Cannot parse a GitHub owner/repo from "${slugStr}". Use "owner/repo" or a full github.com URL.`
58
+ };
59
+ }
60
+ // Check whether the browser is already on a matching GitHub page.
61
+ // If not, navigate there so downstream tools that read the DOM work correctly.
62
+ try {
63
+ const { result } = await session.Runtime.evaluate({
64
+ expression: 'window.location.href',
65
+ returnByValue: true,
66
+ });
67
+ const currentUrl = (typeof result.value === "string" ? result.value : "");
68
+ const isOnMatchingRepo = currentUrl.includes(`github.com/${parsed.owner}/${parsed.repo}`);
69
+ if (!isOnMatchingRepo) {
70
+ const targetUrl = `https://github.com/${parsed.owner}/${parsed.repo}`;
71
+ await session.Page.navigate({ url: targetUrl });
72
+ // Wait briefly for the page to start loading
73
+ await new Promise(r => setTimeout(r, 1500));
74
+ }
75
+ }
76
+ catch { /* non-fatal — proceed with parsed slug */ }
77
+ return parsed;
78
+ }
79
+ // No slug given: read from current tab URL (original behaviour)
80
+ let urlStr;
81
+ try {
82
+ const { result } = await session.Runtime.evaluate({
83
+ expression: 'window.location.href',
84
+ returnByValue: true,
85
+ });
86
+ urlStr = result.value;
87
+ }
88
+ catch (err) {
89
+ return {
90
+ error: "CONTEXT_ERROR",
91
+ message: "Failed to evaluate window.location.href on the active tab."
92
+ };
93
+ }
94
+ if (!urlStr) {
95
+ return {
96
+ error: "CONTEXT_ERROR",
97
+ message: "Active tab URL is empty."
98
+ };
99
+ }
100
+ let url;
101
+ try {
102
+ url = new URL(urlStr);
103
+ }
104
+ catch {
105
+ return {
106
+ error: "INVALID_URL",
107
+ message: "Active tab URL is invalid."
108
+ };
109
+ }
110
+ if (url.hostname !== "github.com") {
111
+ return {
112
+ error: "NOT_ON_GITHUB_REPO",
113
+ message: "Navigate to a GitHub repository first, or pass a 'repo' argument (e.g. \"owner/repo\")."
114
+ };
115
+ }
116
+ const parts = url.pathname.split("/").filter(Boolean);
117
+ if (parts.length < 2) {
118
+ return {
119
+ error: "NOT_ON_GITHUB_REPO",
120
+ message: "Navigate to a specific GitHub repository first (e.g. github.com/owner/repo)."
121
+ };
122
+ }
123
+ const owner = parts[0];
124
+ const repo = parts[1];
125
+ return { owner, repo };
126
+ }
127
+ export function isGithubError(result) {
128
+ return result && typeof result === "object" && "error" in result && "message" in result;
129
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Weavetab — tools/highlight.ts
3
+ * browser_highlight: draws a CSS shimmer animation over the element that auto-fades.
4
+ * Falls back through 5 strategies so w:NN refs always resolve.
5
+ */
6
+ import CDP from "chrome-remote-interface";
7
+ import type { Config } from "../config/loader.js";
8
+ export interface HighlightOptions {
9
+ selector?: string;
10
+ id?: string;
11
+ ref?: string;
12
+ target?: string;
13
+ label?: string;
14
+ nodeId?: number;
15
+ color?: string;
16
+ }
17
+ export declare function browserHighlight(session: CDP.Client, options: HighlightOptions, _config: Config): Promise<{
18
+ success: boolean;
19
+ message: string;
20
+ }>;
@@ -0,0 +1,140 @@
1
+ import { getConfigAgentId } from "../config/loader.js";
2
+ export async function browserHighlight(session, options, _config) {
3
+ const { selector, nodeId } = options;
4
+ // Inject shimmer keyframe once
5
+ await session.Runtime.evaluate({
6
+ expression: `(function(){
7
+ if (!document.getElementById('wt-shimmer-style')) {
8
+ var s = document.createElement('style');
9
+ s.id = 'wt-shimmer-style';
10
+ s.textContent = '@keyframes wt-shimmer{0%{opacity:0;background-position:-200% 0}15%{opacity:0.9}40%{opacity:0.7}85%{opacity:0.3}100%{opacity:0;background-position:200% 0}}';
11
+ (document.head || document.documentElement).appendChild(s);
12
+ }
13
+ })()`,
14
+ returnByValue: true,
15
+ }).catch(() => { });
16
+ // Draw shimmer overlay at raw coordinates — works without any DOM query
17
+ function makeCoordShimmer(x, y, w, h) {
18
+ return `(function(){
19
+ var div = document.createElement('div');
20
+ div.style.cssText = 'position:fixed;left:${x}px;top:${y}px;width:${w}px;height:${h}px;z-index:2147483647;pointer-events:none;background:linear-gradient(120deg,transparent 30%,rgba(139,92,246,0.3) 50%,transparent 70%);background-size:200% 100%;animation:wt-shimmer 0.5s ease-out forwards;border:1.5px solid rgba(139,92,246,0.6);border-radius:3px;box-shadow:0 0 8px rgba(139,92,246,0.25);';
21
+ (document.getElementById('Weavetab-root')?.shadowRoot || document.getElementById('Weavetab-root') || document.body).appendChild(div);
22
+ setTimeout(function(){ if(div.parentNode) div.parentNode.removeChild(div); }, 600);
23
+ return true;
24
+ })()`;
25
+ }
26
+ // Draw shimmer by querying a CSS selector
27
+ function makeSelectorShimmer(sel) {
28
+ const escaped = sel.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
29
+ return `(function(){
30
+ var el;
31
+ try { el = document.querySelector('${escaped}'); } catch(e) {}
32
+ if (!el && window.__Weavetab_shadowQuery) {
33
+ try { el = window.__Weavetab_shadowQuery('${escaped}'); } catch(e) {}
34
+ }
35
+ if (!el) return false;
36
+ var r = el.getBoundingClientRect();
37
+ if (r.width === 0 && r.height === 0) return false;
38
+ var div = document.createElement('div');
39
+ div.style.cssText = 'position:fixed;left:'+r.left+'px;top:'+r.top+'px;width:'+r.width+'px;height:'+r.height+'px;z-index:2147483647;pointer-events:none;background:linear-gradient(120deg,transparent 30%,rgba(139,92,246,0.3) 50%,transparent 70%);background-size:200% 100%;animation:wt-shimmer 0.5s ease-out forwards;border:1.5px solid rgba(139,92,246,0.6);border-radius:3px;box-shadow:0 0 8px rgba(139,92,246,0.25);';
40
+ (document.getElementById('Weavetab-root')?.shadowRoot || document.getElementById('Weavetab-root') || document.body).appendChild(div);
41
+ setTimeout(function(){ if(div.parentNode) div.parentNode.removeChild(div); }, 600);
42
+ return true;
43
+ })()`;
44
+ }
45
+ async function trySelector(sel) {
46
+ const r = await session.Runtime.evaluate({ expression: makeSelectorShimmer(sel), returnByValue: true })
47
+ .catch(() => ({ result: { value: false } }));
48
+ return !!r?.result?.value;
49
+ }
50
+ // Use CDP DOM.getBoxModel to get coordinates for a backendNodeId — no DOM query needed
51
+ async function tryNodeHighlight(bnId) {
52
+ try {
53
+ const { nodeIds } = await session.DOM.pushNodesByBackendIdsToFrontend({ backendNodeIds: [bnId] });
54
+ if (!nodeIds?.[0])
55
+ return false;
56
+ const box = await session.DOM.getBoxModel({ nodeId: nodeIds[0] }).catch(() => null);
57
+ if (!box?.model?.content)
58
+ return false;
59
+ const q = box.model.content;
60
+ const x = q[0], y = q[1], w = q[2] - q[0], h = q[5] - q[1];
61
+ if (w <= 0 || h <= 0)
62
+ return false;
63
+ const r = await session.Runtime.evaluate({ expression: makeCoordShimmer(x, y, w, h), returnByValue: true });
64
+ return !!r?.result?.value;
65
+ }
66
+ catch {
67
+ return false;
68
+ }
69
+ }
70
+ // ── Resolve params ─────────────────────────────────────────────────────────
71
+ let resolvedSelector = selector;
72
+ let numericId = null;
73
+ if (!resolvedSelector) {
74
+ const refId = options.id || options.ref || options.target;
75
+ if (refId) {
76
+ const stripped = refId.replace(/^w:/, '');
77
+ numericId = parseInt(stripped, 10);
78
+ resolvedSelector = `[data-wt-id="${stripped}"]`;
79
+ }
80
+ else if (options.label) {
81
+ resolvedSelector = `[aria-label="${options.label.replace(/"/g, '\\"')}"]`;
82
+ }
83
+ }
84
+ // Strategy 1: CSS selector query
85
+ if (resolvedSelector && await trySelector(resolvedSelector)) {
86
+ return { success: true, message: `Highlighted ${resolvedSelector}` };
87
+ }
88
+ // Strategy 2: Extension oracle coords (__Weavetab_getCoords)
89
+ if (resolvedSelector) {
90
+ const coordsRes = await session.Runtime.evaluate({
91
+ expression: `(window.__Weavetab_getCoords || window.__Weavetab_getCoords || function(){return null;})(${JSON.stringify(resolvedSelector)})`,
92
+ returnByValue: true,
93
+ }).catch(() => null);
94
+ const cv = coordsRes?.result?.value;
95
+ if (cv?.raw) {
96
+ const { left, top, w, h } = cv.raw;
97
+ const r = await session.Runtime.evaluate({ expression: makeCoordShimmer(left, top, w, h), returnByValue: true });
98
+ if (r?.result?.value)
99
+ return { success: true, message: `Highlighted ${resolvedSelector} via coords` };
100
+ }
101
+ }
102
+ // Strategy 3: CDP DOM by backendNodeId (for explicit nodeId param)
103
+ if (nodeId && await tryNodeHighlight(nodeId)) {
104
+ return { success: true, message: `Highlighted backendNodeId ${nodeId}` };
105
+ }
106
+ // Strategy 4: Walk action map cache to find the backendNodeId for a w:NN ref
107
+ if (numericId !== null) {
108
+ try {
109
+ const { getCachedActionMap } = await import('../state/cache.js');
110
+ const { sessionRegistry } = await import('../state/session.js');
111
+ const activeClient = sessionRegistry.getActive(getConfigAgentId(_config));
112
+ const targetId = activeClient?.targetId || 'default';
113
+ const cachedMap = activeClient?.url ? getCachedActionMap(targetId, activeClient.url) : null;
114
+ if (cachedMap?.elements) {
115
+ const el = cachedMap.elements.find(e => {
116
+ const n = parseInt(String(e.id).replace(/^w:/, ''), 10);
117
+ return n === numericId;
118
+ });
119
+ if (el?.selector && await trySelector(el.selector)) {
120
+ return { success: true, message: `Highlighted w:${numericId} via cache selector` };
121
+ }
122
+ if (el?.backendNodeId && await tryNodeHighlight(el.backendNodeId)) {
123
+ return { success: true, message: `Highlighted w:${numericId} via action map backendNodeId` };
124
+ }
125
+ // Strategy 5: coordinate fallback from cached element position
126
+ if (el?.x !== undefined && el?.y !== undefined) {
127
+ const ew = el.width ?? 80, eh = el.height ?? 24;
128
+ const r = await session.Runtime.evaluate({
129
+ expression: makeCoordShimmer(el.x - ew / 2, el.y - eh / 2, ew, eh),
130
+ returnByValue: true,
131
+ });
132
+ if (r?.result?.value)
133
+ return { success: true, message: `Highlighted w:${numericId} via cache coords` };
134
+ }
135
+ }
136
+ }
137
+ catch { /* no cache */ }
138
+ }
139
+ return { success: false, message: `Could not find element to highlight` };
140
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Weavetab — tools/hover.ts
3
+ * browser_hover: dispatch mouseMoveTo CDP event on a nodeId or selector.
4
+ * Weavetab by fy2ne
5
+ */
6
+ import CDP from "chrome-remote-interface";
7
+ import type { Config } from "../config/loader.js";
8
+ export interface HoverOptions {
9
+ selector?: string;
10
+ nodeId?: number;
11
+ waitForChild?: string;
12
+ holdMs?: number;
13
+ }
14
+ export declare function browserHover(session: CDP.Client, options: HoverOptions, config: Config): Promise<{
15
+ success: boolean;
16
+ x: number;
17
+ y: number;
18
+ }>;
19
+ //# sourceMappingURL=hover.d.ts.map
@@ -0,0 +1,57 @@
1
+ export async function browserHover(session, options, config) {
2
+ const { selector, nodeId, waitForChild, holdMs = 300 } = options;
3
+ let x, y;
4
+ if (selector) {
5
+ const res = await session.Runtime.evaluate({
6
+ expression: `window.__Weavetab_getCoords(${JSON.stringify(selector)})`,
7
+ returnByValue: true
8
+ }).catch(() => null);
9
+ const coords = res?.result?.value;
10
+ if (!coords)
11
+ throw new Error(`Selector not found or unresolved: ${selector}`);
12
+ x = coords.x;
13
+ y = coords.y;
14
+ }
15
+ else if (nodeId) {
16
+ try {
17
+ const { model } = await session.DOM.getBoxModel({ backendNodeId: nodeId });
18
+ x = (model.content[0] + model.content[2]) / 2;
19
+ y = (model.content[1] + model.content[5]) / 2;
20
+ }
21
+ catch {
22
+ throw new Error(`Failed to get box model for nodeId ${nodeId}`);
23
+ }
24
+ }
25
+ else {
26
+ throw new Error("browser_hover requires either selector or nodeId");
27
+ }
28
+ // Move mouse to element via CDP
29
+ await session.Input.dispatchMouseEvent({
30
+ type: "mouseMoved",
31
+ x,
32
+ y
33
+ });
34
+ // Dispatch both mouseenter AND mouseover (some sites only listen to one)
35
+ await session.Runtime.evaluate({
36
+ expression: `(function(sel) {
37
+ const el = document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null);
38
+ if (!el) return;
39
+ el.dispatchEvent(new MouseEvent('mouseenter', { bubbles: false, cancelable: true }));
40
+ el.dispatchEvent(new MouseEvent('mouseover', { bubbles: true, cancelable: true }));
41
+ })(${JSON.stringify(selector || '')})`,
42
+ returnByValue: true
43
+ }).catch(() => { });
44
+ await new Promise(r => setTimeout(r, holdMs));
45
+ // If waiting for submenu, use browserWait
46
+ if (waitForChild) {
47
+ const { browserWait } = await import("./wait.js");
48
+ await browserWait(session, {
49
+ for: "element",
50
+ value: waitForChild,
51
+ timeout: 5000
52
+ }, config);
53
+ await new Promise(r => setTimeout(r, 200)); // let animation finish
54
+ }
55
+ return { success: true, x, y };
56
+ }
57
+ //# sourceMappingURL=hover.js.map
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Weavetab — tools/inspect.ts
3
+ * browser_inspect: Return detailed CSS styles, computed styles, and event listeners for an element.
4
+ * Weavetab by fy2ne
5
+ */
6
+ import CDP from "chrome-remote-interface";
7
+ import type { Config } from "../config/loader.js";
8
+ export interface InspectOptions {
9
+ nodeId?: number;
10
+ selector?: string;
11
+ ref?: string;
12
+ }
13
+ export declare function browserInspect(session: CDP.Client, options: InspectOptions, _config: Config): Promise<{
14
+ success: boolean;
15
+ data: any;
16
+ message: string;
17
+ }>;
@@ -0,0 +1,125 @@
1
+ import { getConfigAgentId } from "../config/loader.js";
2
+ import { getCachedActionMap, cacheActionMap } from "../state/cache.js";
3
+ import { sessionRegistry } from "../state/session.js";
4
+ import { buildActionMap } from "../cdp/walker.js";
5
+ export async function browserInspect(session, options, _config) {
6
+ let { nodeId, selector, ref } = options;
7
+ if (!nodeId && !selector && !ref) {
8
+ throw new Error("browser_inspect requires nodeId, selector, or ref");
9
+ }
10
+ const { DOM, CSS, DOMDebugger } = session;
11
+ await DOM.enable();
12
+ const doc = await DOM.getDocument({ depth: -1 }).catch(() => null);
13
+ await CSS.enable().catch(() => { });
14
+ let resolvedNodeId = nodeId || null;
15
+ // Step 1: Resolve a live nodeId from any input
16
+ if (!resolvedNodeId && ref) {
17
+ const active = sessionRegistry.getActive(getConfigAgentId(_config));
18
+ if (active) {
19
+ // getCachedActionMap returns null when the DOM has mutated since the last
20
+ // read (lastMutationTime > lastReadTime). On a cache miss, rebuild the map
21
+ // so ref IDs remain resolvable after any DOM change instead of silently
22
+ // falling through to "Could not resolve element to a node ID".
23
+ let map = getCachedActionMap(active.targetId);
24
+ if (!map) {
25
+ try {
26
+ map = await buildActionMap(session, { lite: true }, active.targetId);
27
+ cacheActionMap(active.targetId, map);
28
+ }
29
+ catch { /* best-effort; will fall through to selector strategies */ }
30
+ }
31
+ if (map) {
32
+ const el = map.elements.find((e) => e.id === ref || e.id.endsWith(ref.startsWith("w:") ? ref.slice(2) : ref));
33
+ if (el?.backendNodeId) {
34
+ const desc = await DOM.describeNode({ backendNodeId: el.backendNodeId }).catch(() => null);
35
+ if (desc?.node?.nodeId)
36
+ resolvedNodeId = desc.node.nodeId;
37
+ }
38
+ if (!resolvedNodeId && el?.selector) {
39
+ selector = el.selector;
40
+ }
41
+ // Also try data-wt-id selector directly when backendNodeId lookup fails
42
+ if (!resolvedNodeId && ref) {
43
+ const rawId = ref.startsWith("w:") ? ref.slice(2) : ref;
44
+ selector = selector || `[data-wt-id="${rawId}"]`;
45
+ }
46
+ }
47
+ }
48
+ }
49
+ if (!resolvedNodeId && selector) {
50
+ const res = await session.Runtime.evaluate({
51
+ expression: `(function(sel) {
52
+ var els = document.querySelectorAll(sel);
53
+ for (var i = 0; i < els.length; i++) {
54
+ if (!els[i].closest('#wt-shadow-host, [data-wt-ignore]')) {
55
+ return els[i];
56
+ }
57
+ }
58
+ return els[0] || null;
59
+ })(${JSON.stringify(selector)})`,
60
+ });
61
+ if (res.result?.objectId) {
62
+ const nodeRes = await DOM.requestNode({ objectId: res.result.objectId }).catch(() => null);
63
+ if (nodeRes?.nodeId)
64
+ resolvedNodeId = nodeRes.nodeId;
65
+ }
66
+ }
67
+ if (!resolvedNodeId) {
68
+ throw new Error("Could not resolve element to a node ID");
69
+ }
70
+ // Step 2: Fetch box model, computed styles, and event listeners in parallel
71
+ const boxModel = await DOM.getBoxModel({ nodeId: resolvedNodeId }).catch(() => null);
72
+ // Try CSS.getComputedStyleForNode first (CDP-native), fall back to Runtime.evaluate
73
+ let computedStylesData = null;
74
+ const cssResult = await CSS.getComputedStyleForNode({ nodeId: resolvedNodeId }).catch(() => null);
75
+ if (cssResult?.computedStyle) {
76
+ const INTERESTING = ['display', 'position', 'width', 'height', 'margin', 'padding', 'color', 'background-color', 'font-size', 'visibility', 'opacity', 'z-index', 'pointer-events'];
77
+ computedStylesData = cssResult.computedStyle
78
+ .filter((s) => INTERESTING.includes(s.name))
79
+ .reduce((acc, curr) => ({ ...acc, [curr.name]: curr.value }), {});
80
+ }
81
+ if (!computedStylesData && selector) {
82
+ // Fallback: use window.getComputedStyle via Runtime.evaluate
83
+ const { result: fallbackRes } = await session.Runtime.evaluate({
84
+ expression: `(function(sel) {
85
+ var el = document.querySelector(sel);
86
+ if (!el) return null;
87
+ var s = window.getComputedStyle(el);
88
+ var KEYS = ['display','position','width','height','margin','padding','color','background-color','font-size','visibility','opacity','z-index','pointer-events'];
89
+ var r = {};
90
+ for (var i = 0; i < KEYS.length; i++) r[KEYS[i]] = s.getPropertyValue(KEYS[i]);
91
+ return JSON.stringify(r);
92
+ })(${JSON.stringify(selector)})`,
93
+ returnByValue: true,
94
+ }).catch(() => ({ result: { value: null } }));
95
+ if (fallbackRes?.value) {
96
+ try {
97
+ computedStylesData = JSON.parse(fallbackRes.value);
98
+ }
99
+ catch { }
100
+ }
101
+ }
102
+ let listeners = [];
103
+ try {
104
+ const resolved = await DOM.resolveNode({ nodeId: resolvedNodeId });
105
+ if (resolved.object?.objectId) {
106
+ const evtRes = await DOMDebugger.getEventListeners({ objectId: resolved.object.objectId, depth: 1, pierce: true }).catch(() => null);
107
+ if (evtRes?.listeners) {
108
+ listeners = evtRes.listeners.map(l => ({ type: l.type, useCapture: l.useCapture, passive: l.passive }));
109
+ }
110
+ }
111
+ }
112
+ catch (e) {
113
+ // Ignore DOMDebugger errors
114
+ }
115
+ const data = {
116
+ boxModel: boxModel?.model ? {
117
+ width: boxModel.model.width,
118
+ height: boxModel.model.height,
119
+ content: boxModel.model.content,
120
+ } : null,
121
+ computedStyles: computedStylesData,
122
+ eventListeners: listeners
123
+ };
124
+ return { success: true, data, message: "Inspected element successfully" };
125
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=integration.test.d.ts.map