@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,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared response interfaces for all interaction tools
|
|
3
|
+
* Standardizes the semantic response contract across WeaveTab tools
|
|
4
|
+
*/
|
|
5
|
+
import type { ElementState, VisibleFeedback } from "../cdp/verify.js";
|
|
6
|
+
/**
|
|
7
|
+
* Base interface for all interaction tool responses
|
|
8
|
+
* Provides standard fields for success confirmation, strategy used, and state verification
|
|
9
|
+
*/
|
|
10
|
+
export interface InteractionResponse {
|
|
11
|
+
success: boolean;
|
|
12
|
+
/** Whether the effect was verified in DOM, not just dispatched */
|
|
13
|
+
confirmed: boolean;
|
|
14
|
+
/** What method/strategy was used for the interaction */
|
|
15
|
+
strategy: string;
|
|
16
|
+
/** Full element state after the action */
|
|
17
|
+
elementState?: ElementState;
|
|
18
|
+
/** Any toast/error/validation text that appeared after the action */
|
|
19
|
+
visibleFeedback?: VisibleFeedback[];
|
|
20
|
+
/** Suggested next action based on page context */
|
|
21
|
+
suggestedNext?: string;
|
|
22
|
+
/** Confidence score (0-1) based on verification strength */
|
|
23
|
+
confidence?: number;
|
|
24
|
+
/** True if result is degraded due to timeout or partial failure */
|
|
25
|
+
degraded?: boolean;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Extended response for click operations
|
|
29
|
+
*/
|
|
30
|
+
export interface ClickResponse extends InteractionResponse {
|
|
31
|
+
clicked: string;
|
|
32
|
+
ref: string;
|
|
33
|
+
execution_ms: number;
|
|
34
|
+
tab_id: string;
|
|
35
|
+
url: string;
|
|
36
|
+
title: string;
|
|
37
|
+
url_changed: boolean;
|
|
38
|
+
page_map?: string;
|
|
39
|
+
dom_changed: boolean;
|
|
40
|
+
stuck_warning: boolean;
|
|
41
|
+
stuck_hint?: string;
|
|
42
|
+
sniper_warning?: string;
|
|
43
|
+
_timing?: {
|
|
44
|
+
element_resolution_ms: number;
|
|
45
|
+
actionability_check_ms: number;
|
|
46
|
+
ghost_move_ms: number;
|
|
47
|
+
click_dispatch_ms: number;
|
|
48
|
+
stuck_check_ms: number;
|
|
49
|
+
fallback_retry_ms?: number;
|
|
50
|
+
recalibration_ms?: number;
|
|
51
|
+
map_build_count: number;
|
|
52
|
+
};
|
|
53
|
+
stuck_check_degraded?: boolean;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Extended response for type operations
|
|
57
|
+
*/
|
|
58
|
+
export interface TypeResponse extends InteractionResponse {
|
|
59
|
+
typed_into: string;
|
|
60
|
+
ref: string;
|
|
61
|
+
length: number;
|
|
62
|
+
execution_ms: number;
|
|
63
|
+
tab_id: string;
|
|
64
|
+
url: string;
|
|
65
|
+
title: string;
|
|
66
|
+
page_map?: string;
|
|
67
|
+
dom_changed: boolean;
|
|
68
|
+
stuck_warning: boolean;
|
|
69
|
+
stuck_hint?: string;
|
|
70
|
+
sniper_warning?: string;
|
|
71
|
+
memory_hit?: boolean;
|
|
72
|
+
delta?: any;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Extended response for fill operations (per-field)
|
|
76
|
+
*/
|
|
77
|
+
export interface FillFieldResponse extends InteractionResponse {
|
|
78
|
+
ref: string;
|
|
79
|
+
field_type: string;
|
|
80
|
+
success: boolean;
|
|
81
|
+
message: string;
|
|
82
|
+
value?: string;
|
|
83
|
+
/** Actual value read back from DOM after fill */
|
|
84
|
+
confirmedValue?: string;
|
|
85
|
+
/** Any validation error that appeared after fill */
|
|
86
|
+
validationError?: string;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Extended response for fill operations (overall)
|
|
90
|
+
*/
|
|
91
|
+
export interface FillResponse {
|
|
92
|
+
success: boolean;
|
|
93
|
+
total_fields: number;
|
|
94
|
+
succeeded: number;
|
|
95
|
+
failed: number;
|
|
96
|
+
submitted: boolean;
|
|
97
|
+
results: FillFieldResponse[];
|
|
98
|
+
execution_ms: number;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Extended response for navigate operations
|
|
102
|
+
*/
|
|
103
|
+
export interface NavigateResponse extends InteractionResponse {
|
|
104
|
+
url: string;
|
|
105
|
+
title: string;
|
|
106
|
+
tab_id: string;
|
|
107
|
+
/** Initial element count after navigation */
|
|
108
|
+
elementCount?: number;
|
|
109
|
+
/** Number of forms detected on page */
|
|
110
|
+
formCount?: number;
|
|
111
|
+
/** Whether cookie banners were dismissed */
|
|
112
|
+
cookiesDismissed?: boolean;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Error response structure
|
|
116
|
+
*/
|
|
117
|
+
export interface ErrorResponse {
|
|
118
|
+
success: false;
|
|
119
|
+
error_code: string;
|
|
120
|
+
message: string;
|
|
121
|
+
suggestion?: string;
|
|
122
|
+
human_needed?: boolean;
|
|
123
|
+
tab_id?: string;
|
|
124
|
+
url?: string;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Union type for all possible tool responses
|
|
128
|
+
*/
|
|
129
|
+
export type ToolResponse = InteractionResponse | ClickResponse | TypeResponse | FillResponse | FillFieldResponse | NavigateResponse | ErrorResponse;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weavetab — tools/scrape.ts
|
|
3
|
+
* browser_scrape: Rapid structured data extraction using CSS selectors/XPath.
|
|
4
|
+
*
|
|
5
|
+
* Secret containment: any element marked data-wt-secret or type="password" has
|
|
6
|
+
* all requested attributes replaced with "[REDACTED]" in the returned result.
|
|
7
|
+
* This is evaluated inside the in-page expression so the secret value never
|
|
8
|
+
* crosses the CDP returnByValue boundary.
|
|
9
|
+
*
|
|
10
|
+
* Weavetab by fy2ne
|
|
11
|
+
*/
|
|
12
|
+
import CDP from "chrome-remote-interface";
|
|
13
|
+
import type { Config } from "../config/loader.js";
|
|
14
|
+
export interface ScrapeOptions {
|
|
15
|
+
query: string;
|
|
16
|
+
type?: "css" | "xpath";
|
|
17
|
+
attributes?: string[];
|
|
18
|
+
multiple?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export declare function browserScrape(session: CDP.Client, options: ScrapeOptions, _config: Config): Promise<{
|
|
21
|
+
success: boolean;
|
|
22
|
+
data: any[];
|
|
23
|
+
message: string;
|
|
24
|
+
}>;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
export async function browserScrape(session, options, _config) {
|
|
2
|
+
const { query, type = "css", attributes = ["textContent"], multiple = true } = options;
|
|
3
|
+
const res = await session.Runtime.evaluate({
|
|
4
|
+
expression: `(function() {
|
|
5
|
+
const results = [];
|
|
6
|
+
let nodes = [];
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
if (${JSON.stringify(type)} === 'css') {
|
|
10
|
+
if (${multiple}) {
|
|
11
|
+
nodes = Array.from(document.querySelectorAll(${JSON.stringify(query)}));
|
|
12
|
+
// Shadow DOM fallback: if nothing found, try the extension's deep-shadow query helper.
|
|
13
|
+
// This handles Vue/React components that attach to shadow roots.
|
|
14
|
+
if (nodes.length === 0 && window.__Weavetab_shadowQuery_all) {
|
|
15
|
+
try { nodes = Array.from(window.__Weavetab_shadowQuery_all(${JSON.stringify(query)})); } catch (_) {}
|
|
16
|
+
}
|
|
17
|
+
} else {
|
|
18
|
+
const node = document.querySelector(${JSON.stringify(query)})
|
|
19
|
+
|| (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(${JSON.stringify(query)}) : null);
|
|
20
|
+
if (node) nodes = [node];
|
|
21
|
+
}
|
|
22
|
+
} else if (${JSON.stringify(type)} === 'xpath') {
|
|
23
|
+
// Use SNAPSHOT type — immune to DOM mutations that would invalidate an ITERATOR mid-walk.
|
|
24
|
+
const snap = document.evaluate(${JSON.stringify(query)}, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
|
|
25
|
+
const limit = ${multiple} ? snap.snapshotLength : Math.min(snap.snapshotLength, 1);
|
|
26
|
+
for (let i = 0; i < limit; i++) {
|
|
27
|
+
const n = snap.snapshotItem(i);
|
|
28
|
+
// Only Element nodes expose getAttribute / tagName / outerHTML safely.
|
|
29
|
+
if (n && n.nodeType === 1) nodes.push(n);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
} catch (e) {
|
|
33
|
+
return { error: e.message };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const attrs = ${JSON.stringify(attributes)};
|
|
37
|
+
nodes.forEach(node => {
|
|
38
|
+
const item = {};
|
|
39
|
+
|
|
40
|
+
// Secret containment: redact any element marked as sensitive.
|
|
41
|
+
// data-wt-secret is set by browser_type_secret at injection time.
|
|
42
|
+
const isSecret = node.hasAttribute && (
|
|
43
|
+
node.hasAttribute('data-wt-secret') ||
|
|
44
|
+
(node.tagName === 'INPUT' && node.type === 'password')
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
if (isSecret) {
|
|
48
|
+
// Replace ALL requested attributes with [REDACTED] — value never crosses boundary
|
|
49
|
+
attrs.forEach(attr => { item[attr] = '[REDACTED]'; });
|
|
50
|
+
} else {
|
|
51
|
+
attrs.forEach(attr => {
|
|
52
|
+
if (attr === 'textContent' || attr === 'innerText') {
|
|
53
|
+
item[attr] = node[attr]?.trim() || '';
|
|
54
|
+
} else if (attr === 'outerHTML') {
|
|
55
|
+
item[attr] = node.outerHTML;
|
|
56
|
+
} else {
|
|
57
|
+
item[attr] = node.getAttribute(attr) || node[attr] || '';
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
results.push(item);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const isXpath = ${JSON.stringify(type)} === 'xpath';
|
|
66
|
+
const shadowFallback = !isXpath && nodes.length > 0 && !document.querySelector(${JSON.stringify(query)});
|
|
67
|
+
return { data: results, usedShadowFallback: shadowFallback };
|
|
68
|
+
})()`,
|
|
69
|
+
returnByValue: true
|
|
70
|
+
});
|
|
71
|
+
let val = res.result?.value;
|
|
72
|
+
if (val?.error) {
|
|
73
|
+
throw new Error(`Scrape failed (type=${type}, query=${query}): ${val.error}`);
|
|
74
|
+
}
|
|
75
|
+
// Async-render retry: if 0 results found (CSS or XPath), wait 300ms and retry once.
|
|
76
|
+
// Handles Vue transitions, React lazy rendering, and SPA route hydration.
|
|
77
|
+
if (!val?.data || val.data.length === 0) {
|
|
78
|
+
await new Promise(r => setTimeout(r, 300));
|
|
79
|
+
// Build a fresh count expression depending on selector type.
|
|
80
|
+
const countExpr = type === 'xpath'
|
|
81
|
+
? `(function(){
|
|
82
|
+
try {
|
|
83
|
+
var s = document.evaluate(${JSON.stringify(query)}, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
|
|
84
|
+
return s.snapshotLength;
|
|
85
|
+
} catch(e) { return 0; }
|
|
86
|
+
})()`
|
|
87
|
+
: `(function(){
|
|
88
|
+
var n = Array.from(document.querySelectorAll(${JSON.stringify(query)}));
|
|
89
|
+
if (n.length === 0 && window.__Weavetab_shadowQuery_all) {
|
|
90
|
+
try { n = Array.from(window.__Weavetab_shadowQuery_all(${JSON.stringify(query)})); } catch(_){}
|
|
91
|
+
}
|
|
92
|
+
return n.length;
|
|
93
|
+
})()`;
|
|
94
|
+
const retry = await session.Runtime.evaluate({ expression: countExpr, returnByValue: true }).catch(() => null);
|
|
95
|
+
if (retry?.result?.value && retry.result.value > 0) {
|
|
96
|
+
// Re-run full scrape now that elements exist.
|
|
97
|
+
// XPath path uses snapshot; CSS path uses querySelectorAll + shadow fallback.
|
|
98
|
+
const fullExpr = type === 'xpath'
|
|
99
|
+
? `(function(){
|
|
100
|
+
var attrs = ${JSON.stringify(attributes)};
|
|
101
|
+
var snap = document.evaluate(${JSON.stringify(query)}, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
|
|
102
|
+
var limit = ${multiple} ? snap.snapshotLength : Math.min(snap.snapshotLength, 1);
|
|
103
|
+
var results = [];
|
|
104
|
+
for (var i = 0; i < limit; i++) {
|
|
105
|
+
var node = snap.snapshotItem(i);
|
|
106
|
+
if (!node || node.nodeType !== 1) continue;
|
|
107
|
+
var item = {};
|
|
108
|
+
attrs.forEach(function(attr) {
|
|
109
|
+
if (attr === 'textContent' || attr === 'innerText') { item[attr] = (node[attr] || '').trim(); }
|
|
110
|
+
else if (attr === 'outerHTML') { item[attr] = node.outerHTML; }
|
|
111
|
+
else { item[attr] = node.getAttribute ? (node.getAttribute(attr) || node[attr] || '') : ''; }
|
|
112
|
+
});
|
|
113
|
+
results.push(item);
|
|
114
|
+
}
|
|
115
|
+
return { data: results };
|
|
116
|
+
})()`
|
|
117
|
+
: `(function(){
|
|
118
|
+
var nodes = Array.from(document.querySelectorAll(${JSON.stringify(query)}));
|
|
119
|
+
if (nodes.length === 0 && window.__Weavetab_shadowQuery_all) {
|
|
120
|
+
try { nodes = Array.from(window.__Weavetab_shadowQuery_all(${JSON.stringify(query)})); } catch(_){}
|
|
121
|
+
}
|
|
122
|
+
var attrs = ${JSON.stringify(attributes)};
|
|
123
|
+
return { data: nodes.map(function(node) {
|
|
124
|
+
var item = {};
|
|
125
|
+
attrs.forEach(function(attr) {
|
|
126
|
+
if (attr === 'textContent' || attr === 'innerText') { item[attr] = (node[attr] || '').trim(); }
|
|
127
|
+
else if (attr === 'outerHTML') { item[attr] = node.outerHTML; }
|
|
128
|
+
else { item[attr] = node.getAttribute(attr) || node[attr] || ''; }
|
|
129
|
+
});
|
|
130
|
+
return item;
|
|
131
|
+
})};
|
|
132
|
+
})()`;
|
|
133
|
+
const res2 = await session.Runtime.evaluate({ expression: fullExpr, returnByValue: true }).catch(() => null);
|
|
134
|
+
if (res2?.result?.value?.data) {
|
|
135
|
+
val = res2.result.value;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return { success: true, data: val?.data || [], message: `Extracted ${val?.data?.length || 0} elements` };
|
|
140
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weavetab — tools/screenshot.ts
|
|
3
|
+
* browser_screenshot: Capture the current tab as a base64 PNG using Page.captureScreenshot.
|
|
4
|
+
*
|
|
5
|
+
* Use this tool when you need pixel-level vision of the page — e.g. to verify a visual
|
|
6
|
+
* result, read text in images, inspect canvas content, or confirm UI state that the DOM
|
|
7
|
+
* tree cannot describe. Returns a data URL the AI can interpret visually by default.
|
|
8
|
+
*
|
|
9
|
+
* Do NOT use this as a replacement for browser_map — use browser_map first to interact
|
|
10
|
+
* with elements. Use browser_screenshot only when visual confirmation is needed.
|
|
11
|
+
* Weavetab by fy2ne
|
|
12
|
+
*/
|
|
13
|
+
import CDP from "chrome-remote-interface";
|
|
14
|
+
import type { Config } from "../config/loader.js";
|
|
15
|
+
export interface ScreenshotOptions {
|
|
16
|
+
/** Capture format — "png" (lossless) or "jpeg" (smaller). Default: "png". */
|
|
17
|
+
format?: "png" | "jpeg";
|
|
18
|
+
/** JPEG quality 0–100. Only used when format is "jpeg". Default: 90. */
|
|
19
|
+
quality?: number;
|
|
20
|
+
/** If true, captures the full scrollable page, not just the viewport. Default: false. */
|
|
21
|
+
fullPage?: boolean;
|
|
22
|
+
/** Clip region {x, y, width, height, scale}. Overrides fullPage if set. */
|
|
23
|
+
clip?: {
|
|
24
|
+
x: number;
|
|
25
|
+
y: number;
|
|
26
|
+
width: number;
|
|
27
|
+
height: number;
|
|
28
|
+
};
|
|
29
|
+
/** If false, keeps the WeaveTab UI overlay visible during capture.
|
|
30
|
+
* Default: true — overlay is always hidden from agent captures. */
|
|
31
|
+
hideOverlay?: boolean;
|
|
32
|
+
}
|
|
33
|
+
export type ScreenshotResult = {
|
|
34
|
+
success: true;
|
|
35
|
+
dataUrl: string;
|
|
36
|
+
width: number;
|
|
37
|
+
height: number;
|
|
38
|
+
format: string;
|
|
39
|
+
};
|
|
40
|
+
export declare function browserScreenshot(session: CDP.Client, options: ScreenshotOptions | undefined, _config: Config): Promise<ScreenshotResult>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { hideWeaveTabOverlay, restoreWeaveTabOverlay } from "../cdp/helpers.js";
|
|
2
|
+
export async function browserScreenshot(session, options = {}, _config) {
|
|
3
|
+
const format = options.format ?? "png";
|
|
4
|
+
const quality = format === "jpeg" ? (options.quality ?? 90) : undefined;
|
|
5
|
+
// Get viewport dimensions
|
|
6
|
+
const { result: vpResult } = await session.Runtime.evaluate({
|
|
7
|
+
expression: `JSON.stringify({ w: window.innerWidth, h: window.innerHeight, dpr: window.devicePixelRatio || 1 })`,
|
|
8
|
+
returnByValue: true,
|
|
9
|
+
});
|
|
10
|
+
const vp = JSON.parse(vpResult.value);
|
|
11
|
+
let clip;
|
|
12
|
+
if (options.clip) {
|
|
13
|
+
clip = { ...options.clip, scale: 1 };
|
|
14
|
+
}
|
|
15
|
+
else if (options.fullPage) {
|
|
16
|
+
const { result: scrollResult } = await session.Runtime.evaluate({
|
|
17
|
+
expression: `JSON.stringify({
|
|
18
|
+
w: Math.max(document.documentElement.scrollWidth, document.body.scrollWidth),
|
|
19
|
+
h: Math.max(document.documentElement.scrollHeight, document.body.scrollHeight)
|
|
20
|
+
})`,
|
|
21
|
+
returnByValue: true,
|
|
22
|
+
});
|
|
23
|
+
const scroll = scrollResult?.value ? JSON.parse(scrollResult.value) : { w: vp.w, h: vp.h };
|
|
24
|
+
clip = { x: 0, y: 0, width: scroll.w, height: scroll.h, scale: 1 };
|
|
25
|
+
}
|
|
26
|
+
// ── Auto-compress: if viewport is wide, downscale to 1280px wide as JPEG 75% ──
|
|
27
|
+
// This cuts token cost by 40-60% with no quality loss for text content.
|
|
28
|
+
// Only applies when no explicit format/clip was requested.
|
|
29
|
+
const params = { format, captureBeyondViewport: !!options.fullPage };
|
|
30
|
+
if (!options.clip && !options.format && vp.w > 1280) {
|
|
31
|
+
const scale = 1280 / vp.w;
|
|
32
|
+
params.format = 'jpeg';
|
|
33
|
+
params.quality = 75;
|
|
34
|
+
params.clip = clip ?? { x: 0, y: 0, width: vp.w, height: vp.h, scale };
|
|
35
|
+
if (params.clip && params.clip.scale === 1) {
|
|
36
|
+
params.clip = { ...params.clip, scale };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
if (quality !== undefined)
|
|
41
|
+
params.quality = quality;
|
|
42
|
+
if (clip)
|
|
43
|
+
params.clip = clip;
|
|
44
|
+
}
|
|
45
|
+
// ── AI-blind: hide Weavetab overlay elements before screenshot ──────────────
|
|
46
|
+
// The chat panel, ghost cursor, thought bubble, HUD iframe, and all scripted
|
|
47
|
+
// UI are FOR THE USER only. The AI must never see or interact with them.
|
|
48
|
+
// hideOverlay defaults to true — only set to false if explicitly requested.
|
|
49
|
+
const shouldHide = options.hideOverlay !== false;
|
|
50
|
+
if (shouldHide) {
|
|
51
|
+
await hideWeaveTabOverlay(session);
|
|
52
|
+
}
|
|
53
|
+
let data;
|
|
54
|
+
try {
|
|
55
|
+
const res = await session.Page.captureScreenshot(params);
|
|
56
|
+
data = res.data;
|
|
57
|
+
}
|
|
58
|
+
catch (e) {
|
|
59
|
+
if (params.captureBeyondViewport) {
|
|
60
|
+
params.captureBeyondViewport = false;
|
|
61
|
+
delete params.clip;
|
|
62
|
+
const res = await session.Page.captureScreenshot(params);
|
|
63
|
+
data = res.data;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
throw e;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Restore overlay visibility immediately after capture
|
|
70
|
+
if (shouldHide) {
|
|
71
|
+
await restoreWeaveTabOverlay(session);
|
|
72
|
+
}
|
|
73
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
74
|
+
const width = clip ? clip.width : vp.w;
|
|
75
|
+
const height = clip ? clip.height : vp.h;
|
|
76
|
+
const actualFormat = params.format || format;
|
|
77
|
+
const dataUrl = `data:image/${actualFormat};base64,${data}`;
|
|
78
|
+
return { success: true, dataUrl, width, height, format: actualFormat };
|
|
79
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weavetab — tools/scroll.ts
|
|
3
|
+
* Scrolls the page or a specific element in any direction.
|
|
4
|
+
* Weavetab by fy2ne
|
|
5
|
+
*/
|
|
6
|
+
import CDP from "chrome-remote-interface";
|
|
7
|
+
import type { Config } from "../config/loader.js";
|
|
8
|
+
import type { WeaveError } from "./click.js";
|
|
9
|
+
export interface ScrollResult {
|
|
10
|
+
success: true;
|
|
11
|
+
scrolled: string;
|
|
12
|
+
direction: string;
|
|
13
|
+
distance: number;
|
|
14
|
+
new_visible_count: number;
|
|
15
|
+
dom_changed: boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare function browserScroll(session: CDP.Client, args: {
|
|
18
|
+
id?: string;
|
|
19
|
+
label?: string;
|
|
20
|
+
direction: "up" | "down" | "left" | "right";
|
|
21
|
+
distance?: number;
|
|
22
|
+
speed?: "human" | "read" | "fast";
|
|
23
|
+
}, config: Config): Promise<ScrollResult | WeaveError>;
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weavetab — tools/scroll.ts
|
|
3
|
+
* Scrolls the page or a specific element in any direction.
|
|
4
|
+
* Weavetab by fy2ne
|
|
5
|
+
*/
|
|
6
|
+
import { buildActionMap, findBestMatch, safeSelector } from "../cdp/walker.js";
|
|
7
|
+
import { getCachedActionMap, cacheActionMap } from "../state/cache.js";
|
|
8
|
+
import { computeDelta, storeSnapshot } from "../state/delta.js";
|
|
9
|
+
import { waitForDOMStable } from "../sensors/guard.js";
|
|
10
|
+
import { sessionRegistry } from "../state/session.js";
|
|
11
|
+
import { logAction } from "../audit/logger.js";
|
|
12
|
+
import { getConfigAgentId } from "../config/loader.js";
|
|
13
|
+
import { sendToExtension } from "../cdp/helpers.js";
|
|
14
|
+
function withTimeout(promise, ms, label) {
|
|
15
|
+
return Promise.race([
|
|
16
|
+
promise,
|
|
17
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms)),
|
|
18
|
+
]);
|
|
19
|
+
}
|
|
20
|
+
// Track scroll frequency
|
|
21
|
+
let recentScrolls = [];
|
|
22
|
+
// helper for realistic human timing
|
|
23
|
+
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
|
|
24
|
+
const randomBetween = (min, max) => Math.random() * (max - min) + min;
|
|
25
|
+
function applyEasing(t, type) {
|
|
26
|
+
switch (type) {
|
|
27
|
+
case 'quarticOut': return 1 - Math.pow(1 - t, 4);
|
|
28
|
+
case 'cubicOut': return 1 - Math.pow(1 - t, 3);
|
|
29
|
+
case 'linear': return t;
|
|
30
|
+
default: return t;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export async function browserScroll(session, args, config) {
|
|
34
|
+
let { id, label, direction, distance = 400, speed = "human" } = args;
|
|
35
|
+
if (distance < 0) {
|
|
36
|
+
distance = Math.abs(distance);
|
|
37
|
+
if (direction === "down")
|
|
38
|
+
direction = "up";
|
|
39
|
+
else if (direction === "up")
|
|
40
|
+
direction = "down";
|
|
41
|
+
else if (direction === "left")
|
|
42
|
+
direction = "right";
|
|
43
|
+
else if (direction === "right")
|
|
44
|
+
direction = "left";
|
|
45
|
+
}
|
|
46
|
+
const now = Date.now();
|
|
47
|
+
recentScrolls = recentScrolls.filter(t => now - t < 5000);
|
|
48
|
+
recentScrolls.push(now);
|
|
49
|
+
if (recentScrolls.length > 3) {
|
|
50
|
+
speed = "fast";
|
|
51
|
+
}
|
|
52
|
+
const { DOM, Runtime, Input } = session;
|
|
53
|
+
let targetId = "default";
|
|
54
|
+
const active = sessionRegistry.getActive(getConfigAgentId(config));
|
|
55
|
+
if (active)
|
|
56
|
+
targetId = active.targetId;
|
|
57
|
+
const { frameTree } = await session.Page.getFrameTree();
|
|
58
|
+
const url = frameTree.frame.url;
|
|
59
|
+
// ── Scroll a specific element into view ────────────────────────────────────
|
|
60
|
+
if (id || label) {
|
|
61
|
+
const { sendToExtension } = await import("../cdp/helpers.js");
|
|
62
|
+
await sendToExtension(session, { type: 'Weavetab_STATE', hud: { action: 'scrolling to element...' } });
|
|
63
|
+
let map = getCachedActionMap(targetId, url);
|
|
64
|
+
if (!map) {
|
|
65
|
+
const _emptyMap = { url, title: '', elements: [], targetId };
|
|
66
|
+
map = await withTimeout(buildActionMap(session, { lite: true }, targetId), 5000, "scroll buildActionMap").catch(() => _emptyMap);
|
|
67
|
+
if (map.elements.length > 0)
|
|
68
|
+
cacheActionMap(targetId, map);
|
|
69
|
+
}
|
|
70
|
+
storeSnapshot(targetId, map.url, map.elements);
|
|
71
|
+
let element = id
|
|
72
|
+
? map.elements.find((el) => el.id === id)
|
|
73
|
+
: null;
|
|
74
|
+
// Fast-fail for stale w: IDs
|
|
75
|
+
if (!element && id && id.startsWith('w:')) {
|
|
76
|
+
return { success: false, error_code: 'ELEMENT_NOT_FOUND', message: `[!] Ref '${id}' not found in current map. Call browser_map() first.` };
|
|
77
|
+
}
|
|
78
|
+
if (!element && label) {
|
|
79
|
+
element = findBestMatch(map.elements, label);
|
|
80
|
+
if (!element) {
|
|
81
|
+
const { resolveByAria } = await import("../cdp/walker.js");
|
|
82
|
+
const ariaEl = await withTimeout(resolveByAria(session, label), 2000, "resolveByAria label timeout").catch(() => null);
|
|
83
|
+
if (ariaEl) {
|
|
84
|
+
element = map.elements.find((el) => el.id === ariaEl.id) || null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (!element && id && !id.startsWith("w:")) {
|
|
89
|
+
element = {
|
|
90
|
+
id: id,
|
|
91
|
+
label: id,
|
|
92
|
+
selector: id,
|
|
93
|
+
role: "element",
|
|
94
|
+
group: "utility",
|
|
95
|
+
depth: 0,
|
|
96
|
+
fingerprint: "selector:" + id,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
if (!element) {
|
|
100
|
+
return {
|
|
101
|
+
success: false,
|
|
102
|
+
error_code: "ELEMENT_NOT_FOUND",
|
|
103
|
+
message: `[!] Could not find element matching "${id ?? label}"`,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
const selector = safeSelector(element);
|
|
107
|
+
if (!selector) {
|
|
108
|
+
return { success: false, error_code: "SELECTOR_RESOLUTION_FAILED", message: `[!] Could not resolve safe selector for element` };
|
|
109
|
+
}
|
|
110
|
+
// ── 3. Element-level Scroll (CDP Wheel Event) ─────────────────────────
|
|
111
|
+
const scrollSelector = selector;
|
|
112
|
+
let elementFound = false;
|
|
113
|
+
try {
|
|
114
|
+
const res = await session.Runtime.evaluate({
|
|
115
|
+
expression: `(function(sel) {
|
|
116
|
+
const el = document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null);
|
|
117
|
+
if (!el) return null;
|
|
118
|
+
const rect = el.getBoundingClientRect();
|
|
119
|
+
return {
|
|
120
|
+
x: rect.left + rect.width / 2,
|
|
121
|
+
y: rect.top + rect.height / 2
|
|
122
|
+
};
|
|
123
|
+
})(${JSON.stringify(scrollSelector)})`,
|
|
124
|
+
returnByValue: true
|
|
125
|
+
});
|
|
126
|
+
const coords = res.result?.value;
|
|
127
|
+
if (coords && coords.x !== undefined && coords.y !== undefined) {
|
|
128
|
+
elementFound = true;
|
|
129
|
+
// Dispatch CDP wheel event precisely over the element
|
|
130
|
+
await session.Input.dispatchMouseEvent({
|
|
131
|
+
type: "mouseWheel",
|
|
132
|
+
x: coords.x,
|
|
133
|
+
y: coords.y,
|
|
134
|
+
deltaX: direction === "right" ? distance : (direction === "left" ? -distance : 0),
|
|
135
|
+
deltaY: direction === "down" ? distance : (direction === "up" ? -distance : 0)
|
|
136
|
+
});
|
|
137
|
+
await new Promise(r => setTimeout(r, 100)); // allow scroll to settle
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch { /* ignore */ }
|
|
141
|
+
if (!elementFound) {
|
|
142
|
+
return { success: false, error_code: "SCROLL_TARGET_NOT_FOUND", message: `Element ${id ?? label} not found or not visible` };
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
// ── Inertial Physics Scroll ────────────────────────────────────────────────
|
|
147
|
+
const profiles = {
|
|
148
|
+
human: { steps: 12, easing: 'quarticOut', baseDelay: 16 },
|
|
149
|
+
read: { steps: 25, easing: 'linear', baseDelay: 40 },
|
|
150
|
+
fast: { steps: 6, easing: 'cubicOut', baseDelay: 8 },
|
|
151
|
+
};
|
|
152
|
+
const profile = profiles[speed];
|
|
153
|
+
const totalPx = direction === 'down' ? distance : direction === 'up' ? -distance : 0;
|
|
154
|
+
const totalPxX = direction === 'right' ? distance : direction === 'left' ? -distance : 0;
|
|
155
|
+
let map = getCachedActionMap(targetId, url);
|
|
156
|
+
if (!map) {
|
|
157
|
+
map = await buildActionMap(session, { lite: true }, targetId);
|
|
158
|
+
cacheActionMap(targetId, map);
|
|
159
|
+
}
|
|
160
|
+
storeSnapshot(targetId, map.url, map.elements);
|
|
161
|
+
const viewportRes = await session.Runtime.evaluate({
|
|
162
|
+
expression: `JSON.stringify({ x: window.innerWidth / 2, y: window.innerHeight / 2 })`,
|
|
163
|
+
returnByValue: true
|
|
164
|
+
}).catch(() => null);
|
|
165
|
+
const vp = viewportRes?.result?.value ? JSON.parse(viewportRes.result.value) : { x: 400, y: 400 };
|
|
166
|
+
await sendToExtension(session, { type: 'Weavetab_STATE', hud: { action: `scrolling page ${direction}` } });
|
|
167
|
+
for (let i = 0; i < profile.steps; i++) {
|
|
168
|
+
const t = (i + 1) / profile.steps;
|
|
169
|
+
const eased = applyEasing(t, profile.easing);
|
|
170
|
+
const prevEased = applyEasing(i / profile.steps, profile.easing);
|
|
171
|
+
const chunkY = (eased - prevEased) * totalPx;
|
|
172
|
+
const chunkX = (eased - prevEased) * totalPxX;
|
|
173
|
+
await Input.dispatchMouseEvent({
|
|
174
|
+
type: 'mouseWheel',
|
|
175
|
+
x: vp.x, y: vp.y,
|
|
176
|
+
deltaX: chunkX, deltaY: chunkY
|
|
177
|
+
});
|
|
178
|
+
await sleep(profile.baseDelay + Math.random() * 8);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
await waitForDOMStable(session, 80, 600).catch(() => { });
|
|
182
|
+
await sendToExtension(session, { type: 'Weavetab_STATE', hud: { action: '' } });
|
|
183
|
+
const nextMap = await buildActionMap(session, { lite: true }, targetId);
|
|
184
|
+
cacheActionMap(targetId, nextMap);
|
|
185
|
+
const diff = computeDelta(targetId, nextMap.url, nextMap.elements);
|
|
186
|
+
const changesCount = diff ? diff.added.length + diff.removed.length + diff.changed.length : 0;
|
|
187
|
+
logAction("browser_scroll", direction, `${distance}px`);
|
|
188
|
+
return {
|
|
189
|
+
success: true,
|
|
190
|
+
scrolled: id ?? label ?? "window",
|
|
191
|
+
direction,
|
|
192
|
+
distance,
|
|
193
|
+
new_visible_count: nextMap.elements.length,
|
|
194
|
+
dom_changed: changesCount > 0,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import CDP from "chrome-remote-interface";
|
|
2
|
+
import type { Config } from "../config/loader.js";
|
|
3
|
+
export interface SelectOptions {
|
|
4
|
+
nodeId?: number;
|
|
5
|
+
selector?: string;
|
|
6
|
+
ref?: string;
|
|
7
|
+
id?: string;
|
|
8
|
+
value: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function browserSelect(session: CDP.Client, options: SelectOptions, _config: Config): Promise<{
|
|
11
|
+
success: boolean;
|
|
12
|
+
message: string;
|
|
13
|
+
}>;
|