@weavetab/mcp 2.5.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +48 -0
- package/ARCHITECTURE.md +590 -0
- package/LICENSE +631 -0
- package/README.md +909 -0
- package/SECURITY.md +196 -0
- package/TROUBLESHOOTING.md +365 -0
- package/dist/anticaptcha/detector.d.ts +49 -0
- package/dist/anticaptcha/detector.js +465 -0
- package/dist/anticaptcha/integration.d.ts +36 -0
- package/dist/anticaptcha/integration.js +123 -0
- package/dist/anticaptcha/solver.d.ts +27 -0
- package/dist/anticaptcha/solver.js +552 -0
- package/dist/audit/logger.d.ts +13 -0
- package/dist/audit/logger.js +177 -0
- package/dist/audit/telemetry.d.ts +36 -0
- package/dist/audit/telemetry.js +162 -0
- package/dist/cdp/bridge.d.ts +16 -0
- package/dist/cdp/bridge.js +826 -0
- package/dist/cdp/confirm.d.ts +26 -0
- package/dist/cdp/confirm.js +79 -0
- package/dist/cdp/connector.d.ts +37 -0
- package/dist/cdp/connector.js +661 -0
- package/dist/cdp/extension.d.ts +36 -0
- package/dist/cdp/extension.js +118 -0
- package/dist/cdp/finder.d.ts +1 -0
- package/dist/cdp/finder.js +2 -0
- package/dist/cdp/helpers.d.ts +11 -0
- package/dist/cdp/helpers.js +195 -0
- package/dist/cdp/label_cache.d.ts +12 -0
- package/dist/cdp/label_cache.js +51 -0
- package/dist/cdp/lock.d.ts +33 -0
- package/dist/cdp/lock.js +206 -0
- package/dist/cdp/omni_broker.d.ts +17 -0
- package/dist/cdp/omni_broker.js +119 -0
- package/dist/cdp/profiles.d.ts +1 -0
- package/dist/cdp/profiles.js +87 -0
- package/dist/cdp/pruner.d.ts +41 -0
- package/dist/cdp/pruner.js +263 -0
- package/dist/cdp/ref_cache.d.ts +101 -0
- package/dist/cdp/ref_cache.js +234 -0
- package/dist/cdp/retry.d.ts +5 -0
- package/dist/cdp/retry.js +39 -0
- package/dist/cdp/retry_enhanced.d.ts +44 -0
- package/dist/cdp/retry_enhanced.js +102 -0
- package/dist/cdp/security.d.ts +50 -0
- package/dist/cdp/security.js +237 -0
- package/dist/cdp/verify.d.ts +39 -0
- package/dist/cdp/verify.js +143 -0
- package/dist/cdp/walker.d.ts +91 -0
- package/dist/cdp/walker.js +896 -0
- package/dist/cdp/walker.test.d.ts +2 -0
- package/dist/cdp/walker.test.js +172 -0
- package/dist/cdp/websocket_transport.d.ts +15 -0
- package/dist/cdp/websocket_transport.js +118 -0
- package/dist/cli/plugin.d.ts +9 -0
- package/dist/cli/plugin.js +1134 -0
- package/dist/cli/updater.d.ts +1 -0
- package/dist/cli/updater.js +72 -0
- package/dist/config/loader.d.ts +120 -0
- package/dist/config/loader.js +704 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1248 -0
- package/dist/intelligence/hints.d.ts +11 -0
- package/dist/intelligence/hints.js +39 -0
- package/dist/intelligence/intent.d.ts +33 -0
- package/dist/intelligence/intent.js +171 -0
- package/dist/intelligence/sessionMemory.d.ts +66 -0
- package/dist/intelligence/sessionMemory.js +284 -0
- package/dist/intelligence/sessionMemory.test.d.ts +2 -0
- package/dist/intelligence/sessionMemory.test.js +62 -0
- package/dist/intelligence/trail.d.ts +64 -0
- package/dist/intelligence/trail.js +233 -0
- package/dist/intelligence/trail.test.d.ts +2 -0
- package/dist/intelligence/trail.test.js +110 -0
- package/dist/intelligence/vision.d.ts +24 -0
- package/dist/intelligence/vision.js +151 -0
- package/dist/lifecycle/mission_manager.d.ts +16 -0
- package/dist/lifecycle/mission_manager.js +44 -0
- package/dist/overlay/bubble.d.ts +16 -0
- package/dist/overlay/bubble.js +37 -0
- package/dist/overlay/ghost.d.ts +35 -0
- package/dist/overlay/ghost.js +508 -0
- package/dist/overlay/injector.d.ts +22 -0
- package/dist/overlay/injector.js +198 -0
- package/dist/plugin/interface.d.ts +134 -0
- package/dist/plugin/interface.js +1 -0
- package/dist/plugin/loader.d.ts +18 -0
- package/dist/plugin/loader.js +155 -0
- package/dist/plugin/registry.d.ts +50 -0
- package/dist/plugin/registry.js +234 -0
- package/dist/security/blacklist.d.ts +24 -0
- package/dist/security/blacklist.js +80 -0
- package/dist/security/filter.d.ts +2 -0
- package/dist/security/filter.js +20 -0
- package/dist/security/guard.d.ts +8 -0
- package/dist/security/guard.js +35 -0
- package/dist/security/ratelimit.d.ts +11 -0
- package/dist/security/ratelimit.js +57 -0
- package/dist/security/secrets.d.ts +75 -0
- package/dist/security/secrets.js +365 -0
- package/dist/sensors/actionability.d.ts +81 -0
- package/dist/sensors/actionability.js +303 -0
- package/dist/sensors/guard.d.ts +25 -0
- package/dist/sensors/guard.js +168 -0
- package/dist/sensors/mutations.d.ts +23 -0
- package/dist/sensors/mutations.js +93 -0
- package/dist/sensors/network.d.ts +67 -0
- package/dist/sensors/network.js +193 -0
- package/dist/sensors/operation_monitor.d.ts +80 -0
- package/dist/sensors/operation_monitor.js +203 -0
- package/dist/sensors/pacing.d.ts +44 -0
- package/dist/sensors/pacing.js +111 -0
- package/dist/sensors/thoughts.d.ts +2 -0
- package/dist/sensors/thoughts.js +59 -0
- package/dist/sensors/wait.d.ts +12 -0
- package/dist/sensors/wait.js +44 -0
- package/dist/server.d.ts +7 -0
- package/dist/server.js +1687 -0
- package/dist/state/agent.d.ts +36 -0
- package/dist/state/agent.js +101 -0
- package/dist/state/cache.d.ts +16 -0
- package/dist/state/cache.js +92 -0
- package/dist/state/cache.test.d.ts +2 -0
- package/dist/state/cache.test.js +76 -0
- package/dist/state/delta.d.ts +71 -0
- package/dist/state/delta.js +234 -0
- package/dist/state/delta.test.d.ts +2 -0
- package/dist/state/delta.test.js +279 -0
- package/dist/state/groups.d.ts +15 -0
- package/dist/state/groups.js +80 -0
- package/dist/state/session.d.ts +30 -0
- package/dist/state/session.js +155 -0
- package/dist/tools/accessibility.d.ts +14 -0
- package/dist/tools/accessibility.js +32 -0
- package/dist/tools/ask.d.ts +14 -0
- package/dist/tools/ask.js +49 -0
- package/dist/tools/automation.d.ts +36 -0
- package/dist/tools/automation.js +218 -0
- package/dist/tools/browser_detect.d.ts +13 -0
- package/dist/tools/browser_detect.js +73 -0
- package/dist/tools/burst.d.ts +35 -0
- package/dist/tools/burst.js +344 -0
- package/dist/tools/canvas.d.ts +25 -0
- package/dist/tools/canvas.js +132 -0
- package/dist/tools/captcha.d.ts +38 -0
- package/dist/tools/captcha.js +209 -0
- package/dist/tools/click.d.ts +36 -0
- package/dist/tools/click.js +561 -0
- package/dist/tools/click_and_wait.d.ts +33 -0
- package/dist/tools/click_and_wait.js +149 -0
- package/dist/tools/clipboard.d.ts +29 -0
- package/dist/tools/clipboard.js +279 -0
- package/dist/tools/console.d.ts +21 -0
- package/dist/tools/console.js +107 -0
- package/dist/tools/cookies.d.ts +67 -0
- package/dist/tools/cookies.js +121 -0
- package/dist/tools/design_extract.d.ts +109 -0
- package/dist/tools/design_extract.js +641 -0
- package/dist/tools/dialog.d.ts +24 -0
- package/dist/tools/dialog.js +109 -0
- package/dist/tools/drag.d.ts +15 -0
- package/dist/tools/drag.js +29 -0
- package/dist/tools/emulation.d.ts +33 -0
- package/dist/tools/emulation.js +69 -0
- package/dist/tools/eval.d.ts +20 -0
- package/dist/tools/eval.js +92 -0
- package/dist/tools/fill.d.ts +41 -0
- package/dist/tools/fill.js +667 -0
- package/dist/tools/find.d.ts +30 -0
- package/dist/tools/find.js +60 -0
- package/dist/tools/geo.d.ts +19 -0
- package/dist/tools/geo.js +29 -0
- package/dist/tools/github/analyze.d.ts +4 -0
- package/dist/tools/github/analyze.js +113 -0
- package/dist/tools/github/cache.d.ts +21 -0
- package/dist/tools/github/cache.js +28 -0
- package/dist/tools/github/index.d.ts +4 -0
- package/dist/tools/github/index.js +4 -0
- package/dist/tools/github/issues.d.ts +9 -0
- package/dist/tools/github/issues.js +88 -0
- package/dist/tools/github/pr.d.ts +5 -0
- package/dist/tools/github/pr.js +88 -0
- package/dist/tools/github/rateLimiter.d.ts +4 -0
- package/dist/tools/github/rateLimiter.js +46 -0
- package/dist/tools/github/read.d.ts +34 -0
- package/dist/tools/github/read.js +74 -0
- package/dist/tools/github/repoContext.d.ts +20 -0
- package/dist/tools/github/repoContext.js +129 -0
- package/dist/tools/highlight.d.ts +20 -0
- package/dist/tools/highlight.js +140 -0
- package/dist/tools/hover.d.ts +19 -0
- package/dist/tools/hover.js +57 -0
- package/dist/tools/inspect.d.ts +17 -0
- package/dist/tools/inspect.js +125 -0
- package/dist/tools/integration.test.d.ts +2 -0
- package/dist/tools/integration.test.js +96 -0
- package/dist/tools/key.d.ts +25 -0
- package/dist/tools/key.js +173 -0
- package/dist/tools/macro_compiler.d.ts +7 -0
- package/dist/tools/macro_compiler.js +82 -0
- package/dist/tools/mouse.d.ts +21 -0
- package/dist/tools/mouse.js +62 -0
- package/dist/tools/navigate.d.ts +16 -0
- package/dist/tools/navigate.js +188 -0
- package/dist/tools/network_intercept.d.ts +17 -0
- package/dist/tools/network_intercept.js +151 -0
- package/dist/tools/office/index.d.ts +9 -0
- package/dist/tools/office/index.js +7 -0
- package/dist/tools/office/list.d.ts +15 -0
- package/dist/tools/office/list.js +41 -0
- package/dist/tools/office/parse.d.ts +20 -0
- package/dist/tools/office/parse.js +172 -0
- package/dist/tools/pdf.d.ts +22 -0
- package/dist/tools/pdf.js +42 -0
- package/dist/tools/performance.d.ts +12 -0
- package/dist/tools/performance.js +47 -0
- package/dist/tools/performance_test.d.ts +70 -0
- package/dist/tools/performance_test.js +128 -0
- package/dist/tools/plan.d.ts +39 -0
- package/dist/tools/plan.js +161 -0
- package/dist/tools/pointer.d.ts +35 -0
- package/dist/tools/pointer.js +522 -0
- package/dist/tools/read.d.ts +30 -0
- package/dist/tools/read.js +488 -0
- package/dist/tools/recording.d.ts +45 -0
- package/dist/tools/recording.js +124 -0
- package/dist/tools/response.d.ts +129 -0
- package/dist/tools/response.js +5 -0
- package/dist/tools/scrape.d.ts +24 -0
- package/dist/tools/scrape.js +140 -0
- package/dist/tools/screenshot.d.ts +40 -0
- package/dist/tools/screenshot.js +79 -0
- package/dist/tools/scroll.d.ts +23 -0
- package/dist/tools/scroll.js +196 -0
- package/dist/tools/select.d.ts +13 -0
- package/dist/tools/select.js +217 -0
- package/dist/tools/snapshot.d.ts +27 -0
- package/dist/tools/snapshot.js +99 -0
- package/dist/tools/storage.d.ts +18 -0
- package/dist/tools/storage.js +29 -0
- package/dist/tools/tabs.d.ts +77 -0
- package/dist/tools/tabs.js +342 -0
- package/dist/tools/thought.d.ts +16 -0
- package/dist/tools/thought.js +79 -0
- package/dist/tools/type.d.ts +65 -0
- package/dist/tools/type.js +1061 -0
- package/dist/tools/type_secret.d.ts +23 -0
- package/dist/tools/type_secret.js +229 -0
- package/dist/tools/upload.d.ts +19 -0
- package/dist/tools/upload.js +103 -0
- package/dist/tools/video_compiler.d.ts +24 -0
- package/dist/tools/video_compiler.js +199 -0
- package/dist/tools/viewport.d.ts +44 -0
- package/dist/tools/viewport.js +230 -0
- package/dist/tools/wait.d.ts +19 -0
- package/dist/tools/wait.js +202 -0
- package/dist/tools/wait_for.d.ts +23 -0
- package/dist/tools/wait_for.js +30 -0
- package/dist/tools/windows.d.ts +22 -0
- package/dist/tools/windows.js +57 -0
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.js +12 -0
- package/docs/DEV_MODE.md +68 -0
- package/docs/DOCUMENTATION.md +532 -0
- package/docs/PLUGINS.md +538 -0
- package/docs/TOOLS.md +104 -0
- package/docs/dev_plugin.md +310 -0
- package/ext-dist/background.js +467 -0
- package/ext-dist/content.js +3089 -0
- package/ext-dist/dashboard.html +583 -0
- package/ext-dist/hud/hud.js +73 -0
- package/ext-dist/hud/index.html +20 -0
- package/ext-dist/manifest.json +77 -0
- package/ext-dist/sidepanel/sidepanel.js +25 -0
- package/ext-dist/sidepanel.html +547 -0
- package/ext-dist/styles.css +218 -0
- package/icon.png +0 -0
- package/mcp.json +796 -0
- package/package.json +113 -0
- package/server.json +23 -0
- package/smithery.yaml +11 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// src/tools/click_and_wait.ts
|
|
2
|
+
// Consolidated tool: find element + click + wait for response in one CDP round-trip
|
|
3
|
+
// Eliminates the common pattern: browser_map → browser_click → browser_wait
|
|
4
|
+
import { buildActionMap, findBestMatch, safeSelector, toMarkdownTable } from "../cdp/walker.js";
|
|
5
|
+
import { recordAction, trackToolCall, detectStuckLoop } from "../intelligence/trail.js";
|
|
6
|
+
import { waitForDOMStable } from "../sensors/guard.js";
|
|
7
|
+
import { waitForNetworkIdle } from "../sensors/wait.js";
|
|
8
|
+
import { ghostMove, ghostClick } from "../overlay/ghost.js";
|
|
9
|
+
import { cacheActionMap, getCachedActionMap } from "../state/cache.js";
|
|
10
|
+
import { storeSnapshot, computeDelta } from "../state/delta.js";
|
|
11
|
+
import { sessionRegistry } from "../state/session.js";
|
|
12
|
+
import { getConfigAgentId } from "../config/loader.js";
|
|
13
|
+
import { operationMonitor } from "../sensors/operation_monitor.js";
|
|
14
|
+
/**
|
|
15
|
+
* Consolidated tool that combines element resolution, clicking, and waiting.
|
|
16
|
+
* Eliminates 2-3 tool calls in the common pattern: find → click → wait
|
|
17
|
+
*/
|
|
18
|
+
export async function weaveClickAndWait(session, args, config) {
|
|
19
|
+
const { id, label, intent, x, y, waitAfter = 500, waitForNetwork = true, includeMap = false, fast = false, tab_id } = args;
|
|
20
|
+
const { Page, DOM, Input } = session;
|
|
21
|
+
const startMs = Date.now();
|
|
22
|
+
const targetId = sessionRegistry.getActive(getConfigAgentId(config))?.targetId ?? "default";
|
|
23
|
+
const operationId = `clickAndWait-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
24
|
+
operationMonitor.startOperation(operationId, 'browser_click_and_wait');
|
|
25
|
+
// Handle tab switching if tab_id is provided
|
|
26
|
+
let targetSession = session;
|
|
27
|
+
if (tab_id) {
|
|
28
|
+
const { connectToTab } = await import("../cdp/connector.js");
|
|
29
|
+
const client = await connectToTab(tab_id);
|
|
30
|
+
targetSession = client.session;
|
|
31
|
+
}
|
|
32
|
+
// Check for stuck loop before processing
|
|
33
|
+
const stuckCheck = detectStuckLoop();
|
|
34
|
+
if (stuckCheck.stuck) {
|
|
35
|
+
return {
|
|
36
|
+
success: false,
|
|
37
|
+
error_code: "STUCK_LOOP",
|
|
38
|
+
message: stuckCheck.diagnosis || "Agent is stuck in a loop",
|
|
39
|
+
suggestion: "Try a different approach or reset the loop counter",
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
// Get initial URL
|
|
43
|
+
const initialUrl = (await targetSession.Page.getFrameTree()).frameTree.frame.url;
|
|
44
|
+
// Build or get cached action map
|
|
45
|
+
let map = getCachedActionMap(targetId, initialUrl);
|
|
46
|
+
if (!map) {
|
|
47
|
+
map = await buildActionMap(targetSession, { lite: true }, targetId);
|
|
48
|
+
cacheActionMap(targetId, map);
|
|
49
|
+
}
|
|
50
|
+
await storeSnapshot(targetId, initialUrl, map.elements);
|
|
51
|
+
// Resolve element
|
|
52
|
+
let element;
|
|
53
|
+
if (id) {
|
|
54
|
+
element = map.elements.find((el) => el.id === id || el.id.endsWith(id));
|
|
55
|
+
}
|
|
56
|
+
if (!element && label) {
|
|
57
|
+
element = findBestMatch(map.elements, label);
|
|
58
|
+
}
|
|
59
|
+
if (!element && intent) {
|
|
60
|
+
element = findBestMatch(map.elements, intent);
|
|
61
|
+
}
|
|
62
|
+
if (!element && !x && !y) {
|
|
63
|
+
return {
|
|
64
|
+
success: false,
|
|
65
|
+
error_code: "ELEMENT_NOT_FOUND",
|
|
66
|
+
message: `Element not found with id="${id}", label="${label}", or intent="${intent}"`,
|
|
67
|
+
suggestion: "Try browser_map to see available elements, or use x/y coordinates",
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const clickX = x ?? element?.x;
|
|
71
|
+
const clickY = y ?? element?.y;
|
|
72
|
+
const selector = element ? safeSelector(element) : undefined;
|
|
73
|
+
if (!clickX || !clickY) {
|
|
74
|
+
return {
|
|
75
|
+
success: false,
|
|
76
|
+
error_code: "NO_COORDINATES",
|
|
77
|
+
message: "Could not determine click coordinates",
|
|
78
|
+
suggestion: "Provide explicit x/y coordinates or ensure element has position data",
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
// Perform click with ghost move
|
|
82
|
+
await ghostMove(targetSession, clickX, clickY, config, element?.label || "target");
|
|
83
|
+
await ghostClick(targetSession, clickX, clickY, { pointerType: 'mouse' });
|
|
84
|
+
await new Promise(r => setTimeout(r, 30 + Math.random() * 40));
|
|
85
|
+
await targetSession.Input.dispatchMouseEvent({
|
|
86
|
+
type: 'mouseReleased',
|
|
87
|
+
x: clickX,
|
|
88
|
+
y: clickY,
|
|
89
|
+
button: 'left',
|
|
90
|
+
clickCount: 1,
|
|
91
|
+
pointerType: 'mouse'
|
|
92
|
+
});
|
|
93
|
+
// Wait for response
|
|
94
|
+
const waitStart = Date.now();
|
|
95
|
+
if (waitForNetwork) {
|
|
96
|
+
await Promise.all([
|
|
97
|
+
waitForDOMStable(targetSession, 50, 300),
|
|
98
|
+
waitForNetworkIdle(targetSession, waitAfter, waitAfter * 2).catch(() => { })
|
|
99
|
+
]);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
await new Promise(r => setTimeout(r, waitAfter));
|
|
103
|
+
}
|
|
104
|
+
const waitedMs = Date.now() - waitStart;
|
|
105
|
+
// Check result
|
|
106
|
+
const finalUrl = (await targetSession.Page.getFrameTree()).frameTree.frame.url;
|
|
107
|
+
const urlChanged = finalUrl !== initialUrl;
|
|
108
|
+
let newMap = map;
|
|
109
|
+
let domChanged = false;
|
|
110
|
+
if (urlChanged || includeMap) {
|
|
111
|
+
newMap = await buildActionMap(targetSession, { lite: true }, targetId);
|
|
112
|
+
cacheActionMap(targetId, newMap);
|
|
113
|
+
const delta = computeDelta(targetId, initialUrl, newMap.elements);
|
|
114
|
+
domChanged = delta ? delta.added.length + delta.removed.length + delta.changed.length > 0 : urlChanged;
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
const delta = computeDelta(targetId, initialUrl, map.elements);
|
|
118
|
+
domChanged = delta ? delta.added.length + delta.removed.length + delta.changed.length > 0 : false;
|
|
119
|
+
}
|
|
120
|
+
const executionMs = Date.now() - startMs;
|
|
121
|
+
// Track tool call
|
|
122
|
+
trackToolCall("browser_click_and_wait", element?.id || id || label || "coordinates", {
|
|
123
|
+
success: true,
|
|
124
|
+
urlChanged
|
|
125
|
+
});
|
|
126
|
+
recordAction({
|
|
127
|
+
timestamp: new Date().toISOString(),
|
|
128
|
+
tool: "browser_click_and_wait",
|
|
129
|
+
input: { ref: element?.id || id || label, waitAfter, waitForNetwork },
|
|
130
|
+
result: { success: true, urlChanged, domChanged, waitedMs },
|
|
131
|
+
});
|
|
132
|
+
operationMonitor.endOperation(operationId);
|
|
133
|
+
const result = {
|
|
134
|
+
success: true,
|
|
135
|
+
clicked: element?.label || "coordinates",
|
|
136
|
+
ref: element?.id || id || "coordinates",
|
|
137
|
+
execution_ms: executionMs,
|
|
138
|
+
tab_id: targetId,
|
|
139
|
+
url: finalUrl,
|
|
140
|
+
title: newMap?.title || "",
|
|
141
|
+
url_changed: urlChanged,
|
|
142
|
+
...(includeMap ? { page_map: newMap ? toMarkdownTable(newMap.elements, finalUrl, newMap.title) : "" } : {}),
|
|
143
|
+
dom_changed: domChanged,
|
|
144
|
+
stuck_warning: false,
|
|
145
|
+
waited_ms: waitedMs,
|
|
146
|
+
network_idle: waitForNetwork,
|
|
147
|
+
};
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weavetab — tools/clipboard.ts
|
|
3
|
+
* browser_clipboard: Read and write text + images to the system/browser clipboard.
|
|
4
|
+
* Uses synthetic mouse click to grant user activation before clipboard API calls.
|
|
5
|
+
* Weavetab by fy2ne
|
|
6
|
+
*/
|
|
7
|
+
import CDP from "chrome-remote-interface";
|
|
8
|
+
import type { Config } from "../config/loader.js";
|
|
9
|
+
export interface ClipboardOptions {
|
|
10
|
+
action: "read" | "write";
|
|
11
|
+
/** Text content to write (required for text write, optional if dataUrl is provided) */
|
|
12
|
+
text?: string;
|
|
13
|
+
/** Base64 data URL for image write (e.g. "data:image/png;base64,iVBOR...") */
|
|
14
|
+
dataUrl?: string;
|
|
15
|
+
/** MIME type override (auto-detected from dataUrl by default) */
|
|
16
|
+
mimeType?: string;
|
|
17
|
+
/**
|
|
18
|
+
* When true, skips the (5,5) user-activation click before clipboard write.
|
|
19
|
+
* Set this when the calling context already has input focus (e.g. browser_type)
|
|
20
|
+
* to prevent blurring the focused field before Ctrl+V.
|
|
21
|
+
*/
|
|
22
|
+
skipActivation?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export declare function browserClipboard(session: CDP.Client, options: ClipboardOptions, _config: Config): Promise<{
|
|
25
|
+
success: boolean;
|
|
26
|
+
text?: string;
|
|
27
|
+
dataUrl?: string;
|
|
28
|
+
message: string;
|
|
29
|
+
}>;
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { ghostClick } from "../overlay/ghost.js";
|
|
2
|
+
export async function browserClipboard(session, options, _config) {
|
|
3
|
+
const { action, text, dataUrl, mimeType } = options;
|
|
4
|
+
// ── Dispatch a synthetic click to grant transient user activation ──
|
|
5
|
+
async function grantActivation() {
|
|
6
|
+
try {
|
|
7
|
+
await ghostClick(session, 5, 5);
|
|
8
|
+
await session.Input.dispatchMouseEvent({ type: "mouseReleased", x: 5, y: 5, button: "left", clickCount: 1 });
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
// Non-critical — proceed anyway
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
// Detect non-secure context (file://, http://) up front. The Clipboard API is
|
|
15
|
+
// undefined there, and raw CDP ops can hang or throw -32001. Surface a typed
|
|
16
|
+
// error instead.
|
|
17
|
+
async function isSecureContext() {
|
|
18
|
+
try {
|
|
19
|
+
const { result } = await session.Runtime.evaluate({
|
|
20
|
+
expression: "window.isSecureContext === true",
|
|
21
|
+
returnByValue: true,
|
|
22
|
+
timeout: 1500,
|
|
23
|
+
});
|
|
24
|
+
return result?.value === true;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (action === "write") {
|
|
31
|
+
const hasText = text !== undefined;
|
|
32
|
+
const hasImage = dataUrl !== undefined;
|
|
33
|
+
if (!hasText && !hasImage) {
|
|
34
|
+
throw new Error("Provide 'text' and/or 'dataUrl' for write action");
|
|
35
|
+
}
|
|
36
|
+
// ── Grant user activation only for standalone clipboard writes ───────────
|
|
37
|
+
// When called from browser_type, the input field is already focused.
|
|
38
|
+
// Clicking (5,5) here would blur the field before Ctrl+V, causing the
|
|
39
|
+
// phantom-click + wipe + write-to-BODY issue.
|
|
40
|
+
if (!options.skipActivation) {
|
|
41
|
+
await grantActivation();
|
|
42
|
+
}
|
|
43
|
+
const secureCtx = await isSecureContext();
|
|
44
|
+
if (hasImage) {
|
|
45
|
+
if (!secureCtx) {
|
|
46
|
+
throw new Error("browser_clipboard: image write requires a secure context (https or chrome://). Current page is non-secure (file:// or http://).");
|
|
47
|
+
}
|
|
48
|
+
const mime = mimeType || (dataUrl.includes(';') ? dataUrl.split(';')[0].split(':')[1] : "image/png");
|
|
49
|
+
const res = await session.Runtime.evaluate({
|
|
50
|
+
expression: `(async function() {
|
|
51
|
+
try {
|
|
52
|
+
const resp = await fetch(${JSON.stringify(dataUrl)});
|
|
53
|
+
const blob = await resp.blob();
|
|
54
|
+
await navigator.clipboard.write([
|
|
55
|
+
new ClipboardItem({ ${JSON.stringify(mime)}: blob })
|
|
56
|
+
]);
|
|
57
|
+
return "ok";
|
|
58
|
+
} catch(e) {
|
|
59
|
+
return "ERR:" + e.message;
|
|
60
|
+
}
|
|
61
|
+
})()`,
|
|
62
|
+
awaitPromise: true,
|
|
63
|
+
returnByValue: true
|
|
64
|
+
});
|
|
65
|
+
const val = res.result?.value;
|
|
66
|
+
if (val === "ok") {
|
|
67
|
+
return { success: true, message: hasText ? "Text and image copied to clipboard" : "Image copied to clipboard" };
|
|
68
|
+
}
|
|
69
|
+
throw new Error("Failed to write image to clipboard: " + (val || "unknown"));
|
|
70
|
+
}
|
|
71
|
+
// Text-only write
|
|
72
|
+
const res = await session.Runtime.evaluate({
|
|
73
|
+
expression: `(async function() {
|
|
74
|
+
try {
|
|
75
|
+
${secureCtx ? `await navigator.clipboard.writeText(${JSON.stringify(text)});` : `throw new Error('non-secure');`}
|
|
76
|
+
return true;
|
|
77
|
+
} catch(e) {
|
|
78
|
+
const ta = document.createElement('textarea');
|
|
79
|
+
ta.value = ${JSON.stringify(text)};
|
|
80
|
+
ta.style.position = 'fixed'; ta.style.opacity = '0';
|
|
81
|
+
document.body.appendChild(ta);
|
|
82
|
+
ta.focus(); ta.select();
|
|
83
|
+
const ok = document.execCommand('copy');
|
|
84
|
+
document.body.removeChild(ta);
|
|
85
|
+
return ok;
|
|
86
|
+
}
|
|
87
|
+
})()`,
|
|
88
|
+
awaitPromise: true,
|
|
89
|
+
returnByValue: true
|
|
90
|
+
});
|
|
91
|
+
if (res.result?.value === true) {
|
|
92
|
+
return { success: true, message: "Copied to clipboard" };
|
|
93
|
+
}
|
|
94
|
+
if (!secureCtx) {
|
|
95
|
+
throw new Error("browser_clipboard: write failed on a non-secure context (file:// or http://). The Clipboard API is unavailable here; navigate to an https page and retry.");
|
|
96
|
+
}
|
|
97
|
+
throw new Error("Failed to write to clipboard (page may not be focused)");
|
|
98
|
+
}
|
|
99
|
+
// ── Read action ──────────────────────────────────────────────────
|
|
100
|
+
// 4-layer clipboard read strategy:
|
|
101
|
+
// Layer 1: CDP mouse click on body + page script click() for user activation
|
|
102
|
+
// Layer 2: navigator.clipboard.read() API
|
|
103
|
+
// Layer 3: Ctrl+V via trusted CDP key event → hidden textarea
|
|
104
|
+
// Layer 4: document.execCommand('paste') fallback
|
|
105
|
+
async function grantUserActivation() {
|
|
106
|
+
try {
|
|
107
|
+
const { result: vp } = await session.Runtime.evaluate({
|
|
108
|
+
expression: '({w: window.innerWidth, h: window.innerHeight})',
|
|
109
|
+
returnByValue: true
|
|
110
|
+
});
|
|
111
|
+
const cx = Math.floor((vp?.value?.w || 800) / 2);
|
|
112
|
+
const cy = Math.floor((vp?.value?.h || 600) / 2);
|
|
113
|
+
await ghostClick(session, cx, cy);
|
|
114
|
+
await session.Input.dispatchMouseEvent({ type: "mouseReleased", x: cx, y: cy, button: "left", clickCount: 1 });
|
|
115
|
+
await session.Runtime.evaluate({ expression: 'document.body.click()' }).catch(() => { });
|
|
116
|
+
}
|
|
117
|
+
catch { /* non-critical */ }
|
|
118
|
+
}
|
|
119
|
+
async function readViaCdpPaste() {
|
|
120
|
+
const { result } = await session.Runtime.evaluate({
|
|
121
|
+
expression: `(function() {
|
|
122
|
+
try {
|
|
123
|
+
var ta = document.createElement('textarea');
|
|
124
|
+
ta.style.cssText = 'position:fixed;top:-999px;left:-999px;opacity:0;width:1px;height:1px';
|
|
125
|
+
document.body.appendChild(ta);
|
|
126
|
+
ta.focus();
|
|
127
|
+
ta.select();
|
|
128
|
+
return true;
|
|
129
|
+
} catch(e) { return false; }
|
|
130
|
+
})()`,
|
|
131
|
+
returnByValue: true
|
|
132
|
+
});
|
|
133
|
+
if (result?.value !== true)
|
|
134
|
+
return null;
|
|
135
|
+
await session.Input.dispatchKeyEvent({ type: "rawKeyDown", key: "v", windowsVirtualKeyCode: 86, nativeVirtualKeyCode: 86, modifiers: 2 });
|
|
136
|
+
await session.Input.dispatchKeyEvent({ type: "keyUp", key: "v", windowsVirtualKeyCode: 86, nativeVirtualKeyCode: 86, modifiers: 2 });
|
|
137
|
+
await new Promise(r => setTimeout(r, 100));
|
|
138
|
+
const { result: valRes } = await session.Runtime.evaluate({
|
|
139
|
+
expression: `(function() {
|
|
140
|
+
var ta = document.querySelector('textarea[style*="top:-999px"]');
|
|
141
|
+
if (!ta) return null;
|
|
142
|
+
var v = ta.value;
|
|
143
|
+
ta.remove();
|
|
144
|
+
return v || null;
|
|
145
|
+
})()`,
|
|
146
|
+
returnByValue: true
|
|
147
|
+
});
|
|
148
|
+
return valRes?.value || null;
|
|
149
|
+
}
|
|
150
|
+
await grantUserActivation();
|
|
151
|
+
const secureCtx = await isSecureContext();
|
|
152
|
+
const originRes = await session.Runtime.evaluate({ expression: "window.location.origin" }).catch(() => null);
|
|
153
|
+
if (originRes && originRes.result.value && secureCtx) {
|
|
154
|
+
await session.Browser.grantPermissions({
|
|
155
|
+
origin: originRes.result.value,
|
|
156
|
+
permissions: ["clipboardReadWrite", "clipboardSanitizedWrite"]
|
|
157
|
+
}).catch(() => { });
|
|
158
|
+
}
|
|
159
|
+
const READ_TIMEOUT_MS = 2000;
|
|
160
|
+
let readResult = null;
|
|
161
|
+
// Layer 1+2: Try clipboard API with user activation (secure contexts only)
|
|
162
|
+
const apiRead = async () => {
|
|
163
|
+
if (!secureCtx)
|
|
164
|
+
return null;
|
|
165
|
+
const res = await session.Runtime.evaluate({
|
|
166
|
+
expression: `(async function() {
|
|
167
|
+
try {
|
|
168
|
+
if (typeof ClipboardItem !== 'undefined') {
|
|
169
|
+
var items = await navigator.clipboard.read();
|
|
170
|
+
var textVal = null, dataUrlVal = null;
|
|
171
|
+
for (var i = 0; i < items.length; i++) {
|
|
172
|
+
var item = items[i];
|
|
173
|
+
var types = item.types;
|
|
174
|
+
for (var j = 0; j < types.length; j++) {
|
|
175
|
+
var type = types[j];
|
|
176
|
+
var blob = await item.getType(type);
|
|
177
|
+
if (type.indexOf('text/') === 0) {
|
|
178
|
+
textVal = await blob.text();
|
|
179
|
+
} else if (type.indexOf('image/') === 0) {
|
|
180
|
+
dataUrlVal = await new Promise(function(rsv) {
|
|
181
|
+
var r = new FileReader();
|
|
182
|
+
r.onload = function() { rsv(r.result); };
|
|
183
|
+
r.readAsDataURL(blob);
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (textVal || dataUrlVal) return JSON.stringify({ text: textVal, dataUrl: dataUrlVal });
|
|
189
|
+
}
|
|
190
|
+
var t = await navigator.clipboard.readText();
|
|
191
|
+
if (t) return JSON.stringify({ text: t });
|
|
192
|
+
return null;
|
|
193
|
+
} catch(e) {
|
|
194
|
+
return 'ERR:' + e.message;
|
|
195
|
+
}
|
|
196
|
+
})()`,
|
|
197
|
+
awaitPromise: true,
|
|
198
|
+
returnByValue: true,
|
|
199
|
+
timeout: READ_TIMEOUT_MS
|
|
200
|
+
});
|
|
201
|
+
if (res.exceptionDetails)
|
|
202
|
+
return null;
|
|
203
|
+
const raw = res.result?.value;
|
|
204
|
+
if (!raw || raw.startsWith('ERR:'))
|
|
205
|
+
return null;
|
|
206
|
+
return raw;
|
|
207
|
+
};
|
|
208
|
+
try {
|
|
209
|
+
readResult = await Promise.race([
|
|
210
|
+
apiRead(),
|
|
211
|
+
new Promise(r => setTimeout(() => r(null), READ_TIMEOUT_MS))
|
|
212
|
+
]);
|
|
213
|
+
}
|
|
214
|
+
catch (e) {
|
|
215
|
+
// If API read fails, fall through to CDP paste method
|
|
216
|
+
readResult = null;
|
|
217
|
+
}
|
|
218
|
+
// Layer 3: Ctrl+V via CDP key dispatch (trusted event — no user activation needed)
|
|
219
|
+
if (!readResult) {
|
|
220
|
+
try {
|
|
221
|
+
const pasteVal = await Promise.race([
|
|
222
|
+
readViaCdpPaste(),
|
|
223
|
+
new Promise(r => setTimeout(() => r(null), 1000))
|
|
224
|
+
]);
|
|
225
|
+
if (pasteVal) {
|
|
226
|
+
readResult = JSON.stringify({ text: pasteVal });
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
readResult = null;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// Layer 4: execCommand fallback
|
|
234
|
+
if (!readResult) {
|
|
235
|
+
try {
|
|
236
|
+
const evalResult = await Promise.race([
|
|
237
|
+
session.Runtime.evaluate({
|
|
238
|
+
expression: `(function() {
|
|
239
|
+
try {
|
|
240
|
+
var ta = document.createElement('textarea');
|
|
241
|
+
ta.style.cssText = 'position:fixed;top:-999px;left:-999px;opacity:0';
|
|
242
|
+
document.body.appendChild(ta);
|
|
243
|
+
ta.focus();
|
|
244
|
+
var ok = document.execCommand('paste');
|
|
245
|
+
var v = ta.value;
|
|
246
|
+
ta.remove();
|
|
247
|
+
return ok && v ? JSON.stringify({ text: v }) : null;
|
|
248
|
+
} catch(e) { return null; }
|
|
249
|
+
})()`,
|
|
250
|
+
returnByValue: true
|
|
251
|
+
}),
|
|
252
|
+
new Promise(r => setTimeout(() => r({ result: { value: null } }), 500))
|
|
253
|
+
]);
|
|
254
|
+
const result = evalResult;
|
|
255
|
+
readResult = result?.result?.value || null;
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
readResult = null;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if (!readResult) {
|
|
262
|
+
if (!secureCtx) {
|
|
263
|
+
return { success: false, message: "Clipboard is empty or inaccessible. Current page is a non-secure context (file:// or http://) where the Clipboard API is unavailable; trusted CDP paste also returned nothing. Navigate to an https page and retry." };
|
|
264
|
+
}
|
|
265
|
+
return { success: true, message: "Clipboard is empty or inaccessible (browser may require user permission)" };
|
|
266
|
+
}
|
|
267
|
+
try {
|
|
268
|
+
const obj = JSON.parse(readResult);
|
|
269
|
+
return {
|
|
270
|
+
success: true,
|
|
271
|
+
text: obj.text || undefined,
|
|
272
|
+
dataUrl: obj.dataUrl || undefined,
|
|
273
|
+
message: obj.text && obj.dataUrl ? "Read text and image from clipboard" : obj.dataUrl ? "Read image from clipboard" : "Read from clipboard"
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
return { success: true, text: readResult, message: "Read from clipboard" };
|
|
278
|
+
}
|
|
279
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weavetab — tools/console.ts
|
|
3
|
+
* browser_console: Buffer last 50 console entries and exception events.
|
|
4
|
+
* Weavetab by fy2ne
|
|
5
|
+
*/
|
|
6
|
+
import CDP from "chrome-remote-interface";
|
|
7
|
+
import type { Config } from "../config/loader.js";
|
|
8
|
+
export interface ConsoleEntry {
|
|
9
|
+
type: string;
|
|
10
|
+
text: string;
|
|
11
|
+
timestamp: number;
|
|
12
|
+
url?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function initConsoleBuffering(session: CDP.Client, targetId: string): void;
|
|
15
|
+
export interface ConsoleOptions {
|
|
16
|
+
action: "read" | "clear";
|
|
17
|
+
}
|
|
18
|
+
export declare function browserConsole(session: CDP.Client, options: ConsoleOptions, _config: Config, targetId: string): Promise<{
|
|
19
|
+
success: boolean;
|
|
20
|
+
logs: ConsoleEntry[];
|
|
21
|
+
}>;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { SessionSecretTracker } from "../security/secrets.js";
|
|
2
|
+
/**
|
|
3
|
+
* Server-side noise filter (Q4 decision).
|
|
4
|
+
* Suppresses Weavetab-internal log spam before it enters the agent-visible buffer.
|
|
5
|
+
* Extension-side gating is the long-term fix (when ext-dist/ is rebuilt), but
|
|
6
|
+
* this filter acts as a reliable safety net for any messages that escape.
|
|
7
|
+
*
|
|
8
|
+
* Pattern matching is case-insensitive substring. Keep the list minimal.
|
|
9
|
+
*/
|
|
10
|
+
const NOISE_PATTERNS = [
|
|
11
|
+
/Weavetab_GROUP_STATE/i, // Extension broadcast: idle/busy state changes
|
|
12
|
+
/cursor host missing/i, // Extension watchdog: fires every 5s on some pages
|
|
13
|
+
/feedback panel missing/i, // Extension watchdog: harmless recovery notice
|
|
14
|
+
/\[Weavetab\] Cursor host/i, // Same watchdog, alternate prefix
|
|
15
|
+
/wt-cursor-trail/i, // Extension CSS transition noise
|
|
16
|
+
/__wt_push_analytics/i, // Analytics ping echo (already fire-and-forget)
|
|
17
|
+
];
|
|
18
|
+
/** Dedup: suppress the same message within this window (ms) */
|
|
19
|
+
const DEDUP_WINDOW_MS = 10_000;
|
|
20
|
+
function isNoise(text) {
|
|
21
|
+
for (const pattern of NOISE_PATTERNS) {
|
|
22
|
+
if (pattern.test(text))
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
const consoleBuffers = new Map();
|
|
28
|
+
/** Per-tab last-seen text for deduplication */
|
|
29
|
+
const lastSeenText = new Map();
|
|
30
|
+
export function initConsoleBuffering(session, targetId) {
|
|
31
|
+
if (consoleBuffers.has(targetId))
|
|
32
|
+
return;
|
|
33
|
+
const buffer = [];
|
|
34
|
+
consoleBuffers.set(targetId, buffer);
|
|
35
|
+
const push = (entry) => {
|
|
36
|
+
// Server-side noise suppression
|
|
37
|
+
if (isNoise(entry.text))
|
|
38
|
+
return;
|
|
39
|
+
// Deduplication: same text within 10s window
|
|
40
|
+
const last = lastSeenText.get(targetId);
|
|
41
|
+
if (last && last.text === entry.text && Date.now() - last.at < DEDUP_WINDOW_MS)
|
|
42
|
+
return;
|
|
43
|
+
lastSeenText.set(targetId, { text: entry.text, at: Date.now() });
|
|
44
|
+
buffer.push(entry);
|
|
45
|
+
if (buffer.length > 50)
|
|
46
|
+
buffer.shift();
|
|
47
|
+
};
|
|
48
|
+
session.Runtime.on("consoleAPICalled", (event) => {
|
|
49
|
+
const text = event.args.map((a) => a.value || a.description || "").join(" ");
|
|
50
|
+
push({ type: event.type, text, timestamp: event.timestamp });
|
|
51
|
+
});
|
|
52
|
+
session.Runtime.on("exceptionThrown", (event) => {
|
|
53
|
+
push({
|
|
54
|
+
type: "exception",
|
|
55
|
+
text: event.exceptionDetails.text + (event.exceptionDetails.exception?.description || ""),
|
|
56
|
+
timestamp: event.timestamp,
|
|
57
|
+
url: event.exceptionDetails.url
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
// Clear the per-tab buffer when the MAIN frame navigates to a new document,
|
|
61
|
+
// so site A's logs don't leak into site B's console reads.
|
|
62
|
+
session.Page.on("frameNavigated", (event) => {
|
|
63
|
+
const frame = event.frame;
|
|
64
|
+
if (frame && !frame.parentId) {
|
|
65
|
+
const buf = consoleBuffers.get(targetId);
|
|
66
|
+
if (buf)
|
|
67
|
+
buf.length = 0;
|
|
68
|
+
lastSeenText.delete(targetId);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
export async function browserConsole(session, options, _config, targetId) {
|
|
73
|
+
if (options.action === "clear") {
|
|
74
|
+
await session.Runtime.evaluate({
|
|
75
|
+
expression: `if (window.__consoleBuffer) { window.__consoleBuffer.length = 0; window.__consoleCursor = 0; }`
|
|
76
|
+
}).catch(() => { });
|
|
77
|
+
const buf = consoleBuffers.get(targetId);
|
|
78
|
+
if (buf)
|
|
79
|
+
buf.length = 0;
|
|
80
|
+
return { success: true, logs: [] };
|
|
81
|
+
}
|
|
82
|
+
const res = await session.Runtime.evaluate({
|
|
83
|
+
expression: `window.__Weavetab_readConsole({ since: 'last' })`,
|
|
84
|
+
returnByValue: true
|
|
85
|
+
}).catch(() => null);
|
|
86
|
+
let logs = [];
|
|
87
|
+
if (res?.result?.value) {
|
|
88
|
+
logs = res.result.value.map((l) => ({
|
|
89
|
+
type: l.level,
|
|
90
|
+
text: l.text,
|
|
91
|
+
timestamp: l.time
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
// Fallback to CDP buffer
|
|
96
|
+
const buf = consoleBuffers.get(targetId);
|
|
97
|
+
if (buf) {
|
|
98
|
+
logs = [...buf];
|
|
99
|
+
buf.length = 0; // Simulate 'since: last'
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const redactedLogs = logs.map(l => ({
|
|
103
|
+
...l,
|
|
104
|
+
text: typeof l.text === 'string' ? SessionSecretTracker.redact(targetId, l.text) : l.text
|
|
105
|
+
}));
|
|
106
|
+
return { success: true, logs: redactedLogs };
|
|
107
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weavetab — tools/cookies.ts
|
|
3
|
+
* browser_cookies: Read and inject browser cookies via the CDP Network domain.
|
|
4
|
+
*
|
|
5
|
+
* Use this tool to:
|
|
6
|
+
* - GET: Read all cookies for the current page's URL (for inspection or debugging)
|
|
7
|
+
* - SET: Inject a cookie to pre-authenticate a session without logging in manually
|
|
8
|
+
* - DELETE: Remove a specific cookie by name and domain
|
|
9
|
+
*
|
|
10
|
+
* This is the correct tool for session pre-authentication. Do NOT use browser_eval
|
|
11
|
+
* for cookie manipulation — this tool uses the proper CDP Network domain which
|
|
12
|
+
* works correctly with HttpOnly and Secure cookies.
|
|
13
|
+
* Weavetab by fy2ne
|
|
14
|
+
*/
|
|
15
|
+
import CDP from "chrome-remote-interface";
|
|
16
|
+
import type { Config } from "../config/loader.js";
|
|
17
|
+
export interface CookieEntry {
|
|
18
|
+
name: string;
|
|
19
|
+
value: string;
|
|
20
|
+
domain: string;
|
|
21
|
+
path: string;
|
|
22
|
+
expires: number;
|
|
23
|
+
httpOnly: boolean;
|
|
24
|
+
secure: boolean;
|
|
25
|
+
sameSite?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface CookiesGetResult {
|
|
28
|
+
success: true;
|
|
29
|
+
action: "get";
|
|
30
|
+
cookies: CookieEntry[];
|
|
31
|
+
count: number;
|
|
32
|
+
found?: boolean;
|
|
33
|
+
message?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface CookiesSetResult {
|
|
36
|
+
success: true;
|
|
37
|
+
action: "set";
|
|
38
|
+
name: string;
|
|
39
|
+
}
|
|
40
|
+
export interface CookiesDeleteResult {
|
|
41
|
+
success: true;
|
|
42
|
+
action: "delete";
|
|
43
|
+
name: string;
|
|
44
|
+
}
|
|
45
|
+
export type CookiesResult = CookiesGetResult | CookiesSetResult | CookiesDeleteResult;
|
|
46
|
+
export interface CookieSetOptions {
|
|
47
|
+
name: string;
|
|
48
|
+
value: string;
|
|
49
|
+
domain?: string;
|
|
50
|
+
path?: string;
|
|
51
|
+
expires?: number;
|
|
52
|
+
httpOnly?: boolean;
|
|
53
|
+
secure?: boolean;
|
|
54
|
+
sameSite?: "Strict" | "Lax" | "None";
|
|
55
|
+
}
|
|
56
|
+
export declare function browserCookies(session: CDP.Client, args: {
|
|
57
|
+
action: "get" | "set" | "delete";
|
|
58
|
+
name?: string;
|
|
59
|
+
value?: string;
|
|
60
|
+
domain?: string;
|
|
61
|
+
path?: string;
|
|
62
|
+
expires?: number;
|
|
63
|
+
httpOnly?: boolean;
|
|
64
|
+
secure?: boolean;
|
|
65
|
+
sameSite?: "Strict" | "Lax" | "None";
|
|
66
|
+
url?: string;
|
|
67
|
+
}, _config: Config): Promise<CookiesResult>;
|