@weavetab/mcp 2.5.0-beta.0 → 2.5.0-beta.1
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.
- package/dist/cdp/bridge.js +19 -5
- package/dist/cdp/connector.js +0 -3
- package/dist/cdp/extension.js +0 -1
- package/dist/cdp/helpers.js +0 -2
- package/dist/lifecycle/mission_manager.d.ts +0 -1
- package/dist/lifecycle/mission_manager.js +0 -3
- package/dist/overlay/ghost.d.ts +4 -0
- package/dist/overlay/ghost.js +117 -93
- package/dist/overlay/injector.d.ts +1 -1
- package/dist/overlay/injector.js +0 -12
- package/dist/server.js +4 -30
- package/dist/tools/browser_detect.js +0 -2
- package/dist/tools/read.js +1 -1
- package/ext-dist/content.js +126 -100
- package/mcp.json +1 -10
- package/package.json +1 -1
- package/server.json +1 -1
- package/dist/tools/plan.d.ts +0 -39
- package/dist/tools/plan.js +0 -161
package/dist/cdp/bridge.js
CHANGED
|
@@ -13,7 +13,7 @@ import { fileURLToPath } from "node:url";
|
|
|
13
13
|
import CDP from "chrome-remote-interface";
|
|
14
14
|
import { readLock, writeLock, clearLock, isLockValid, isLockPending } from "./lock.js";
|
|
15
15
|
import { getSessionProfile } from "./profiles.js";
|
|
16
|
-
import { getOutputDir, getDownloadDir, getConfigHeadless, getConfigProxy, getConfigDisableExtensions, getConfigDevMode, getConfigBlock, getConfigAllow, getConfigBrowserType, getConfigBrowserPath, getConfigBrowserFlags, getConfigCookies, getConfigProfileDir } from "../config/loader.js";
|
|
16
|
+
import { getOutputDir, getDownloadDir, getConfigHeadless, getConfigProxy, getConfigDisableExtensions, getConfigDevMode, getConfigBlock, getConfigAllow, getConfigBrowserType, getConfigBrowserPath, getConfigBrowserFlags, getConfigCookies, getConfigProfileDir, getSystemDir } from "../config/loader.js";
|
|
17
17
|
import { createBrowserFingerprint } from "./security.js";
|
|
18
18
|
function withCDPTimeout(promise, ms, label) {
|
|
19
19
|
return Promise.race([
|
|
@@ -524,10 +524,24 @@ export async function launchWithBridge(config) {
|
|
|
524
524
|
await new Promise(r => setTimeout(r, 1500));
|
|
525
525
|
}
|
|
526
526
|
try {
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
if (
|
|
530
|
-
|
|
527
|
+
let extPath = path.resolve(__dirname, "../../ext-dist");
|
|
528
|
+
const systemExtPath = getSystemDir("extension");
|
|
529
|
+
if (fs.existsSync(extPath)) {
|
|
530
|
+
try {
|
|
531
|
+
fs.cpSync(extPath, systemExtPath, { recursive: true, force: true });
|
|
532
|
+
}
|
|
533
|
+
catch (e) {
|
|
534
|
+
logDebug(`[Weavetab] Failed to sync extension to system directory: ${e}`);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
else {
|
|
538
|
+
if (fs.existsSync(systemExtPath)) {
|
|
539
|
+
logDebug(`[Weavetab] Local extension missing. Falling back to system extension directory.`);
|
|
540
|
+
extPath = systemExtPath;
|
|
541
|
+
}
|
|
542
|
+
else {
|
|
543
|
+
throw new Error(`[Weavetab] Extension directory does not exist: ${extPath}. Build the extension first.`);
|
|
544
|
+
}
|
|
531
545
|
}
|
|
532
546
|
const manifestPath = path.join(extPath, "manifest.json");
|
|
533
547
|
const contentPath = path.join(extPath, "content.js");
|
package/dist/cdp/connector.js
CHANGED
|
@@ -150,9 +150,6 @@ async function initSession(session, targetId, port, config) {
|
|
|
150
150
|
if (clientRef)
|
|
151
151
|
clientRef.overlayInstalled = false;
|
|
152
152
|
await ensureVisualLayer(session, cfg, clientRef ?? undefined);
|
|
153
|
-
// Re-inject active plan into the new page
|
|
154
|
-
const { restoreActivePlanIntoPage } = await import("../tools/plan.js");
|
|
155
|
-
await restoreActivePlanIntoPage(session);
|
|
156
153
|
}
|
|
157
154
|
catch { /* ignore */ }
|
|
158
155
|
}
|
package/dist/cdp/extension.js
CHANGED
|
@@ -55,7 +55,6 @@ export async function extensionHealth(session) {
|
|
|
55
55
|
if (document.querySelector('weavetab-host') || document.getElementById('__Weavetab_cursor_host')) features.push('cursor');
|
|
56
56
|
if (document.querySelector('[data-wt-internal]') || document.getElementById('wt-ui-styles')) features.push('styles');
|
|
57
57
|
if (document.querySelector('.wt-bubble')) features.push('bubble');
|
|
58
|
-
if (document.getElementById('wt-plan-panel')) features.push('plan');
|
|
59
58
|
return JSON.stringify({
|
|
60
59
|
loaded: ${loaded},
|
|
61
60
|
features
|
package/dist/cdp/helpers.js
CHANGED
|
@@ -164,7 +164,6 @@ export async function hideWeaveTabOverlay(session) {
|
|
|
164
164
|
// - [data-wt-ignore] : any element flagged as agent-invisible
|
|
165
165
|
// - .wt-bubble : thought bubble / action text popups
|
|
166
166
|
// - #__wt_ask_bridge : question input bridge element
|
|
167
|
-
// - #wt-plan-dashboard : mission plan dashboard
|
|
168
167
|
// - #wt-debug-overlay : debug overlay panel
|
|
169
168
|
// - #wt-main-container : WeaveTab OS panel (todos + chat)
|
|
170
169
|
style.textContent = [
|
|
@@ -175,7 +174,6 @@ export async function hideWeaveTabOverlay(session) {
|
|
|
175
174
|
'[data-wt-ignore]',
|
|
176
175
|
'.wt-bubble',
|
|
177
176
|
'#__wt_ask_bridge',
|
|
178
|
-
'#wt-plan-dashboard',
|
|
179
177
|
'#wt-debug-overlay',
|
|
180
178
|
'#wt-main-container',
|
|
181
179
|
].join(', ') + ' { display: none !important; visibility: hidden !important; opacity: 0 !important; }';
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
* Handles suspension, hibernation, and resumption of Weavetab missions.
|
|
5
5
|
* Weavetab by fy2ne
|
|
6
6
|
*/
|
|
7
|
-
import { getActivePlan } from "../tools/plan.js";
|
|
8
7
|
import { getTabList } from "../cdp/connector.js";
|
|
9
8
|
import * as fs from "node:fs";
|
|
10
9
|
import * as path from "node:path";
|
|
@@ -17,14 +16,12 @@ export async function suspendMission(agentId, missionId) {
|
|
|
17
16
|
if (!fs.existsSync(stateDir)) {
|
|
18
17
|
fs.mkdirSync(stateDir, { recursive: true });
|
|
19
18
|
}
|
|
20
|
-
const plan = getActivePlan();
|
|
21
19
|
const tabs = await getTabList(agentId).catch(() => []);
|
|
22
20
|
const state = {
|
|
23
21
|
missionId,
|
|
24
22
|
agentId,
|
|
25
23
|
status: "suspended",
|
|
26
24
|
timestamp: Date.now(),
|
|
27
|
-
plan,
|
|
28
25
|
tabs
|
|
29
26
|
};
|
|
30
27
|
fs.writeFileSync(getMissionPath(missionId), JSON.stringify(state, null, 2));
|
package/dist/overlay/ghost.d.ts
CHANGED
|
@@ -5,6 +5,10 @@ export interface CursorOptions {
|
|
|
5
5
|
opacity?: number;
|
|
6
6
|
speed?: "human" | "fast" | "instant";
|
|
7
7
|
}
|
|
8
|
+
export declare function ensureInitialCursorPosition(session: CDP.Client): Promise<{
|
|
9
|
+
x: number;
|
|
10
|
+
y: number;
|
|
11
|
+
}>;
|
|
8
12
|
export interface BurstProfile {
|
|
9
13
|
groupSize: [number, number];
|
|
10
14
|
interCharMin: number;
|
package/dist/overlay/ghost.js
CHANGED
|
@@ -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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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(
|
|
32
|
-
const ty = Math.max(
|
|
33
|
-
const steps =
|
|
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 =
|
|
36
|
-
const cp1Y =
|
|
37
|
-
const cp2X =
|
|
38
|
-
const cp2Y =
|
|
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,
|
|
44
|
-
y: cubicBezier(t,
|
|
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 *
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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 =
|
|
294
|
-
const startX = currentX
|
|
295
|
-
const startY = 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(
|
|
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
|
-
|
|
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
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
const
|
|
367
|
-
const
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
const
|
|
371
|
-
const
|
|
372
|
-
const
|
|
373
|
-
const
|
|
374
|
-
const
|
|
375
|
-
|
|
380
|
+
wtCursor(session, currentX, currentY, label && distance >= 150 ? "searching" : "idle", cursorOpts);
|
|
381
|
+
if (label && distance >= 150 && getConfigVisuals(config) !== false) {
|
|
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
|
-
|
|
379
|
-
|
|
380
|
-
const
|
|
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,
|
|
384
|
-
y: cubicBezier(t, currentY, cp1Y, cp2Y,
|
|
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
|
|
409
|
+
// Pre-announce waypoints for smooth client-side interpolation
|
|
388
410
|
sendToExtension(session, {
|
|
389
411
|
type: 'Weavetab_STATE',
|
|
390
|
-
cursor: { waypoints, duration_ms: steps *
|
|
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
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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.
|
package/dist/overlay/injector.js
CHANGED
|
@@ -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") {
|
package/dist/server.js
CHANGED
|
@@ -11,7 +11,6 @@ import { checkRateLimit } from "./security/ratelimit.js";
|
|
|
11
11
|
import { checkDomain, isToolAllowedInMode } from "./security/blacklist.js";
|
|
12
12
|
import { logAction, logActionStructured } from "./audit/logger.js";
|
|
13
13
|
import { recordToolUsage, broadcastTelemetry } from "./audit/telemetry.js";
|
|
14
|
-
import { clearLock } from "./cdp/lock.js";
|
|
15
14
|
import { browserMap } from "./tools/read.js";
|
|
16
15
|
import { weaveNavigate } from "./tools/navigate.js";
|
|
17
16
|
import { weaveClick } from "./tools/click.js";
|
|
@@ -25,7 +24,6 @@ import { browserWait } from "./tools/wait.js";
|
|
|
25
24
|
import { browserPointer } from "./tools/pointer.js";
|
|
26
25
|
import { browserSelect } from "./tools/select.js";
|
|
27
26
|
import { browserFill } from "./tools/fill.js";
|
|
28
|
-
import { weavePlan } from "./tools/plan.js";
|
|
29
27
|
import { browserHighlight } from "./tools/highlight.js";
|
|
30
28
|
import { browserUpload } from "./tools/upload.js";
|
|
31
29
|
import { browserFind } from "./tools/find.js";
|
|
@@ -794,28 +792,6 @@ export async function startServer(config, transportMode = "stdio", wsPort = 3000
|
|
|
794
792
|
return wrapError(e);
|
|
795
793
|
}
|
|
796
794
|
});
|
|
797
|
-
// ── 15. browser_plan ───────────────────────────────────────────────────────
|
|
798
|
-
server.tool("browser_plan", "Create a live task panel in the browser.", {
|
|
799
|
-
action: z.enum(["create", "update", "comments", "hide"]).describe("Action to perform."),
|
|
800
|
-
goal: z.string().optional().describe("Mission goal description (required for create)."),
|
|
801
|
-
tasks: z.array(z.object({
|
|
802
|
-
id: z.number(),
|
|
803
|
-
description: z.string(),
|
|
804
|
-
status: z.enum(["pending", "in-progress", "done", "failed"]),
|
|
805
|
-
})).optional().describe("Task list with statuses (required for create)."),
|
|
806
|
-
taskId: z.number().optional().describe("Task ID to update (required for update)."),
|
|
807
|
-
status: z.enum(["pending", "in-progress", "done", "failed"]).optional().describe("New status (required for update)."),
|
|
808
|
-
}, async (args) => {
|
|
809
|
-
try {
|
|
810
|
-
return await withSecurity(config, "browser_plan", async (session, liveConfig) => {
|
|
811
|
-
const result = await weavePlan(session, args, liveConfig);
|
|
812
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
813
|
-
});
|
|
814
|
-
}
|
|
815
|
-
catch (e) {
|
|
816
|
-
return wrapError(e);
|
|
817
|
-
}
|
|
818
|
-
});
|
|
819
795
|
// ── 18. browser_highlight ───────────────────────────────────────────────────
|
|
820
796
|
server.tool("browser_highlight", "Highlight an element with a colored overlay.", {
|
|
821
797
|
selector: z.string().optional().describe("CSS Selector of the element to highlight."),
|
|
@@ -1678,10 +1654,8 @@ process.on("SIGTERM", async () => {
|
|
|
1678
1654
|
process.exit(0);
|
|
1679
1655
|
});
|
|
1680
1656
|
process.on("exit", () => {
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
}
|
|
1686
|
-
catch { /* ignore */ }
|
|
1657
|
+
// We purposefully do NOT clear the lock file here.
|
|
1658
|
+
// Weavetab launches the browser detached, so it survives the server dying.
|
|
1659
|
+
// Leaving the lock file allows the next MCP server run to find and reuse
|
|
1660
|
+
// the existing browser instead of launching a duplicate.
|
|
1687
1661
|
});
|
|
@@ -9,7 +9,6 @@ const ENGINE_COVERAGE = {
|
|
|
9
9
|
"browser_network_intercept",
|
|
10
10
|
"browser_viewport",
|
|
11
11
|
"browser_recording",
|
|
12
|
-
"browser_plan",
|
|
13
12
|
"browser_thought",
|
|
14
13
|
"browser_highlight",
|
|
15
14
|
"browser_inspect",
|
|
@@ -24,7 +23,6 @@ const ENGINE_COVERAGE = {
|
|
|
24
23
|
"browser_network_intercept",
|
|
25
24
|
"browser_viewport",
|
|
26
25
|
"browser_recording",
|
|
27
|
-
"browser_plan",
|
|
28
26
|
"browser_thought",
|
|
29
27
|
"browser_highlight",
|
|
30
28
|
"browser_inspect",
|
package/dist/tools/read.js
CHANGED
|
@@ -484,5 +484,5 @@ export async function browserMap(session, options = {}, targetId = "default", co
|
|
|
484
484
|
sendToExtension(session, { type: 'Weavetab_STATE', hud: { action: '' } }).catch(() => { });
|
|
485
485
|
return output;
|
|
486
486
|
}
|
|
487
|
-
//
|
|
487
|
+
// Backward-compat alias
|
|
488
488
|
export { browserMap as weaveRead };
|
package/ext-dist/content.js
CHANGED
|
@@ -164,7 +164,9 @@ function __wt_safeAppendToBody(el) {
|
|
|
164
164
|
}
|
|
165
165
|
if (typeof __weaveCursor !== 'undefined' && __weaveCursor) {
|
|
166
166
|
__weaveCursor.setVisible(__wt_masterVisualsEnabled);
|
|
167
|
-
if (__weaveCursor.
|
|
167
|
+
if (__weaveCursor._cursor) {
|
|
168
|
+
__weaveCursor._cursor.style.display = __wt_masterVisualsEnabled ? 'block' : 'none';
|
|
169
|
+
} else if (__weaveCursor._canvas) {
|
|
168
170
|
__weaveCursor._canvas.style.display = __wt_masterVisualsEnabled ? 'block' : 'none';
|
|
169
171
|
}
|
|
170
172
|
}
|
|
@@ -213,8 +215,13 @@ function __wt_safeAppendToBody(el) {
|
|
|
213
215
|
if (msg.type === 'WT_TOGGLE_MASTER_VISUALS') {
|
|
214
216
|
__wt_applyMasterVisuals(msg.enabled);
|
|
215
217
|
}
|
|
216
|
-
if (msg.type === 'GHOST_CURSOR_MOVE'
|
|
217
|
-
|
|
218
|
+
if (msg.type === 'GHOST_CURSOR_MOVE') {
|
|
219
|
+
if (typeof msg.x === 'number' && typeof msg.y === 'number') {
|
|
220
|
+
moveCursor(msg.x, msg.y, msg.phase);
|
|
221
|
+
}
|
|
222
|
+
if (iframe && iframe.contentWindow) {
|
|
223
|
+
iframe.contentWindow.postMessage({ type: 'RT_CURSOR_MOVE', x: msg.x, y: msg.y, phase: msg.phase }, '*');
|
|
224
|
+
}
|
|
218
225
|
}
|
|
219
226
|
if (msg.type === 'TOOL_CALL_EVENT' && iframe && iframe.contentWindow) {
|
|
220
227
|
iframe.contentWindow.postMessage({ type: 'TOOL_CALL_EVENT', payload: msg.payload }, '*');
|
|
@@ -932,71 +939,88 @@ window.__Weavetab_readConsole = function({ since = 'last', level = null } = {})
|
|
|
932
939
|
return level ? entries.filter(e => e.level === level) : entries;
|
|
933
940
|
};
|
|
934
941
|
|
|
935
|
-
// ──
|
|
936
|
-
// Shadow-DOM-isolated,
|
|
937
|
-
// and state-driven visual transitions: idle, moving, reading, typing, clicking, thinking
|
|
942
|
+
// ── BLUE SVG WEAVE CURSOR ───────────────────────────────────────
|
|
943
|
+
// Shadow-DOM-isolated, hardware-accelerated SVG cursor with smooth interpolation
|
|
938
944
|
class WeaveCursor {
|
|
939
945
|
constructor() {
|
|
940
946
|
// Attach a shadow host that sits above all page content
|
|
941
947
|
this._host = document.createElement('div');
|
|
942
948
|
this._host.id = '__Weavetab_cursor_host';
|
|
949
|
+
this._host.setAttribute('data-wt-ignore', 'true');
|
|
943
950
|
Object.assign(this._host.style, {
|
|
944
951
|
position: 'fixed',
|
|
945
952
|
top: '0',
|
|
946
953
|
left: '0',
|
|
947
954
|
width: '0',
|
|
948
955
|
height: '0',
|
|
949
|
-
zIndex: '
|
|
956
|
+
zIndex: '2147483647',
|
|
950
957
|
pointerEvents: 'none',
|
|
951
958
|
overflow: 'visible',
|
|
952
959
|
});
|
|
953
960
|
|
|
954
961
|
const shadow = this._host.attachShadow({ mode: 'closed' });
|
|
955
962
|
|
|
956
|
-
//
|
|
957
|
-
this.
|
|
958
|
-
this.
|
|
959
|
-
this.
|
|
960
|
-
|
|
963
|
+
// Hotspot aligned with the top-left pointer tip (3.0, 5.0 in 32x32 viewBox)
|
|
964
|
+
this._hotX = 3.0;
|
|
965
|
+
this._hotY = 5.0;
|
|
966
|
+
this._scale = 0.4;
|
|
967
|
+
|
|
968
|
+
const initX = typeof window !== 'undefined' && window.innerWidth ? Math.round(window.innerWidth / 2) : 640;
|
|
969
|
+
const initY = typeof window !== 'undefined' && window.innerHeight ? Math.round(window.innerHeight / 2) : 400;
|
|
970
|
+
|
|
971
|
+
// Exact vector SVG cursor matching user design
|
|
972
|
+
this._cursor = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
973
|
+
this._cursor.setAttribute('class', 'cursor');
|
|
974
|
+
this._cursor.setAttribute('viewBox', '0 0 32 32');
|
|
975
|
+
this._cursor.setAttribute('width', '32');
|
|
976
|
+
this._cursor.setAttribute('height', '32');
|
|
977
|
+
this._cursor.setAttribute('aria-hidden', 'true');
|
|
978
|
+
Object.assign(this._cursor.style, {
|
|
961
979
|
position: 'fixed',
|
|
962
980
|
top: '0',
|
|
963
981
|
left: '0',
|
|
964
|
-
width: '
|
|
965
|
-
height: '
|
|
982
|
+
width: '32px',
|
|
983
|
+
height: '32px',
|
|
984
|
+
display: 'block',
|
|
985
|
+
transformOrigin: `${this._hotX}px ${this._hotY}px`,
|
|
986
|
+
transform: `translate3d(${initX - this._hotX}px, ${initY - this._hotY}px, 0) scale(${this._scale})`,
|
|
987
|
+
overflow: 'visible',
|
|
966
988
|
pointerEvents: 'none',
|
|
967
|
-
transform: 'translate(-9999px,-9999px)',
|
|
968
989
|
willChange: 'transform',
|
|
969
|
-
transition: 'opacity 0.
|
|
990
|
+
transition: 'opacity 0.2s ease-out',
|
|
991
|
+
opacity: '1',
|
|
992
|
+
zIndex: '2147483647',
|
|
993
|
+
filter: 'drop-shadow(0 2px 4px rgba(0,0,0,0.35))',
|
|
970
994
|
});
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
995
|
+
|
|
996
|
+
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
997
|
+
path.setAttribute('class', 'cursor-shape');
|
|
998
|
+
path.setAttribute('d', 'M 6.1,4.7 C 4.5,4.1 3.0,5.0 3.0,6.7 C 3.0,7.1 3.1,7.5 3.3,8.0 L 10.2,27.0 C 10.8,28.7 12.0,29.6 13.6,29.5 C 15.1,29.4 16.0,28.5 16.5,27.0 L 19.4,18.8 L 25.9,17.0 C 27.7,16.5 28.9,15.2 28.9,13.6 C 28.9,12.1 28.0,10.9 26.4,10.3 Z');
|
|
999
|
+
path.setAttribute('fill', '#1268ff');
|
|
1000
|
+
path.setAttribute('stroke', '#ffffff');
|
|
1001
|
+
path.setAttribute('stroke-width', '3.1');
|
|
1002
|
+
path.setAttribute('stroke-linejoin', 'round');
|
|
1003
|
+
path.setAttribute('stroke-linecap', 'round');
|
|
1004
|
+
|
|
1005
|
+
this._cursor.appendChild(path);
|
|
1006
|
+
shadow.appendChild(this._cursor);
|
|
1007
|
+
|
|
1008
|
+
// Alias _canvas to _cursor for backward compatibility
|
|
1009
|
+
this._canvas = this._cursor;
|
|
1010
|
+
|
|
1011
|
+
this._x = initX;
|
|
1012
|
+
this._y = initY;
|
|
1013
|
+
this._targetX = initX;
|
|
1014
|
+
this._targetY = initY;
|
|
1015
|
+
this._lerpX = initX;
|
|
1016
|
+
this._lerpY = initY;
|
|
980
1017
|
this._state = 'idle';
|
|
981
|
-
this._visible =
|
|
1018
|
+
this._visible = true;
|
|
982
1019
|
this._animFrame = null;
|
|
983
|
-
this._lastMoveTime = 0;
|
|
984
1020
|
this._lastTime = performance.now();
|
|
985
1021
|
this._waypointQueue = [];
|
|
986
1022
|
this._playingQueue = false;
|
|
987
1023
|
|
|
988
|
-
this._hotX = 0;
|
|
989
|
-
this._hotY = 0;
|
|
990
|
-
|
|
991
|
-
// Sharp precision arrow pointer path (tip at 0,0)
|
|
992
|
-
this._outerPath = new Path2D("M 0 0 L 0 16 L 4.2 12.2 L 7.5 19 L 10.2 17.6 L 6.8 10.8 L 11.8 10.8 Z");
|
|
993
|
-
this._innerPath = new Path2D("M 1.2 2.5 L 1.2 13.5 L 3.8 10.8 L 6.6 16.5 L 8.2 15.6 L 5.4 9.8 L 9.8 9.8 Z");
|
|
994
|
-
|
|
995
|
-
// Vivid linear gradient (#06B6D4 -> #3B82F6)
|
|
996
|
-
this._blueGrad = this._ctx.createLinearGradient(0, 0, 12, 19);
|
|
997
|
-
this._blueGrad.addColorStop(0, '#06B6D4');
|
|
998
|
-
this._blueGrad.addColorStop(1, '#3B82F6');
|
|
999
|
-
|
|
1000
1024
|
__wt_safeAppendToBody(this._host);
|
|
1001
1025
|
this._startLoop();
|
|
1002
1026
|
}
|
|
@@ -1018,23 +1042,25 @@ class WeaveCursor {
|
|
|
1018
1042
|
setVisible(visible) {
|
|
1019
1043
|
if (typeof __wt_masterVisualsEnabled !== 'undefined' && !__wt_masterVisualsEnabled) {
|
|
1020
1044
|
this._visible = false;
|
|
1021
|
-
this.
|
|
1045
|
+
this._cursor.style.opacity = '0';
|
|
1022
1046
|
return;
|
|
1023
1047
|
}
|
|
1024
1048
|
this._visible = visible;
|
|
1025
|
-
this.
|
|
1049
|
+
this._cursor.style.opacity = visible ? '1' : '0';
|
|
1026
1050
|
}
|
|
1027
1051
|
|
|
1028
1052
|
moveTo(x, y) {
|
|
1029
1053
|
if (typeof __wt_masterVisualsEnabled !== 'undefined' && !__wt_masterVisualsEnabled) return;
|
|
1030
|
-
if (
|
|
1054
|
+
if (typeof x !== 'number' || typeof y !== 'number' || isNaN(x) || isNaN(y)) return;
|
|
1031
1055
|
this._targetX = x;
|
|
1032
1056
|
this._targetY = y;
|
|
1033
|
-
if (this._lerpX === -9999) {
|
|
1034
|
-
|
|
1057
|
+
if (this._lerpX === -9999) {
|
|
1058
|
+
this._lerpX = x;
|
|
1059
|
+
this._lerpY = y;
|
|
1060
|
+
}
|
|
1035
1061
|
if (!this._visible) {
|
|
1036
1062
|
this._visible = true;
|
|
1037
|
-
this.
|
|
1063
|
+
this._cursor.style.opacity = '1';
|
|
1038
1064
|
}
|
|
1039
1065
|
if (!this._animFrame) {
|
|
1040
1066
|
this._startLoop();
|
|
@@ -1043,20 +1069,23 @@ class WeaveCursor {
|
|
|
1043
1069
|
|
|
1044
1070
|
setWaypoints(waypoints, durationMs) {
|
|
1045
1071
|
if (typeof __wt_masterVisualsEnabled !== 'undefined' && !__wt_masterVisualsEnabled) return;
|
|
1072
|
+
if (!waypoints || !waypoints.length) return;
|
|
1046
1073
|
this._waypointQueue = waypoints;
|
|
1047
1074
|
this._playingQueue = true;
|
|
1048
1075
|
this._waypointStartTime = performance.now();
|
|
1049
|
-
this._waypointDuration = durationMs;
|
|
1076
|
+
this._waypointDuration = durationMs || (waypoints.length * 16);
|
|
1050
1077
|
if (waypoints.length > 0 && this._lerpX === -9999) {
|
|
1051
1078
|
this._lerpX = waypoints[0].x;
|
|
1052
1079
|
this._lerpY = waypoints[0].y;
|
|
1053
1080
|
}
|
|
1054
|
-
if (!this._visible) {
|
|
1081
|
+
if (!this._visible) {
|
|
1082
|
+
this._visible = true;
|
|
1083
|
+
this._cursor.style.opacity = '1';
|
|
1084
|
+
}
|
|
1055
1085
|
if (!this._animFrame) this._startLoop();
|
|
1056
1086
|
}
|
|
1057
1087
|
|
|
1058
1088
|
_startLoop() {
|
|
1059
|
-
this._idleFrames = 0;
|
|
1060
1089
|
this._lastTime = performance.now();
|
|
1061
1090
|
const draw = (currentTime) => {
|
|
1062
1091
|
const dt = Math.min((currentTime - this._lastTime) / 1000, 0.1);
|
|
@@ -1064,89 +1093,86 @@ class WeaveCursor {
|
|
|
1064
1093
|
|
|
1065
1094
|
if (this._playingQueue && this._waypointQueue.length > 0) {
|
|
1066
1095
|
const elapsed = currentTime - this._waypointStartTime;
|
|
1067
|
-
const progress = Math.min(elapsed / this._waypointDuration, 1);
|
|
1068
|
-
const
|
|
1069
|
-
|
|
1070
|
-
if (
|
|
1071
|
-
this._targetX =
|
|
1072
|
-
this._targetY =
|
|
1073
|
-
|
|
1096
|
+
const progress = Math.max(0, Math.min(elapsed / (this._waypointDuration || 1), 1));
|
|
1097
|
+
const totalSegments = this._waypointQueue.length - 1;
|
|
1098
|
+
|
|
1099
|
+
if (totalSegments <= 0) {
|
|
1100
|
+
this._targetX = this._waypointQueue[0].x;
|
|
1101
|
+
this._targetY = this._waypointQueue[0].y;
|
|
1102
|
+
} else {
|
|
1103
|
+
const floatIdx = progress * totalSegments;
|
|
1104
|
+
const i0 = Math.floor(floatIdx);
|
|
1105
|
+
const i1 = Math.min(i0 + 1, totalSegments);
|
|
1106
|
+
const t = floatIdx - i0;
|
|
1107
|
+
|
|
1108
|
+
const p0 = this._waypointQueue[i0];
|
|
1109
|
+
const p1 = this._waypointQueue[i1];
|
|
1110
|
+
if (p0 && p1) {
|
|
1111
|
+
this._targetX = p0.x + (p1.x - p0.x) * t;
|
|
1112
|
+
this._targetY = p0.y + (p1.y - p0.y) * t;
|
|
1113
|
+
}
|
|
1074
1114
|
}
|
|
1115
|
+
|
|
1075
1116
|
if (progress >= 1) {
|
|
1076
1117
|
this._playingQueue = false;
|
|
1077
1118
|
this._waypointQueue = [];
|
|
1078
1119
|
}
|
|
1079
1120
|
}
|
|
1080
1121
|
|
|
1081
|
-
// Smooth lerp (k =
|
|
1082
|
-
const lerpFactor = 1 - Math.exp(-
|
|
1122
|
+
// Smooth exponential lerp (k = 24 for fluid human-smooth tracking)
|
|
1123
|
+
const lerpFactor = 1 - Math.exp(-24 * dt);
|
|
1083
1124
|
if (this._targetX !== -9999) {
|
|
1084
1125
|
this._lerpX += (this._targetX - this._lerpX) * lerpFactor;
|
|
1085
1126
|
this._lerpY += (this._targetY - this._lerpY) * lerpFactor;
|
|
1086
|
-
if (Math.abs(this._targetX - this._lerpX) < 0.
|
|
1087
|
-
if (Math.abs(this._targetY - this._lerpY) < 0.
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
}
|
|
1096
|
-
this._idleFrames = 0;
|
|
1097
|
-
} else {
|
|
1098
|
-
this._idleFrames++;
|
|
1099
|
-
if (this._idleFrames > 90 || !__wt_masterVisualsEnabled) {
|
|
1127
|
+
if (Math.abs(this._targetX - this._lerpX) < 0.1) this._lerpX = this._targetX;
|
|
1128
|
+
if (Math.abs(this._targetY - this._lerpY) < 0.1) this._lerpY = this._targetY;
|
|
1129
|
+
|
|
1130
|
+
// "always show in browser" - stays visible once loaded and active
|
|
1131
|
+
if (typeof __wt_masterVisualsEnabled === 'undefined' || __wt_masterVisualsEnabled) {
|
|
1132
|
+
if (!this._visible) {
|
|
1133
|
+
this._visible = true;
|
|
1134
|
+
this._cursor.style.opacity = '1';
|
|
1135
|
+
}
|
|
1136
|
+
} else {
|
|
1100
1137
|
if (this._visible) {
|
|
1101
1138
|
this._visible = false;
|
|
1102
|
-
this.
|
|
1139
|
+
this._cursor.style.opacity = '0';
|
|
1103
1140
|
}
|
|
1104
1141
|
}
|
|
1142
|
+
|
|
1143
|
+
this._render();
|
|
1105
1144
|
}
|
|
1106
1145
|
|
|
1107
|
-
this._render();
|
|
1108
1146
|
this._animFrame = requestAnimationFrame(draw);
|
|
1109
1147
|
};
|
|
1148
|
+
if (this._animFrame) cancelAnimationFrame(this._animFrame);
|
|
1110
1149
|
this._animFrame = requestAnimationFrame(draw);
|
|
1111
1150
|
}
|
|
1112
1151
|
|
|
1113
1152
|
_render() {
|
|
1114
1153
|
if (typeof __wt_masterVisualsEnabled !== 'undefined' && !__wt_masterVisualsEnabled) {
|
|
1115
|
-
this.
|
|
1154
|
+
this._cursor.style.opacity = '0';
|
|
1116
1155
|
return;
|
|
1117
1156
|
}
|
|
1118
|
-
|
|
1119
|
-
const
|
|
1120
|
-
|
|
1121
|
-
this.
|
|
1122
|
-
ctx.clearRect(0, 0, 64, 64);
|
|
1123
|
-
|
|
1124
|
-
ctx.save();
|
|
1125
|
-
ctx.translate(2, 2);
|
|
1126
|
-
|
|
1127
|
-
// Sharp outer stroke — White
|
|
1128
|
-
ctx.fillStyle = '#FFFFFF';
|
|
1129
|
-
ctx.strokeStyle = '#FFFFFF';
|
|
1130
|
-
ctx.lineWidth = 2;
|
|
1131
|
-
ctx.lineJoin = 'miter';
|
|
1132
|
-
ctx.miterLimit = 3;
|
|
1133
|
-
ctx.fill(this._outerPath);
|
|
1134
|
-
ctx.stroke(this._outerPath);
|
|
1135
|
-
|
|
1136
|
-
// Sharp inner core — Gradient
|
|
1137
|
-
ctx.fillStyle = this._blueGrad;
|
|
1138
|
-
ctx.fill(this._innerPath);
|
|
1139
|
-
|
|
1140
|
-
ctx.restore();
|
|
1157
|
+
const x = this._lerpX;
|
|
1158
|
+
const y = this._lerpY;
|
|
1159
|
+
if (x === -9999 || y === -9999) return;
|
|
1160
|
+
this._cursor.style.transform = `translate3d(${x - this._hotX}px, ${y - this._hotY}px, 0) scale(${this._scale})`;
|
|
1141
1161
|
}
|
|
1142
1162
|
}
|
|
1143
1163
|
|
|
1144
|
-
// Instantiate the
|
|
1145
|
-
let __weaveCursor =
|
|
1164
|
+
// Instantiate the active cursor
|
|
1165
|
+
let __weaveCursor = new WeaveCursor();
|
|
1146
1166
|
window.__weaveCursor_instance = __weaveCursor;
|
|
1147
1167
|
|
|
1148
1168
|
function moveCursor(x, y, state) {
|
|
1149
|
-
|
|
1169
|
+
if (typeof __wt_masterVisualsEnabled !== 'undefined' && !__wt_masterVisualsEnabled) return;
|
|
1170
|
+
if (__weaveCursor && typeof __weaveCursor.moveTo === 'function') {
|
|
1171
|
+
if (typeof x === 'number' && typeof y === 'number' && x >= 0 && y >= 0) {
|
|
1172
|
+
__weaveCursor.moveTo(x, y);
|
|
1173
|
+
}
|
|
1174
|
+
if (state) __weaveCursor.setState(state);
|
|
1175
|
+
}
|
|
1150
1176
|
}
|
|
1151
1177
|
|
|
1152
1178
|
|
package/mcp.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weavetab/mcp",
|
|
3
3
|
"mcpName": "io.github.weavetab/mcp",
|
|
4
|
-
"version": "2.5.0-beta.
|
|
4
|
+
"version": "2.5.0-beta.1",
|
|
5
5
|
"description": "Weavetab MCP — Production-grade browser automation MCP server with direct Chrome DevTools Protocol (CDP) control. Direct socket transport, zero WebDriver/Node.js wrapper overhead, deterministic state execution.",
|
|
6
6
|
"icon": "icon.png",
|
|
7
7
|
"author": "fy2ne",
|
|
@@ -185,15 +185,6 @@
|
|
|
185
185
|
"Optional form submission click step"
|
|
186
186
|
]
|
|
187
187
|
},
|
|
188
|
-
"browser_plan": {
|
|
189
|
-
"category": "mission_execution",
|
|
190
|
-
"description": "Create or update a live task panel in the browser. Renders a real-time task dashboard within a Shadow DOM overlay displaying goal progress and step status.",
|
|
191
|
-
"features": [
|
|
192
|
-
"Real-time task state and progress display",
|
|
193
|
-
"Isolated Shadow DOM layout structure",
|
|
194
|
-
"Structured task state tracking (pending, in-progress, done, failed)"
|
|
195
|
-
]
|
|
196
|
-
},
|
|
197
188
|
"browser_highlight": {
|
|
198
189
|
"category": "canvas_and_visual",
|
|
199
190
|
"description": "Highlight an element with a colored overlay. Renders temporary visual CDP overlay highlights over target elements without DOM tree mutations.",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weavetab/mcp",
|
|
3
|
-
"version": "2.5.0-beta.
|
|
3
|
+
"version": "2.5.0-beta.1",
|
|
4
4
|
"description": "A production-grade local MCP server for AI browser automation via Chrome DevTools Protocol. No extensions. No cloud. Full control.",
|
|
5
5
|
"author": "fy2ne",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
package/server.json
CHANGED
package/dist/tools/plan.d.ts
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import CDP from "chrome-remote-interface";
|
|
2
|
-
import type { Config } from "../config/loader.js";
|
|
3
|
-
interface PlanTask {
|
|
4
|
-
id: number;
|
|
5
|
-
description: string;
|
|
6
|
-
status: "pending" | "in-progress" | "done" | "failed";
|
|
7
|
-
comment?: string;
|
|
8
|
-
}
|
|
9
|
-
export interface PlanOptions {
|
|
10
|
-
action: "create" | "update" | "comments" | "hide";
|
|
11
|
-
goal?: string;
|
|
12
|
-
tasks?: PlanTask[];
|
|
13
|
-
taskId?: number;
|
|
14
|
-
status?: PlanTask["status"];
|
|
15
|
-
}
|
|
16
|
-
interface PlanComment {
|
|
17
|
-
taskId: number;
|
|
18
|
-
comment: string;
|
|
19
|
-
time: number;
|
|
20
|
-
}
|
|
21
|
-
export declare function weavePlan(session: CDP.Client, options: PlanOptions, _config: Config): Promise<{
|
|
22
|
-
success: boolean;
|
|
23
|
-
plan?: {
|
|
24
|
-
goal: string;
|
|
25
|
-
tasks: PlanTask[];
|
|
26
|
-
};
|
|
27
|
-
comments?: PlanComment[];
|
|
28
|
-
error?: string;
|
|
29
|
-
}>;
|
|
30
|
-
export declare function addPlanComment(taskId: number, comment: string): void;
|
|
31
|
-
/** Returns current in-memory plan (goal + tasks) — used to re-inject on page navigation */
|
|
32
|
-
export declare function getActivePlan(): {
|
|
33
|
-
goal: string;
|
|
34
|
-
tasks: PlanTask[];
|
|
35
|
-
} | null;
|
|
36
|
-
export declare function setActivePlan(goal: string, tasks: PlanTask[]): void;
|
|
37
|
-
/** Re-inject plan into a (possibly fresh) page session — used after navigation / reconnect */
|
|
38
|
-
export declare function restoreActivePlanIntoPage(session: CDP.Client): Promise<void>;
|
|
39
|
-
export {};
|
package/dist/tools/plan.js
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
import { logAction } from "../audit/logger.js";
|
|
2
|
-
import { sendToExtension } from "../cdp/helpers.js";
|
|
3
|
-
// In-memory comment store (agent-side)
|
|
4
|
-
const planComments = [];
|
|
5
|
-
let activeGoal = "";
|
|
6
|
-
let activeTasks = [];
|
|
7
|
-
// Dormant Guard (Layer 1)
|
|
8
|
-
let dormantTimer = null;
|
|
9
|
-
const DORMANT_TIMEOUT_MS = 45000;
|
|
10
|
-
function resetDormantGuard(session) {
|
|
11
|
-
if (dormantTimer)
|
|
12
|
-
clearTimeout(dormantTimer);
|
|
13
|
-
dormantTimer = setTimeout(async () => {
|
|
14
|
-
try {
|
|
15
|
-
await session.Runtime.evaluate({
|
|
16
|
-
expression: `(function() { if (typeof window.__wt_plan === 'function') window.__wt_plan('hide'); })()`
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
catch { /* session may be dead */ }
|
|
20
|
-
}, DORMANT_TIMEOUT_MS);
|
|
21
|
-
}
|
|
22
|
-
/** Poll the page for new user comments on plan tasks */
|
|
23
|
-
async function pollComments(session) {
|
|
24
|
-
try {
|
|
25
|
-
const { result } = await session.Runtime.evaluate({
|
|
26
|
-
expression: `(function() {
|
|
27
|
-
var q = window.__wt_plan_comments || [];
|
|
28
|
-
if (q.length > 0) { window.__wt_plan_comments = []; return JSON.stringify(q); }
|
|
29
|
-
return '[]';
|
|
30
|
-
})()`,
|
|
31
|
-
returnByValue: true,
|
|
32
|
-
});
|
|
33
|
-
if (result?.value && result.value !== '[]') {
|
|
34
|
-
return JSON.parse(result.value);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
catch { /* non-fatal */ }
|
|
38
|
-
return [];
|
|
39
|
-
}
|
|
40
|
-
export async function weavePlan(session, options, _config) {
|
|
41
|
-
const { action, goal, tasks, taskId, status } = options;
|
|
42
|
-
// Always poll for new user comments from the overlay
|
|
43
|
-
const newComments = await pollComments(session);
|
|
44
|
-
for (const c of newComments) {
|
|
45
|
-
planComments.push(c);
|
|
46
|
-
const task = activeTasks.find(t => t.id === c.taskId);
|
|
47
|
-
if (task) {
|
|
48
|
-
task.comment = c.comment;
|
|
49
|
-
logAction("browser_plan", "comment", `task ${c.taskId}: ${c.comment}`);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
if (action === "create") {
|
|
53
|
-
activeGoal = goal || "";
|
|
54
|
-
activeTasks = tasks || [];
|
|
55
|
-
// Use sendToExtension for reliable delivery to content script
|
|
56
|
-
await sendToExtension(session, {
|
|
57
|
-
type: 'Weavetab_STATE',
|
|
58
|
-
plan: { action: 'create', goal: activeGoal, tasks: activeTasks },
|
|
59
|
-
}).catch(() => { });
|
|
60
|
-
// Also try direct injection as fallback
|
|
61
|
-
await session.Runtime.evaluate({
|
|
62
|
-
expression: `(function() {
|
|
63
|
-
if (typeof window.__wt_plan === 'function') {
|
|
64
|
-
window.__wt_plan('create', ${JSON.stringify(activeGoal)}, ${JSON.stringify(activeTasks)});
|
|
65
|
-
}
|
|
66
|
-
})()`,
|
|
67
|
-
returnByValue: true,
|
|
68
|
-
}).catch(() => { });
|
|
69
|
-
resetDormantGuard(session);
|
|
70
|
-
logAction("browser_plan", "create", `goal: ${activeGoal}, ${activeTasks.length} tasks`);
|
|
71
|
-
return { success: true, plan: { goal: activeGoal, tasks: activeTasks } };
|
|
72
|
-
}
|
|
73
|
-
if (action === "update") {
|
|
74
|
-
if (taskId === undefined || !status) {
|
|
75
|
-
return { success: false, error: "browser_plan update requires taskId and status" };
|
|
76
|
-
}
|
|
77
|
-
const task = activeTasks.find(t => t.id === taskId);
|
|
78
|
-
if (!task) {
|
|
79
|
-
return { success: false, error: `Task ${taskId} not found` };
|
|
80
|
-
}
|
|
81
|
-
task.status = status;
|
|
82
|
-
// Use sendToExtension for reliable delivery
|
|
83
|
-
await sendToExtension(session, {
|
|
84
|
-
type: 'Weavetab_STATE',
|
|
85
|
-
plan: { action: 'update', goal: activeGoal, tasks: activeTasks },
|
|
86
|
-
}).catch(() => { });
|
|
87
|
-
// Also try direct injection as fallback
|
|
88
|
-
await session.Runtime.evaluate({
|
|
89
|
-
expression: `(function() {
|
|
90
|
-
if (typeof window.__wt_plan === 'function') {
|
|
91
|
-
window.__wt_plan('update', ${JSON.stringify(activeGoal)}, ${JSON.stringify(activeTasks)});
|
|
92
|
-
}
|
|
93
|
-
})()`,
|
|
94
|
-
returnByValue: true,
|
|
95
|
-
}).catch(() => { });
|
|
96
|
-
resetDormantGuard(session);
|
|
97
|
-
logAction("browser_plan", "update", `task ${taskId} \u2192 ${status}`);
|
|
98
|
-
return { success: true, plan: { goal: activeGoal, tasks: activeTasks } };
|
|
99
|
-
}
|
|
100
|
-
if (action === "hide") {
|
|
101
|
-
if (dormantTimer) {
|
|
102
|
-
clearTimeout(dormantTimer);
|
|
103
|
-
dormantTimer = null;
|
|
104
|
-
}
|
|
105
|
-
// Use sendToExtension for reliable delivery
|
|
106
|
-
await sendToExtension(session, {
|
|
107
|
-
type: 'Weavetab_STATE',
|
|
108
|
-
plan: { action: 'hide', goal: '', tasks: [] },
|
|
109
|
-
}).catch(() => { });
|
|
110
|
-
// Also try direct injection as fallback
|
|
111
|
-
await session.Runtime.evaluate({
|
|
112
|
-
expression: `(function() {
|
|
113
|
-
if (typeof window.__wt_plan === 'function') window.__wt_plan('hide');
|
|
114
|
-
})()`,
|
|
115
|
-
returnByValue: true,
|
|
116
|
-
}).catch(() => { });
|
|
117
|
-
return { success: true };
|
|
118
|
-
}
|
|
119
|
-
if (action === "comments") {
|
|
120
|
-
return { success: true, comments: planComments.slice(), plan: { goal: activeGoal, tasks: activeTasks } };
|
|
121
|
-
}
|
|
122
|
-
return { success: false, error: `Unknown action: ${action}` };
|
|
123
|
-
}
|
|
124
|
-
export function addPlanComment(taskId, comment) {
|
|
125
|
-
planComments.push({ taskId, comment, time: Date.now() });
|
|
126
|
-
}
|
|
127
|
-
/** Returns current in-memory plan (goal + tasks) — used to re-inject on page navigation */
|
|
128
|
-
export function getActivePlan() {
|
|
129
|
-
if (!activeGoal && activeTasks.length === 0)
|
|
130
|
-
return null;
|
|
131
|
-
return { goal: activeGoal, tasks: activeTasks };
|
|
132
|
-
}
|
|
133
|
-
export function setActivePlan(goal, tasks) {
|
|
134
|
-
activeGoal = goal || "";
|
|
135
|
-
activeTasks = tasks || [];
|
|
136
|
-
}
|
|
137
|
-
/** Re-inject plan into a (possibly fresh) page session — used after navigation / reconnect */
|
|
138
|
-
export async function restoreActivePlanIntoPage(session) {
|
|
139
|
-
if (!activeGoal && activeTasks.length === 0)
|
|
140
|
-
return;
|
|
141
|
-
// Use sendToExtension for reliable delivery
|
|
142
|
-
try {
|
|
143
|
-
await sendToExtension(session, {
|
|
144
|
-
type: 'Weavetab_STATE',
|
|
145
|
-
plan: { action: 'create', goal: activeGoal, tasks: activeTasks },
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
catch { /* non-fatal */ }
|
|
149
|
-
// Also try direct injection as fallback
|
|
150
|
-
try {
|
|
151
|
-
await session.Runtime.evaluate({
|
|
152
|
-
expression: `(function() {
|
|
153
|
-
if (typeof window.__wt_plan === 'function') {
|
|
154
|
-
window.__wt_plan('create', ${JSON.stringify(activeGoal)}, ${JSON.stringify(activeTasks)});
|
|
155
|
-
}
|
|
156
|
-
})()`,
|
|
157
|
-
returnByValue: true,
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
catch { /* page may not be ready */ }
|
|
161
|
-
}
|