@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,552 @@
|
|
|
1
|
+
import { logDebug } from "../audit/logger.js";
|
|
2
|
+
// ─── Main dispatcher ─────────────────────────────────────────────────────────
|
|
3
|
+
export async function solveCaptcha(session, detection) {
|
|
4
|
+
const t0 = Date.now();
|
|
5
|
+
try {
|
|
6
|
+
logDebug(`[Weavetab AntiCaptcha] Solving ${detection.type} (${detection.interactionType})`);
|
|
7
|
+
switch (detection.interactionType) {
|
|
8
|
+
case "drag":
|
|
9
|
+
return await solveSliderDrag(session, detection, t0);
|
|
10
|
+
case "hold":
|
|
11
|
+
return await solvePressAndHold(session, detection, t0);
|
|
12
|
+
case "checkbox":
|
|
13
|
+
return await solveCheckbox(session, detection, t0);
|
|
14
|
+
case "none":
|
|
15
|
+
return await solveInvisible(session, detection, t0);
|
|
16
|
+
case "math":
|
|
17
|
+
return await solveMathCaptcha(session, detection, t0);
|
|
18
|
+
case "text":
|
|
19
|
+
return await solveTextCaptcha(session, detection, t0);
|
|
20
|
+
case "image_select":
|
|
21
|
+
return await solveImageSelect(session, detection, t0);
|
|
22
|
+
default:
|
|
23
|
+
// Unknown type — try generic approaches in order
|
|
24
|
+
return await solveGenericFallback(session, detection, t0);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
logDebug("[Weavetab AntiCaptcha] Solver threw:", err);
|
|
29
|
+
return {
|
|
30
|
+
solved: false,
|
|
31
|
+
method: "failed",
|
|
32
|
+
confidence: 0,
|
|
33
|
+
requiresUserIntervention: true,
|
|
34
|
+
userInstructions: `Automated CAPTCHA solving encountered an error. ${getHumanInstructions(detection)}`,
|
|
35
|
+
timeToSolve: Date.now() - t0,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// ─── 1. Slider / Drag CAPTCHA ────────────────────────────────────────────────
|
|
40
|
+
async function solveSliderDrag(session, detection, t0) {
|
|
41
|
+
try {
|
|
42
|
+
// Locate slider thumb and track
|
|
43
|
+
const sliderInfo = await session.Runtime.evaluate({
|
|
44
|
+
expression: `(function() {
|
|
45
|
+
// Priority selectors for slider thumb
|
|
46
|
+
var thumbSels = [
|
|
47
|
+
'.geetest_slider_button', '.geetest_drag_thumb', '.geetest-slider-btn',
|
|
48
|
+
'.slide-verify-slider', '[class*="slider-btn"]', '[class*="slider-thumb"]',
|
|
49
|
+
'[class*="drag-btn"]', '[class*="slide-btn"]', '[class*="slider_button"]',
|
|
50
|
+
'.nc-lang-cnt [class*="btn"]', '.btn_slide', '.handler',
|
|
51
|
+
'[class*="verify"][class*="btn"]',
|
|
52
|
+
];
|
|
53
|
+
var trackSels = [
|
|
54
|
+
'.geetest_slider_bar', '.geetest_track', '.slide-verify-bar',
|
|
55
|
+
'[class*="slider-bar"]', '[class*="track"]', '[class*="slider-wrap"]',
|
|
56
|
+
'.nc-tpl [class*="bg"]', '[class*="verify"][class*="bar"]',
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
var thumb = null, track = null;
|
|
60
|
+
|
|
61
|
+
// Also try the provided selector
|
|
62
|
+
${detection.selector ? `thumb = document.querySelector(${JSON.stringify(detection.selector)});` : ''}
|
|
63
|
+
|
|
64
|
+
if (!thumb) {
|
|
65
|
+
for (var i = 0; i < thumbSels.length; i++) {
|
|
66
|
+
var el = document.querySelector(thumbSels[i]);
|
|
67
|
+
if (el && el.getBoundingClientRect().width > 0) {
|
|
68
|
+
thumb = el;
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
for (var j = 0; j < trackSels.length; j++) {
|
|
74
|
+
var el2 = document.querySelector(trackSels[j]);
|
|
75
|
+
if (el2 && el2.getBoundingClientRect().width > 20) {
|
|
76
|
+
track = el2;
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (!thumb) return null;
|
|
82
|
+
var tr = thumb.getBoundingClientRect();
|
|
83
|
+
var trackWidth = track ? track.getBoundingClientRect().width : window.innerWidth * 0.6;
|
|
84
|
+
return {
|
|
85
|
+
thumbX: Math.round(tr.left + tr.width / 2),
|
|
86
|
+
thumbY: Math.round(tr.top + tr.height / 2),
|
|
87
|
+
thumbWidth: Math.round(tr.width),
|
|
88
|
+
trackWidth: Math.round(trackWidth),
|
|
89
|
+
};
|
|
90
|
+
})()`,
|
|
91
|
+
returnByValue: true,
|
|
92
|
+
});
|
|
93
|
+
const info = sliderInfo.result?.value;
|
|
94
|
+
if (!info) {
|
|
95
|
+
logDebug("[Weavetab AntiCaptcha] Slider thumb not found");
|
|
96
|
+
return manualFallback(detection, t0, "Could not locate the slider handle.");
|
|
97
|
+
}
|
|
98
|
+
const { thumbX, thumbY, thumbWidth, trackWidth } = info;
|
|
99
|
+
// Target drag distance: track width minus thumb half-width minus small margin
|
|
100
|
+
// We don't know the exact gap position, so we attempt full-width drag
|
|
101
|
+
// (most slider CAPTCHAs need drag to a specific notch — we overshoot & correct)
|
|
102
|
+
const dragDistance = Math.round((detection.sliderTrackWidth || trackWidth) - thumbWidth - 8);
|
|
103
|
+
logDebug(`[Weavetab AntiCaptcha] Slider: thumb=(${thumbX},${thumbY}), drag=${dragDistance}px`);
|
|
104
|
+
// Human-like bezier drag sequence
|
|
105
|
+
await humanSliderDrag(session, thumbX, thumbY, dragDistance);
|
|
106
|
+
// Wait for CAPTCHA to verify
|
|
107
|
+
await sleep(1200);
|
|
108
|
+
const stillBlocking = await checkCaptchaGone(session, detection);
|
|
109
|
+
if (stillBlocking === false) {
|
|
110
|
+
return {
|
|
111
|
+
solved: true,
|
|
112
|
+
method: "automated",
|
|
113
|
+
confidence: 0.88,
|
|
114
|
+
requiresUserIntervention: false,
|
|
115
|
+
timeToSolve: Date.now() - t0,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
// If still present, try a partial drag (maybe the gap is not at the end)
|
|
119
|
+
// Retry with 60% of track width
|
|
120
|
+
const partialDist = Math.round(dragDistance * 0.6);
|
|
121
|
+
await sleep(500);
|
|
122
|
+
await humanSliderDrag(session, thumbX, thumbY, partialDist);
|
|
123
|
+
await sleep(1200);
|
|
124
|
+
const stillBlockingRetry = await checkCaptchaGone(session, detection);
|
|
125
|
+
if (stillBlockingRetry === false) {
|
|
126
|
+
return {
|
|
127
|
+
solved: true,
|
|
128
|
+
method: "automated",
|
|
129
|
+
confidence: 0.78,
|
|
130
|
+
requiresUserIntervention: false,
|
|
131
|
+
timeToSolve: Date.now() - t0,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return manualFallback(detection, t0, "Slider drag attempted but CAPTCHA is still present. The puzzle piece position may require visual verification.");
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
logDebug("[Weavetab AntiCaptcha] Slider drag error:", err);
|
|
138
|
+
return manualFallback(detection, t0);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Simulate a human-like slider drag using CDP Input events.
|
|
143
|
+
* Uses a multi-phase motion: slow start → fast middle → slow end with overshoot + correction.
|
|
144
|
+
*/
|
|
145
|
+
async function humanSliderDrag(session, startX, startY, dragPixels) {
|
|
146
|
+
const { Input } = session;
|
|
147
|
+
const endX = startX + dragPixels;
|
|
148
|
+
// Phase 1: hover over thumb
|
|
149
|
+
await Input.dispatchMouseEvent({ type: "mouseMoved", x: startX, y: startY });
|
|
150
|
+
await sleep(80 + rand(40));
|
|
151
|
+
// Phase 2: mouse down
|
|
152
|
+
await Input.dispatchMouseEvent({
|
|
153
|
+
type: "mousePressed", x: startX, y: startY,
|
|
154
|
+
button: "left", clickCount: 1,
|
|
155
|
+
});
|
|
156
|
+
await sleep(60 + rand(40));
|
|
157
|
+
// Phase 3: Human bezier motion
|
|
158
|
+
// Build waypoints: slow start, accelerate, decelerate at end with slight overshoot
|
|
159
|
+
const totalSteps = 28 + rand(8);
|
|
160
|
+
const overshoot = 6 + rand(4); // pixels past target before correcting
|
|
161
|
+
for (let i = 1; i <= totalSteps + 2; i++) {
|
|
162
|
+
let t;
|
|
163
|
+
if (i <= totalSteps) {
|
|
164
|
+
// Ease-in-out + slight overshoot at end
|
|
165
|
+
const raw = i / totalSteps;
|
|
166
|
+
t = raw < 0.5
|
|
167
|
+
? 2 * raw * raw
|
|
168
|
+
: -1 + (4 - 2 * raw) * raw;
|
|
169
|
+
// Add overshoot near the end
|
|
170
|
+
if (i === totalSteps)
|
|
171
|
+
t = 1 + (overshoot / dragPixels);
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
// Correction back to target after overshoot
|
|
175
|
+
t = 1.0;
|
|
176
|
+
}
|
|
177
|
+
const cx = Math.round(startX + t * dragPixels);
|
|
178
|
+
const cy = startY + rand(3) - 1; // tiny vertical jitter
|
|
179
|
+
await Input.dispatchMouseEvent({
|
|
180
|
+
type: "mouseMoved", x: cx, y: cy, button: "left",
|
|
181
|
+
});
|
|
182
|
+
// Variable inter-step delay: faster in middle, slower at start/end
|
|
183
|
+
const stepProgress = i / totalSteps;
|
|
184
|
+
const delay = stepProgress < 0.2 || stepProgress > 0.85
|
|
185
|
+
? 18 + rand(12)
|
|
186
|
+
: 8 + rand(8);
|
|
187
|
+
await sleep(delay);
|
|
188
|
+
}
|
|
189
|
+
// Final position
|
|
190
|
+
await Input.dispatchMouseEvent({
|
|
191
|
+
type: "mouseMoved", x: endX, y: startY, button: "left",
|
|
192
|
+
});
|
|
193
|
+
await sleep(80 + rand(40));
|
|
194
|
+
// Phase 4: mouse up
|
|
195
|
+
await Input.dispatchMouseEvent({
|
|
196
|
+
type: "mouseReleased", x: endX, y: startY,
|
|
197
|
+
button: "left", clickCount: 1,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
// ─── 2. Press-and-Hold CAPTCHA ───────────────────────────────────────────────
|
|
201
|
+
async function solvePressAndHold(session, detection, t0) {
|
|
202
|
+
try {
|
|
203
|
+
const holdMs = detection.holdDurationMs || 2500;
|
|
204
|
+
// Locate the hold button
|
|
205
|
+
const btnInfo = await session.Runtime.evaluate({
|
|
206
|
+
expression: `(function() {
|
|
207
|
+
var sels = [
|
|
208
|
+
${detection.selector ? JSON.stringify(detection.selector) + ',' : ''}
|
|
209
|
+
'#px-captcha', '._pxCaptcha',
|
|
210
|
+
'[class*="press-hold"]', '[class*="hold-btn"]', '[class*="presshold"]',
|
|
211
|
+
'button[class*="verify"]',
|
|
212
|
+
];
|
|
213
|
+
for (var i = 0; i < sels.length; i++) {
|
|
214
|
+
var el = document.querySelector(sels[i]);
|
|
215
|
+
if (el) {
|
|
216
|
+
var r = el.getBoundingClientRect();
|
|
217
|
+
if (r.width > 0 && r.height > 0) {
|
|
218
|
+
return { x: Math.round(r.left + r.width/2), y: Math.round(r.top + r.height/2) };
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// Last resort: find any visible button with "hold" in text
|
|
223
|
+
var btns = document.querySelectorAll('button, [role="button"]');
|
|
224
|
+
for (var j = 0; j < btns.length; j++) {
|
|
225
|
+
if (/hold|press/i.test(btns[j].textContent || '')) {
|
|
226
|
+
var br = btns[j].getBoundingClientRect();
|
|
227
|
+
if (br.width > 0) return { x: Math.round(br.left+br.width/2), y: Math.round(br.top+br.height/2) };
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return null;
|
|
231
|
+
})()`,
|
|
232
|
+
returnByValue: true,
|
|
233
|
+
});
|
|
234
|
+
const btn = btnInfo.result?.value;
|
|
235
|
+
if (!btn) {
|
|
236
|
+
return manualFallback(detection, t0, "Could not locate the press-and-hold button.");
|
|
237
|
+
}
|
|
238
|
+
logDebug(`[Weavetab AntiCaptcha] Hold button at (${btn.x},${btn.y}), hold=${holdMs}ms`);
|
|
239
|
+
const { Input } = session;
|
|
240
|
+
// Move to button
|
|
241
|
+
await Input.dispatchMouseEvent({ type: "mouseMoved", x: btn.x, y: btn.y });
|
|
242
|
+
await sleep(120 + rand(60));
|
|
243
|
+
// Press down
|
|
244
|
+
await Input.dispatchMouseEvent({
|
|
245
|
+
type: "mousePressed", x: btn.x, y: btn.y,
|
|
246
|
+
button: "left", clickCount: 1,
|
|
247
|
+
});
|
|
248
|
+
// Hold with micro-jitter to simulate real human pressure
|
|
249
|
+
const jitterInterval = 80 + rand(40);
|
|
250
|
+
const jitterSteps = Math.floor(holdMs / jitterInterval);
|
|
251
|
+
for (let i = 0; i < jitterSteps; i++) {
|
|
252
|
+
const jx = btn.x + (rand(3) - 1);
|
|
253
|
+
const jy = btn.y + (rand(3) - 1);
|
|
254
|
+
await Input.dispatchMouseEvent({ type: "mouseMoved", x: jx, y: jy, button: "left" });
|
|
255
|
+
await sleep(jitterInterval);
|
|
256
|
+
}
|
|
257
|
+
// Release
|
|
258
|
+
await Input.dispatchMouseEvent({
|
|
259
|
+
type: "mouseReleased", x: btn.x, y: btn.y,
|
|
260
|
+
button: "left", clickCount: 1,
|
|
261
|
+
});
|
|
262
|
+
await sleep(1000);
|
|
263
|
+
const stillBlocking = await checkCaptchaGone(session, detection);
|
|
264
|
+
if (stillBlocking === false) {
|
|
265
|
+
return {
|
|
266
|
+
solved: true,
|
|
267
|
+
method: "automated",
|
|
268
|
+
confidence: 0.88,
|
|
269
|
+
requiresUserIntervention: false,
|
|
270
|
+
timeToSolve: Date.now() - t0,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
// PerimeterX sometimes needs a longer hold — retry with 2x duration
|
|
274
|
+
logDebug("[Weavetab AntiCaptcha] Hold captcha still present, retrying with longer hold");
|
|
275
|
+
await sleep(500);
|
|
276
|
+
await Input.dispatchMouseEvent({ type: "mousePressed", x: btn.x, y: btn.y, button: "left", clickCount: 1 });
|
|
277
|
+
await sleep(holdMs * 2 + rand(500));
|
|
278
|
+
await Input.dispatchMouseEvent({ type: "mouseReleased", x: btn.x, y: btn.y, button: "left", clickCount: 1 });
|
|
279
|
+
await sleep(1200);
|
|
280
|
+
const final = await checkCaptchaGone(session, detection);
|
|
281
|
+
if (final === false) {
|
|
282
|
+
return { solved: true, method: "automated", confidence: 0.75, requiresUserIntervention: false, timeToSolve: Date.now() - t0 };
|
|
283
|
+
}
|
|
284
|
+
return manualFallback(detection, t0, "Press-and-hold executed but CAPTCHA did not dismiss. Try holding the button manually for 3-5 seconds.");
|
|
285
|
+
}
|
|
286
|
+
catch (err) {
|
|
287
|
+
logDebug("[Weavetab AntiCaptcha] Hold error:", err);
|
|
288
|
+
return manualFallback(detection, t0);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
// ─── 3. Checkbox CAPTCHA (reCAPTCHA v2 simple, hCaptcha simple) ─────────────
|
|
292
|
+
async function solveCheckbox(session, detection, t0) {
|
|
293
|
+
try {
|
|
294
|
+
// Find the iframe and click the checkbox inside it via coordinates
|
|
295
|
+
const frameInfo = await session.Runtime.evaluate({
|
|
296
|
+
expression: `(function() {
|
|
297
|
+
var frames = document.querySelectorAll('iframe[src*="recaptcha"], iframe[src*="hcaptcha"]');
|
|
298
|
+
for (var i = 0; i < frames.length; i++) {
|
|
299
|
+
var r = frames[i].getBoundingClientRect();
|
|
300
|
+
if (r.width > 0 && r.height > 0) {
|
|
301
|
+
// Checkbox is typically at 20px from left, center vertically
|
|
302
|
+
return { x: Math.round(r.left + 20), y: Math.round(r.top + r.height/2) };
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return null;
|
|
306
|
+
})()`,
|
|
307
|
+
returnByValue: true,
|
|
308
|
+
});
|
|
309
|
+
const coords = frameInfo.result?.value;
|
|
310
|
+
if (!coords) {
|
|
311
|
+
return manualFallback(detection, t0, "Could not locate the CAPTCHA checkbox iframe.");
|
|
312
|
+
}
|
|
313
|
+
const { Input } = session;
|
|
314
|
+
await Input.dispatchMouseEvent({ type: "mouseMoved", x: coords.x, y: coords.y });
|
|
315
|
+
await sleep(150 + rand(100));
|
|
316
|
+
await Input.dispatchMouseEvent({ type: "mousePressed", x: coords.x, y: coords.y, button: "left", clickCount: 1 });
|
|
317
|
+
await sleep(80 + rand(40));
|
|
318
|
+
await Input.dispatchMouseEvent({ type: "mouseReleased", x: coords.x, y: coords.y, button: "left", clickCount: 1 });
|
|
319
|
+
// Wait for the CAPTCHA to either solve (simple) or present a challenge
|
|
320
|
+
await sleep(2000);
|
|
321
|
+
// Check if a challenge appeared (image grid)
|
|
322
|
+
const challengeAppeared = await session.Runtime.evaluate({
|
|
323
|
+
expression: `!!document.querySelector('iframe[src*="bframe"], iframe[src*="challenge"]')`,
|
|
324
|
+
returnByValue: true,
|
|
325
|
+
});
|
|
326
|
+
if (challengeAppeared.result?.value === true) {
|
|
327
|
+
// A visual challenge appeared — this requires image selection, escalate to AI agent
|
|
328
|
+
return {
|
|
329
|
+
solved: false,
|
|
330
|
+
method: "vision_assisted",
|
|
331
|
+
confidence: 0.5,
|
|
332
|
+
requiresUserIntervention: true,
|
|
333
|
+
userInstructions: "reCAPTCHA / hCaptcha image challenge appeared. Please take a screenshot, use browser_click to select the matching images, and click Verify.",
|
|
334
|
+
timeToSolve: Date.now() - t0,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
const stillBlocking = await checkCaptchaGone(session, detection);
|
|
338
|
+
if (stillBlocking === false) {
|
|
339
|
+
return { solved: true, method: "automated", confidence: 0.9, requiresUserIntervention: false, timeToSolve: Date.now() - t0 };
|
|
340
|
+
}
|
|
341
|
+
return manualFallback(detection, t0, "Clicked the CAPTCHA checkbox but it did not verify. A visual challenge may be required — please complete it manually.");
|
|
342
|
+
}
|
|
343
|
+
catch (err) {
|
|
344
|
+
logDebug("[Weavetab AntiCaptcha] Checkbox error:", err);
|
|
345
|
+
return manualFallback(detection, t0);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
// ─── 4. Invisible CAPTCHA (reCAPTCHA v3, Turnstile) ─────────────────────────
|
|
349
|
+
async function solveInvisible(session, detection, t0) {
|
|
350
|
+
try {
|
|
351
|
+
// Invisible CAPTCHAs score based on user interaction signals.
|
|
352
|
+
// Simulate natural page interaction to boost the score.
|
|
353
|
+
await session.Runtime.evaluate({
|
|
354
|
+
expression: `(function() {
|
|
355
|
+
// Scroll slightly to simulate reading
|
|
356
|
+
window.scrollBy({ top: 120, behavior: 'smooth' });
|
|
357
|
+
// Move mouse (won't dispatch but registers user-intent in page context)
|
|
358
|
+
document.dispatchEvent(new MouseEvent('mousemove', { bubbles: true, clientX: 400, clientY: 300 }));
|
|
359
|
+
// Trigger focus/blur cycle on any form input
|
|
360
|
+
var inputs = document.querySelectorAll('input:not([type="hidden"]), textarea');
|
|
361
|
+
if (inputs.length > 0) {
|
|
362
|
+
inputs[0].focus();
|
|
363
|
+
setTimeout(function() { inputs[0].blur(); }, 200);
|
|
364
|
+
}
|
|
365
|
+
})()`,
|
|
366
|
+
returnByValue: false,
|
|
367
|
+
});
|
|
368
|
+
await sleep(1500);
|
|
369
|
+
// Try to programmatically trigger grecaptcha execute (v3)
|
|
370
|
+
if (detection.type === "recaptcha_v3") {
|
|
371
|
+
await session.Runtime.evaluate({
|
|
372
|
+
expression: `(function() {
|
|
373
|
+
try {
|
|
374
|
+
if (window.grecaptcha && window.grecaptcha.execute) {
|
|
375
|
+
window.grecaptcha.ready(function() {
|
|
376
|
+
window.grecaptcha.execute().catch(function(){});
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
} catch(e) {}
|
|
380
|
+
})()`,
|
|
381
|
+
returnByValue: false,
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
// Turnstile: just wait, it auto-verifies with good browser signals
|
|
385
|
+
await sleep(2000);
|
|
386
|
+
return {
|
|
387
|
+
solved: true,
|
|
388
|
+
method: "automated",
|
|
389
|
+
confidence: 0.7,
|
|
390
|
+
requiresUserIntervention: false,
|
|
391
|
+
userInstructions: "Invisible CAPTCHA — interaction signals sent. If the form still fails, wait 2-3 more seconds.",
|
|
392
|
+
timeToSolve: Date.now() - t0,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
catch (err) {
|
|
396
|
+
return manualFallback(detection, t0, "Invisible CAPTCHA interaction failed. Wait 2-3 seconds and retry the form submission.");
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
// ─── 5. Math CAPTCHA ─────────────────────────────────────────────────────────
|
|
400
|
+
async function solveMathCaptcha(session, detection, t0) {
|
|
401
|
+
try {
|
|
402
|
+
const result = await session.Runtime.evaluate({
|
|
403
|
+
expression: `(function(sel) {
|
|
404
|
+
var container = document.querySelector(sel) || document.body;
|
|
405
|
+
var text = container.textContent || '';
|
|
406
|
+
// Match expressions like "3 + 7 =", "12 - 4 =", "6 × 3", "8 ÷ 2"
|
|
407
|
+
var m = text.match(/(\\d+)\\s*([+\\-*×x÷/])\\s*(\\d+)/);
|
|
408
|
+
if (!m) return null;
|
|
409
|
+
var a = parseInt(m[1]), op = m[2], b = parseInt(m[3]);
|
|
410
|
+
var ans;
|
|
411
|
+
if (op === '+') ans = a + b;
|
|
412
|
+
else if (op === '-') ans = a - b;
|
|
413
|
+
else if (op === '*' || op === '×' || op === 'x') ans = a * b;
|
|
414
|
+
else if (op === '/' || op === '÷') ans = Math.floor(a / b);
|
|
415
|
+
else return null;
|
|
416
|
+
// Fill the answer input
|
|
417
|
+
var inp = container.querySelector('input[type="text"], input[type="number"]');
|
|
418
|
+
if (!inp) inp = document.querySelector('input[name*="captcha"], input[id*="captcha"]');
|
|
419
|
+
if (inp) {
|
|
420
|
+
inp.focus();
|
|
421
|
+
var nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')?.set;
|
|
422
|
+
if (nativeInputValueSetter) nativeInputValueSetter.call(inp, String(ans));
|
|
423
|
+
inp.dispatchEvent(new Event('input', { bubbles: true }));
|
|
424
|
+
inp.dispatchEvent(new Event('change', { bubbles: true }));
|
|
425
|
+
return { solved: true, answer: ans };
|
|
426
|
+
}
|
|
427
|
+
return { solved: false, answer: ans };
|
|
428
|
+
})(${JSON.stringify(detection.selector || 'body')})`,
|
|
429
|
+
returnByValue: true,
|
|
430
|
+
});
|
|
431
|
+
const val = result.result?.value;
|
|
432
|
+
if (val?.solved) {
|
|
433
|
+
return { solved: true, method: "automated", confidence: 0.95, requiresUserIntervention: false, solution: String(val.answer), timeToSolve: Date.now() - t0 };
|
|
434
|
+
}
|
|
435
|
+
return manualFallback(detection, t0, val ? `Math answer is ${val.answer} — please type it in the CAPTCHA field.` : "Could not parse the math CAPTCHA expression.");
|
|
436
|
+
}
|
|
437
|
+
catch {
|
|
438
|
+
return manualFallback(detection, t0);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
// ─── 6. Text CAPTCHA (OCR-free best-effort) ──────────────────────────────────
|
|
442
|
+
async function solveTextCaptcha(session, detection, t0) {
|
|
443
|
+
// No OCR API — we can't read distorted images automatically.
|
|
444
|
+
// Delegate to the AI agent to use its vision capabilities.
|
|
445
|
+
return {
|
|
446
|
+
solved: false,
|
|
447
|
+
method: "vision_assisted",
|
|
448
|
+
confidence: 0,
|
|
449
|
+
requiresUserIntervention: true,
|
|
450
|
+
userInstructions: "Text CAPTCHA detected. Please take a screenshot to read the distorted characters, and use browser_type to enter them into the input field.",
|
|
451
|
+
timeToSolve: Date.now() - t0,
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
// ─── 7. Image Select CAPTCHA ─────────────────────────────────────────────────
|
|
455
|
+
async function solveImageSelect(session, detection, t0) {
|
|
456
|
+
// Without a vision API, we can't reliably identify the correct images.
|
|
457
|
+
// Delegate to the AI agent to use its vision capabilities.
|
|
458
|
+
const instructions = detection.type === "recaptcha_v2"
|
|
459
|
+
? "reCAPTCHA image challenge: Take a screenshot, identify the requested category, and use browser_click to select the matching images, then click Verify."
|
|
460
|
+
: detection.type === "hcaptcha"
|
|
461
|
+
? "hCaptcha challenge: Take a screenshot, look at the visual puzzle, use browser_click to select the correct images, and click Verify."
|
|
462
|
+
: "Image selection CAPTCHA: Take a screenshot and use browser_click to select all images that match the description shown.";
|
|
463
|
+
return {
|
|
464
|
+
solved: false,
|
|
465
|
+
method: "vision_assisted",
|
|
466
|
+
confidence: 0,
|
|
467
|
+
requiresUserIntervention: true,
|
|
468
|
+
userInstructions: instructions,
|
|
469
|
+
timeToSolve: Date.now() - t0,
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
// ─── 8. Generic fallback ─────────────────────────────────────────────────────
|
|
473
|
+
async function solveGenericFallback(session, detection, t0) {
|
|
474
|
+
// Try slider first (most common interactive CAPTCHA)
|
|
475
|
+
if (detection.selector) {
|
|
476
|
+
const sliderAttempt = await solveSliderDrag(session, { ...detection, interactionType: "drag" }, t0);
|
|
477
|
+
if (sliderAttempt.solved)
|
|
478
|
+
return sliderAttempt;
|
|
479
|
+
}
|
|
480
|
+
// Try checkbox
|
|
481
|
+
const cbAttempt = await solveCheckbox(session, detection, t0);
|
|
482
|
+
if (cbAttempt.solved)
|
|
483
|
+
return cbAttempt;
|
|
484
|
+
return manualFallback(detection, t0);
|
|
485
|
+
}
|
|
486
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
487
|
+
/**
|
|
488
|
+
* Returns true if the CAPTCHA is STILL present, false if it's gone (= solved).
|
|
489
|
+
* null = undetermined.
|
|
490
|
+
*/
|
|
491
|
+
async function checkCaptchaGone(session, detection) {
|
|
492
|
+
try {
|
|
493
|
+
if (!detection.selector)
|
|
494
|
+
return null;
|
|
495
|
+
const check = await session.Runtime.evaluate({
|
|
496
|
+
expression: `(function(sel) {
|
|
497
|
+
var el = document.querySelector(sel);
|
|
498
|
+
if (!el) return false; // gone = solved
|
|
499
|
+
var r = el.getBoundingClientRect();
|
|
500
|
+
return r.width > 0 && r.height > 0 && r.top < window.innerHeight;
|
|
501
|
+
})(${JSON.stringify(detection.selector)})`,
|
|
502
|
+
returnByValue: true,
|
|
503
|
+
});
|
|
504
|
+
return check.result?.value;
|
|
505
|
+
}
|
|
506
|
+
catch {
|
|
507
|
+
return null;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
function manualFallback(detection, t0, customMsg) {
|
|
511
|
+
return {
|
|
512
|
+
solved: false,
|
|
513
|
+
method: "manual_guidance",
|
|
514
|
+
confidence: 0,
|
|
515
|
+
requiresUserIntervention: true,
|
|
516
|
+
userInstructions: customMsg || getHumanInstructions(detection),
|
|
517
|
+
timeToSolve: Date.now() - t0,
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
function getHumanInstructions(detection) {
|
|
521
|
+
switch (detection.type) {
|
|
522
|
+
case "recaptcha_v2":
|
|
523
|
+
return "Click the 'I'm not a robot' checkbox. If images appear, select all matching ones and click Verify.";
|
|
524
|
+
case "recaptcha_v3":
|
|
525
|
+
return "Invisible CAPTCHA — wait 2-3 seconds and retry. If it fails again, interact with the page first (scroll, move mouse).";
|
|
526
|
+
case "hcaptcha":
|
|
527
|
+
return "Click the hCaptcha checkbox. If a visual challenge appears, complete it by clicking the matching images.";
|
|
528
|
+
case "turnstile":
|
|
529
|
+
return "Cloudflare Turnstile detected. Wait a few seconds for automatic verification.";
|
|
530
|
+
case "geetest":
|
|
531
|
+
case "geetest_v4":
|
|
532
|
+
return "GeeTest CAPTCHA: Drag the slider to the right until the puzzle piece fits into the gap.";
|
|
533
|
+
case "funcaptcha":
|
|
534
|
+
return "FunCaptcha: Rotate the image shown until it matches the correct orientation, then click Next.";
|
|
535
|
+
case "slider":
|
|
536
|
+
return "Drag the slider all the way to the right to verify.";
|
|
537
|
+
case "press_and_hold":
|
|
538
|
+
return "Press and hold the verification button for 2-3 seconds until it completes.";
|
|
539
|
+
case "datadome":
|
|
540
|
+
return "DataDome CAPTCHA: Drag the slider to complete the verification.";
|
|
541
|
+
case "perimeter_x":
|
|
542
|
+
return "PerimeterX CAPTCHA: Press and hold the button for 3 seconds.";
|
|
543
|
+
default:
|
|
544
|
+
return "CAPTCHA detected. Please complete the challenge shown on screen to continue.";
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
function sleep(ms) {
|
|
548
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
549
|
+
}
|
|
550
|
+
function rand(max) {
|
|
551
|
+
return Math.floor(Math.random() * max);
|
|
552
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface AuditEntry {
|
|
2
|
+
timestamp: string;
|
|
3
|
+
sessionId?: string;
|
|
4
|
+
tool: string;
|
|
5
|
+
parameters?: Record<string, unknown>;
|
|
6
|
+
duration?: number;
|
|
7
|
+
result: string;
|
|
8
|
+
pageUrl?: string;
|
|
9
|
+
tabTitle?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function logAction(action: string, target: string, result: string): void;
|
|
12
|
+
export declare function logActionStructured(entry: AuditEntry): void;
|
|
13
|
+
export declare function logDebug(message: string | any, ...optionalParams: any[]): void;
|