@weavetab/mcp 2.5.0-beta.1 → 2.5.0-beta.4

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 (156) hide show
  1. package/AGENTS.md +2 -1
  2. package/ARCHITECTURE.md +6 -6
  3. package/LICENSE +1 -1
  4. package/README.md +164 -802
  5. package/SECURITY.md +33 -22
  6. package/TROUBLESHOOTING.md +3 -3
  7. package/dist/cdp/bridge.d.ts +10 -0
  8. package/dist/cdp/bridge.js +285 -78
  9. package/dist/cdp/connector.d.ts +7 -3
  10. package/dist/cdp/connector.js +196 -75
  11. package/dist/cdp/helpers.d.ts +4 -0
  12. package/dist/cdp/helpers.js +181 -0
  13. package/dist/cdp/lock.d.ts +9 -6
  14. package/dist/cdp/lock.js +72 -59
  15. package/dist/cdp/omni_broker.d.ts +2 -1
  16. package/dist/cdp/omni_broker.js +13 -7
  17. package/dist/cdp/profiles.d.ts +10 -0
  18. package/dist/cdp/profiles.js +65 -0
  19. package/dist/cdp/walker.d.ts +25 -1
  20. package/dist/cdp/walker.js +354 -7
  21. package/dist/cli/plugin.d.ts +0 -2
  22. package/dist/cli/plugin.js +51 -696
  23. package/dist/cli/updater.d.ts +1 -1
  24. package/dist/cli/updater.js +27 -7
  25. package/dist/config/loader.d.ts +33 -6
  26. package/dist/config/loader.js +174 -22
  27. package/dist/desktop/discovery.d.ts +44 -0
  28. package/dist/desktop/discovery.js +364 -0
  29. package/dist/index.js +140 -64
  30. package/dist/intelligence/marks.d.ts +36 -0
  31. package/dist/intelligence/marks.js +30 -0
  32. package/dist/intelligence/patternCompiler.d.ts +118 -0
  33. package/dist/intelligence/patternCompiler.js +259 -0
  34. package/dist/intelligence/sessionMemory.d.ts +2 -0
  35. package/dist/intelligence/sessionMemory.js +18 -8
  36. package/dist/overlay/ghost.js +2 -2
  37. package/dist/overlay/injector.js +3 -5
  38. package/dist/plugin/extension_bridge.d.ts +40 -0
  39. package/dist/plugin/extension_bridge.js +158 -0
  40. package/dist/plugin/interface.d.ts +151 -15
  41. package/dist/plugin/loader.d.ts +19 -1
  42. package/dist/plugin/loader.js +331 -63
  43. package/dist/plugin/pipeline.d.ts +75 -0
  44. package/dist/plugin/pipeline.js +183 -0
  45. package/dist/plugin/registry.js +12 -1
  46. package/dist/plugin/storage.d.ts +51 -0
  47. package/dist/plugin/storage.js +164 -0
  48. package/dist/security/secrets.d.ts +1 -0
  49. package/dist/security/secrets.js +13 -0
  50. package/dist/sensors/actionability.js +1 -2
  51. package/dist/sensors/guard.js +4 -10
  52. package/dist/sensors/mutations.js +12 -0
  53. package/dist/sensors/operation_monitor.js +1 -0
  54. package/dist/server.d.ts +1 -1
  55. package/dist/server.js +600 -236
  56. package/dist/state/agent.d.ts +1 -0
  57. package/dist/state/agent.js +3 -0
  58. package/dist/state/delta.d.ts +12 -4
  59. package/dist/state/delta.js +67 -14
  60. package/dist/state/session.d.ts +8 -2
  61. package/dist/state/session.js +60 -13
  62. package/dist/tools/browser_detect.d.ts +16 -2
  63. package/dist/tools/browser_detect.js +54 -6
  64. package/dist/tools/burst.d.ts +17 -7
  65. package/dist/tools/burst.js +240 -83
  66. package/dist/tools/checkpoint.d.ts +38 -0
  67. package/dist/tools/checkpoint.js +271 -0
  68. package/dist/tools/click.d.ts +1 -0
  69. package/dist/tools/click.js +97 -98
  70. package/dist/tools/clipboard.js +31 -24
  71. package/dist/tools/design_extract.js +1 -1
  72. package/dist/tools/eval.js +1 -1
  73. package/dist/tools/fill.d.ts +20 -1
  74. package/dist/tools/fill.js +199 -40
  75. package/dist/tools/find.d.ts +16 -0
  76. package/dist/tools/find.js +67 -44
  77. package/dist/tools/github/index.d.ts +19 -4
  78. package/dist/tools/github/index.js +40 -4
  79. package/dist/tools/highlight.d.ts +6 -2
  80. package/dist/tools/highlight.js +158 -72
  81. package/dist/tools/inspect.js +1 -1
  82. package/dist/tools/key.js +65 -56
  83. package/dist/tools/navigate.d.ts +1 -1
  84. package/dist/tools/navigate.js +57 -44
  85. package/dist/tools/pdf.js +21 -13
  86. package/dist/tools/pointer.d.ts +18 -3
  87. package/dist/tools/pointer.js +89 -1
  88. package/dist/tools/read.d.ts +28 -8
  89. package/dist/tools/read.js +230 -56
  90. package/dist/tools/scrape.js +67 -13
  91. package/dist/tools/screenshot.js +21 -16
  92. package/dist/tools/scroll.js +6 -11
  93. package/dist/tools/select.js +8 -2
  94. package/dist/tools/snapshot.js +3 -34
  95. package/dist/tools/tabs.d.ts +10 -10
  96. package/dist/tools/tabs.js +55 -39
  97. package/dist/tools/thought.d.ts +2 -0
  98. package/dist/tools/thought.js +13 -14
  99. package/dist/tools/type.d.ts +2 -0
  100. package/dist/tools/type.js +109 -60
  101. package/dist/tools/type_secret.js +42 -76
  102. package/dist/tools/upload.js +1 -1
  103. package/dist/tools/video_compiler.d.ts +1 -1
  104. package/dist/tools/video_compiler.js +11 -12
  105. package/dist/version.d.ts +5 -0
  106. package/dist/version.js +5 -0
  107. package/docs/DOCUMENTATION.md +82 -36
  108. package/docs/PLUGINS.md +82 -499
  109. package/docs/TOOLS.md +51 -99
  110. package/docs/architecture/blind-injection.md +83 -0
  111. package/docs/architecture/dual-injection.md +32 -0
  112. package/docs/architecture/overview.md +49 -0
  113. package/docs/architecture/security.md +35 -0
  114. package/docs/architecture/transport.md +37 -0
  115. package/docs/captcha-stealth/captcha-solver.md +75 -0
  116. package/docs/captcha-stealth/stealth-physics.md +60 -0
  117. package/docs/cli/cli-commands.md +44 -0
  118. package/docs/cli/cli-flags.md +20 -0
  119. package/docs/cli/cli-introduction.md +20 -0
  120. package/docs/cli/cli-scripting.md +32 -0
  121. package/docs/cli/g-ide/ide-antigravity.md +24 -0
  122. package/docs/cli/g-ide/ide-claude-code.md +24 -0
  123. package/docs/cli/g-ide/ide-claude-desktop.md +32 -0
  124. package/docs/cli/g-ide/ide-cursor.md +31 -0
  125. package/docs/cli/g-ide/ide-generic.md +34 -0
  126. package/docs/cli/g-ide/ide-opencode.md +24 -0
  127. package/docs/cli/g-ide/ide-vscode-copilot.md +24 -0
  128. package/docs/cli/g-ide/ide-windsurf.md +24 -0
  129. package/docs/configuration/config-locations.md +24 -0
  130. package/docs/configuration/configuration.md +73 -0
  131. package/docs/configuration/env-variables.md +19 -0
  132. package/docs/configuration/folders.md +55 -0
  133. package/docs/desktop-automation/electron-apps.md +217 -0
  134. package/docs/get-started/installation.md +47 -0
  135. package/docs/get-started/introduction.md +60 -0
  136. package/docs/get-started/quickstart.md +54 -0
  137. package/docs/get-started/setup-wizard.md +26 -0
  138. package/docs/intelligence/site-profiles.md +151 -0
  139. package/docs/skills-ecosystem/skills-overview.md +35 -0
  140. package/docs/skills-ecosystem/skills-recipes.md +36 -0
  141. package/docs/tools/tools.md +631 -0
  142. package/docs/troubleshooting/trouble-browser.md +27 -0
  143. package/docs/troubleshooting/trouble-config.md +20 -0
  144. package/docs/troubleshooting/trouble-loop.md +31 -0
  145. package/ext-dist/background.js +10 -62
  146. package/ext-dist/content.js +333 -921
  147. package/ext-dist/hud/hud.js +9 -9
  148. package/ext-dist/manifest.json +2 -6
  149. package/ext-dist/styles.css +6 -1
  150. package/mcp.json +108 -72
  151. package/package.json +14 -9
  152. package/server.json +3 -3
  153. package/docs/DEV_MODE.md +0 -68
  154. package/docs/dev_plugin.md +0 -310
  155. package/ext-dist/sidepanel/sidepanel.js +0 -25
  156. package/ext-dist/sidepanel.html +0 -547
@@ -1,4 +1,19 @@
1
- export { githubAnalyze } from "./analyze.js";
2
- export { githubRead } from "./read.js";
3
- export { githubIssues } from "./issues.js";
4
- export { githubGetPr } from "./pr.js";
1
+ import CDP from "chrome-remote-interface";
2
+ import type { Config } from "../../config/loader.js";
3
+ import { githubAnalyze } from "./analyze.js";
4
+ import { githubRead } from "./read.js";
5
+ import { githubIssues } from "./issues.js";
6
+ import { githubGetPr } from "./pr.js";
7
+ export { githubAnalyze, githubRead, githubIssues, githubGetPr };
8
+ export interface BrowserGithubArgs {
9
+ action: "analyze" | "read" | "issues" | "pr";
10
+ repo?: string;
11
+ paths?: string[];
12
+ line_range?: [number, number];
13
+ number?: number;
14
+ search?: string;
15
+ labels?: string[];
16
+ state?: "open" | "closed" | "all";
17
+ limit?: number;
18
+ }
19
+ export declare function browserGithub(session: CDP.Client, args: BrowserGithubArgs, cfg: Config): Promise<any>;
@@ -1,4 +1,40 @@
1
- export { githubAnalyze } from "./analyze.js";
2
- export { githubRead } from "./read.js";
3
- export { githubIssues } from "./issues.js";
4
- export { githubGetPr } from "./pr.js";
1
+ import { githubAnalyze } from "./analyze.js";
2
+ import { githubRead } from "./read.js";
3
+ import { githubIssues } from "./issues.js";
4
+ import { githubGetPr } from "./pr.js";
5
+ export { githubAnalyze, githubRead, githubIssues, githubGetPr };
6
+ export async function browserGithub(session, args, cfg) {
7
+ switch (args.action) {
8
+ case "analyze":
9
+ return await githubAnalyze(session, cfg, args.repo);
10
+ case "read": {
11
+ const paths = args.paths || [];
12
+ if (!paths.length) {
13
+ return {
14
+ error: "MISSING_PATHS",
15
+ message: "action 'read' requires 'paths' parameter with at least one file path."
16
+ };
17
+ }
18
+ return await githubRead(session, { repo: args.repo, paths, line_range: args.line_range }, cfg);
19
+ }
20
+ case "issues":
21
+ return await githubIssues(session, {
22
+ repo: args.repo,
23
+ search: args.search,
24
+ number: args.number,
25
+ labels: args.labels,
26
+ state: args.state,
27
+ limit: args.limit
28
+ });
29
+ case "pr":
30
+ return await githubGetPr(session, {
31
+ repo: args.repo,
32
+ number: args.number
33
+ });
34
+ default:
35
+ return {
36
+ error: "INVALID_ACTION",
37
+ message: `Unknown action: ${args.action}. Must be 'analyze', 'read', 'issues', or 'pr'.`
38
+ };
39
+ }
40
+ }
@@ -1,18 +1,22 @@
1
1
  /**
2
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.
3
+ * browser_highlight: draws visual consideration, target lock, danger/success or shimmer overlays.
4
+ * Supports multiple candidate refs, custom styles, floating labels, and multi-tier fallbacks.
5
5
  */
6
6
  import CDP from "chrome-remote-interface";
7
7
  import type { Config } from "../config/loader.js";
8
+ export type HighlightStyle = "consideration" | "target" | "danger" | "success" | "shimmer";
8
9
  export interface HighlightOptions {
9
10
  selector?: string;
10
11
  id?: string;
11
12
  ref?: string;
13
+ refs?: string[];
12
14
  target?: string;
13
15
  label?: string;
14
16
  nodeId?: number;
15
17
  color?: string;
18
+ style?: HighlightStyle;
19
+ duration?: number;
16
20
  }
17
21
  export declare function browserHighlight(session: CDP.Client, options: HighlightOptions, _config: Config): Promise<{
18
22
  success: boolean;
@@ -1,31 +1,95 @@
1
1
  import { getConfigAgentId } from "../config/loader.js";
2
2
  export async function browserHighlight(session, options, _config) {
3
- const { selector, nodeId } = options;
4
- // Inject shimmer keyframe once
3
+ const style = options.style || (options.refs && options.refs.length > 0 ? "consideration" : "shimmer");
4
+ const defaultDuration = style === "shimmer" ? 600 : 2000;
5
+ const duration = typeof options.duration === "number" && options.duration > 0 ? options.duration : defaultDuration;
6
+ const customLabel = options.label || "";
7
+ const customColor = options.color;
8
+ // Inject required keyframes once
5
9
  await session.Runtime.evaluate({
6
10
  expression: `(function(){
7
- if (!document.getElementById('wt-shimmer-style')) {
11
+ if (!document.getElementById('wt-highlight-styles')) {
8
12
  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}}';
13
+ s.id = 'wt-highlight-styles';
14
+ s.textContent = \`
15
+ @keyframes wt-shimmer {
16
+ 0% { opacity: 0; background-position: -200% 0; }
17
+ 15% { opacity: 0.9; }
18
+ 40% { opacity: 0.7; }
19
+ 85% { opacity: 0.3; }
20
+ 100% { opacity: 0; background-position: 200% 0; }
21
+ }
22
+ @keyframes wt-consider-pulse {
23
+ 0%, 100% { box-shadow: 0 0 0 1px rgba(56,189,248,0.7), 0 0 14px rgba(56,189,248,0.35); opacity: 0.92; }
24
+ 50% { box-shadow: 0 0 0 2px rgba(56,189,248,0.95), 0 0 22px rgba(56,189,248,0.6); opacity: 1; }
25
+ }
26
+ @keyframes wt-target-lock {
27
+ 0% { transform: scale(1.05); opacity: 0.5; }
28
+ 100% { transform: scale(1); opacity: 1; }
29
+ }
30
+ @keyframes wt-danger-pulse {
31
+ 0%, 100% { box-shadow: 0 0 0 1px rgba(239,68,68,0.7), 0 0 14px rgba(239,68,68,0.35); opacity: 0.92; }
32
+ 50% { box-shadow: 0 0 0 2px rgba(239,68,68,0.95), 0 0 22px rgba(239,68,68,0.6); opacity: 1; }
33
+ }
34
+ @keyframes wt-success-glow {
35
+ 0% { opacity: 0.6; box-shadow: 0 0 0 1px rgba(16,185,129,0.7); }
36
+ 100% { opacity: 1; box-shadow: 0 0 0 2px rgba(16,185,129,0.9), 0 0 18px rgba(16,185,129,0.45); }
37
+ }
38
+ \`;
11
39
  (document.head || document.documentElement).appendChild(s);
12
40
  }
13
41
  })()`,
14
42
  returnByValue: true,
15
43
  }).catch(() => { });
16
- // Draw shimmer overlay at raw coordinates — works without any DOM query
17
- function makeCoordShimmer(x, y, w, h) {
44
+ function getOverlayCss(styleName, durMs, colorOverride) {
45
+ if (colorOverride) {
46
+ return `border:2px solid ${colorOverride};box-shadow:0 0 14px ${colorOverride};background:${colorOverride}1a;border-radius:4px;transition:opacity 0.2s ease-out;`;
47
+ }
48
+ switch (styleName) {
49
+ case "consideration":
50
+ return `border:1.5px solid rgba(56,189,248,0.9);background:rgba(56,189,248,0.08);box-shadow:0 0 14px rgba(56,189,248,0.4);border-radius:4px;animation:wt-consider-pulse 1.4s ease-in-out infinite;`;
51
+ case "target":
52
+ return `border:2px solid rgba(245,158,11,0.95);background:rgba(245,158,11,0.12);box-shadow:0 0 16px rgba(245,158,11,0.45);border-radius:4px;animation:wt-target-lock 0.2s cubic-bezier(0.16,1,0.3,1);`;
53
+ case "danger":
54
+ return `border:1.5px solid rgba(239,68,68,0.95);background:rgba(239,68,68,0.1);box-shadow:0 0 16px rgba(239,68,68,0.45);border-radius:4px;animation:wt-danger-pulse 1.4s ease-in-out infinite;`;
55
+ case "success":
56
+ return `border:1.5px solid rgba(16,185,129,0.95);background:rgba(16,185,129,0.1);box-shadow:0 0 16px rgba(16,185,129,0.45);border-radius:4px;animation:wt-success-glow 0.3s ease-out forwards;`;
57
+ case "shimmer":
58
+ default:
59
+ return `background:linear-gradient(120deg,transparent 30%,rgba(139,92,246,0.3) 50%,transparent 70%);background-size:200% 100%;animation:wt-shimmer ${durMs / 1000}s 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);`;
60
+ }
61
+ }
62
+ // Draw overlay at raw coordinates
63
+ function makeCoordOverlay(x, y, w, h, badgeLabel) {
64
+ const styleCss = getOverlayCss(style, duration, customColor);
65
+ const escapedBadge = badgeLabel ? badgeLabel.replace(/"/g, '&quot;') : '';
66
+ const badgeHtml = escapedBadge
67
+ ? `<span style="position:absolute;top:-20px;left:0;background:#09090b;border:1px solid #27272a;color:#f4f4f5;font-size:10px;font-family:-apple-system,BlinkMacSystemFont,\\'Segoe UI\\',sans-serif;font-weight:500;padding:1px 6px;border-radius:4px;white-space:nowrap;pointer-events:none;box-shadow:0 2px 6px rgba(0,0,0,0.5);">${escapedBadge}</span>`
68
+ : '';
18
69
  return `(function(){
19
70
  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);';
71
+ div.setAttribute('data-wt-ignore', 'true');
72
+ div.style.cssText = 'position:fixed;left:${x}px;top:${y}px;width:${w}px;height:${h}px;z-index:2147483647;pointer-events:none;${styleCss}';
73
+ if (${JSON.stringify(badgeHtml)}) {
74
+ div.innerHTML = ${JSON.stringify(badgeHtml)};
75
+ }
21
76
  (document.getElementById('Weavetab-root')?.shadowRoot || document.getElementById('Weavetab-root') || document.body).appendChild(div);
22
- setTimeout(function(){ if(div.parentNode) div.parentNode.removeChild(div); }, 600);
77
+ setTimeout(function(){
78
+ div.style.transition = 'opacity 0.2s ease-out';
79
+ div.style.opacity = '0';
80
+ setTimeout(function(){ if(div.parentNode) div.parentNode.removeChild(div); }, 220);
81
+ }, ${duration});
23
82
  return true;
24
83
  })()`;
25
84
  }
26
- // Draw shimmer by querying a CSS selector
27
- function makeSelectorShimmer(sel) {
85
+ // Draw overlay by querying selector
86
+ function makeSelectorOverlay(sel, badgeLabel) {
28
87
  const escaped = sel.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
88
+ const styleCss = getOverlayCss(style, duration, customColor);
89
+ const escapedBadge = badgeLabel ? badgeLabel.replace(/"/g, '&quot;') : '';
90
+ const badgeHtml = escapedBadge
91
+ ? `<span style="position:absolute;top:-20px;left:0;background:#09090b;border:1px solid #27272a;color:#f4f4f5;font-size:10px;font-family:-apple-system,BlinkMacSystemFont,\\'Segoe UI\\',sans-serif;font-weight:500;padding:1px 6px;border-radius:4px;white-space:nowrap;pointer-events:none;box-shadow:0 2px 6px rgba(0,0,0,0.5);">${escapedBadge}</span>`
92
+ : '';
29
93
  return `(function(){
30
94
  var el;
31
95
  try { el = document.querySelector('${escaped}'); } catch(e) {}
@@ -36,19 +100,26 @@ export async function browserHighlight(session, options, _config) {
36
100
  var r = el.getBoundingClientRect();
37
101
  if (r.width === 0 && r.height === 0) return false;
38
102
  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);';
103
+ div.setAttribute('data-wt-ignore', 'true');
104
+ 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;${styleCss}';
105
+ if (${JSON.stringify(badgeHtml)}) {
106
+ div.innerHTML = ${JSON.stringify(badgeHtml)};
107
+ }
40
108
  (document.getElementById('Weavetab-root')?.shadowRoot || document.getElementById('Weavetab-root') || document.body).appendChild(div);
41
- setTimeout(function(){ if(div.parentNode) div.parentNode.removeChild(div); }, 600);
109
+ setTimeout(function(){
110
+ div.style.transition = 'opacity 0.2s ease-out';
111
+ div.style.opacity = '0';
112
+ setTimeout(function(){ if(div.parentNode) div.parentNode.removeChild(div); }, 220);
113
+ }, ${duration});
42
114
  return true;
43
115
  })()`;
44
116
  }
45
- async function trySelector(sel) {
46
- const r = await session.Runtime.evaluate({ expression: makeSelectorShimmer(sel), returnByValue: true })
117
+ async function trySelector(sel, badgeLabel) {
118
+ const r = await session.Runtime.evaluate({ expression: makeSelectorOverlay(sel, badgeLabel), returnByValue: true })
47
119
  .catch(() => ({ result: { value: false } }));
48
120
  return !!r?.result?.value;
49
121
  }
50
- // Use CDP DOM.getBoxModel to get coordinates for a backendNodeId — no DOM query needed
51
- async function tryNodeHighlight(bnId) {
122
+ async function tryNodeHighlight(bnId, badgeLabel) {
52
123
  try {
53
124
  const { nodeIds } = await session.DOM.pushNodesByBackendIdsToFrontend({ backendNodeIds: [bnId] });
54
125
  if (!nodeIds?.[0])
@@ -60,81 +131,96 @@ export async function browserHighlight(session, options, _config) {
60
131
  const x = q[0], y = q[1], w = q[2] - q[0], h = q[5] - q[1];
61
132
  if (w <= 0 || h <= 0)
62
133
  return false;
63
- const r = await session.Runtime.evaluate({ expression: makeCoordShimmer(x, y, w, h), returnByValue: true });
134
+ const r = await session.Runtime.evaluate({ expression: makeCoordOverlay(x, y, w, h, badgeLabel), returnByValue: true });
64
135
  return !!r?.result?.value;
65
136
  }
66
137
  catch {
67
138
  return false;
68
139
  }
69
140
  }
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:/, '');
141
+ async function highlightSingleTarget(targetIdentifier, badge) {
142
+ let sel = targetIdentifier;
143
+ let numericId = null;
144
+ if (targetIdentifier.startsWith("w:")) {
145
+ const stripped = targetIdentifier.slice(2);
77
146
  numericId = parseInt(stripped, 10);
78
- resolvedSelector = `[data-wt-id="${stripped}"]`;
147
+ sel = `[data-wt-id="${stripped}"]`;
79
148
  }
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) {
149
+ if (await trySelector(sel, badge))
150
+ return true;
151
+ // Extension oracle coords fallback
90
152
  const coordsRes = await session.Runtime.evaluate({
91
- expression: `(window.__Weavetab_getCoords || window.__Weavetab_getCoords || function(){return null;})(${JSON.stringify(resolvedSelector)})`,
153
+ expression: `(window.__Weavetab_getCoords || function(){return null;})(${JSON.stringify(sel)})`,
92
154
  returnByValue: true,
93
155
  }).catch(() => null);
94
156
  const cv = coordsRes?.result?.value;
95
157
  if (cv?.raw) {
96
158
  const { left, top, w, h } = cv.raw;
97
- const r = await session.Runtime.evaluate({ expression: makeCoordShimmer(left, top, w, h), returnByValue: true });
159
+ const r = await session.Runtime.evaluate({ expression: makeCoordOverlay(left, top, w, h, badge), returnByValue: true });
98
160
  if (r?.result?.value)
99
- return { success: true, message: `Highlighted ${resolvedSelector} via coords` };
161
+ return true;
100
162
  }
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,
163
+ // Action map cache fallback
164
+ if (numericId !== null) {
165
+ try {
166
+ const { getCachedActionMap } = await import('../state/cache.js');
167
+ const { sessionRegistry } = await import('../state/session.js');
168
+ const activeClient = sessionRegistry.getActive(getConfigAgentId(_config));
169
+ const targetId = activeClient?.targetId || 'default';
170
+ const cachedMap = activeClient?.url ? getCachedActionMap(targetId, activeClient.url) : null;
171
+ if (cachedMap?.elements) {
172
+ const el = cachedMap.elements.find(e => {
173
+ const n = parseInt(String(e.id).replace(/^w:/, ''), 10);
174
+ return n === numericId;
131
175
  });
132
- if (r?.result?.value)
133
- return { success: true, message: `Highlighted w:${numericId} via cache coords` };
176
+ if (el?.selector && await trySelector(el.selector, badge))
177
+ return true;
178
+ if (el?.backendNodeId && await tryNodeHighlight(el.backendNodeId, badge))
179
+ return true;
180
+ if (el?.x !== undefined && el?.y !== undefined) {
181
+ const ew = el.width ?? 80, eh = el.height ?? 24;
182
+ const r = await session.Runtime.evaluate({
183
+ expression: makeCoordOverlay(el.x - ew / 2, el.y - eh / 2, ew, eh, badge),
184
+ returnByValue: true,
185
+ });
186
+ if (r?.result?.value)
187
+ return true;
188
+ }
134
189
  }
135
190
  }
191
+ catch { /* ignore */ }
192
+ }
193
+ return false;
194
+ }
195
+ // ── Multi-ref consideration overlay ───────────────────────────────────────
196
+ if (options.refs && Array.isArray(options.refs) && options.refs.length > 0) {
197
+ let successCount = 0;
198
+ for (let i = 0; i < options.refs.length; i++) {
199
+ const refItem = options.refs[i];
200
+ const itemBadge = customLabel ? (options.refs.length > 1 ? `${customLabel} [${i + 1}]` : customLabel) : refItem;
201
+ const ok = await highlightSingleTarget(refItem, itemBadge);
202
+ if (ok)
203
+ successCount++;
204
+ }
205
+ return {
206
+ success: successCount > 0,
207
+ message: `Highlighted ${successCount}/${options.refs.length} element(s) with style '${style}' (${duration}ms)`
208
+ };
209
+ }
210
+ // ── Single target resolution ──────────────────────────────────────────────
211
+ if (options.nodeId && await tryNodeHighlight(options.nodeId, customLabel)) {
212
+ return { success: true, message: `Highlighted backendNodeId ${options.nodeId} with style '${style}'` };
213
+ }
214
+ let singleRef = options.selector || options.id || options.ref || options.target;
215
+ if (!singleRef && options.label) {
216
+ singleRef = `[aria-label="${options.label.replace(/"/g, '\\"')}"]`;
217
+ }
218
+ if (singleRef) {
219
+ const badgeText = customLabel || (singleRef.startsWith("w:") ? singleRef : undefined);
220
+ const ok = await highlightSingleTarget(singleRef, badgeText);
221
+ if (ok) {
222
+ return { success: true, message: `Highlighted ${singleRef} with style '${style}' (${duration}ms)` };
136
223
  }
137
- catch { /* no cache */ }
138
224
  }
139
225
  return { success: false, message: `Could not find element to highlight` };
140
226
  }
@@ -121,5 +121,5 @@ export async function browserInspect(session, options, _config) {
121
121
  computedStyles: computedStylesData,
122
122
  eventListeners: listeners
123
123
  };
124
- return { success: true, data, message: "Inspected element successfully" };
124
+ return { success: true, data, message: "inspected element" };
125
125
  }
package/dist/tools/key.js CHANGED
@@ -4,11 +4,8 @@
4
4
  * Weavetab by fy2ne
5
5
  */
6
6
  import { recordAction } from "../intelligence/trail.js";
7
- import { waitForDOMStable } from "../sensors/guard.js";
8
- import { waitForNetworkIdle } from "../sensors/wait.js";
9
7
  import { buildActionMap, toRefEntries } from "../cdp/walker.js";
10
- import { cacheActionMap, cacheBoxModel } from "../state/cache.js";
11
- import { computeDelta, storeSnapshot } from "../state/delta.js";
8
+ import { computeDelta } from "../state/delta.js";
12
9
  import { sessionRegistry } from "../state/session.js";
13
10
  import { getConfigAgentId } from "../config/loader.js";
14
11
  import { sendToExtension } from "../cdp/helpers.js";
@@ -20,36 +17,74 @@ const ALLOWED_KEYS = new Set([
20
17
  ]);
21
18
  export async function weaveKey(session, args, _config) {
22
19
  const { key, modifiers } = args;
20
+ let effectiveKey = key.trim();
21
+ const effectiveModifiers = [...(modifiers || [])];
22
+ // Auto-parse combo strings e.g. "Ctrl+A", "Control+Shift+P", "Cmd+K", "Alt+ArrowDown"
23
+ if (effectiveKey.includes("+")) {
24
+ const parts = effectiveKey.split("+").map(p => p.trim());
25
+ effectiveKey = parts[parts.length - 1] || "Enter";
26
+ for (let i = 0; i < parts.length - 1; i++) {
27
+ const mod = parts[i].toLowerCase();
28
+ if ((mod === "ctrl" || mod === "control") && !effectiveModifiers.includes("Ctrl"))
29
+ effectiveModifiers.push("Ctrl");
30
+ else if (mod === "shift" && !effectiveModifiers.includes("Shift"))
31
+ effectiveModifiers.push("Shift");
32
+ else if ((mod === "alt" || mod === "option") && !effectiveModifiers.includes("Alt"))
33
+ effectiveModifiers.push("Alt");
34
+ else if ((mod === "meta" || mod === "cmd" || mod === "command" || mod === "win") && !effectiveModifiers.includes("Meta"))
35
+ effectiveModifiers.push("Meta");
36
+ }
37
+ }
38
+ // Capitalize common key names if needed (e.g. "enter" -> "Enter", "escape" -> "Escape", "tab" -> "Tab")
39
+ const keyMap = {
40
+ enter: "Enter",
41
+ escape: "Escape",
42
+ esc: "Escape",
43
+ tab: "Tab",
44
+ backspace: "Backspace",
45
+ delete: "Delete",
46
+ space: "Space",
47
+ arrowup: "ArrowUp",
48
+ arrowdown: "ArrowDown",
49
+ arrowleft: "ArrowLeft",
50
+ arrowright: "ArrowRight",
51
+ up: "ArrowUp",
52
+ down: "ArrowDown",
53
+ left: "ArrowLeft",
54
+ right: "ArrowRight",
55
+ home: "Home",
56
+ end: "End",
57
+ pageup: "PageUp",
58
+ pagedown: "PageDown",
59
+ };
60
+ if (keyMap[effectiveKey.toLowerCase()]) {
61
+ effectiveKey = keyMap[effectiveKey.toLowerCase()];
62
+ }
23
63
  // Allow single printable chars or control keys from the allowlist
24
- if (key.length > 1 && !ALLOWED_KEYS.has(key)) {
64
+ if (effectiveKey.length > 1 && !ALLOWED_KEYS.has(effectiveKey)) {
25
65
  return {
26
66
  success: false,
27
67
  error_code: "INVALID_KEY",
28
- message: `[!] Key "${key}" is not a valid control key. Use single characters or: ${[...ALLOWED_KEYS].join(", ")}`,
68
+ message: `[!] Key "${effectiveKey}" is not a valid control key. Use single characters or: ${[...ALLOWED_KEYS].join(", ")}`,
29
69
  };
30
70
  }
31
71
  let modMask = 0;
32
- if (modifiers) {
33
- if (modifiers.includes("Alt"))
34
- modMask |= 1;
35
- if (modifiers.includes("Ctrl"))
36
- modMask |= 2;
37
- if (modifiers.includes("Meta"))
38
- modMask |= 4;
39
- if (modifiers.includes("Shift"))
40
- modMask |= 8;
41
- }
42
- const label = modifiers && modifiers.length > 0 ? `${modifiers.join("+")}+${key}` : key;
72
+ if (effectiveModifiers.includes("Alt"))
73
+ modMask |= 1;
74
+ if (effectiveModifiers.includes("Ctrl"))
75
+ modMask |= 2;
76
+ if (effectiveModifiers.includes("Meta"))
77
+ modMask |= 4;
78
+ if (effectiveModifiers.includes("Shift"))
79
+ modMask |= 8;
80
+ const label = effectiveModifiers.length > 0 ? `${effectiveModifiers.join("+")}+${effectiveKey}` : effectiveKey;
43
81
  await sendToExtension(session, { type: 'Weavetab_STATE', hud: { action: `pressing ${label}` } });
44
- const { frameTree: beforeFrameTree } = await session.Page.getFrameTree();
82
+ const { frameTree: beforeFrameTree } = await session.Page.getFrameTree().catch(() => ({ frameTree: { frame: { url: '' } } }));
45
83
  const urlBefore = beforeFrameTree.frame.url;
46
- // Track map before for delta if Enter is pressed
47
84
  let targetId = "default";
48
85
  const active = sessionRegistry.getActive(getConfigAgentId(_config));
49
86
  if (active)
50
87
  targetId = active.targetId;
51
- const mapBefore = await buildActionMap(session, { lite: true }, targetId);
52
- storeSnapshot(targetId, urlBefore, toRefEntries(mapBefore.elements));
53
88
  // We removed the direct toast injection here because the extension handles HUD display via Weavetab_STATE (line 66)
54
89
  await session.Input.dispatchKeyEvent({ type: "rawKeyDown", key, modifiers: modMask, windowsVirtualKeyCode: key === "Enter" ? 13 : undefined, nativeVirtualKeyCode: key === "Enter" ? 13 : undefined });
55
90
  if (key === "Enter") {
@@ -92,49 +127,23 @@ export async function weaveKey(session, args, _config) {
92
127
  })();
93
128
  ` }).catch(() => { });
94
129
  }
95
- await session.Input.dispatchKeyEvent({ type: "keyUp", key, modifiers: modMask, windowsVirtualKeyCode: key === "Enter" ? 13 : undefined, nativeVirtualKeyCode: key === "Enter" ? 13 : undefined });
96
- // ── Post-Action Pipeline & Predictive Action Queue ────────────────────────
97
- const predictiveQueue = async () => {
98
- try {
99
- const nextMap = await buildActionMap(session, { lite: true }, targetId);
100
- cacheActionMap(targetId, nextMap);
101
- for (const el of nextMap.elements.slice(0, 10)) {
102
- if (el.backendNodeId) {
103
- const { nodeIds } = await session.DOM.pushNodesByBackendIdsToFrontend({ backendNodeIds: [el.backendNodeId] }).catch(() => ({ nodeIds: [] }));
104
- if (nodeIds[0]) {
105
- const box = await session.DOM.getBoxModel({ nodeId: nodeIds[0] }).catch(() => null);
106
- if (box)
107
- cacheBoxModel(targetId, el.backendNodeId, box.model);
108
- }
109
- }
110
- }
111
- }
112
- catch { /* ignore pre-fetch errors */ }
113
- };
114
- const settleTimeout = key === "Enter" ? 5000 : 2500;
115
- await Promise.all([
116
- waitForDOMStable(session, 50, settleTimeout),
117
- waitForNetworkIdle(session, 50, settleTimeout).catch(() => { }),
118
- predictiveQueue()
119
- ]);
130
+ await session.Input.dispatchKeyEvent({ type: "keyUp", key: effectiveKey, modifiers: modMask, windowsVirtualKeyCode: effectiveKey === "Enter" ? 13 : undefined, nativeVirtualKeyCode: effectiveKey === "Enter" ? 13 : undefined });
131
+ // Fast return for non-submission keys (Tab, Escape, Arrows, Shortcuts)
132
+ if (effectiveKey === "Enter") {
133
+ await new Promise(r => setTimeout(r, 60));
134
+ }
135
+ else {
136
+ await new Promise(r => setTimeout(r, 15));
137
+ }
120
138
  // Post-key location context
121
139
  let postUrl = "";
122
140
  let postTitle = "";
123
141
  try {
124
142
  const { frameTree } = await session.Page.getFrameTree();
125
143
  postUrl = frameTree.frame.url;
126
- try {
127
- const titleRes = await session.Runtime.evaluate({ expression: 'document.title', returnByValue: true });
128
- postTitle = titleRes?.result?.value || postUrl;
129
- }
130
- catch {
131
- postTitle = postUrl;
132
- }
133
144
  const activeSession = sessionRegistry.getActive(getConfigAgentId(_config));
134
- if (activeSession) {
145
+ if (activeSession)
135
146
  activeSession.url = postUrl;
136
- activeSession.title = postTitle;
137
- }
138
147
  }
139
148
  catch { /* ignore */ }
140
149
  const urlChanged = postUrl !== urlBefore;
@@ -12,5 +12,5 @@ interface NavigateResult {
12
12
  title: string;
13
13
  tab_id: string;
14
14
  }
15
- export declare function weaveNavigate(session: CDP.Client, url: string, config: Config, targetId?: string, force?: boolean, fast?: boolean): Promise<NavigateResult | WeaveError>;
15
+ export declare function weaveNavigate(session: CDP.Client, url: string, config: Config, targetId?: string, force?: boolean, fast?: boolean, appName?: string): Promise<NavigateResult | WeaveError>;
16
16
  export {};