@cyberstrike-io/cyberstrike 1.1.16-beta.2 → 1.1.16-beta.4
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/hackbrowser-worker.js +307 -74
- package/package.json +12 -12
- package/postinstall.mjs +44 -0
- package/skill/aws-postexploit/SKILL.md +12 -4
- package/skill/azure-postexploit/SKILL.md +14 -7
- package/skill/ci-assessment/SKILL.md +82 -0
- package/skill/cloud-assessment/SKILL.md +140 -0
- package/skill/gcp-postexploit/SKILL.md +129 -0
- package/skill/k8s-assessment/SKILL.md +107 -0
- package/skill/k8s-postexploit/SKILL.md +1 -1
- package/skill/linux-postexploit/SKILL.md +295 -0
- package/skill/macos-postexploit/SKILL.md +136 -59
- package/skill/windows-postexploit/SKILL.md +600 -34
- package/web/assets/{ghostty-web-ClrKZYRt.js → ghostty-web-BljlRKGw.js} +1 -1
- package/web/assets/{home-CBbUenzB.js → home-DselBJMO.js} +1 -1
- package/web/assets/{index-DMx52TJy.js → index-CJ0Cgzs8.js} +40 -41
- package/web/assets/session-D2a1VaLl.js +56 -0
- package/web/index.html +1 -1
- package/web/assets/session-BpWko3PJ.js +0 -55
package/hackbrowser-worker.js
CHANGED
|
@@ -5,43 +5,25 @@ var __getProtoOf = Object.getPrototypeOf;
|
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
function __accessProp(key) {
|
|
9
|
-
return this[key];
|
|
10
|
-
}
|
|
11
|
-
var __toESMCache_node;
|
|
12
|
-
var __toESMCache_esm;
|
|
13
8
|
var __toESM = (mod, isNodeMode, target) => {
|
|
14
|
-
var canCache = mod != null && typeof mod === "object";
|
|
15
|
-
if (canCache) {
|
|
16
|
-
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
17
|
-
var cached = cache.get(mod);
|
|
18
|
-
if (cached)
|
|
19
|
-
return cached;
|
|
20
|
-
}
|
|
21
9
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
22
10
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
23
11
|
for (let key of __getOwnPropNames(mod))
|
|
24
12
|
if (!__hasOwnProp.call(to, key))
|
|
25
13
|
__defProp(to, key, {
|
|
26
|
-
get:
|
|
14
|
+
get: () => mod[key],
|
|
27
15
|
enumerable: true
|
|
28
16
|
});
|
|
29
|
-
if (canCache)
|
|
30
|
-
cache.set(mod, to);
|
|
31
17
|
return to;
|
|
32
18
|
};
|
|
33
19
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
34
|
-
var __returnValue = (v) => v;
|
|
35
|
-
function __exportSetter(name, newValue) {
|
|
36
|
-
this[name] = __returnValue.bind(null, newValue);
|
|
37
|
-
}
|
|
38
20
|
var __export = (target, all) => {
|
|
39
21
|
for (var name in all)
|
|
40
22
|
__defProp(target, name, {
|
|
41
23
|
get: all[name],
|
|
42
24
|
enumerable: true,
|
|
43
25
|
configurable: true,
|
|
44
|
-
set:
|
|
26
|
+
set: (newValue) => all[name] = () => newValue
|
|
45
27
|
});
|
|
46
28
|
};
|
|
47
29
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
@@ -19343,7 +19325,7 @@ var require_tracestate_impl = __commonJS((exports) => {
|
|
|
19343
19325
|
const value = listMember.slice(i2 + 1, part.length);
|
|
19344
19326
|
if ((0, tracestate_validators_1.validateKey)(key) && (0, tracestate_validators_1.validateValue)(value)) {
|
|
19345
19327
|
agg.set(key, value);
|
|
19346
|
-
}
|
|
19328
|
+
} else {}
|
|
19347
19329
|
}
|
|
19348
19330
|
return agg;
|
|
19349
19331
|
}, new Map);
|
|
@@ -30852,7 +30834,7 @@ class JSONSchemaGenerator {
|
|
|
30852
30834
|
if (val === undefined) {
|
|
30853
30835
|
if (this.unrepresentable === "throw") {
|
|
30854
30836
|
throw new Error("Literal `undefined` cannot be represented in JSON Schema");
|
|
30855
|
-
}
|
|
30837
|
+
} else {}
|
|
30856
30838
|
} else if (typeof val === "bigint") {
|
|
30857
30839
|
if (this.unrepresentable === "throw") {
|
|
30858
30840
|
throw new Error("BigInt literals cannot be represented in JSON Schema");
|
|
@@ -87530,7 +87512,7 @@ function finalize(ctx, schema) {
|
|
|
87530
87512
|
result.$schema = "http://json-schema.org/draft-07/schema#";
|
|
87531
87513
|
} else if (ctx.target === "draft-04") {
|
|
87532
87514
|
result.$schema = "http://json-schema.org/draft-04/schema#";
|
|
87533
|
-
} else if (ctx.target === "openapi-3.0") {}
|
|
87515
|
+
} else if (ctx.target === "openapi-3.0") {} else {}
|
|
87534
87516
|
if (ctx.external?.uri) {
|
|
87535
87517
|
const id = ctx.external.registry.get(schema)?.id;
|
|
87536
87518
|
if (!id)
|
|
@@ -87777,7 +87759,7 @@ var literalProcessor = (schema, ctx, json2, _params) => {
|
|
|
87777
87759
|
if (val === undefined) {
|
|
87778
87760
|
if (ctx.unrepresentable === "throw") {
|
|
87779
87761
|
throw new Error("Literal `undefined` cannot be represented in JSON Schema");
|
|
87780
|
-
}
|
|
87762
|
+
} else {}
|
|
87781
87763
|
} else if (typeof val === "bigint") {
|
|
87782
87764
|
if (ctx.unrepresentable === "throw") {
|
|
87783
87765
|
throw new Error("BigInt literals cannot be represented in JSON Schema");
|
|
@@ -115538,6 +115520,9 @@ A JSON snapshot with:
|
|
|
115538
115520
|
- url: current page URL
|
|
115539
115521
|
- viewportCenterBlocked: true if an overlay/modal/sidebar is open
|
|
115540
115522
|
- elements: interactive elements with id, role, label, type, value, placeholder, href, options
|
|
115523
|
+
- occludedBy: present when this element is physically covered by an overlay and
|
|
115524
|
+
cannot be clicked until that overlay is cleared. The value is the overlay's
|
|
115525
|
+
visible text (which usually includes its own dismiss control's label).
|
|
115541
115526
|
- outOfScope (optional): array of labels the user has excluded — never plan tasks matching these
|
|
115542
115527
|
- pendingMutations (optional): mutation keywords awaiting a triggering task.
|
|
115543
115528
|
If a task you plan would trigger one of these, set \`triggersMutation\` on that
|
|
@@ -115639,6 +115624,25 @@ Also include:
|
|
|
115639
115624
|
- Product/item cards — include max 2-3 (the API pattern repeats)
|
|
115640
115625
|
- CRUD buttons (Edit, Delete, View, Export, etc.) — one per action type is enough
|
|
115641
115626
|
|
|
115627
|
+
### Modal/dialog you opened to fill (viewportCenterBlocked + form fields)
|
|
115628
|
+
When viewportCenterBlocked is true because a MODAL/DIALOG containing form fields is
|
|
115629
|
+
open (you likely just opened it via a "+ Add" / "New" / "Create" / "Edit" button),
|
|
115630
|
+
that modal IS the content to explore: FILL its fields and SUBMIT via its primary
|
|
115631
|
+
action button (e.g. Kaydet / Save / Submit / Create). Do NOT plan a click on its
|
|
115632
|
+
close / X / Cancel control — closing it discards the form before it is submitted.
|
|
115633
|
+
Only dismiss a centered overlay that has NO form to complete (a banner, tour, cookie
|
|
115634
|
+
or notification notice covering the content behind it).
|
|
115635
|
+
|
|
115636
|
+
### Occluded elements (occludedBy present)
|
|
115637
|
+
An element with an \`occludedBy\` field is physically covered by an overlay and
|
|
115638
|
+
cannot be clicked until that overlay is cleared. \`occludedBy\` is the overlay's
|
|
115639
|
+
visible text. BEFORE planning any task on an occluded element, plan a click that
|
|
115640
|
+
dismisses the overlay FIRST (make it the first task). Identify the dismiss control
|
|
115641
|
+
among the listed elements using the \`occludedBy\` text, and choose the LEAST-COMMITTAL
|
|
115642
|
+
option — one that closes or postpones without granting permissions, subscribing,
|
|
115643
|
+
accepting terms, or changing account state. If no such dismiss control is present,
|
|
115644
|
+
simply omit the occluded element (do not invent a control, do not click a destructive one).
|
|
115645
|
+
|
|
115642
115646
|
### Skip these (do NOT include as tasks):
|
|
115643
115647
|
- Language / locale selectors (labels: "EN", "English", "DE", "🌐")
|
|
115644
115648
|
- Logout / sign-out / delete account (system handles logout separately)
|
|
@@ -115830,8 +115834,11 @@ async function planPage(snapshot, model, usageAcc) {
|
|
|
115830
115834
|
model,
|
|
115831
115835
|
system: systemPrompt,
|
|
115832
115836
|
messages: [{ role: "user", content: userMessage }],
|
|
115833
|
-
maxOutputTokens:
|
|
115834
|
-
temperature: 0
|
|
115837
|
+
maxOutputTokens: 16384,
|
|
115838
|
+
temperature: 0,
|
|
115839
|
+
providerOptions: {
|
|
115840
|
+
openai: { reasoningEffort: "low" }
|
|
115841
|
+
}
|
|
115835
115842
|
});
|
|
115836
115843
|
if (usageAcc) {
|
|
115837
115844
|
usageAcc.inputTokens += result.usage.inputTokens ?? 0;
|
|
@@ -115884,8 +115891,11 @@ async function planUnexploredElements(snapshot, unexploredLabels, model, usageAc
|
|
|
115884
115891
|
model,
|
|
115885
115892
|
system: systemPrompt,
|
|
115886
115893
|
messages: [{ role: "user", content: userMessage }],
|
|
115887
|
-
maxOutputTokens:
|
|
115888
|
-
temperature: 0
|
|
115894
|
+
maxOutputTokens: 16384,
|
|
115895
|
+
temperature: 0,
|
|
115896
|
+
providerOptions: {
|
|
115897
|
+
openai: { reasoningEffort: "low" }
|
|
115898
|
+
}
|
|
115889
115899
|
});
|
|
115890
115900
|
if (usageAcc) {
|
|
115891
115901
|
usageAcc.inputTokens += result.usage.inputTokens ?? 0;
|
|
@@ -116123,12 +116133,20 @@ function elementToPrompt(el) {
|
|
|
116123
116133
|
}
|
|
116124
116134
|
return result;
|
|
116125
116135
|
}
|
|
116126
|
-
function buildPlannerSnapshot(url3, elements, globalState, credId, viewportCenterBlocked) {
|
|
116136
|
+
function buildPlannerSnapshot(url3, elements, globalState, credId, viewportCenterBlocked, occlusions) {
|
|
116137
|
+
const promptElements = elements.map(elementToPrompt);
|
|
116138
|
+
if (occlusions && occlusions.length > 0) {
|
|
116139
|
+
for (const occ of occlusions) {
|
|
116140
|
+
const match = promptElements.find((e2) => e2.label === occ.label && e2.role === occ.role);
|
|
116141
|
+
if (match)
|
|
116142
|
+
match.occludedBy = occ.occluderText;
|
|
116143
|
+
}
|
|
116144
|
+
}
|
|
116127
116145
|
const snapshot = {
|
|
116128
116146
|
url: url3,
|
|
116129
116147
|
viewportCenterBlocked,
|
|
116130
116148
|
totalPagesVisited: globalState.visitedPages.size,
|
|
116131
|
-
elements:
|
|
116149
|
+
elements: promptElements
|
|
116132
116150
|
};
|
|
116133
116151
|
if (globalState.outOfScope.length > 0) {
|
|
116134
116152
|
snapshot.outOfScope = [...globalState.outOfScope];
|
|
@@ -116299,7 +116317,7 @@ async function collectInteractiveElements(page) {
|
|
|
116299
116317
|
return true;
|
|
116300
116318
|
return /^(mui-\d|radix-|headlessui-|react-aria-|rc-_?\d|ember\d)/i.test(id);
|
|
116301
116319
|
}
|
|
116302
|
-
function
|
|
116320
|
+
function buildCSSSelectorCandidate(el) {
|
|
116303
116321
|
const tag = el.tagName.toLowerCase();
|
|
116304
116322
|
const id = el.getAttribute("id");
|
|
116305
116323
|
if (id && !isEphemeralId(id))
|
|
@@ -116347,6 +116365,35 @@ async function collectInteractiveElements(page) {
|
|
|
116347
116365
|
}
|
|
116348
116366
|
return tag;
|
|
116349
116367
|
}
|
|
116368
|
+
function uniquePositionalPath(el) {
|
|
116369
|
+
const segs = [];
|
|
116370
|
+
let cur = el;
|
|
116371
|
+
while (cur && cur !== document.documentElement) {
|
|
116372
|
+
const tag = cur.tagName.toLowerCase();
|
|
116373
|
+
const id = cur.getAttribute("id");
|
|
116374
|
+
if (id && !isEphemeralId(id)) {
|
|
116375
|
+
segs.unshift(`${tag}#${CSS.escape(id)}`);
|
|
116376
|
+
break;
|
|
116377
|
+
}
|
|
116378
|
+
const parent = cur.parentElement;
|
|
116379
|
+
if (!parent) {
|
|
116380
|
+
segs.unshift(tag);
|
|
116381
|
+
break;
|
|
116382
|
+
}
|
|
116383
|
+
const sameTag = Array.from(parent.children).filter((c) => c.tagName === cur.tagName);
|
|
116384
|
+
segs.unshift(sameTag.length > 1 ? `${tag}:nth-of-type(${sameTag.indexOf(cur) + 1})` : tag);
|
|
116385
|
+
cur = parent;
|
|
116386
|
+
}
|
|
116387
|
+
return segs.join(" > ");
|
|
116388
|
+
}
|
|
116389
|
+
function buildCSSSelector(el) {
|
|
116390
|
+
const candidate = buildCSSSelectorCandidate(el);
|
|
116391
|
+
try {
|
|
116392
|
+
if (document.querySelectorAll(candidate).length === 1)
|
|
116393
|
+
return candidate;
|
|
116394
|
+
} catch {}
|
|
116395
|
+
return uniquePositionalPath(el);
|
|
116396
|
+
}
|
|
116350
116397
|
const INTERACTIVE_SELECTORS = [
|
|
116351
116398
|
"button",
|
|
116352
116399
|
"a[href]",
|
|
@@ -116422,7 +116469,12 @@ async function collectInteractiveElements(page) {
|
|
|
116422
116469
|
const label = getLabel(el);
|
|
116423
116470
|
const tag = el.tagName.toLowerCase();
|
|
116424
116471
|
const type = el.type?.toLowerCase() || "";
|
|
116425
|
-
const
|
|
116472
|
+
const rawHref = (el.getAttribute("href") || "").trim();
|
|
116473
|
+
const navigates = !!rawHref && rawHref !== "#" && !rawHref.startsWith("#") && !rawHref.startsWith("javascript:");
|
|
116474
|
+
const href = navigates ? el.href || "" : "";
|
|
116475
|
+
const nonNavAnchor = tag === "a" && !navigates;
|
|
116476
|
+
if (nonNavAnchor)
|
|
116477
|
+
role = "button";
|
|
116426
116478
|
const isSlider = role === "slider";
|
|
116427
116479
|
const value = isSlider ? el.getAttribute("aria-valuenow") ?? el.value ?? "" : el.value || "";
|
|
116428
116480
|
const placeholder = el.placeholder || "";
|
|
@@ -116438,7 +116490,8 @@ async function collectInteractiveElements(page) {
|
|
|
116438
116490
|
const disambiguatedLabel = count > 1 ? `${label} (${count})` : label;
|
|
116439
116491
|
const ariaLabelRaw = (el.getAttribute("aria-label") || "").trim();
|
|
116440
116492
|
const safeAriaLabel = ariaLabelRaw.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
116441
|
-
const
|
|
116493
|
+
const isFileInput = el.matches("input[type=file]");
|
|
116494
|
+
const selectorRole = syntheticRole || count > 1 || isFileInput || nonNavAnchor ? "" : safeAriaLabel ? `role=${role}[name="${safeAriaLabel}"]` : `role=${role}`;
|
|
116442
116495
|
const selectorCSS = buildCSSSelector(el);
|
|
116443
116496
|
const inChrome = !!el.closest("nav, header, footer, aside, [role=navigation], [role=banner], [role=contentinfo], [role=complementary]");
|
|
116444
116497
|
const roleCount = (seenRoleSelectors.get(selectorRole) ?? 0) + 1;
|
|
@@ -116573,8 +116626,17 @@ function assignIds(browserElements, startId) {
|
|
|
116573
116626
|
}
|
|
116574
116627
|
async function collectElements(page) {
|
|
116575
116628
|
const browserElements = await collectInteractiveElements(page);
|
|
116576
|
-
const sampled = sampleTemplates(browserElements);
|
|
116577
|
-
return assignIds(sampled, 1)
|
|
116629
|
+
const sampled = capElements(sampleTemplates(browserElements));
|
|
116630
|
+
return assignIds(sampled, 1);
|
|
116631
|
+
}
|
|
116632
|
+
var ACTION_ROLES_CAP = new Set(["button", "link", "menuitem", "tab"]);
|
|
116633
|
+
function capElements(els) {
|
|
116634
|
+
if (els.length <= MAX_ELEMENTS)
|
|
116635
|
+
return els;
|
|
116636
|
+
const actions = els.filter((e2) => ACTION_ROLES_CAP.has(e2.role));
|
|
116637
|
+
const rest = els.filter((e2) => !ACTION_ROLES_CAP.has(e2.role));
|
|
116638
|
+
const kept = new Set([...actions, ...rest].slice(0, MAX_ELEMENTS));
|
|
116639
|
+
return els.filter((e2) => kept.has(e2));
|
|
116578
116640
|
}
|
|
116579
116641
|
function sampleTemplates(elements) {
|
|
116580
116642
|
const clusterCount = new Map;
|
|
@@ -116703,6 +116765,48 @@ function evalIsViewportCenterBlocked(page) {
|
|
|
116703
116765
|
return false;
|
|
116704
116766
|
});
|
|
116705
116767
|
}
|
|
116768
|
+
async function probeClickPoint(page, selector) {
|
|
116769
|
+
try {
|
|
116770
|
+
return await page.evaluate((sel) => {
|
|
116771
|
+
const el = document.querySelector(sel);
|
|
116772
|
+
if (!el)
|
|
116773
|
+
return { status: "clickable" };
|
|
116774
|
+
const r2 = el.getBoundingClientRect();
|
|
116775
|
+
if (r2.width === 0 || r2.height === 0)
|
|
116776
|
+
return { status: "clickable" };
|
|
116777
|
+
const cx = r2.left + r2.width / 2;
|
|
116778
|
+
const cy = r2.top + r2.height / 2;
|
|
116779
|
+
if (cx < 0 || cy < 0 || cx > window.innerWidth || cy > window.innerHeight) {
|
|
116780
|
+
return { status: "offscreen" };
|
|
116781
|
+
}
|
|
116782
|
+
const top = document.elementFromPoint(cx, cy);
|
|
116783
|
+
if (!top)
|
|
116784
|
+
return { status: "offscreen" };
|
|
116785
|
+
if (top === el || el.contains(top) || top.contains(el))
|
|
116786
|
+
return { status: "clickable" };
|
|
116787
|
+
if (top.tagName === "IFRAME")
|
|
116788
|
+
return { status: "clickable" };
|
|
116789
|
+
let node = el;
|
|
116790
|
+
while (node) {
|
|
116791
|
+
const root = node.getRootNode();
|
|
116792
|
+
if (root instanceof ShadowRoot) {
|
|
116793
|
+
const host = root.host;
|
|
116794
|
+
if (host === top || top.contains(host))
|
|
116795
|
+
return { status: "clickable" };
|
|
116796
|
+
node = host;
|
|
116797
|
+
} else
|
|
116798
|
+
break;
|
|
116799
|
+
}
|
|
116800
|
+
const text2 = (top.innerText || top.getAttribute("aria-label") || "").trim().replace(/\s+/g, " ").slice(0, 80);
|
|
116801
|
+
return {
|
|
116802
|
+
status: "occluded",
|
|
116803
|
+
occluder: { tag: top.tagName.toLowerCase(), role: top.getAttribute("role") || "", text: text2 }
|
|
116804
|
+
};
|
|
116805
|
+
}, selector);
|
|
116806
|
+
} catch {
|
|
116807
|
+
return { status: "clickable" };
|
|
116808
|
+
}
|
|
116809
|
+
}
|
|
116706
116810
|
function filterVisitedLinks(elements, currentUrl, visitedPages) {
|
|
116707
116811
|
let currentPath;
|
|
116708
116812
|
try {
|
|
@@ -116728,6 +116832,59 @@ function filterVisitedLinks(elements, currentUrl, visitedPages) {
|
|
|
116728
116832
|
});
|
|
116729
116833
|
}
|
|
116730
116834
|
|
|
116835
|
+
// ../hackbrowser/src/upload-samples.ts
|
|
116836
|
+
var nodeBuffer = globalThis.Buffer;
|
|
116837
|
+
var b64 = (s2) => nodeBuffer.from(s2, "base64");
|
|
116838
|
+
var txt = (s2) => nodeBuffer.from(s2, "utf8");
|
|
116839
|
+
var SAMPLES = [
|
|
116840
|
+
{
|
|
116841
|
+
name: "sample.png",
|
|
116842
|
+
ext: "png",
|
|
116843
|
+
mimeType: "image/png",
|
|
116844
|
+
buffer: b64("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGJgAQAAAAUAAaX2RUAAAAAASUVORK5CYII=")
|
|
116845
|
+
},
|
|
116846
|
+
{
|
|
116847
|
+
name: "sample.jpg",
|
|
116848
|
+
ext: "jpg",
|
|
116849
|
+
mimeType: "image/jpeg",
|
|
116850
|
+
buffer: b64("/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAABAAEBAREA/8QAFAABAAAAAAAAAAAAAAAAAAAAA//EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAD8AfwD/2Q==")
|
|
116851
|
+
},
|
|
116852
|
+
{
|
|
116853
|
+
name: "sample.pdf",
|
|
116854
|
+
ext: "pdf",
|
|
116855
|
+
mimeType: "application/pdf",
|
|
116856
|
+
buffer: b64("JVBERi0xLjQKMSAwIG9iajw8L1R5cGUvQ2F0YWxvZy9QYWdlcyAyIDAgUj4+ZW5kb2JqCjIgMCBvYmo8PC9UeXBlL1BhZ2VzL0tpZHNbMyAwIFJdL0NvdW50IDE+PmVuZG9iagozIDAgb2JqPDwvVHlwZS9QYWdlL1BhcmVudCAyIDAgUi9NZWRpYUJveFswIDAgMjAwIDIwMF0+PmVuZG9iagp0cmFpbGVyPDwvUm9vdCAxIDAgUj4+CiUlRU9GCg==")
|
|
116857
|
+
},
|
|
116858
|
+
{ name: "sample.csv", ext: "csv", mimeType: "text/csv", buffer: txt(`name,email
|
|
116859
|
+
Test User,test@example.com
|
|
116860
|
+
`) },
|
|
116861
|
+
{ name: "sample.txt", ext: "txt", mimeType: "text/plain", buffer: txt(`sample text file
|
|
116862
|
+
`) },
|
|
116863
|
+
{ name: "sample.json", ext: "json", mimeType: "application/json", buffer: txt(`{"sample":true,"value":123}
|
|
116864
|
+
`) }
|
|
116865
|
+
];
|
|
116866
|
+
var DEFAULT_SAMPLE = SAMPLES[0];
|
|
116867
|
+
function tokenMatches(sample, token) {
|
|
116868
|
+
const t2 = token.trim().toLowerCase();
|
|
116869
|
+
if (!t2)
|
|
116870
|
+
return false;
|
|
116871
|
+
if (t2.startsWith("."))
|
|
116872
|
+
return t2.slice(1) === sample.ext;
|
|
116873
|
+
if (t2.endsWith("/*"))
|
|
116874
|
+
return sample.mimeType.startsWith(t2.slice(0, -1));
|
|
116875
|
+
return t2 === sample.mimeType;
|
|
116876
|
+
}
|
|
116877
|
+
function pickSample(accept) {
|
|
116878
|
+
if (!accept || !accept.trim())
|
|
116879
|
+
return DEFAULT_SAMPLE;
|
|
116880
|
+
const tokens = accept.split(",");
|
|
116881
|
+
for (const sample of SAMPLES) {
|
|
116882
|
+
if (tokens.some((tok) => tokenMatches(sample, tok)))
|
|
116883
|
+
return sample;
|
|
116884
|
+
}
|
|
116885
|
+
return DEFAULT_SAMPLE;
|
|
116886
|
+
}
|
|
116887
|
+
|
|
116731
116888
|
// ../hackbrowser/src/executor.ts
|
|
116732
116889
|
var log6 = Log.create({ service: "hackbrowser:executor" });
|
|
116733
116890
|
var CLICK_TIMEOUT = 2000;
|
|
@@ -116773,6 +116930,9 @@ async function executeFill(page, element, value) {
|
|
|
116773
116930
|
if (element.role === "slider" || element.type === "range") {
|
|
116774
116931
|
return executeSliderFill(page, selector || "role=slider", value);
|
|
116775
116932
|
}
|
|
116933
|
+
if (element.type === "file") {
|
|
116934
|
+
return executeFileSelect(page, selector);
|
|
116935
|
+
}
|
|
116776
116936
|
const fillErr = await page.fill(selector, value, { timeout: FILL_TIMEOUT }).then(() => null).catch((e2) => e2.message.split(`
|
|
116777
116937
|
`)[0]);
|
|
116778
116938
|
if (!fillErr)
|
|
@@ -116784,6 +116944,18 @@ async function executeFill(page, element, value) {
|
|
|
116784
116944
|
log6.warn("fill failed", { selector, error: fillErr });
|
|
116785
116945
|
return fillErr;
|
|
116786
116946
|
}
|
|
116947
|
+
async function executeFileSelect(page, selector) {
|
|
116948
|
+
const accept = await page.getAttribute(selector, "accept").catch(() => null);
|
|
116949
|
+
const sample = pickSample(accept);
|
|
116950
|
+
const err = await page.setInputFiles(selector, { name: sample.name, mimeType: sample.mimeType, buffer: sample.buffer }, { timeout: FILL_TIMEOUT }).then(() => null).catch((e2) => e2.message.split(`
|
|
116951
|
+
`)[0]);
|
|
116952
|
+
if (err) {
|
|
116953
|
+
log6.warn("file select failed", { selector, accept, error: err });
|
|
116954
|
+
return err;
|
|
116955
|
+
}
|
|
116956
|
+
log6.info("file selected", { selector, file: sample.name });
|
|
116957
|
+
return;
|
|
116958
|
+
}
|
|
116787
116959
|
async function executeSliderFill(page, selector, value) {
|
|
116788
116960
|
const numericValue = parseInt(value);
|
|
116789
116961
|
if (isNaN(numericValue) || numericValue <= 0) {
|
|
@@ -127843,7 +128015,8 @@ function normalizeScope(input) {
|
|
|
127843
128015
|
s2 = s2.replace(/^https?:\/\//, "");
|
|
127844
128016
|
s2 = s2.split(/[/?#]/)[0] ?? "";
|
|
127845
128017
|
s2 = s2.replace(/:\d+$/, "");
|
|
127846
|
-
|
|
128018
|
+
while (s2.endsWith("."))
|
|
128019
|
+
s2 = s2.slice(0, -1);
|
|
127847
128020
|
return s2;
|
|
127848
128021
|
}
|
|
127849
128022
|
function deriveScope(targetUrl) {
|
|
@@ -127872,8 +128045,10 @@ var log7 = Log.create({ service: "hackbrowser:agent" });
|
|
|
127872
128045
|
var MAX_STEPS_PER_PAGE = 30;
|
|
127873
128046
|
var MAX_UNPLANNED_ITERATIONS = 2;
|
|
127874
128047
|
var OVERLAY_ESCAPE_WAIT = 400;
|
|
128048
|
+
var OVERLAY_CLEAR_WAIT = 800;
|
|
127875
128049
|
var SPA_RENDER_RETRY_WAIT = 600;
|
|
127876
128050
|
var POST_GOTO_WAIT = 400;
|
|
128051
|
+
var MAX_INLINE_DEPTH = 2;
|
|
127877
128052
|
var LOGIN_SUCCESS_PATTERN = /POST\s+.*\/(login|signin|authenticate)\S*\s+\[200\]/i;
|
|
127878
128053
|
function triggerReDiscovery(globalState) {
|
|
127879
128054
|
if (globalState.authPhase === "authenticated")
|
|
@@ -128036,6 +128211,15 @@ function attachDialogAutoAccept(page) {
|
|
|
128036
128211
|
dialog.accept().catch(() => {});
|
|
128037
128212
|
});
|
|
128038
128213
|
}
|
|
128214
|
+
function attachFileChooserAutoFill(page) {
|
|
128215
|
+
page.on("filechooser", async (chooser) => {
|
|
128216
|
+
try {
|
|
128217
|
+
const accept = await chooser.element().getAttribute("accept").catch(() => null);
|
|
128218
|
+
const sample = pickSample(accept);
|
|
128219
|
+
await chooser.setFiles({ name: sample.name, mimeType: sample.mimeType, buffer: sample.buffer });
|
|
128220
|
+
} catch {}
|
|
128221
|
+
});
|
|
128222
|
+
}
|
|
128039
128223
|
function setupRequestInterceptor(page, inScope, onCapture, credentialId = SINGLE_CRED) {
|
|
128040
128224
|
let pendingUIContext = null;
|
|
128041
128225
|
let pendingTrigger = null;
|
|
@@ -128185,9 +128369,10 @@ function getPathPatternKey(url3) {
|
|
|
128185
128369
|
return null;
|
|
128186
128370
|
}
|
|
128187
128371
|
}
|
|
128188
|
-
async function explorePageWithAI(page, pageUrl, interceptor, model, globalState, inScope, credentialId, maxPages, usageAcc) {
|
|
128372
|
+
async function explorePageWithAI(page, pageUrl, interceptor, model, globalState, inScope, credentialId, maxPages, usageAcc, nav = { explore: async () => {}, depth: MAX_INLINE_DEPTH }) {
|
|
128189
128373
|
const linksToEnqueue = [];
|
|
128190
128374
|
const semanticActionsDone = new Set;
|
|
128375
|
+
const occ = { pending: [], signaled: new Set };
|
|
128191
128376
|
csEmit(page, {
|
|
128192
128377
|
type: "page-change",
|
|
128193
128378
|
url: pageUrl,
|
|
@@ -128246,9 +128431,9 @@ async function explorePageWithAI(page, pageUrl, interceptor, model, globalState,
|
|
|
128246
128431
|
remaining: taskQueue.length
|
|
128247
128432
|
});
|
|
128248
128433
|
if (task.type === "form") {
|
|
128249
|
-
await executeFormTask(task, page, elements, interceptor, semanticActionsDone, globalState, credentialId, linksToEnqueue, pageUrl, inScope);
|
|
128434
|
+
await executeFormTask(task, page, elements, interceptor, semanticActionsDone, globalState, credentialId, linksToEnqueue, pageUrl, inScope, occ, nav);
|
|
128250
128435
|
} else {
|
|
128251
|
-
await executeClickTask(task, page, elements, interceptor, semanticActionsDone, globalState, credentialId, linksToEnqueue, pageUrl, inScope);
|
|
128436
|
+
await executeClickTask(task, page, elements, interceptor, semanticActionsDone, globalState, credentialId, linksToEnqueue, pageUrl, inScope, occ, nav);
|
|
128252
128437
|
}
|
|
128253
128438
|
const postActionElements = filterVisitedLinks(await collectElements(page), pageUrl, globalState.visitedPages);
|
|
128254
128439
|
if (task.type === "click" && task.role === "combobox") {
|
|
@@ -128259,9 +128444,10 @@ async function explorePageWithAI(page, pageUrl, interceptor, model, globalState,
|
|
|
128259
128444
|
}
|
|
128260
128445
|
const hasNewElements = discoverNewElements(postActionElements, seenKeys);
|
|
128261
128446
|
const hasStaleTargets = queueHasStaleTargets(taskQueue, postActionElements);
|
|
128262
|
-
if (hasNewElements || hasStaleTargets) {
|
|
128447
|
+
if (hasNewElements || hasStaleTargets || occ.pending.length > 0) {
|
|
128263
128448
|
const freshElements = filterVisitedLinks(await collectElements(page), pageUrl, globalState.visitedPages);
|
|
128264
|
-
const snapshot2 = buildPlannerSnapshot(pageUrl, freshElements, globalState, credentialId, await isViewportCenterBlocked(page));
|
|
128449
|
+
const snapshot2 = buildPlannerSnapshot(pageUrl, freshElements, globalState, credentialId, await isViewportCenterBlocked(page), occ.pending);
|
|
128450
|
+
occ.pending = [];
|
|
128265
128451
|
csEmit(page, {
|
|
128266
128452
|
type: "llm-thinking",
|
|
128267
128453
|
reason: "replan",
|
|
@@ -128367,9 +128553,9 @@ async function explorePageWithAI(page, pageUrl, interceptor, model, globalState,
|
|
|
128367
128553
|
remaining: additionalQueue.length
|
|
128368
128554
|
});
|
|
128369
128555
|
if (task.type === "form") {
|
|
128370
|
-
await executeFormTask(task, page, elements, interceptor, semanticActionsDone, globalState, credentialId, linksToEnqueue, pageUrl, inScope);
|
|
128556
|
+
await executeFormTask(task, page, elements, interceptor, semanticActionsDone, globalState, credentialId, linksToEnqueue, pageUrl, inScope, occ, nav);
|
|
128371
128557
|
} else {
|
|
128372
|
-
await executeClickTask(task, page, elements, interceptor, semanticActionsDone, globalState, credentialId, linksToEnqueue, pageUrl, inScope);
|
|
128558
|
+
await executeClickTask(task, page, elements, interceptor, semanticActionsDone, globalState, credentialId, linksToEnqueue, pageUrl, inScope, occ, nav);
|
|
128373
128559
|
}
|
|
128374
128560
|
const postActionElements = filterVisitedLinks(await collectElements(page), pageUrl, globalState.visitedPages);
|
|
128375
128561
|
if (task.type === "click" && task.role === "combobox") {
|
|
@@ -128379,9 +128565,10 @@ async function explorePageWithAI(page, pageUrl, interceptor, model, globalState,
|
|
|
128379
128565
|
}
|
|
128380
128566
|
const hasNewElements = discoverNewElements(postActionElements, seenKeys);
|
|
128381
128567
|
const hasStaleTargets = queueHasStaleTargets(additionalQueue, postActionElements);
|
|
128382
|
-
if (hasNewElements || hasStaleTargets) {
|
|
128568
|
+
if (hasNewElements || hasStaleTargets || occ.pending.length > 0) {
|
|
128383
128569
|
const freshElements = filterVisitedLinks(await collectElements(page), pageUrl, globalState.visitedPages);
|
|
128384
|
-
const freshSnap = buildPlannerSnapshot(pageUrl, freshElements, globalState, credentialId, await isViewportCenterBlocked(page));
|
|
128570
|
+
const freshSnap = buildPlannerSnapshot(pageUrl, freshElements, globalState, credentialId, await isViewportCenterBlocked(page), occ.pending);
|
|
128571
|
+
occ.pending = [];
|
|
128385
128572
|
csEmit(page, {
|
|
128386
128573
|
type: "llm-thinking",
|
|
128387
128574
|
reason: "replan",
|
|
@@ -128431,7 +128618,45 @@ async function explorePageWithAI(page, pageUrl, interceptor, model, globalState,
|
|
|
128431
128618
|
}
|
|
128432
128619
|
return linksToEnqueue;
|
|
128433
128620
|
}
|
|
128434
|
-
async function
|
|
128621
|
+
async function recoverFromOcclusion(page, el, original, retry) {
|
|
128622
|
+
if (original.success)
|
|
128623
|
+
return original;
|
|
128624
|
+
if ((await probeClickPoint(page, el.selector)).status !== "occluded")
|
|
128625
|
+
return original;
|
|
128626
|
+
log7.debug("click occluded — clearing overlay + waiting, then retrying once", { label: el.label });
|
|
128627
|
+
await closeOverlay(page);
|
|
128628
|
+
await page.waitForTimeout(OVERLAY_CLEAR_WAIT);
|
|
128629
|
+
return retry();
|
|
128630
|
+
}
|
|
128631
|
+
async function diagnoseOcclusion(page, el, occ) {
|
|
128632
|
+
const probe = await probeClickPoint(page, el.selector);
|
|
128633
|
+
if (probe.status === "occluded" && !occ.signaled.has(probe.occluder.text)) {
|
|
128634
|
+
occ.signaled.add(probe.occluder.text);
|
|
128635
|
+
occ.pending.push({ label: el.label, role: el.role, occluderText: probe.occluder.text });
|
|
128636
|
+
log7.debug("action target occluded — signalling planner to dismiss overlay first", {
|
|
128637
|
+
label: el.label,
|
|
128638
|
+
occluder: probe.occluder.text
|
|
128639
|
+
});
|
|
128640
|
+
return true;
|
|
128641
|
+
}
|
|
128642
|
+
return false;
|
|
128643
|
+
}
|
|
128644
|
+
async function handleNavigation(page, parentUrl, linksToEnqueue, inScope, globalState, nav) {
|
|
128645
|
+
const cur = page.url();
|
|
128646
|
+
const curNorm = normalizeUrl(cur);
|
|
128647
|
+
if (curNorm !== normalizeUrl(parentUrl) && isInScope(cur, inScope)) {
|
|
128648
|
+
if (!globalState.visitedPages.has(curNorm) && nav.depth < MAX_INLINE_DEPTH) {
|
|
128649
|
+
globalState.visitedPages.add(curNorm);
|
|
128650
|
+
await nav.explore(page, cur, nav.depth + 1);
|
|
128651
|
+
} else {
|
|
128652
|
+
linksToEnqueue.push(cur);
|
|
128653
|
+
}
|
|
128654
|
+
}
|
|
128655
|
+
await page.goto(parentUrl, { waitUntil: "domcontentloaded", timeout: 15000 }).catch(() => {});
|
|
128656
|
+
await page.waitForTimeout(POST_GOTO_WAIT);
|
|
128657
|
+
await page.waitForLoadState("domcontentloaded", { timeout: 5000 }).catch(() => {});
|
|
128658
|
+
}
|
|
128659
|
+
async function executeFormTask(task, page, elements, interceptor, semanticActionsDone, globalState, credentialId, linksToEnqueue, pageUrl, inScope, occ, nav) {
|
|
128435
128660
|
for (const field of task.fields) {
|
|
128436
128661
|
let el = resolveElement(elements, field.role, field.label);
|
|
128437
128662
|
if (!el) {
|
|
@@ -128483,23 +128708,19 @@ async function executeFormTask(task, page, elements, interceptor, semanticAction
|
|
|
128483
128708
|
targetSelector: submitEl.selector,
|
|
128484
128709
|
credential: credentialId
|
|
128485
128710
|
});
|
|
128486
|
-
|
|
128711
|
+
let result = await execute(page, submitEl, "click", undefined, interceptor.setPendingUI, elements.length);
|
|
128712
|
+
result = await recoverFromOcclusion(page, submitEl, result, () => execute(page, submitEl, "click", undefined, interceptor.setPendingUI, elements.length));
|
|
128713
|
+
const occluded = !result.success && await diagnoseOcclusion(page, submitEl, occ);
|
|
128487
128714
|
interceptor.clearPendingTrigger();
|
|
128488
128715
|
trackResult(result, interceptor, globalState, credentialId, task.triggersMutation, page);
|
|
128489
128716
|
csEmit(page, { type: "action-end", ok: result.success, credential: credentialId });
|
|
128490
|
-
|
|
128717
|
+
if (!occluded)
|
|
128718
|
+
semanticActionsDone.add(submitKey);
|
|
128491
128719
|
globalState.totalSteps++;
|
|
128492
|
-
if (result.navigated)
|
|
128493
|
-
|
|
128494
|
-
if (cur !== pageUrl && isInScope(cur, inScope))
|
|
128495
|
-
linksToEnqueue.push(cur);
|
|
128496
|
-
log7.debug("reloading after form submit", { pageUrl });
|
|
128497
|
-
await page.goto(pageUrl, { waitUntil: "domcontentloaded", timeout: 15000 }).catch(() => {});
|
|
128498
|
-
await page.waitForTimeout(POST_GOTO_WAIT);
|
|
128499
|
-
await page.waitForLoadState("domcontentloaded", { timeout: 5000 }).catch(() => {});
|
|
128500
|
-
}
|
|
128720
|
+
if (result.navigated)
|
|
128721
|
+
await handleNavigation(page, pageUrl, linksToEnqueue, inScope, globalState, nav);
|
|
128501
128722
|
}
|
|
128502
|
-
async function executeClickTask(task, page, elements, interceptor, semanticActionsDone, globalState, credentialId, linksToEnqueue, pageUrl, inScope) {
|
|
128723
|
+
async function executeClickTask(task, page, elements, interceptor, semanticActionsDone, globalState, credentialId, linksToEnqueue, pageUrl, inScope, occ, nav) {
|
|
128503
128724
|
const el = resolveElement(elements, task.role, task.label);
|
|
128504
128725
|
if (!el) {
|
|
128505
128726
|
log7.debug("click target not found", { role: task.role, label: task.label });
|
|
@@ -128518,15 +128739,13 @@ async function executeClickTask(task, page, elements, interceptor, semanticActio
|
|
|
128518
128739
|
credential: credentialId
|
|
128519
128740
|
});
|
|
128520
128741
|
let result = await execute(page, el, "click", undefined, interceptor.setPendingUI, elements.length);
|
|
128521
|
-
|
|
128522
|
-
|
|
128523
|
-
await closeOverlay(page);
|
|
128524
|
-
result = await execute(page, el, "click", undefined, interceptor.setPendingUI, elements.length);
|
|
128525
|
-
}
|
|
128742
|
+
result = await recoverFromOcclusion(page, el, result, () => execute(page, el, "click", undefined, interceptor.setPendingUI, elements.length));
|
|
128743
|
+
const occluded = !result.success && await diagnoseOcclusion(page, el, occ);
|
|
128526
128744
|
interceptor.clearPendingTrigger();
|
|
128527
128745
|
trackResult(result, interceptor, globalState, credentialId, task.triggersMutation, page);
|
|
128528
128746
|
csEmit(page, { type: "action-end", ok: result.success, credential: credentialId });
|
|
128529
|
-
|
|
128747
|
+
if (!occluded)
|
|
128748
|
+
semanticActionsDone.add(key);
|
|
128530
128749
|
globalState.totalSteps++;
|
|
128531
128750
|
if (el.role === "option") {
|
|
128532
128751
|
const parentCombobox = elements.find((e2) => e2.role === "combobox");
|
|
@@ -128535,12 +128754,7 @@ async function executeClickTask(task, page, elements, interceptor, semanticActio
|
|
|
128535
128754
|
}
|
|
128536
128755
|
}
|
|
128537
128756
|
if (result.navigated) {
|
|
128538
|
-
|
|
128539
|
-
if (cur !== pageUrl && isInScope(cur, inScope))
|
|
128540
|
-
linksToEnqueue.push(cur);
|
|
128541
|
-
await page.goto(pageUrl, { waitUntil: "domcontentloaded", timeout: 15000 }).catch(() => {});
|
|
128542
|
-
await page.waitForTimeout(POST_GOTO_WAIT);
|
|
128543
|
-
await page.waitForLoadState("domcontentloaded", { timeout: 5000 }).catch(() => {});
|
|
128757
|
+
await handleNavigation(page, pageUrl, linksToEnqueue, inScope, globalState, nav);
|
|
128544
128758
|
return;
|
|
128545
128759
|
}
|
|
128546
128760
|
if (el.role === "combobox")
|
|
@@ -128870,6 +129084,7 @@ async function runMultiCredential(config3, credentials) {
|
|
|
128870
129084
|
await browserContext.addInitScript(PANEL_INIT_SCRIPT);
|
|
128871
129085
|
const page = await browserContext.newPage();
|
|
128872
129086
|
attachDialogAutoAccept(page);
|
|
129087
|
+
attachFileChooserAutoFill(page);
|
|
128873
129088
|
await page.goto(targetUrl, { waitUntil: "domcontentloaded", timeout: 30000 });
|
|
128874
129089
|
csEmit(page, {
|
|
128875
129090
|
type: "init",
|
|
@@ -128975,11 +129190,14 @@ async function runMultiCredential(config3, credentials) {
|
|
|
128975
129190
|
});
|
|
128976
129191
|
continue;
|
|
128977
129192
|
}
|
|
128978
|
-
|
|
129193
|
+
const landed = normalizeUrl(r2.ctx.page.url());
|
|
129194
|
+
log7.info("redirect to different page, enqueueing landed URL", {
|
|
128979
129195
|
credential: r2.ctx.id,
|
|
128980
129196
|
intended: entry.url,
|
|
128981
|
-
|
|
129197
|
+
landed
|
|
128982
129198
|
});
|
|
129199
|
+
enqueueWithContext(landed, r2.ctx.id, pageQueue, visitedPages, inScope, pathPatternCounts);
|
|
129200
|
+
continue;
|
|
128983
129201
|
}
|
|
128984
129202
|
visitableContexts.push(r2.ctx);
|
|
128985
129203
|
}
|
|
@@ -129022,7 +129240,12 @@ async function runMultiCredential(config3, credentials) {
|
|
|
129022
129240
|
}
|
|
129023
129241
|
for (const ctx of visitableContexts) {
|
|
129024
129242
|
log7.info("exploring", { credential: ctx.id, url: entry.url });
|
|
129025
|
-
const
|
|
129243
|
+
const exploreInline = async (p, url3, depth) => {
|
|
129244
|
+
const found = await explorePageWithAI(p, url3, ctx.interceptor, model, globalState, inScope, ctx.id, maxPages, usageAcc, { explore: exploreInline, depth });
|
|
129245
|
+
for (const u of found)
|
|
129246
|
+
enqueueWithContext(u, ctx.id, pageQueue, visitedPages, inScope, pathPatternCounts);
|
|
129247
|
+
};
|
|
129248
|
+
const discovered = await explorePageWithAI(ctx.page, entry.url, ctx.interceptor, model, globalState, inScope, ctx.id, maxPages, usageAcc, { explore: exploreInline, depth: 0 });
|
|
129026
129249
|
for (const url3 of discovered) {
|
|
129027
129250
|
enqueueWithContext(url3, ctx.id, pageQueue, visitedPages, inScope, pathPatternCounts);
|
|
129028
129251
|
}
|
|
@@ -129115,6 +129338,7 @@ async function run(config3) {
|
|
|
129115
129338
|
await context.addInitScript(PANEL_INIT_SCRIPT);
|
|
129116
129339
|
const page = await context.newPage();
|
|
129117
129340
|
attachDialogAutoAccept(page);
|
|
129341
|
+
attachFileChooserAutoFill(page);
|
|
129118
129342
|
if (config3.auth.sessionFile) {
|
|
129119
129343
|
await loadSession(context, config3.auth.sessionFile);
|
|
129120
129344
|
}
|
|
@@ -129207,6 +129431,10 @@ async function run(config3) {
|
|
|
129207
129431
|
continue;
|
|
129208
129432
|
}
|
|
129209
129433
|
const currentUrl = normalizeUrl(page.url());
|
|
129434
|
+
if (currentUrl !== normalizeUrl(nextUrl) && globalState.visitedPages.has(currentUrl)) {
|
|
129435
|
+
log7.info("redirect to already-explored page, skipping", { requested: normalizeUrl(nextUrl), landed: currentUrl });
|
|
129436
|
+
continue;
|
|
129437
|
+
}
|
|
129210
129438
|
globalState.visitedPages.add(currentUrl);
|
|
129211
129439
|
log7.info("exploring page", { page: `${pagesExplored}/${maxPages}`, url: currentUrl });
|
|
129212
129440
|
log7.debug("state", {
|
|
@@ -129258,7 +129486,12 @@ async function run(config3) {
|
|
|
129258
129486
|
}
|
|
129259
129487
|
log7.info("page changed after auth, re-exploring", { url: currentUrl });
|
|
129260
129488
|
}
|
|
129261
|
-
const
|
|
129489
|
+
const exploreInline = async (p, url3, depth) => {
|
|
129490
|
+
const found = await explorePageWithAI(p, url3, interceptor, model, globalState, inScope, SINGLE_CRED, maxPages, usageAcc, { explore: exploreInline, depth });
|
|
129491
|
+
for (const u of found)
|
|
129492
|
+
enqueueUrl(u, globalState, inScope);
|
|
129493
|
+
};
|
|
129494
|
+
const discovered = await explorePageWithAI(page, currentUrl, interceptor, model, globalState, inScope, SINGLE_CRED, maxPages, usageAcc, { explore: exploreInline, depth: 0 });
|
|
129262
129495
|
let newEnqueued = 0;
|
|
129263
129496
|
for (const url3 of discovered) {
|
|
129264
129497
|
if (enqueueUrl(url3, globalState, inScope))
|