@weavetab/mcp 2.5.0-beta.0 → 2.5.0-beta.2

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 (98) hide show
  1. package/AGENTS.md +1 -0
  2. package/LICENSE +2 -2
  3. package/README.md +208 -43
  4. package/SECURITY.md +3 -3
  5. package/TROUBLESHOOTING.md +1 -1
  6. package/dist/cdp/bridge.d.ts +10 -0
  7. package/dist/cdp/bridge.js +288 -68
  8. package/dist/cdp/connector.d.ts +5 -3
  9. package/dist/cdp/connector.js +188 -74
  10. package/dist/cdp/extension.js +0 -1
  11. package/dist/cdp/helpers.js +0 -2
  12. package/dist/cdp/lock.d.ts +9 -6
  13. package/dist/cdp/lock.js +72 -59
  14. package/dist/cdp/omni_broker.d.ts +1 -0
  15. package/dist/cdp/omni_broker.js +12 -6
  16. package/dist/cdp/profiles.d.ts +10 -0
  17. package/dist/cdp/profiles.js +65 -0
  18. package/dist/cdp/walker.js +50 -0
  19. package/dist/cli/plugin.d.ts +0 -2
  20. package/dist/cli/plugin.js +31 -683
  21. package/dist/cli/updater.js +2 -2
  22. package/dist/config/loader.d.ts +25 -6
  23. package/dist/config/loader.js +133 -20
  24. package/dist/index.js +48 -52
  25. package/dist/lifecycle/mission_manager.d.ts +0 -1
  26. package/dist/lifecycle/mission_manager.js +0 -3
  27. package/dist/overlay/ghost.d.ts +4 -0
  28. package/dist/overlay/ghost.js +118 -94
  29. package/dist/overlay/injector.d.ts +1 -1
  30. package/dist/overlay/injector.js +1 -15
  31. package/dist/plugin/extension_bridge.d.ts +40 -0
  32. package/dist/plugin/extension_bridge.js +158 -0
  33. package/dist/plugin/interface.d.ts +78 -16
  34. package/dist/plugin/loader.d.ts +4 -0
  35. package/dist/plugin/loader.js +178 -57
  36. package/dist/plugin/registry.js +11 -1
  37. package/dist/plugin/storage.d.ts +51 -0
  38. package/dist/plugin/storage.js +164 -0
  39. package/dist/server.js +217 -151
  40. package/dist/state/session.d.ts +2 -2
  41. package/dist/state/session.js +33 -13
  42. package/dist/tools/browser_detect.d.ts +2 -1
  43. package/dist/tools/browser_detect.js +6 -5
  44. package/dist/tools/burst.d.ts +1 -0
  45. package/dist/tools/burst.js +4 -6
  46. package/dist/tools/click.js +23 -28
  47. package/dist/tools/clipboard.js +31 -24
  48. package/dist/tools/navigate.d.ts +1 -1
  49. package/dist/tools/navigate.js +28 -10
  50. package/dist/tools/read.js +1 -1
  51. package/dist/tools/tabs.d.ts +10 -10
  52. package/dist/tools/tabs.js +47 -39
  53. package/dist/tools/type.js +2 -2
  54. package/docs/DOCUMENTATION.md +63 -6
  55. package/docs/architecture/blind-injection.md +20 -0
  56. package/docs/architecture/dual-injection.md +32 -0
  57. package/docs/architecture/overview.md +49 -0
  58. package/docs/architecture/security.md +35 -0
  59. package/docs/architecture/transport.md +37 -0
  60. package/docs/captcha-stealth/captcha-solver.md +75 -0
  61. package/docs/captcha-stealth/stealth-physics.md +60 -0
  62. package/docs/cli/cli-commands.md +44 -0
  63. package/docs/cli/cli-flags.md +20 -0
  64. package/docs/cli/cli-introduction.md +20 -0
  65. package/docs/cli/cli-scripting.md +32 -0
  66. package/docs/cli/g-ide/ide-antigravity.md +24 -0
  67. package/docs/cli/g-ide/ide-claude-code.md +24 -0
  68. package/docs/cli/g-ide/ide-claude-desktop.md +32 -0
  69. package/docs/cli/g-ide/ide-cursor.md +31 -0
  70. package/docs/cli/g-ide/ide-generic.md +34 -0
  71. package/docs/cli/g-ide/ide-opencode.md +24 -0
  72. package/docs/cli/g-ide/ide-vscode-copilot.md +24 -0
  73. package/docs/cli/g-ide/ide-windsurf.md +24 -0
  74. package/docs/configuration/config-locations.md +24 -0
  75. package/docs/configuration/configuration.md +73 -0
  76. package/docs/configuration/env-variables.md +19 -0
  77. package/docs/configuration/folders.md +54 -0
  78. package/docs/desktop-automation/electron-apps.md +162 -0
  79. package/docs/get-started/installation.md +47 -0
  80. package/docs/get-started/introduction.md +60 -0
  81. package/docs/get-started/quickstart.md +54 -0
  82. package/docs/get-started/setup-wizard.md +26 -0
  83. package/docs/skills-ecosystem/skills-overview.md +35 -0
  84. package/docs/skills-ecosystem/skills-recipes.md +36 -0
  85. package/docs/tools/tools.md +293 -0
  86. package/docs/troubleshooting/trouble-browser.md +27 -0
  87. package/docs/troubleshooting/trouble-config.md +20 -0
  88. package/docs/troubleshooting/trouble-loop.md +31 -0
  89. package/ext-dist/content.js +126 -101
  90. package/mcp.json +8 -15
  91. package/package.json +11 -5
  92. package/server.json +3 -3
  93. package/dist/tools/plan.d.ts +0 -39
  94. package/dist/tools/plan.js +0 -161
  95. package/docs/DEV_MODE.md +0 -68
  96. package/docs/PLUGINS.md +0 -538
  97. package/docs/TOOLS.md +0 -104
  98. package/docs/dev_plugin.md +0 -310
@@ -1,5 +1,5 @@
1
1
  import { logDebug } from "../audit/logger.js";
2
- import { getConfigHeadless, getConfigVisuals } from "../config/loader.js";
2
+ import { getConfigHeadless } from "../config/loader.js";
3
3
  function sleep(ms) {
4
4
  return new Promise(r => setTimeout(r, ms));
5
5
  }
@@ -22,42 +22,87 @@ function wtCursor(session, x, y, state, opts) {
22
22
  const trail = opts?.trail && opts.trail !== "off" ? getCursorTrail(opts.trail === "full" ? 20 : 8) : undefined;
23
23
  sendToExtension(session, { type: 'Weavetab_STATE', cursor: { x, y, trail }, state }).catch(() => { });
24
24
  }
25
+ let _wanderActive = false;
26
+ export async function ensureInitialCursorPosition(session) {
27
+ if (currentX >= 0 && currentY >= 0) {
28
+ return { x: currentX, y: currentY };
29
+ }
30
+ try {
31
+ const layout = await session.Page.getLayoutMetrics().catch(() => null);
32
+ if (layout?.visualViewport?.clientWidth && layout?.visualViewport?.clientHeight) {
33
+ currentX = Math.round(layout.visualViewport.clientWidth / 2);
34
+ currentY = Math.round(layout.visualViewport.clientHeight / 2);
35
+ }
36
+ else {
37
+ const evalRes = await session.Runtime.evaluate({
38
+ expression: `({ x: Math.round(window.innerWidth / 2), y: Math.round(window.innerHeight / 2) })`,
39
+ returnByValue: true
40
+ }).catch(() => null);
41
+ if (evalRes?.result?.value?.x && evalRes?.result?.value?.y) {
42
+ currentX = evalRes.result.value.x;
43
+ currentY = evalRes.result.value.y;
44
+ }
45
+ else {
46
+ currentX = 640;
47
+ currentY = 400;
48
+ }
49
+ }
50
+ }
51
+ catch {
52
+ currentX = 640;
53
+ currentY = 400;
54
+ }
55
+ wtCursor(session, currentX, currentY, "idle");
56
+ return { x: currentX, y: currentY };
57
+ }
25
58
  async function wtThought(session, text) {
26
59
  await sendToExtension(session, { type: 'Weavetab_STATE', hud: { action: text } }).catch(() => { });
27
- // Add realistic wandering movement connected to thoughts
28
- if (currentX > 0 && currentY > 0 && Math.random() > 0.2) {
29
- const wanderDistance = 15 + Math.random() * 35;
60
+ if (currentX < 0 || currentY < 0) {
61
+ await ensureInitialCursorPosition(session);
62
+ }
63
+ // Add realistic wandering movement connected to thoughts (non-racing)
64
+ if (currentX > 0 && currentY > 0 && !_wanderActive && Math.random() > 0.3) {
65
+ _wanderActive = true;
66
+ const startX = currentX;
67
+ const startY = currentY;
68
+ const wanderDistance = 10 + Math.random() * 25;
30
69
  const angle = Math.random() * Math.PI * 2;
31
- const tx = Math.max(0, currentX + Math.cos(angle) * wanderDistance);
32
- const ty = Math.max(0, currentY + Math.sin(angle) * wanderDistance);
33
- const steps = 15;
70
+ const tx = Math.max(20, startX + Math.cos(angle) * wanderDistance);
71
+ const ty = Math.max(20, startY + Math.sin(angle) * wanderDistance);
72
+ const steps = 12;
34
73
  const waypoints = [];
35
- const cp1X = currentX + (tx - currentX) * 0.3 + (Math.random() - 0.5) * 15;
36
- const cp1Y = currentY + (ty - currentY) * 0.3 + (Math.random() - 0.5) * 15;
37
- const cp2X = currentX + (tx - currentX) * 0.7 + (Math.random() - 0.5) * 15;
38
- const cp2Y = currentY + (ty - currentY) * 0.7 + (Math.random() - 0.5) * 15;
74
+ const cp1X = startX + (tx - startX) * 0.3;
75
+ const cp1Y = startY + (ty - startY) * 0.3;
76
+ const cp2X = startX + (tx - startX) * 0.7;
77
+ const cp2Y = startY + (ty - startY) * 0.7;
39
78
  for (let i = 1; i <= steps; i++) {
40
79
  let t = i / steps;
41
80
  t = 1 - Math.pow(1 - t, 3);
42
81
  waypoints.push({
43
- x: cubicBezier(t, currentX, cp1X, cp2X, tx),
44
- y: cubicBezier(t, currentY, cp1Y, cp2Y, ty)
82
+ x: Math.round(cubicBezier(t, startX, cp1X, cp2X, tx)),
83
+ y: Math.round(cubicBezier(t, startY, cp1Y, cp2Y, ty))
45
84
  });
46
85
  }
47
86
  sendToExtension(session, {
48
87
  type: 'Weavetab_STATE',
49
- cursor: { waypoints, duration_ms: steps * 20, x: currentX, y: currentY },
88
+ cursor: { waypoints, duration_ms: steps * 25, x: startX, y: startY },
50
89
  state: "thinking"
51
90
  }).catch(() => { });
52
- // Async physical dispatch to not block the main logic flow
53
91
  (async () => {
54
- for (let i = 1; i <= steps; i++) {
55
- const x = waypoints[i - 1].x;
56
- const y = waypoints[i - 1].y;
57
- await session.Input.dispatchMouseEvent({ type: "mouseMoved", x, y, button: "none" }).catch(() => { });
58
- currentX = x;
59
- currentY = y;
60
- await new Promise(r => setTimeout(r, 20));
92
+ try {
93
+ for (let i = 1; i <= steps; i++) {
94
+ if (!_wanderActive)
95
+ break;
96
+ const x = waypoints[i - 1].x;
97
+ const y = waypoints[i - 1].y;
98
+ await session.Input.dispatchMouseEvent({ type: "mouseMoved", x, y, button: "none" }).catch(() => { });
99
+ currentX = x;
100
+ currentY = y;
101
+ await new Promise(r => setTimeout(r, 25));
102
+ }
103
+ }
104
+ finally {
105
+ _wanderActive = false;
61
106
  }
62
107
  })();
63
108
  }
@@ -139,6 +184,9 @@ targetSelector, profileName) {
139
184
  let wordsSinceBreak = 0;
140
185
  let wordCharCount = 0;
141
186
  let prevChar = '';
187
+ if (currentX < 0 || currentY < 0) {
188
+ await ensureInitialCursorPosition(session);
189
+ }
142
190
  wtCursor(session, currentX, currentY, "typing");
143
191
  let currentGroupSize = Math.floor(Math.random() * (p.groupSize[1] - p.groupSize[0] + 1)) + p.groupSize[0];
144
192
  let charsTypedInGroup = 0;
@@ -271,6 +319,10 @@ function cubicBezier(t, p0, p1, p2, p3) {
271
319
  }
272
320
  let _firstMoveDone = false;
273
321
  export async function ghostMove(session, targetX, targetY, config, label, profile, cursorOpts) {
322
+ _wanderActive = false; // Stop any ongoing thought wander immediately
323
+ if (currentX < 0 || currentY < 0) {
324
+ await ensureInitialCursorPosition(session);
325
+ }
274
326
  const resolvedProfile = profile || (getConfigHeadless(config) ? "instant" : "human");
275
327
  if (resolvedProfile === "instant") {
276
328
  await session.Input.dispatchMouseEvent({ type: "mouseMoved", x: targetX, y: targetY, button: "none" });
@@ -290,9 +342,9 @@ export async function ghostMove(session, targetX, targetY, config, label, profil
290
342
  return;
291
343
  }
292
344
  if (resolvedProfile === "fast") {
293
- const steps = 4;
294
- const startX = currentX === -1 ? targetX : currentX;
295
- const startY = currentY === -1 ? targetY : currentY;
345
+ const steps = 6;
346
+ const startX = currentX;
347
+ const startY = currentY;
296
348
  for (let i = 1; i <= steps; i++) {
297
349
  const t = i / steps;
298
350
  const x = Math.round(startX + (targetX - startX) * t);
@@ -301,7 +353,7 @@ export async function ghostMove(session, targetX, targetY, config, label, profil
301
353
  currentX = x;
302
354
  currentY = y;
303
355
  wtCursor(session, x, y, "moving", cursorOpts);
304
- await sleep(5);
356
+ await sleep(6);
305
357
  }
306
358
  currentX = targetX;
307
359
  currentY = targetY;
@@ -311,93 +363,61 @@ export async function ghostMove(session, targetX, targetY, config, label, profil
311
363
  return;
312
364
  }
313
365
  if (!_firstMoveDone) {
314
- logDebug(`[Weavetab Ghost] First mouse move → (${targetX}, ${targetY}) — CDP session active`);
366
+ logDebug(`[Weavetab Ghost] First mouse move from (${currentX}, ${currentY}) → (${targetX}, ${targetY}) — CDP session active`);
315
367
  _firstMoveDone = true;
316
368
  }
317
- // Don't fake-init cursor position — injector.ts hides cursor until first real move
318
- if (currentX === -1 && currentY === -1) {
319
- currentX = targetX;
320
- currentY = targetY;
321
- wtCursor(session, targetX, targetY, "hover", cursorOpts);
322
- recordCursorPosition(targetX, targetY);
323
- await session.Input.dispatchMouseEvent({ type: "mouseMoved", x: targetX, y: targetY, button: "none" });
324
- await session.Runtime.evaluate({
325
- expression: `(function(){
326
- var el = document.elementFromPoint(${targetX}, ${targetY});
327
- if (el) {
328
- el.dispatchEvent(new MouseEvent('mouseover', {bubbles:true,cancelable:true,clientX:${targetX},clientY:${targetY}}));
329
- el.dispatchEvent(new MouseEvent('mouseenter', {bubbles:false,cancelable:true,clientX:${targetX},clientY:${targetY}}));
330
- }
331
- })()`
332
- }).catch(() => { });
333
- return;
334
- }
335
369
  const dx = targetX - currentX;
336
370
  const dy = targetY - currentY;
337
371
  const distance = Math.sqrt(dx * dx + dy * dy);
338
- wtCursor(session, currentX, currentY, label && distance >= 150 ? "searching" : "idle", cursorOpts);
339
- if (label && distance >= 150 && getConfigVisuals(config) !== false) {
340
- wtThought(session, `moving to ${label.slice(0, 40)}...`);
341
- await sleep(80 + Math.random() * 120);
342
- }
343
- if (distance < 150) {
372
+ if (distance < 4) {
344
373
  currentX = targetX;
345
374
  currentY = targetY;
346
375
  await session.Input.dispatchMouseEvent({ type: "mouseMoved", x: targetX, y: targetY, button: "none" });
347
376
  wtCursor(session, targetX, targetY, "hover", cursorOpts);
348
377
  recordCursorPosition(targetX, targetY);
349
- await session.Runtime.evaluate({
350
- expression: `(function(){
351
- var el = document.elementFromPoint(${targetX}, ${targetY});
352
- if (el) {
353
- el.dispatchEvent(new MouseEvent('mouseover', {bubbles:true,cancelable:true,clientX:${targetX},clientY:${targetY}}));
354
- el.dispatchEvent(new MouseEvent('mouseenter', {bubbles:false,cancelable:true,clientX:${targetX},clientY:${targetY}}));
355
- }
356
- })()`
357
- }).catch(() => { });
358
378
  return;
359
379
  }
360
- const duration = Math.max(80, Math.min(300, distance * 0.6));
361
- let steps = Math.floor(duration / 16);
362
- if (steps > 10)
363
- steps = 10;
364
- if (steps < 3)
365
- steps = 3;
366
- const cp1X = currentX + dx * 0.2 + randomGaussian(0, distance * 0.2);
367
- const cp1Y = currentY + dy * 0.2 + randomGaussian(0, distance * 0.2);
368
- const cp2X = currentX + dx * 0.8 + randomGaussian(0, distance * 0.2);
369
- const cp2Y = currentY + dy * 0.8 + randomGaussian(0, distance * 0.2);
370
- const doOvershoot = distance >= 300 && Math.random() > 0.3;
371
- const overshootAmount = doOvershoot ? randomGaussian(10, 5) : 0;
372
- const angle = Math.atan2(dy, dx);
373
- const overshootX = targetX + Math.cos(angle) * overshootAmount;
374
- const overshootY = targetY + Math.sin(angle) * overshootAmount;
375
- wtCursor(session, targetX, targetY, "idle", cursorOpts);
380
+ wtCursor(session, currentX, currentY, label && distance >= 150 ? "searching" : "idle", cursorOpts);
381
+ if (label && distance >= 150) {
382
+ wtThought(session, `moving to ${label.slice(0, 40)}...`);
383
+ await sleep(60 + Math.random() * 80);
384
+ }
385
+ // Smooth, natural human hand curve
386
+ const duration = Math.max(120, Math.min(380, 80 + Math.sqrt(distance) * 12));
387
+ const steps = Math.max(8, Math.min(24, Math.floor(duration / 16)));
388
+ const stepDelay = Math.max(10, Math.floor(duration / steps));
389
+ // Gentle perpendicular curvature (arcing motion)
390
+ const nx = -dy / distance;
391
+ const ny = dx / distance;
392
+ const maxArc = Math.min(45, distance * 0.15);
393
+ const arcSign = (Math.random() > 0.5 ? 1 : -1);
394
+ const arcAmount = maxArc * arcSign * (0.4 + Math.random() * 0.6);
395
+ const cp1X = currentX + dx * 0.3 + nx * arcAmount;
396
+ const cp1Y = currentY + dy * 0.3 + ny * arcAmount;
397
+ const cp2X = currentX + dx * 0.7 + nx * (arcAmount * 0.6);
398
+ const cp2Y = currentY + dy * 0.7 + ny * (arcAmount * 0.6);
376
399
  const waypoints = [];
377
400
  for (let i = 1; i <= steps; i++) {
378
- let t = i / steps;
379
- t = 1 - Math.pow(1 - t, 4);
380
- const actualTargetX = doOvershoot && i < steps * 0.8 ? overshootX : targetX;
381
- const actualTargetY = doOvershoot && i < steps * 0.8 ? overshootY : targetY;
401
+ const rawT = i / steps;
402
+ // Cubic ease-out for natural deceleration
403
+ const t = 1 - Math.pow(1 - rawT, 3);
382
404
  waypoints.push({
383
- x: cubicBezier(t, currentX, cp1X, cp2X, actualTargetX),
384
- y: cubicBezier(t, currentY, cp1Y, cp2Y, actualTargetY)
405
+ x: Math.round(cubicBezier(t, currentX, cp1X, cp2X, targetX)),
406
+ y: Math.round(cubicBezier(t, currentY, cp1Y, cp2Y, targetY))
385
407
  });
386
408
  }
387
- // Pre-announce the move
409
+ // Pre-announce waypoints for smooth client-side interpolation
388
410
  sendToExtension(session, {
389
411
  type: 'Weavetab_STATE',
390
- cursor: { waypoints, duration_ms: steps * 8, x: currentX, y: currentY },
412
+ cursor: { waypoints, duration_ms: steps * stepDelay, x: currentX, y: currentY },
391
413
  state: "moving"
392
414
  }).catch(() => { });
393
415
  for (let i = 1; i <= steps; i++) {
394
- const x = waypoints[i - 1].x;
395
- const y = waypoints[i - 1].y;
396
- await session.Input.dispatchMouseEvent({ type: "mouseMoved", x, y, button: "none" });
397
- currentX = x;
398
- currentY = y;
399
- wtCursor(session, x, y, "moving", cursorOpts);
400
- await sleep(8);
416
+ const pt = waypoints[i - 1];
417
+ await session.Input.dispatchMouseEvent({ type: "mouseMoved", x: pt.x, y: pt.y, button: "none" }).catch(() => { });
418
+ currentX = pt.x;
419
+ currentY = pt.y;
420
+ await sleep(stepDelay);
401
421
  }
402
422
  currentX = targetX;
403
423
  currentY = targetY;
@@ -405,7 +425,7 @@ export async function ghostMove(session, targetX, targetY, config, label, profil
405
425
  recordCursorPosition(targetX, targetY);
406
426
  if (label)
407
427
  wtClearThought(session);
408
- await session.Input.dispatchMouseEvent({ type: "mouseMoved", x: targetX, y: targetY, button: "none" });
428
+ await session.Input.dispatchMouseEvent({ type: "mouseMoved", x: targetX, y: targetY, button: "none" }).catch(() => { });
409
429
  await session.Runtime.evaluate({
410
430
  expression: `(function(){
411
431
  var el = document.elementFromPoint(${targetX}, ${targetY});
@@ -501,8 +521,12 @@ export async function ghostClick(session, x, y, options) {
501
521
  }).catch(() => { });
502
522
  }
503
523
  export function ghostWait(session) {
504
- wtCursor(session, currentX, currentY, "waiting");
524
+ const x = currentX >= 0 ? currentX : 640;
525
+ const y = currentY >= 0 ? currentY : 400;
526
+ wtCursor(session, x, y, "waiting");
505
527
  }
506
528
  export function ghostIdle(session) {
507
- wtCursor(session, currentX, currentY, "idle");
529
+ const x = currentX >= 0 ? currentX : 640;
530
+ const y = currentY >= 0 ? currentY : 400;
531
+ wtCursor(session, x, y, "idle");
508
532
  }
@@ -2,7 +2,7 @@
2
2
  * src/overlay/injector.ts
3
3
  * Weavetab — Browser Presence Layer v3 (stripped)
4
4
  *
5
- * Kept: __wt_plan (mission dashboard), SPA nav detection,
5
+ * Kept: SPA nav detection,
6
6
  * __wt_setConfig, __wt_status, favicon state.
7
7
  * Removed: cursor (moved to content.js), chat panel, thought bubble,
8
8
  * ask modal, screen glow, CSS keyframes, emoji system.
@@ -1,5 +1,5 @@
1
1
  import { logDebug } from "../audit/logger.js";
2
- import { getConfigVisuals, getConfigDevMode } from "../config/loader.js";
2
+ import { getConfigDevMode } from "../config/loader.js";
3
3
  // ─── Minimal Overlay Script ───────────────────────────────────────────────────
4
4
  export function getMinimalOverlayScript() {
5
5
  return `(function(){
@@ -52,23 +52,11 @@ export function getMinimalOverlayScript() {
52
52
  document.addEventListener('NavigationEnd', function() { em('angular', location.href); });
53
53
  }
54
54
 
55
- // ── Mission plan dashboard ──────────────────────────────────────────────
56
- window.__wt_plan_comments = window.__wt_plan_comments || [];
57
- window.__wt_plan = function(action, goal, tasks) {
58
- window.postMessage({ type: '__WT_PLAN', action: action, goal: goal, tasks: tasks }, '*');
59
- };
60
-
61
55
  // The message listeners below are guarded so they are registered exactly once
62
56
  // per page, even when __wt_install_minimal() is called repeatedly (e.g. SPAs
63
57
  // with frequent navigations).
64
58
  if (!window.__wt_msg_installed) {
65
59
  window.__wt_msg_installed = true;
66
- window.addEventListener("message", function(e) {
67
- if (e.data && e.data.type === "__WT_PLAN_COMMENT") {
68
- window.__wt_plan_comments.push({ taskId: Number(e.data.taskId), comment: e.data.comment, time: Date.now() });
69
- }
70
- });
71
-
72
60
  window.__wt_chat_queue = window.__wt_chat_queue || [];
73
61
  window.addEventListener("message", function(e) {
74
62
  if (e.data && e.data.type === "__WT_FEEDBACK") {
@@ -79,7 +67,6 @@ export function getMinimalOverlayScript() {
79
67
 
80
68
  window.__wt_setConfig = function(c) {
81
69
  if (c && c.devMode) window.__wt_devMode = true;
82
- if (c && c.visuals !== undefined) window.__wt_showVisuals = !!c.visuals;
83
70
  };
84
71
 
85
72
  window.__wt_status = function() { return JSON.stringify({ rootInjected: true }); };
@@ -133,7 +120,6 @@ function buildConfigExpr(config) {
133
120
  return `
134
121
  if(window.__wt_setConfig)window.__wt_setConfig(${JSON.stringify({
135
122
  ...config,
136
- visuals: getConfigVisuals(config) !== false,
137
123
  })});
138
124
  window.postMessage({type:'Weavetab-msg-in',data:${JSON.stringify(msg)}},'*');
139
125
  `;
@@ -0,0 +1,40 @@
1
+ import type CDP from "chrome-remote-interface";
2
+ import type { WeavetabPermissions } from "./interface.js";
3
+ export interface IPluginExtensionBridge {
4
+ send(session: CDP.Client, message: object): Promise<boolean>;
5
+ setHudState(session: CDP.Client, state: {
6
+ action?: string;
7
+ badge?: string;
8
+ badgeColor?: string;
9
+ groupState?: string;
10
+ }): Promise<void>;
11
+ showThought(session: CDP.Client, text: string, options?: {
12
+ durationMs?: number;
13
+ }): Promise<void>;
14
+ clearThought(session: CDP.Client): Promise<void>;
15
+ injectStyles(session: CDP.Client, css: string): Promise<void>;
16
+ injectScript<T = any>(session: CDP.Client, js: string): Promise<T>;
17
+ hideOverlay(session: CDP.Client): Promise<void>;
18
+ restoreOverlay(session: CDP.Client): Promise<void>;
19
+ }
20
+ export declare class PluginExtensionBridge implements IPluginExtensionBridge {
21
+ readonly pluginName: string;
22
+ private readonly permissions?;
23
+ constructor(pluginName: string, permissions?: WeavetabPermissions | undefined);
24
+ private checkPermission;
25
+ send(session: CDP.Client, message: object): Promise<boolean>;
26
+ setHudState(session: CDP.Client, state: {
27
+ action?: string;
28
+ badge?: string;
29
+ badgeColor?: string;
30
+ groupState?: string;
31
+ }): Promise<void>;
32
+ showThought(session: CDP.Client, text: string, options?: {
33
+ durationMs?: number;
34
+ }): Promise<void>;
35
+ clearThought(session: CDP.Client): Promise<void>;
36
+ injectStyles(session: CDP.Client, css: string): Promise<void>;
37
+ injectScript<T = any>(session: CDP.Client, js: string): Promise<T>;
38
+ hideOverlay(session: CDP.Client): Promise<void>;
39
+ restoreOverlay(session: CDP.Client): Promise<void>;
40
+ }
@@ -0,0 +1,158 @@
1
+ import { sendToExtension, hideWeaveTabOverlay, restoreWeaveTabOverlay } from "../cdp/helpers.js";
2
+ import { logDebug } from "../audit/logger.js";
3
+ export class PluginExtensionBridge {
4
+ pluginName;
5
+ permissions;
6
+ constructor(pluginName, permissions) {
7
+ this.pluginName = pluginName;
8
+ this.permissions = permissions;
9
+ }
10
+ checkPermission(category) {
11
+ if (!this.permissions || !this.permissions.extension)
12
+ return true; // Default permissive if not restricted
13
+ return this.permissions.extension[category] !== false;
14
+ }
15
+ async send(session, message) {
16
+ if (!session)
17
+ return false;
18
+ return await sendToExtension(session, {
19
+ ...message,
20
+ plugin: this.pluginName,
21
+ });
22
+ }
23
+ async setHudState(session, state) {
24
+ if (!this.checkPermission("hud")) {
25
+ logDebug(`[Weavetab PluginExtension] Plugin "${this.pluginName}" lacks permissions.extension.hud`);
26
+ return;
27
+ }
28
+ await this.send(session, {
29
+ type: "wt-hud-update",
30
+ action: state.action,
31
+ badge: state.badge,
32
+ badgeColor: state.badgeColor,
33
+ groupState: state.groupState,
34
+ timestamp: Date.now(),
35
+ });
36
+ }
37
+ async showThought(session, text, options) {
38
+ if (!this.checkPermission("hud"))
39
+ return;
40
+ if (!session || !session.Runtime)
41
+ return;
42
+ const duration = options?.durationMs || 4000;
43
+ const safeText = JSON.stringify(text);
44
+ try {
45
+ await session.Runtime.evaluate({
46
+ expression: `(function() {
47
+ var existing = document.getElementById('__wt_plugin_thought_${this.pluginName}');
48
+ if (existing) existing.remove();
49
+
50
+ var bubble = document.createElement('div');
51
+ bubble.id = '__wt_plugin_thought_${this.pluginName}';
52
+ bubble.className = 'wt-bubble';
53
+ bubble.setAttribute('data-wt-internal', 'true');
54
+ bubble.style.cssText = [
55
+ 'position: fixed',
56
+ 'bottom: 24px',
57
+ 'right: 24px',
58
+ 'background: linear-gradient(135deg, rgba(17, 24, 39, 0.95), rgba(30, 41, 59, 0.95))',
59
+ 'color: #f8fafc',
60
+ 'padding: 12px 18px',
61
+ 'border-radius: 12px',
62
+ 'font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
63
+ 'font-size: 13px',
64
+ 'font-weight: 500',
65
+ 'box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.1)',
66
+ 'z-index: 2147483647',
67
+ 'backdrop-filter: blur(8px)',
68
+ 'pointer-events: none',
69
+ 'animation: wtThoughtFade 0.25s cubic-bezier(0.16, 1, 0.3, 1)',
70
+ 'display: flex',
71
+ 'align-items: center',
72
+ 'gap: 8px'
73
+ ].join(';');
74
+
75
+ var icon = document.createElement('span');
76
+ icon.textContent = '✨';
77
+ var textSpan = document.createElement('span');
78
+ textSpan.textContent = ${safeText};
79
+
80
+ bubble.appendChild(icon);
81
+ bubble.appendChild(textSpan);
82
+ document.body.appendChild(bubble);
83
+
84
+ setTimeout(function() {
85
+ if (bubble && bubble.parentNode) {
86
+ bubble.style.opacity = '0';
87
+ bubble.style.transition = 'opacity 0.3s ease';
88
+ setTimeout(function() { bubble.remove(); }, 300);
89
+ }
90
+ }, ${duration});
91
+ })()`,
92
+ returnByValue: false,
93
+ });
94
+ }
95
+ catch (e) {
96
+ logDebug(`[Weavetab PluginExtension] Failed to show thought:`, e);
97
+ }
98
+ }
99
+ async clearThought(session) {
100
+ if (!session || !session.Runtime)
101
+ return;
102
+ try {
103
+ await session.Runtime.evaluate({
104
+ expression: `(function() {
105
+ var existing = document.getElementById('__wt_plugin_thought_${this.pluginName}');
106
+ if (existing) existing.remove();
107
+ })()`,
108
+ returnByValue: false,
109
+ });
110
+ }
111
+ catch { /* ignore */ }
112
+ }
113
+ async injectStyles(session, css) {
114
+ if (!this.checkPermission("styles")) {
115
+ throw new Error(`Permission Denied: Plugin "${this.pluginName}" does not have permissions.extension.styles`);
116
+ }
117
+ if (!session || !session.Runtime)
118
+ return;
119
+ const safeCss = JSON.stringify(css);
120
+ await session.Runtime.evaluate({
121
+ expression: `(function() {
122
+ var styleId = '__wt_plugin_styles_${this.pluginName}';
123
+ var style = document.getElementById(styleId) || document.createElement('style');
124
+ style.id = styleId;
125
+ style.textContent = ${safeCss};
126
+ if (!style.parentNode) document.head.appendChild(style);
127
+ })()`,
128
+ returnByValue: false,
129
+ });
130
+ }
131
+ async injectScript(session, js) {
132
+ if (!this.checkPermission("scripts")) {
133
+ throw new Error(`Permission Denied: Plugin "${this.pluginName}" does not have permissions.extension.scripts`);
134
+ }
135
+ if (!session || !session.Runtime)
136
+ throw new Error("No active CDP session available");
137
+ const res = await session.Runtime.evaluate({
138
+ expression: js,
139
+ returnByValue: true,
140
+ awaitPromise: true,
141
+ });
142
+ return res.result?.value ?? res;
143
+ }
144
+ async hideOverlay(session) {
145
+ if (!this.checkPermission("overlay"))
146
+ return;
147
+ if (!session)
148
+ return;
149
+ await hideWeaveTabOverlay(session);
150
+ }
151
+ async restoreOverlay(session) {
152
+ if (!this.checkPermission("overlay"))
153
+ return;
154
+ if (!session)
155
+ return;
156
+ await restoreWeaveTabOverlay(session);
157
+ }
158
+ }