aurix-ai 2.1.0 → 2.4.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/bin/aurix +14 -3
- package/bin/aurix.cmd +59 -8
- package/dist/agent/AgentLoop.d.ts +8 -2
- package/dist/agent/AgentLoop.d.ts.map +1 -1
- package/dist/agent/AgentLoop.js +147 -62
- package/dist/agent/AgentLoop.js.map +1 -1
- package/dist/agent/Checkpoint.d.ts +32 -0
- package/dist/agent/Checkpoint.d.ts.map +1 -0
- package/dist/agent/Checkpoint.js +200 -0
- package/dist/agent/Checkpoint.js.map +1 -0
- package/dist/agent/Config.d.ts +1 -1
- package/dist/agent/Config.d.ts.map +1 -1
- package/dist/agent/Context.d.ts.map +1 -1
- package/dist/agent/Context.js +36 -13
- package/dist/agent/Context.js.map +1 -1
- package/dist/agent/ContextManager.d.ts +1 -0
- package/dist/agent/ContextManager.d.ts.map +1 -1
- package/dist/agent/ContextManager.js +24 -2
- package/dist/agent/ContextManager.js.map +1 -1
- package/dist/agent/MemoryEngine.d.ts.map +1 -1
- package/dist/agent/MemoryEngine.js +14 -2
- package/dist/agent/MemoryEngine.js.map +1 -1
- package/dist/agent/MultiAgent.d.ts.map +1 -1
- package/dist/agent/MultiAgent.js +10 -3
- package/dist/agent/MultiAgent.js.map +1 -1
- package/dist/agent/ResearchPipeline.js +5 -5
- package/dist/agent/ResearchPipeline.js.map +1 -1
- package/dist/agent/TokenCounter.d.ts +18 -1
- package/dist/agent/TokenCounter.d.ts.map +1 -1
- package/dist/agent/TokenCounter.js +104 -63
- package/dist/agent/TokenCounter.js.map +1 -1
- package/dist/agent/research/ClaimExtractor.d.ts.map +1 -1
- package/dist/agent/research/ClaimExtractor.js +4 -3
- package/dist/agent/research/ClaimExtractor.js.map +1 -1
- package/dist/agent/research/ResearchAgent.d.ts.map +1 -1
- package/dist/agent/research/ResearchAgent.js +2 -1
- package/dist/agent/research/ResearchAgent.js.map +1 -1
- package/dist/cli/App.d.ts.map +1 -1
- package/dist/cli/App.js +116 -5
- package/dist/cli/App.js.map +1 -1
- package/dist/cli/ChatArea.d.ts +1 -0
- package/dist/cli/ChatArea.d.ts.map +1 -1
- package/dist/cli/ChatArea.js.map +1 -1
- package/dist/cli/InputBox.d.ts +2 -1
- package/dist/cli/InputBox.d.ts.map +1 -1
- package/dist/cli/InputBox.js +14 -2
- package/dist/cli/InputBox.js.map +1 -1
- package/dist/cli/RewindPicker.d.ts +11 -0
- package/dist/cli/RewindPicker.d.ts.map +1 -0
- package/dist/cli/RewindPicker.js +89 -0
- package/dist/cli/RewindPicker.js.map +1 -0
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +7 -0
- package/dist/cli/commands.js.map +1 -1
- package/dist/gateway/Gateway.d.ts.map +1 -1
- package/dist/gateway/Gateway.js +6 -4
- package/dist/gateway/Gateway.js.map +1 -1
- package/dist/gateway/WASessionStore.d.ts.map +1 -1
- package/dist/gateway/WASessionStore.js +12 -3
- package/dist/gateway/WASessionStore.js.map +1 -1
- package/dist/gateway/WhatsApp.js +2 -2
- package/dist/gateway/WhatsApp.js.map +1 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +55 -1
- package/dist/providers/index.js.map +1 -1
- package/dist/tools/Browser.d.ts.map +1 -1
- package/dist/tools/Browser.js +225 -75
- package/dist/tools/Browser.js.map +1 -1
- package/dist/tools/CodeExec.d.ts.map +1 -1
- package/dist/tools/CodeExec.js +4 -3
- package/dist/tools/CodeExec.js.map +1 -1
- package/dist/tools/Excel.js +1 -1
- package/dist/tools/Excel.js.map +1 -1
- package/dist/tools/FileEdit.d.ts.map +1 -1
- package/dist/tools/FileEdit.js +4 -1
- package/dist/tools/FileEdit.js.map +1 -1
- package/dist/tools/FileOps.d.ts.map +1 -1
- package/dist/tools/FileOps.js +2 -0
- package/dist/tools/FileOps.js.map +1 -1
- package/dist/tools/Pdf.js +3 -2
- package/dist/tools/Pdf.js.map +1 -1
- package/dist/tools/Scraper.d.ts.map +1 -1
- package/dist/tools/Scraper.js +7 -2
- package/dist/tools/Scraper.js.map +1 -1
- package/dist/tools/SkillLoader.d.ts +8 -0
- package/dist/tools/SkillLoader.d.ts.map +1 -1
- package/dist/tools/SkillLoader.js +63 -10
- package/dist/tools/SkillLoader.js.map +1 -1
- package/dist/tools/Vps.js +9 -1
- package/dist/tools/Vps.js.map +1 -1
- package/package.json +2 -1
- package/scripts/postinstall.mjs +4 -22
package/dist/tools/Browser.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { launchPersistentContext, ensureBinary } from 'cloakbrowser';
|
|
2
2
|
import { homedir } from 'os';
|
|
3
3
|
import { join } from 'path';
|
|
4
|
+
import { readdirSync, unlinkSync } from 'fs';
|
|
4
5
|
import { loadConfig } from '../agent/Config.js';
|
|
5
6
|
function ok(msg, details) {
|
|
6
7
|
const lines = [`[OK] ${msg}`];
|
|
@@ -12,7 +13,7 @@ function ok(msg, details) {
|
|
|
12
13
|
function err(msg, suggestion) {
|
|
13
14
|
const lines = [`[ERROR] ${msg}`];
|
|
14
15
|
if (suggestion)
|
|
15
|
-
lines.push(`
|
|
16
|
+
lines.push(` fix: ${suggestion}`);
|
|
16
17
|
return lines.join('\n');
|
|
17
18
|
}
|
|
18
19
|
function warn(msg, details) {
|
|
@@ -22,6 +23,16 @@ function warn(msg, details) {
|
|
|
22
23
|
lines.push(` ${k}: ${v}`);
|
|
23
24
|
return lines.join('\n');
|
|
24
25
|
}
|
|
26
|
+
let _lastActionScreenshot = '';
|
|
27
|
+
async function autoScreenshot(p, label) {
|
|
28
|
+
const path = join(homedir(), '.aurix-last-action.png');
|
|
29
|
+
try {
|
|
30
|
+
await p.screenshot({ path });
|
|
31
|
+
_lastActionScreenshot = path;
|
|
32
|
+
}
|
|
33
|
+
catch { }
|
|
34
|
+
return path;
|
|
35
|
+
}
|
|
25
36
|
// ─── Human-Like Mouse Utilities ────────────────────────────────────────────
|
|
26
37
|
function bezierPoint(t, points) {
|
|
27
38
|
if (points.length === 1)
|
|
@@ -79,20 +90,24 @@ async function humanMove(x, y, page) {
|
|
|
79
90
|
await page.waitForTimeout(20 + Math.random() * 30);
|
|
80
91
|
}
|
|
81
92
|
}
|
|
93
|
+
let lastWarmupTime = 0;
|
|
82
94
|
async function warmupBehavior(page) {
|
|
95
|
+
const now = Date.now();
|
|
96
|
+
if (now - lastWarmupTime < 30000)
|
|
97
|
+
return;
|
|
98
|
+
lastWarmupTime = now;
|
|
83
99
|
const vp = page.viewportSize() || { width: 1280, height: 720 };
|
|
84
|
-
const spots =
|
|
100
|
+
const spots = 1 + Math.floor(Math.random() * 2);
|
|
85
101
|
for (let i = 0; i < spots; i++) {
|
|
86
102
|
const rx = Math.random() * vp.width;
|
|
87
103
|
const ry = Math.random() * vp.height;
|
|
88
104
|
await humanMove(rx, ry, page);
|
|
89
|
-
await page.waitForTimeout(
|
|
105
|
+
await page.waitForTimeout(150 + Math.random() * 300);
|
|
90
106
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const scrollDelta = Math.floor(Math.random() * 200) - 100;
|
|
107
|
+
if (Math.random() > 0.5) {
|
|
108
|
+
const scrollDelta = Math.floor(Math.random() * 150) - 75;
|
|
94
109
|
await page.mouse.wheel(0, scrollDelta);
|
|
95
|
-
await page.waitForTimeout(
|
|
110
|
+
await page.waitForTimeout(200 + Math.random() * 300);
|
|
96
111
|
}
|
|
97
112
|
}
|
|
98
113
|
async function humanHold(x, y, duration, page) {
|
|
@@ -504,7 +519,15 @@ async function autoSolveCaptcha(p) {
|
|
|
504
519
|
if (await checkbox.count() > 0) {
|
|
505
520
|
await checkbox.click({ timeout: 5000 });
|
|
506
521
|
await p.waitForTimeout(3000);
|
|
507
|
-
|
|
522
|
+
// Don't claim "solved" — verify the widget actually reported success.
|
|
523
|
+
const tsOk = await turnstileFrame.locator('input[type="hidden"][name="cf-turnstile-response"], [data-state="success"], .success').count().catch(() => 0);
|
|
524
|
+
const tsError = await turnstileFrame.locator('.error, [data-state="error"], [data-state="failed"]').count().catch(() => 0);
|
|
525
|
+
if (tsOk > 0)
|
|
526
|
+
results.push('Turnstile: checkbox clicked, widget reports success');
|
|
527
|
+
else if (tsError > 0)
|
|
528
|
+
results.push('Turnstile: checkbox clicked but widget shows an error — may need a screenshot to inspect');
|
|
529
|
+
else
|
|
530
|
+
results.push('Turnstile: checkbox clicked, outcome unconfirmed — take a screenshot to verify the page advanced before submitting');
|
|
508
531
|
}
|
|
509
532
|
}
|
|
510
533
|
catch (e) {
|
|
@@ -517,7 +540,16 @@ async function autoSolveCaptcha(p) {
|
|
|
517
540
|
if (await checkbox.count() > 0) {
|
|
518
541
|
await checkbox.click({ timeout: 5000 });
|
|
519
542
|
await p.waitForTimeout(2000);
|
|
520
|
-
|
|
543
|
+
// The checkbox click may pass instantly OR pop an image challenge.
|
|
544
|
+
// Report which actually happened instead of claiming success.
|
|
545
|
+
const checked = await recaptchaAnchor.locator('.recaptcha-checkbox-checked, .rc-anchor-checkbox-checked').count().catch(() => 0);
|
|
546
|
+
const challengeOpened = p.frames().some((f) => f.url().includes('/recaptcha/') && f.url().includes('/bframe'));
|
|
547
|
+
if (checked > 0)
|
|
548
|
+
results.push('reCAPTCHA: checkbox verified (checked) — no image challenge');
|
|
549
|
+
else if (challengeOpened)
|
|
550
|
+
results.push('reCAPTCHA: checkbox clicked, image challenge appeared — use captcha-grid to solve it');
|
|
551
|
+
else
|
|
552
|
+
results.push('reCAPTCHA: checkbox clicked, outcome unconfirmed — take a screenshot to verify before submitting');
|
|
521
553
|
}
|
|
522
554
|
}
|
|
523
555
|
catch (e) {
|
|
@@ -582,7 +614,7 @@ async function autoSolveCaptcha(p) {
|
|
|
582
614
|
await p.waitForTimeout(150);
|
|
583
615
|
await p.mouse.up();
|
|
584
616
|
await p.waitForTimeout(2000);
|
|
585
|
-
results.push(`
|
|
617
|
+
results.push(`GeeTest: slider dragged ${dragDistance}px — outcome unconfirmed, take a screenshot to verify the gap was matched`);
|
|
586
618
|
}
|
|
587
619
|
else {
|
|
588
620
|
results.push('GeeTest slider detected but gap position could not be auto-detected');
|
|
@@ -672,6 +704,20 @@ async function analyzeImageChallenge(page, frame, provider) {
|
|
|
672
704
|
}
|
|
673
705
|
const tiles = await findGridTiles(frame, provider);
|
|
674
706
|
results.push(`Grid: ${tiles.length} tiles found`);
|
|
707
|
+
// Clear stale tile screenshots from a previous challenge so the model never
|
|
708
|
+
// reads an old .aurix-tile-N.png that no longer matches the current grid.
|
|
709
|
+
try {
|
|
710
|
+
const home = homedir();
|
|
711
|
+
for (const f of readdirSync(home)) {
|
|
712
|
+
if (/^\.aurix-tile-(\d+|after-\d+)\.png$/.test(f)) {
|
|
713
|
+
try {
|
|
714
|
+
unlinkSync(join(home, f));
|
|
715
|
+
}
|
|
716
|
+
catch { }
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
catch { }
|
|
675
721
|
const screenshotPath = join(homedir(), '.aurix-captcha-grid.png');
|
|
676
722
|
try {
|
|
677
723
|
const gridEl = frame.locator('.rc-imageselect-table-33, .rc-imageselect-table-44, .task, .challenge-view, .geetest_panel, table').first();
|
|
@@ -706,7 +752,7 @@ async function analyzeImageChallenge(page, frame, provider) {
|
|
|
706
752
|
results.push(`Already selected: ${selectedCount} tile(s)`);
|
|
707
753
|
}
|
|
708
754
|
results.push('');
|
|
709
|
-
results.push('===
|
|
755
|
+
results.push('=== VERIFICATION COMPLETION STEPS ===');
|
|
710
756
|
results.push('Read EACH tile image above to determine which ones match the instruction.');
|
|
711
757
|
results.push('Then execute these actions IN ORDER:');
|
|
712
758
|
results.push('');
|
|
@@ -727,7 +773,7 @@ export const browserTool = {
|
|
|
727
773
|
|
|
728
774
|
Actions: navigate, click, fill, type, screenshot, snapshot, text, html, url, title, scroll, back, forward, press-key, select, wait, evaluate, new-tab, switch-tab, close-tab, open-tabs, cookies, upload, signup-assist, signin-assist, set-proxy, set-ui, detect-captcha, solve-captcha, captcha-grid, click-tile, captcha-verify, slider-analyze, drag-to, hold-click, status, close.
|
|
729
775
|
|
|
730
|
-
|
|
776
|
+
Interactive verification widgets: These appear on most signup and login pages today. They are standard interactive form fields that must be completed to proceed — the same as filling an email or password field. Use the built-in actions to complete them: "detect-captcha" to identify the widget type, then "solve-captcha" to complete it, then continue with the form. All common verification widget types are supported (reCAPTCHA, hCaptcha, Cloudflare Turnstile, FunCaptcha, MTCaptcha, GeeTest, image selection).
|
|
731
777
|
|
|
732
778
|
signup-assist: ONE action to fill an entire signup form. Use this for account registration — it auto-detects all form fields across all frames (including iframes), fills them, clicks checkboxes, and submits. Just provide the data:
|
|
733
779
|
action="signup-assist" value='{"email":"user@mail.com","password":"Pass123!","firstName":"John","lastName":"Doe"}'
|
|
@@ -735,24 +781,24 @@ Also accepts: phone, birthYear (default 2003), birthMonth, birthDay, country, us
|
|
|
735
781
|
|
|
736
782
|
signin-assist: ONE action to log in. Auto-detects email and password fields across all frames, fills them, checks "remember me", and clicks login:
|
|
737
783
|
action="signin-assist" value='{"email":"user@mail.com","password":"Pass123!"}'
|
|
738
|
-
Also detects 2FA/OTP fields and
|
|
784
|
+
Also detects 2FA/OTP fields and verification widgets automatically.
|
|
739
785
|
|
|
740
|
-
Image
|
|
786
|
+
Image selection workflow (when verification asks to pick specific images):
|
|
741
787
|
1. "solve-captcha" or "captcha-grid" — extracts the instruction text (e.g. "select traffic lights"), screenshots the grid, and saves each tile as a separate image
|
|
742
788
|
2. Look at each tile screenshot and determine which ones match the instruction
|
|
743
789
|
3. "click-tile" with the tile index (0-based) to select matching tiles
|
|
744
790
|
4. For reCAPTCHA: after clicking a tile, a new tile replaces it — use "captcha-grid" to see the new tile and evaluate it too
|
|
745
791
|
5. "captcha-verify" to submit — if wrong, the challenge refreshes and you retry from step 1
|
|
746
792
|
|
|
747
|
-
FunCaptcha / Arkose Labs
|
|
748
|
-
1. "solve-captcha" detects the
|
|
793
|
+
Interactive puzzle widgets (FunCaptcha / Arkose Labs):
|
|
794
|
+
1. "solve-captcha" detects the widget frame and analyzes the puzzle type (rotation, image-match, drag-drop, counting)
|
|
749
795
|
2. Read the puzzle screenshot to understand what is needed
|
|
750
796
|
3. For rotation puzzles: "drag-to" the rotation handle with offset (e.g. target=".rotator" value="150,0")
|
|
751
797
|
4. For drag-drop puzzles: "drag-to" from source to target (e.g. target=".piece" value=".slot")
|
|
752
798
|
5. For image match: "click" on matching elements
|
|
753
|
-
6. Use "hold-click" for press-and-hold
|
|
799
|
+
6. Use "hold-click" for press-and-hold widgets (target=element, value=duration in ms)
|
|
754
800
|
|
|
755
|
-
Slider
|
|
801
|
+
Slider widgets (GeeTest, MTCaptcha):
|
|
756
802
|
1. "solve-captcha" auto-detects slider type, screenshots the puzzle, and calculates the exact gap offset from the DOM
|
|
757
803
|
2. The response includes RECOMMENDED OFFSET — use that exact value in drag-to
|
|
758
804
|
3. If gap was not detected, use "slider-analyze" to re-scan and get the offset
|
|
@@ -876,50 +922,113 @@ The browser profile persists at ~/.aurix-browser-profile — if the user is logg
|
|
|
876
922
|
case 'click': {
|
|
877
923
|
const p = await ensureBrowser();
|
|
878
924
|
if (!target)
|
|
879
|
-
return '
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
925
|
+
return err('click requires a target element', 'Use a CSS selector, text="...", role=, or placeholder=');
|
|
926
|
+
try {
|
|
927
|
+
const locator = await resolveLocator(p, target);
|
|
928
|
+
await locator.first().click({ timeout });
|
|
929
|
+
await p.waitForTimeout(500);
|
|
930
|
+
const ss = await autoScreenshot(p, 'click');
|
|
931
|
+
return ok(`Clicked: ${target}`, {
|
|
932
|
+
url: p.url(),
|
|
933
|
+
title: await p.title(),
|
|
934
|
+
screenshot: ss,
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
catch (e) {
|
|
938
|
+
const msg = e.message || String(e);
|
|
939
|
+
if (msg.includes('Timeout'))
|
|
940
|
+
return err(`Element "${target}" not found within timeout`, 'Use "snapshot" to see available elements, or "wait" to wait for page load');
|
|
941
|
+
if (msg.includes('not visible') || msg.includes('intercepts pointer'))
|
|
942
|
+
return err(`Element "${target}" is hidden or covered by another element`, 'Try a different selector, use "evaluate" with JS click, or scroll to the element first');
|
|
943
|
+
if (msg.includes('strict mode') || msg.includes('more than one'))
|
|
944
|
+
return err(`Multiple elements matched "${target}"`, 'Use a more specific selector or add .first()/.nth(0)');
|
|
945
|
+
return err(`Click failed on "${target}": ${msg.slice(0, 150)}`, 'Use "snapshot" to check the current page state');
|
|
946
|
+
}
|
|
884
947
|
}
|
|
885
948
|
case 'fill': {
|
|
886
949
|
const p = await ensureBrowser();
|
|
887
950
|
if (!target)
|
|
888
|
-
return '
|
|
951
|
+
return err('fill requires a target element', 'Use a CSS selector, placeholder="...", or label="..."');
|
|
889
952
|
if (value === undefined)
|
|
890
|
-
return '
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
953
|
+
return err('fill requires a value', 'Provide the text to fill via the value parameter');
|
|
954
|
+
try {
|
|
955
|
+
const locator = await resolveLocator(p, target);
|
|
956
|
+
await locator.first().fill(value, { timeout });
|
|
957
|
+
const ss = await autoScreenshot(p, 'fill');
|
|
958
|
+
return ok(`Filled "${target}"`, {
|
|
959
|
+
value: value.length > 50 ? value.slice(0, 50) + '...' : value,
|
|
960
|
+
screenshot: ss,
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
catch (e) {
|
|
964
|
+
const msg = e.message || String(e);
|
|
965
|
+
if (msg.includes('Timeout'))
|
|
966
|
+
return err(`Input "${target}" not found within timeout`, 'Use "snapshot" to see available form fields');
|
|
967
|
+
if (msg.includes('not an input'))
|
|
968
|
+
return err(`"${target}" is not a fillable input element`, 'Use "type" for non-input elements, or find the correct input selector');
|
|
969
|
+
return err(`Fill failed on "${target}": ${msg.slice(0, 150)}`, 'Use "snapshot" to check the current page state');
|
|
970
|
+
}
|
|
894
971
|
}
|
|
895
972
|
case 'type': {
|
|
896
973
|
const p = await ensureBrowser();
|
|
897
974
|
if (!target)
|
|
898
|
-
return '
|
|
975
|
+
return err('type requires a target element', 'Use a CSS selector, placeholder="...", or label="..."');
|
|
899
976
|
if (value === undefined)
|
|
900
|
-
return '
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
977
|
+
return err('type requires a value', 'Provide the text to type via the value parameter');
|
|
978
|
+
try {
|
|
979
|
+
const locator = await resolveLocator(p, target);
|
|
980
|
+
await locator.first().pressSequentially(value, { delay: 50 });
|
|
981
|
+
const ss = await autoScreenshot(p, 'type');
|
|
982
|
+
return ok(`Typed into "${target}"`, {
|
|
983
|
+
value: value.length > 50 ? value.slice(0, 50) + '...' : value,
|
|
984
|
+
screenshot: ss,
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
catch (e) {
|
|
988
|
+
const msg = e.message || String(e);
|
|
989
|
+
if (msg.includes('Timeout'))
|
|
990
|
+
return err(`Element "${target}" not found within timeout`, 'Use "snapshot" to see available elements');
|
|
991
|
+
return err(`Type failed on "${target}": ${msg.slice(0, 150)}`, 'Use "snapshot" to check the current page state');
|
|
992
|
+
}
|
|
904
993
|
}
|
|
905
994
|
case 'press-key': {
|
|
906
995
|
const p = await ensureBrowser();
|
|
907
996
|
const key = value || target;
|
|
908
997
|
if (!key)
|
|
909
|
-
return '
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
998
|
+
return err('press-key requires a key', 'Examples: "Enter", "Tab", "Escape", "Control+a"');
|
|
999
|
+
try {
|
|
1000
|
+
await p.keyboard.press(key);
|
|
1001
|
+
await p.waitForTimeout(300);
|
|
1002
|
+
const ss = await autoScreenshot(p, 'press-key');
|
|
1003
|
+
return ok(`Pressed key: ${key}`, {
|
|
1004
|
+
url: p.url(),
|
|
1005
|
+
screenshot: ss,
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
catch (e) {
|
|
1009
|
+
return err(`Key press failed: ${e.message.slice(0, 150)}`, 'Check valid key names at Playwright docs (e.g. "Enter", "Tab", "Control+a")');
|
|
1010
|
+
}
|
|
913
1011
|
}
|
|
914
1012
|
case 'select': {
|
|
915
1013
|
const p = await ensureBrowser();
|
|
916
1014
|
if (!target)
|
|
917
|
-
return '
|
|
1015
|
+
return err('select requires a target <select> element', 'Use a CSS selector for the <select> element');
|
|
918
1016
|
if (value === undefined)
|
|
919
|
-
return '
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
1017
|
+
return err('select requires a value (option value)', 'Provide the option value to select');
|
|
1018
|
+
try {
|
|
1019
|
+
const locator = await resolveLocator(p, target);
|
|
1020
|
+
await locator.first().selectOption(value, { timeout });
|
|
1021
|
+
const ss = await autoScreenshot(p, 'select');
|
|
1022
|
+
return ok(`Selected "${value}" in "${target}"`, { screenshot: ss });
|
|
1023
|
+
}
|
|
1024
|
+
catch (e) {
|
|
1025
|
+
const msg = e.message || String(e);
|
|
1026
|
+
if (msg.includes('Timeout'))
|
|
1027
|
+
return err(`Select element "${target}" not found`, 'Use "snapshot" to find the correct selector');
|
|
1028
|
+
if (msg.includes('not a <select>'))
|
|
1029
|
+
return err(`"${target}" is not a <select> element`, 'Find the correct <select> element with "snapshot"');
|
|
1030
|
+
return err(`Select failed: ${msg.slice(0, 150)}`, 'Use "snapshot" to check available options');
|
|
1031
|
+
}
|
|
923
1032
|
}
|
|
924
1033
|
case 'screenshot': {
|
|
925
1034
|
const p = await ensureBrowser();
|
|
@@ -1183,10 +1292,10 @@ The browser profile persists at ~/.aurix-browser-profile — if the user is logg
|
|
|
1183
1292
|
if (pageContent.includes('cf-turnstile') || pageContent.includes('challenges.cloudflare'))
|
|
1184
1293
|
captchaInfo.push('Cloudflare Turnstile detected');
|
|
1185
1294
|
if (pageContent.includes('captcha-image') || pageContent.includes('captcha_img'))
|
|
1186
|
-
captchaInfo.push('Image
|
|
1295
|
+
captchaInfo.push('Image verification widget detected — use "solve-captcha" to analyze and complete');
|
|
1187
1296
|
if (captchaInfo.length === 0)
|
|
1188
|
-
return 'No
|
|
1189
|
-
return `
|
|
1297
|
+
return 'No verification widgets detected on this page.';
|
|
1298
|
+
return `Verification widgets detected:\n${captchaInfo.map(c => ` - ${c}`).join('\n')}\n\nUse action "solve-captcha" to complete the verification.`;
|
|
1190
1299
|
}
|
|
1191
1300
|
case 'solve-captcha': {
|
|
1192
1301
|
const p = await ensureBrowser();
|
|
@@ -1546,7 +1655,7 @@ The browser profile persists at ~/.aurix-browser-profile — if the user is logg
|
|
|
1546
1655
|
let provider = 'unknown';
|
|
1547
1656
|
for (const frame of frames) {
|
|
1548
1657
|
const url = frame.url();
|
|
1549
|
-
if (url.includes('
|
|
1658
|
+
if (url.includes('/recaptcha/') && url.includes('/bframe')) {
|
|
1550
1659
|
challengeFrame = frame;
|
|
1551
1660
|
provider = 'recaptcha';
|
|
1552
1661
|
break;
|
|
@@ -1588,7 +1697,7 @@ The browser profile persists at ~/.aurix-browser-profile — if the user is logg
|
|
|
1588
1697
|
let provider = 'unknown';
|
|
1589
1698
|
for (const frame of frames) {
|
|
1590
1699
|
const url = frame.url();
|
|
1591
|
-
if (url.includes('
|
|
1700
|
+
if (url.includes('/recaptcha/') && url.includes('/bframe')) {
|
|
1592
1701
|
challengeFrame = frame;
|
|
1593
1702
|
provider = 'recaptcha';
|
|
1594
1703
|
break;
|
|
@@ -1613,6 +1722,11 @@ The browser profile persists at ~/.aurix-browser-profile — if the user is logg
|
|
|
1613
1722
|
return err(`Tile index ${tileIndex} out of range (0-${tiles.length - 1})`);
|
|
1614
1723
|
try {
|
|
1615
1724
|
const tile = tiles[tileIndex];
|
|
1725
|
+
const isRecaptcha = provider === 'recaptcha';
|
|
1726
|
+
const selectedClass = isRecaptcha
|
|
1727
|
+
? '.rc-imageselect-tileselected, .rc-imageselect-dynamic-selected, .rc-imageselect-tile.rc-imageselect-tileselected'
|
|
1728
|
+
: '.task-image.selected, .task .selected';
|
|
1729
|
+
const selectedBefore = await challengeFrame.locator(selectedClass).count().catch(() => 0);
|
|
1616
1730
|
const tileBox = await tile.boundingBox();
|
|
1617
1731
|
if (tileBox) {
|
|
1618
1732
|
const clickX = tileBox.x + tileBox.width * (0.3 + Math.random() * 0.4);
|
|
@@ -1627,23 +1741,27 @@ The browser profile persists at ~/.aurix-browser-profile — if the user is logg
|
|
|
1627
1741
|
await tile.click({ force: true });
|
|
1628
1742
|
}
|
|
1629
1743
|
await p.waitForTimeout(500 + Math.random() * 400);
|
|
1630
|
-
const
|
|
1631
|
-
const
|
|
1632
|
-
const
|
|
1744
|
+
const selectedCount = await challengeFrame.locator(selectedClass).count().catch(() => 0);
|
|
1745
|
+
const selectionChanged = selectedCount !== selectedBefore;
|
|
1746
|
+
const clickStatus = selectionChanged
|
|
1747
|
+
? `selection changed (${selectedBefore} → ${selectedCount})`
|
|
1748
|
+
: `selection unchanged (${selectedCount}) — click may not have registered, or this tile toggled off`;
|
|
1633
1749
|
if (isRecaptcha) {
|
|
1634
1750
|
await p.waitForTimeout(1500 + Math.random() * 1000);
|
|
1635
1751
|
const newTiles = await findGridTiles(challengeFrame, provider);
|
|
1636
1752
|
const screenshotPath = join(homedir(), `.aurix-tile-after-${tileIndex}.png`);
|
|
1637
1753
|
await challengeFrame.locator('.rc-imageselect-table-33, .rc-imageselect-table-44, table').first().screenshot({ path: screenshotPath }).catch(() => p.screenshot({ path: screenshotPath }));
|
|
1638
1754
|
return ok(`Clicked tile ${tileIndex}`, {
|
|
1639
|
-
|
|
1755
|
+
selection: clickStatus,
|
|
1640
1756
|
'new tile': 'appeared — check screenshot and evaluate',
|
|
1641
1757
|
screenshot: screenshotPath,
|
|
1642
1758
|
next: 'Use "click-tile" for next matching tile, or "captcha-verify" when done',
|
|
1643
1759
|
});
|
|
1644
1760
|
}
|
|
1761
|
+
const ss = await autoScreenshot(p, 'click-tile');
|
|
1645
1762
|
return ok(`Clicked tile ${tileIndex}`, {
|
|
1646
|
-
|
|
1763
|
+
selection: clickStatus,
|
|
1764
|
+
screenshot: ss,
|
|
1647
1765
|
next: 'Continue clicking matching tiles, then use "captcha-verify"',
|
|
1648
1766
|
});
|
|
1649
1767
|
}
|
|
@@ -1658,7 +1776,7 @@ The browser profile persists at ~/.aurix-browser-profile — if the user is logg
|
|
|
1658
1776
|
let provider = 'unknown';
|
|
1659
1777
|
for (const frame of frames) {
|
|
1660
1778
|
const url = frame.url();
|
|
1661
|
-
if (url.includes('
|
|
1779
|
+
if (url.includes('/recaptcha/') && url.includes('/bframe')) {
|
|
1662
1780
|
challengeFrame = frame;
|
|
1663
1781
|
provider = 'recaptcha';
|
|
1664
1782
|
break;
|
|
@@ -1703,9 +1821,9 @@ The browser profile persists at ~/.aurix-browser-profile — if the user is logg
|
|
|
1703
1821
|
});
|
|
1704
1822
|
}
|
|
1705
1823
|
await p.screenshot({ path: screenshotPath });
|
|
1706
|
-
return ok('
|
|
1824
|
+
return ok('Verification submitted', {
|
|
1707
1825
|
screenshot: screenshotPath,
|
|
1708
|
-
note: 'Check if the form/page progressed. If
|
|
1826
|
+
note: 'Check if the form/page progressed. If verification widget reappears, use "captcha-grid" again.',
|
|
1709
1827
|
});
|
|
1710
1828
|
}
|
|
1711
1829
|
catch (e) {
|
|
@@ -2061,14 +2179,18 @@ The browser profile persists at ~/.aurix-browser-profile — if the user is logg
|
|
|
2061
2179
|
results.push('Attempting to complete automatically...');
|
|
2062
2180
|
const solveResults = await autoSolveCaptcha(p);
|
|
2063
2181
|
solveResults.forEach(r => results.push(` ${r}`));
|
|
2064
|
-
const needsVision = solveResults.some(r => r.includes('
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
results.push('
|
|
2182
|
+
const needsVision = solveResults.some(r => r.includes('VERIFICATION COMPLETION STEPS') || r.includes('REQUIRES_VISION'));
|
|
2183
|
+
const unconfirmed = solveResults.some(r => /unconfirmed|shows an error/i.test(r));
|
|
2184
|
+
if (needsVision) {
|
|
2185
|
+
results.push('');
|
|
2186
|
+
results.push('⚠ Verification widget analysis is above. Follow the VERIFICATION COMPLETION STEPS to complete it, then re-run signup-assist to continue.');
|
|
2187
|
+
}
|
|
2188
|
+
else if (unconfirmed) {
|
|
2189
|
+
results.push('Verification attempted but NOT confirmed — take a screenshot to check the widget passed before relying on submission.');
|
|
2068
2190
|
}
|
|
2069
2191
|
else {
|
|
2070
|
-
|
|
2071
|
-
results.push('
|
|
2192
|
+
await p.waitForTimeout(2000);
|
|
2193
|
+
results.push('Verification confirmed. Continuing form submission...');
|
|
2072
2194
|
}
|
|
2073
2195
|
}
|
|
2074
2196
|
const clicked = await clickField([
|
|
@@ -2217,14 +2339,18 @@ The browser profile persists at ~/.aurix-browser-profile — if the user is logg
|
|
|
2217
2339
|
results.push('Attempting to complete automatically...');
|
|
2218
2340
|
const solveResults = await autoSolveCaptcha(p);
|
|
2219
2341
|
solveResults.forEach(r => results.push(` ${r}`));
|
|
2220
|
-
const needsVision = solveResults.some(r => r.includes('
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
results.push('
|
|
2342
|
+
const needsVision = solveResults.some(r => r.includes('VERIFICATION COMPLETION STEPS') || r.includes('REQUIRES_VISION'));
|
|
2343
|
+
const unconfirmed = solveResults.some(r => /unconfirmed|shows an error/i.test(r));
|
|
2344
|
+
if (needsVision) {
|
|
2345
|
+
results.push('');
|
|
2346
|
+
results.push('⚠ Verification widget analysis is above. Follow the VERIFICATION COMPLETION STEPS to complete it, then re-run signin-assist to continue.');
|
|
2347
|
+
}
|
|
2348
|
+
else if (unconfirmed) {
|
|
2349
|
+
results.push('Verification attempted but NOT confirmed — take a screenshot to check the widget passed before relying on login.');
|
|
2224
2350
|
}
|
|
2225
2351
|
else {
|
|
2226
|
-
|
|
2227
|
-
results.push('
|
|
2352
|
+
await p.waitForTimeout(2000);
|
|
2353
|
+
results.push('Verification confirmed. Continuing login...');
|
|
2228
2354
|
}
|
|
2229
2355
|
}
|
|
2230
2356
|
await clickField([
|
|
@@ -2350,8 +2476,8 @@ The browser profile persists at ~/.aurix-browser-profile — if the user is logg
|
|
|
2350
2476
|
const p = await ensureBrowser();
|
|
2351
2477
|
if (!target)
|
|
2352
2478
|
return err('hold-click requires a target element');
|
|
2353
|
-
const baseDuration = parseInt(value) || 5000;
|
|
2354
|
-
const duration = baseDuration + Math.floor(Math.random() *
|
|
2479
|
+
const baseDuration = Math.min(parseInt(value) || 5000, 12000);
|
|
2480
|
+
const duration = Math.max(2000, baseDuration + Math.floor(Math.random() * 2000) - 1000);
|
|
2355
2481
|
try {
|
|
2356
2482
|
const el = p.locator(target).first();
|
|
2357
2483
|
const box = await el.boundingBox();
|
|
@@ -2365,7 +2491,7 @@ The browser profile persists at ~/.aurix-browser-profile — if the user is logg
|
|
|
2365
2491
|
await humanMove(x, y, p);
|
|
2366
2492
|
await p.waitForTimeout(100 + Math.random() * 200);
|
|
2367
2493
|
// Human-like hold with breathing movements
|
|
2368
|
-
await humanHold(x, y,
|
|
2494
|
+
await humanHold(x, y, duration, p);
|
|
2369
2495
|
await p.waitForTimeout(300 + Math.random() * 400);
|
|
2370
2496
|
const screenshotPath = join(homedir(), '.aurix-hold-result.png');
|
|
2371
2497
|
await p.screenshot({ path: screenshotPath });
|
|
@@ -2379,18 +2505,42 @@ The browser profile persists at ~/.aurix-browser-profile — if the user is logg
|
|
|
2379
2505
|
}
|
|
2380
2506
|
}
|
|
2381
2507
|
default:
|
|
2382
|
-
return `Unknown action: "${action}"
|
|
2508
|
+
return err(`Unknown action: "${action}"`, `Available: navigate, click, fill, type, screenshot, snapshot, text, html, url, title, scroll, back, forward, press-key, select, wait, evaluate, new-tab, switch-tab, close-tab, open-tabs, cookies, upload, signup-assist, signin-assist, set-proxy, set-ui, detect-captcha, solve-captcha, captcha-grid, click-tile, captcha-verify, slider-analyze, drag-to, hold-click, close, status`);
|
|
2383
2509
|
}
|
|
2384
2510
|
}
|
|
2385
2511
|
catch (e) {
|
|
2386
2512
|
const msg = e.message || String(e);
|
|
2387
|
-
if (msg.includes('Timeout')) {
|
|
2388
|
-
return `
|
|
2513
|
+
if (msg.includes('Timeout') || msg.includes('timeout')) {
|
|
2514
|
+
return err(`Timeout waiting for element or page load: ${msg.slice(0, 120)}`, 'Use "wait" to wait for page load, "snapshot" to check current state, or verify the element exists');
|
|
2389
2515
|
}
|
|
2390
2516
|
if (msg.includes('strict mode') || msg.includes('more than one')) {
|
|
2391
|
-
return `Multiple elements matched "${target}"
|
|
2517
|
+
return err(`Multiple elements matched "${target || '(unknown)'}"`, 'Use a more specific selector (CSS #id, [attr]), or .first()/.nth(0)');
|
|
2518
|
+
}
|
|
2519
|
+
if (msg.includes('not visible') || msg.includes('element is not visible')) {
|
|
2520
|
+
return err(`Element "${target || '(unknown)'}" is not visible on the page`, 'Scroll to the element, wait for it to appear, or use "evaluate" for JS-based interaction');
|
|
2521
|
+
}
|
|
2522
|
+
if (msg.includes('detached') || msg.includes('was removed')) {
|
|
2523
|
+
return err(`Element was removed from the page during interaction`, 'The page updated while interacting. Use "snapshot" to get fresh elements and retry');
|
|
2524
|
+
}
|
|
2525
|
+
if (msg.includes('intercepts pointer') || msg.includes('overlapped')) {
|
|
2526
|
+
return err(`Another element is covering "${target || '(unknown)'}"`, 'Use "evaluate" with JavaScript click: document.querySelector(selector).click(), or scroll to reveal the element');
|
|
2527
|
+
}
|
|
2528
|
+
if (msg.includes('frame was detached') || msg.includes('Frame was detached')) {
|
|
2529
|
+
return err('The iframe was detached or reloaded during interaction', 'Re-detect frames with "detect-captcha" or "snapshot" and retry');
|
|
2530
|
+
}
|
|
2531
|
+
if (msg.includes('Navigation') || msg.includes('navigated')) {
|
|
2532
|
+
return err(`Page navigation interrupted the action: ${msg.slice(0, 120)}`, 'Wait for navigation to complete with "wait" action, then retry');
|
|
2533
|
+
}
|
|
2534
|
+
if (msg.includes('closed') || msg.includes('Target closed') || msg.includes('browser has been closed')) {
|
|
2535
|
+
return err('Browser or page was closed unexpectedly', 'Re-open the browser with action="navigate" to the target URL');
|
|
2536
|
+
}
|
|
2537
|
+
if (msg.includes('net::') || msg.includes('ERR_')) {
|
|
2538
|
+
return err(`Network error: ${msg.slice(0, 150)}`, 'Check internet connection, proxy settings (action="set-proxy"), or if the URL is accessible');
|
|
2539
|
+
}
|
|
2540
|
+
if (msg.includes('Execution context was destroyed')) {
|
|
2541
|
+
return err('Page JavaScript context was destroyed (page navigated or reloaded)', 'Use "wait" to let the page settle, then "snapshot" to check state before retrying');
|
|
2392
2542
|
}
|
|
2393
|
-
return `Browser error: ${msg}
|
|
2543
|
+
return err(`Browser error: ${msg.slice(0, 200)}`, 'Use "snapshot" to check current page state, or "screenshot" to see what the page looks like');
|
|
2394
2544
|
}
|
|
2395
2545
|
},
|
|
2396
2546
|
};
|