@browserbasehq/stagehand 2.5.4 → 2.5.5
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/dist/index.d.ts +2 -2
- package/dist/index.js +129 -42
- package/dist/lib/StagehandContext.d.ts +1 -0
- package/dist/lib/StagehandPage.d.ts +0 -1
- package/dist/lib/dom/build/scriptContent.d.ts +1 -1
- package/dist/lib/dom/index.d.ts +1 -2
- package/dist/lib/dom/process.d.ts +0 -17
- package/dist/lib/version.d.ts +1 -1
- package/dist/types/context.d.ts +2 -0
- package/dist/types/model.d.ts +1 -1
- package/package.json +1 -1
- package/dist/lib/dom/elementCheckUtils.d.ts +0 -2
- package/dist/lib/dom/utils.d.ts +0 -7
- package/dist/lib/dom/xpathUtils.d.ts +0 -14
package/dist/index.d.ts
CHANGED
|
@@ -52,7 +52,7 @@ interface LLMTool {
|
|
|
52
52
|
parameters: Record<string, unknown>;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
declare const AvailableModelSchema: z.ZodEnum<["gpt-4.1", "gpt-4.1-mini", "gpt-4.1-nano", "o4-mini", "o3", "o3-mini", "o1", "o1-mini", "gpt-4o", "gpt-4o-mini", "gpt-4o-2024-08-06", "gpt-4.5-preview", "o1-preview", "claude-
|
|
55
|
+
declare const AvailableModelSchema: z.ZodEnum<["gpt-4.1", "gpt-4.1-mini", "gpt-4.1-nano", "o4-mini", "o3", "o3-mini", "o1", "o1-mini", "gpt-4o", "gpt-4o-mini", "gpt-4o-2024-08-06", "gpt-4.5-preview", "o1-preview", "claude-haiku-4-5", "claude-3-7-sonnet-latest", "claude-3-7-sonnet-20250219", "cerebras-llama-3.3-70b", "cerebras-llama-3.1-8b", "groq-llama-3.3-70b-versatile", "groq-llama-3.3-70b-specdec", "gemini-1.5-flash", "gemini-1.5-pro", "gemini-1.5-flash-8b", "gemini-2.0-flash-lite", "gemini-2.0-flash", "gemini-2.5-flash-preview-04-17", "gemini-2.5-pro-preview-03-25"]>;
|
|
56
56
|
type AvailableModel = z.infer<typeof AvailableModelSchema> | string;
|
|
57
57
|
type ModelProvider = "openai" | "anthropic" | "cerebras" | "groq" | "google" | "aisdk";
|
|
58
58
|
type ClientOptions = ClientOptions$1 | ClientOptions$2;
|
|
@@ -796,6 +796,7 @@ declare class StagehandContext {
|
|
|
796
796
|
private pageMap;
|
|
797
797
|
private activeStagehandPage;
|
|
798
798
|
private readonly frameIdMap;
|
|
799
|
+
private static readonly contextsWithInitScript;
|
|
799
800
|
private constructor();
|
|
800
801
|
private createStagehandPage;
|
|
801
802
|
static init(context: BrowserContext$1, stagehand: Stagehand): Promise<StagehandContext>;
|
|
@@ -835,7 +836,6 @@ declare class StagehandPage {
|
|
|
835
836
|
ordinalForFrameId(fid: string | undefined): number;
|
|
836
837
|
encodeWithFrameId(fid: string | undefined, backendId: number): EncodedId;
|
|
837
838
|
resetFrameOrdinals(): void;
|
|
838
|
-
private ensureStagehandScript;
|
|
839
839
|
/** Register the custom selector engine that pierces open/closed shadow roots. */
|
|
840
840
|
private ensureStagehandSelectorEngine;
|
|
841
841
|
/**
|
package/dist/index.js
CHANGED
|
@@ -494,7 +494,7 @@ var StagehandFunctionName = /* @__PURE__ */ ((StagehandFunctionName2) => {
|
|
|
494
494
|
})(StagehandFunctionName || {});
|
|
495
495
|
|
|
496
496
|
// lib/version.ts
|
|
497
|
-
var STAGEHAND_VERSION = "2.5.
|
|
497
|
+
var STAGEHAND_VERSION = "2.5.5";
|
|
498
498
|
|
|
499
499
|
// types/stagehandErrors.ts
|
|
500
500
|
var StagehandError = class extends Error {
|
|
@@ -2609,6 +2609,115 @@ var PUA_START = 57344;
|
|
|
2609
2609
|
var PUA_END = 63743;
|
|
2610
2610
|
var NBSP_CHARS = /* @__PURE__ */ new Set([160, 8239, 8199, 65279]);
|
|
2611
2611
|
var WORLD_NAME = "stagehand-world";
|
|
2612
|
+
var DOM_DEPTH_ATTEMPTS = [-1, 256, 128, 64, 32, 16, 8, 4, 2, 1];
|
|
2613
|
+
var DESCRIBE_DEPTH_ATTEMPTS = [-1, 64, 32, 16, 8, 4, 2, 1];
|
|
2614
|
+
function isCborStackError(message) {
|
|
2615
|
+
return message.includes("CBOR: stack limit exceeded");
|
|
2616
|
+
}
|
|
2617
|
+
function shouldExpandNode(node) {
|
|
2618
|
+
var _a15, _b, _c;
|
|
2619
|
+
const declaredChildren = (_a15 = node.childNodeCount) != null ? _a15 : 0;
|
|
2620
|
+
const realizedChildren = (_c = (_b = node.children) == null ? void 0 : _b.length) != null ? _c : 0;
|
|
2621
|
+
return declaredChildren > realizedChildren;
|
|
2622
|
+
}
|
|
2623
|
+
function mergeDomNodes(target, source) {
|
|
2624
|
+
var _a15, _b, _c, _d;
|
|
2625
|
+
target.childNodeCount = (_a15 = source.childNodeCount) != null ? _a15 : target.childNodeCount;
|
|
2626
|
+
target.children = (_b = source.children) != null ? _b : target.children;
|
|
2627
|
+
target.shadowRoots = (_c = source.shadowRoots) != null ? _c : target.shadowRoots;
|
|
2628
|
+
target.contentDocument = (_d = source.contentDocument) != null ? _d : target.contentDocument;
|
|
2629
|
+
}
|
|
2630
|
+
function collectDomTraversalTargets(node) {
|
|
2631
|
+
const targets = [];
|
|
2632
|
+
if (node.children) targets.push(...node.children);
|
|
2633
|
+
if (node.shadowRoots) targets.push(...node.shadowRoots);
|
|
2634
|
+
if (node.contentDocument) targets.push(node.contentDocument);
|
|
2635
|
+
return targets;
|
|
2636
|
+
}
|
|
2637
|
+
function hydrateDomTree(session, root) {
|
|
2638
|
+
return __async(this, null, function* () {
|
|
2639
|
+
var _a15, _b;
|
|
2640
|
+
const stack = [root];
|
|
2641
|
+
const expandedNodeIds = /* @__PURE__ */ new Set();
|
|
2642
|
+
const expandedBackendIds = /* @__PURE__ */ new Set();
|
|
2643
|
+
let describeCalls = 0;
|
|
2644
|
+
while (stack.length) {
|
|
2645
|
+
const node = stack.pop();
|
|
2646
|
+
const nodeId = typeof node.nodeId === "number" && node.nodeId > 0 ? node.nodeId : void 0;
|
|
2647
|
+
const backendId = typeof node.backendNodeId === "number" && node.backendNodeId > 0 ? node.backendNodeId : void 0;
|
|
2648
|
+
const seenByNode = nodeId ? expandedNodeIds.has(nodeId) : false;
|
|
2649
|
+
const seenByBackend = !nodeId && backendId ? expandedBackendIds.has(backendId) : false;
|
|
2650
|
+
if (seenByNode || seenByBackend) continue;
|
|
2651
|
+
if (nodeId) expandedNodeIds.add(nodeId);
|
|
2652
|
+
else if (backendId) expandedBackendIds.add(backendId);
|
|
2653
|
+
const needsExpansion = shouldExpandNode(node);
|
|
2654
|
+
if (needsExpansion && (nodeId || backendId)) {
|
|
2655
|
+
const describeParamsBase = nodeId ? { nodeId } : { backendNodeId: backendId };
|
|
2656
|
+
let expanded = false;
|
|
2657
|
+
for (const depth of DESCRIBE_DEPTH_ATTEMPTS) {
|
|
2658
|
+
try {
|
|
2659
|
+
const described = yield session.send("DOM.describeNode", __spreadProps(__spreadValues({}, describeParamsBase), {
|
|
2660
|
+
depth,
|
|
2661
|
+
pierce: true
|
|
2662
|
+
}));
|
|
2663
|
+
mergeDomNodes(node, described.node);
|
|
2664
|
+
if (!nodeId && described.node.nodeId && described.node.nodeId > 0) {
|
|
2665
|
+
node.nodeId = described.node.nodeId;
|
|
2666
|
+
expandedNodeIds.add(described.node.nodeId);
|
|
2667
|
+
}
|
|
2668
|
+
describeCalls++;
|
|
2669
|
+
expanded = true;
|
|
2670
|
+
break;
|
|
2671
|
+
} catch (err) {
|
|
2672
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2673
|
+
if (isCborStackError(message)) {
|
|
2674
|
+
continue;
|
|
2675
|
+
}
|
|
2676
|
+
const identifier = (_a15 = nodeId != null ? nodeId : backendId) != null ? _a15 : "unknown";
|
|
2677
|
+
throw new StagehandDomProcessError(
|
|
2678
|
+
`Failed to expand DOM node ${identifier}: ${String(err)}`
|
|
2679
|
+
);
|
|
2680
|
+
}
|
|
2681
|
+
}
|
|
2682
|
+
if (!expanded) {
|
|
2683
|
+
const identifier = (_b = nodeId != null ? nodeId : backendId) != null ? _b : "unknown";
|
|
2684
|
+
throw new StagehandDomProcessError(
|
|
2685
|
+
`Unable to expand DOM node ${identifier} after describeNode depth retries`
|
|
2686
|
+
);
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2689
|
+
for (const child of collectDomTraversalTargets(node)) {
|
|
2690
|
+
stack.push(child);
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
return describeCalls;
|
|
2694
|
+
});
|
|
2695
|
+
}
|
|
2696
|
+
function getDomTreeWithFallback(session) {
|
|
2697
|
+
return __async(this, null, function* () {
|
|
2698
|
+
let lastCborMessage = "";
|
|
2699
|
+
for (const depth of DOM_DEPTH_ATTEMPTS) {
|
|
2700
|
+
try {
|
|
2701
|
+
const params = { depth, pierce: true };
|
|
2702
|
+
const { root } = yield session.send("DOM.getDocument", params);
|
|
2703
|
+
if (depth !== -1) {
|
|
2704
|
+
yield hydrateDomTree(session, root);
|
|
2705
|
+
}
|
|
2706
|
+
return root;
|
|
2707
|
+
} catch (err) {
|
|
2708
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2709
|
+
if (isCborStackError(message)) {
|
|
2710
|
+
lastCborMessage = message;
|
|
2711
|
+
continue;
|
|
2712
|
+
}
|
|
2713
|
+
throw err;
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
throw new StagehandDomProcessError(
|
|
2717
|
+
lastCborMessage ? `CDP DOM.getDocument failed after adaptive depth retries: ${lastCborMessage}` : "CDP DOM.getDocument failed after adaptive depth retries."
|
|
2718
|
+
);
|
|
2719
|
+
});
|
|
2720
|
+
}
|
|
2612
2721
|
function cleanText(input) {
|
|
2613
2722
|
let out = "";
|
|
2614
2723
|
let prevWasSpace = false;
|
|
@@ -2666,10 +2775,7 @@ function buildBackendIdMaps(experimental, sp, targetFrame) {
|
|
|
2666
2775
|
session === (yield sp.getCDPClient()) ? void 0 : targetFrame
|
|
2667
2776
|
);
|
|
2668
2777
|
try {
|
|
2669
|
-
const
|
|
2670
|
-
depth: -1,
|
|
2671
|
-
pierce: true
|
|
2672
|
-
});
|
|
2778
|
+
const root = yield getDomTreeWithFallback(session);
|
|
2673
2779
|
let startNode = root;
|
|
2674
2780
|
let rootFid = targetFrame && (yield getCDPFrameId(sp, targetFrame));
|
|
2675
2781
|
if (targetFrame && targetFrame !== sp.page.mainFrame() && session === (yield sp.getCDPClient())) {
|
|
@@ -3771,9 +3877,6 @@ var StagehandResponseParseError = class extends StagehandAPIError {
|
|
|
3771
3877
|
}
|
|
3772
3878
|
};
|
|
3773
3879
|
|
|
3774
|
-
// lib/dom/build/scriptContent.ts
|
|
3775
|
-
var scriptContent = '(() => {\n // lib/dom/elementCheckUtils.ts\n function isElementNode(node) {\n return node.nodeType === Node.ELEMENT_NODE;\n }\n function isTextNode(node) {\n return node.nodeType === Node.TEXT_NODE && Boolean(node.textContent?.trim());\n }\n\n // lib/dom/xpathUtils.ts\n function getParentElement(node) {\n return isElementNode(node) ? node.parentElement : node.parentNode;\n }\n function getCombinations(attributes, size) {\n const results = [];\n function helper(start, combo) {\n if (combo.length === size) {\n results.push([...combo]);\n return;\n }\n for (let i = start; i < attributes.length; i++) {\n combo.push(attributes[i]);\n helper(i + 1, combo);\n combo.pop();\n }\n }\n helper(0, []);\n return results;\n }\n function isXPathFirstResultElement(xpath, target) {\n try {\n const result = document.evaluate(\n xpath,\n document.documentElement,\n null,\n XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,\n null\n );\n return result.snapshotItem(0) === target;\n } catch (error) {\n console.warn(`Invalid XPath expression: ${xpath}`, error);\n return false;\n }\n }\n function escapeXPathString(value) {\n if (value.includes("\'")) {\n if (value.includes(\'"\')) {\n return "concat(" + value.split(/(\'+)/).map((part) => {\n if (part === "\'") {\n return `"\'"`;\n } else if (part.startsWith("\'") && part.endsWith("\'")) {\n return `"${part}"`;\n } else {\n return `\'${part}\'`;\n }\n }).join(",") + ")";\n } else {\n return `"${value}"`;\n }\n } else {\n return `\'${value}\'`;\n }\n }\n async function generateXPathsForElement(element) {\n if (!element) return [];\n const [complexXPath, standardXPath, idBasedXPath] = await Promise.all([\n generateComplexXPath(element),\n generateStandardXPath(element),\n generatedIdBasedXPath(element)\n ]);\n return [standardXPath, ...idBasedXPath ? [idBasedXPath] : [], complexXPath];\n }\n async function generateComplexXPath(element) {\n const parts = [];\n let currentElement = element;\n while (currentElement && (isTextNode(currentElement) || isElementNode(currentElement))) {\n if (isElementNode(currentElement)) {\n const el = currentElement;\n let selector = el.tagName.toLowerCase();\n const attributePriority = [\n "data-qa",\n "data-component",\n "data-role",\n "role",\n "aria-role",\n "type",\n "name",\n "aria-label",\n "placeholder",\n "title",\n "alt"\n ];\n const attributes = attributePriority.map((attr) => {\n let value = el.getAttribute(attr);\n if (attr === "href-full" && value) {\n value = el.getAttribute("href");\n }\n return value ? { attr: attr === "href-full" ? "href" : attr, value } : null;\n }).filter((attr) => attr !== null);\n let uniqueSelector = "";\n for (let i = 1; i <= attributes.length; i++) {\n const combinations = getCombinations(attributes, i);\n for (const combo of combinations) {\n const conditions = combo.map((a) => `@${a.attr}=${escapeXPathString(a.value)}`).join(" and ");\n const xpath2 = `//${selector}[${conditions}]`;\n if (isXPathFirstResultElement(xpath2, el)) {\n uniqueSelector = xpath2;\n break;\n }\n }\n if (uniqueSelector) break;\n }\n if (uniqueSelector) {\n parts.unshift(uniqueSelector.replace("//", ""));\n break;\n } else {\n const parent = getParentElement(el);\n if (parent) {\n const siblings = Array.from(parent.children).filter(\n (sibling) => sibling.tagName === el.tagName\n );\n const index = siblings.indexOf(el) + 1;\n selector += siblings.length > 1 ? `[${index}]` : "";\n }\n parts.unshift(selector);\n }\n }\n currentElement = getParentElement(currentElement);\n }\n const xpath = "//" + parts.join("/");\n return xpath;\n }\n async function generateStandardXPath(element) {\n const parts = [];\n while (element && (isTextNode(element) || isElementNode(element))) {\n let index = 0;\n let hasSameTypeSiblings = false;\n const siblings = element.parentElement ? Array.from(element.parentElement.childNodes) : [];\n for (let i = 0; i < siblings.length; i++) {\n const sibling = siblings[i];\n if (sibling.nodeType === element.nodeType && sibling.nodeName === element.nodeName) {\n index = index + 1;\n hasSameTypeSiblings = true;\n if (sibling.isSameNode(element)) {\n break;\n }\n }\n }\n if (element.nodeName !== "#text") {\n const tagName = element.nodeName.toLowerCase();\n const pathIndex = hasSameTypeSiblings ? `[${index}]` : "";\n parts.unshift(`${tagName}${pathIndex}`);\n }\n element = element.parentElement;\n }\n return parts.length ? `/${parts.join("/")}` : "";\n }\n async function generatedIdBasedXPath(element) {\n if (isElementNode(element) && element.id) {\n return `//*[@id=\'${element.id}\']`;\n }\n return null;\n }\n\n // types/stagehandErrors.ts\n var StagehandError = class extends Error {\n constructor(message) {\n super(message);\n this.name = this.constructor.name;\n }\n };\n var StagehandDomProcessError = class extends StagehandError {\n constructor(message) {\n super(`Error Processing Dom: ${message}`);\n }\n };\n\n // lib/dom/utils.ts\n function canElementScroll(elem) {\n if (typeof elem.scrollTo !== "function") {\n console.warn("canElementScroll: .scrollTo is not a function.");\n return false;\n }\n try {\n const originalTop = elem.scrollTop;\n elem.scrollTo({\n top: originalTop + 100,\n left: 0,\n behavior: "instant"\n });\n if (elem.scrollTop === originalTop) {\n throw new StagehandDomProcessError("scrollTop did not change");\n }\n elem.scrollTo({\n top: originalTop,\n left: 0,\n behavior: "instant"\n });\n return true;\n } catch (error) {\n console.warn("canElementScroll error:", error.message || error);\n return false;\n }\n }\n function getNodeFromXpath(xpath) {\n return document.evaluate(\n xpath,\n document.documentElement,\n null,\n XPathResult.FIRST_ORDERED_NODE_TYPE,\n null\n ).singleNodeValue;\n }\n function waitForElementScrollEnd(element, idleMs = 100) {\n return new Promise((resolve) => {\n let scrollEndTimer;\n const handleScroll = () => {\n clearTimeout(scrollEndTimer);\n scrollEndTimer = window.setTimeout(() => {\n element.removeEventListener("scroll", handleScroll);\n resolve();\n }, idleMs);\n };\n element.addEventListener("scroll", handleScroll, { passive: true });\n handleScroll();\n });\n }\n\n // lib/dom/process.ts\n function getScrollableElements(topN) {\n const docEl = document.documentElement;\n const scrollableElements = [docEl];\n const allElements = document.querySelectorAll("*");\n for (const elem of allElements) {\n const style = window.getComputedStyle(elem);\n const overflowY = style.overflowY;\n const isPotentiallyScrollable = overflowY === "auto" || overflowY === "scroll" || overflowY === "overlay";\n if (isPotentiallyScrollable) {\n const candidateScrollDiff = elem.scrollHeight - elem.clientHeight;\n if (candidateScrollDiff > 0 && canElementScroll(elem)) {\n scrollableElements.push(elem);\n }\n }\n }\n scrollableElements.sort((a, b) => b.scrollHeight - a.scrollHeight);\n if (topN !== void 0) {\n return scrollableElements.slice(0, topN);\n }\n return scrollableElements;\n }\n async function getScrollableElementXpaths(topN) {\n const scrollableElems = getScrollableElements(topN);\n const xpaths = [];\n for (const elem of scrollableElems) {\n const allXPaths = await generateXPathsForElement(elem);\n const firstXPath = allXPaths?.[0] || "";\n xpaths.push(firstXPath);\n }\n return xpaths;\n }\n (() => {\n const closedRoots = /* @__PURE__ */ new WeakMap();\n const nativeAttachShadow = Element.prototype.attachShadow;\n Element.prototype.attachShadow = function(init) {\n const root = nativeAttachShadow.call(this, init);\n if (init.mode === "closed") closedRoots.set(this, root);\n return root;\n };\n const backdoor = {\n getClosedRoot: (host) => closedRoots.get(host),\n queryClosed: (host, selector) => {\n const root = closedRoots.get(host);\n return root ? Array.from(root.querySelectorAll(selector)) : [];\n },\n xpathClosed: (host, xp) => {\n const root = closedRoots.get(host);\n if (!root) return [];\n const it = document.evaluate(\n xp,\n root,\n null,\n XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,\n null\n );\n const out = [];\n for (let i = 0; i < it.snapshotLength; ++i) {\n const n = it.snapshotItem(i);\n if (n) out.push(n);\n }\n return out;\n }\n };\n if (!("__stagehand__" in window)) {\n Object.defineProperty(window, "__stagehand__", {\n value: backdoor,\n enumerable: false,\n writable: false,\n configurable: false\n });\n }\n })();\n window.getScrollableElementXpaths = getScrollableElementXpaths;\n window.getNodeFromXpath = getNodeFromXpath;\n window.waitForElementScrollEnd = waitForElementScrollEnd;\n})();\n';
|
|
3776
|
-
|
|
3777
3880
|
// lib/StagehandPage.ts
|
|
3778
3881
|
function getCurrentRootFrameId(session) {
|
|
3779
3882
|
return __async(this, null, function* () {
|
|
@@ -3858,32 +3961,6 @@ var StagehandPage = class _StagehandPage {
|
|
|
3858
3961
|
resetFrameOrdinals() {
|
|
3859
3962
|
this.fidOrdinals = /* @__PURE__ */ new Map([[void 0, 0]]);
|
|
3860
3963
|
}
|
|
3861
|
-
ensureStagehandScript() {
|
|
3862
|
-
return __async(this, null, function* () {
|
|
3863
|
-
try {
|
|
3864
|
-
const injected = yield this.rawPage.evaluate(
|
|
3865
|
-
() => !!window.__stagehandInjected
|
|
3866
|
-
);
|
|
3867
|
-
if (injected) return;
|
|
3868
|
-
const guardedScript = `if (!window.__stagehandInjected) { window.__stagehandInjected = true; ${scriptContent} }`;
|
|
3869
|
-
yield this.rawPage.addInitScript({ content: guardedScript });
|
|
3870
|
-
yield this.rawPage.evaluate(guardedScript);
|
|
3871
|
-
} catch (err) {
|
|
3872
|
-
if (!this.stagehand.isClosed) {
|
|
3873
|
-
this.stagehand.log({
|
|
3874
|
-
category: "dom",
|
|
3875
|
-
message: "Failed to inject Stagehand helper script",
|
|
3876
|
-
level: 1,
|
|
3877
|
-
auxiliary: {
|
|
3878
|
-
error: { value: err.message, type: "string" },
|
|
3879
|
-
trace: { value: err.stack, type: "string" }
|
|
3880
|
-
}
|
|
3881
|
-
});
|
|
3882
|
-
throw err;
|
|
3883
|
-
}
|
|
3884
|
-
}
|
|
3885
|
-
});
|
|
3886
|
-
}
|
|
3887
3964
|
/** Register the custom selector engine that pierces open/closed shadow roots. */
|
|
3888
3965
|
ensureStagehandSelectorEngine() {
|
|
3889
3966
|
return __async(this, null, function* () {
|
|
@@ -4039,7 +4116,6 @@ var StagehandPage = class _StagehandPage {
|
|
|
4039
4116
|
const value = target[prop];
|
|
4040
4117
|
if (prop === "evaluate" || prop === "evaluateHandle" || prop === "$eval" || prop === "$$eval") {
|
|
4041
4118
|
return (...args) => __async(this, null, function* () {
|
|
4042
|
-
yield this.ensureStagehandScript();
|
|
4043
4119
|
return value.apply(
|
|
4044
4120
|
target,
|
|
4045
4121
|
args
|
|
@@ -4646,8 +4722,17 @@ var StagehandPage = class _StagehandPage {
|
|
|
4646
4722
|
}
|
|
4647
4723
|
};
|
|
4648
4724
|
|
|
4725
|
+
// lib/dom/build/scriptContent.ts
|
|
4726
|
+
var scriptContent = '(() => {\n // lib/dom/process.ts\n (() => {\n const closedRoots = /* @__PURE__ */ new WeakMap();\n const nativeAttachShadow = Element.prototype.attachShadow;\n Element.prototype.attachShadow = function(init) {\n const root = nativeAttachShadow.call(this, init);\n if (init.mode === "closed") closedRoots.set(this, root);\n return root;\n };\n const backdoor = {\n getClosedRoot: (host) => closedRoots.get(host),\n queryClosed: (host, selector) => {\n const root = closedRoots.get(host);\n return root ? Array.from(root.querySelectorAll(selector)) : [];\n },\n xpathClosed: (host, xp) => {\n const root = closedRoots.get(host);\n if (!root) return [];\n const it = document.evaluate(\n xp,\n root,\n null,\n XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,\n null\n );\n const out = [];\n for (let i = 0; i < it.snapshotLength; ++i) {\n const n = it.snapshotItem(i);\n if (n) out.push(n);\n }\n return out;\n }\n };\n if (!("__stagehand__" in window)) {\n Object.defineProperty(window, "__stagehand__", {\n value: backdoor,\n enumerable: false,\n writable: false,\n configurable: false\n });\n }\n })();\n})();\n';
|
|
4727
|
+
|
|
4649
4728
|
// lib/StagehandContext.ts
|
|
4650
|
-
var
|
|
4729
|
+
var stagehandInitScript = `
|
|
4730
|
+
if (!window.__stagehandInjected) {
|
|
4731
|
+
window.__stagehandInjected = true;
|
|
4732
|
+
${scriptContent}
|
|
4733
|
+
}
|
|
4734
|
+
`;
|
|
4735
|
+
var _StagehandContext = class _StagehandContext {
|
|
4651
4736
|
constructor(context, stagehand) {
|
|
4652
4737
|
this.activeStagehandPage = null;
|
|
4653
4738
|
this.frameIdMap = /* @__PURE__ */ new Map();
|
|
@@ -4706,6 +4791,10 @@ var StagehandContext = class _StagehandContext {
|
|
|
4706
4791
|
}
|
|
4707
4792
|
static init(context, stagehand) {
|
|
4708
4793
|
return __async(this, null, function* () {
|
|
4794
|
+
if (!_StagehandContext.contextsWithInitScript.has(context)) {
|
|
4795
|
+
yield context.addInitScript({ content: stagehandInitScript });
|
|
4796
|
+
_StagehandContext.contextsWithInitScript.add(context);
|
|
4797
|
+
}
|
|
4709
4798
|
const instance = new _StagehandContext(context, stagehand);
|
|
4710
4799
|
context.on("page", (pwPage) => __async(null, null, function* () {
|
|
4711
4800
|
yield instance.handleNewPlaywrightPage(pwPage);
|
|
@@ -4808,6 +4897,8 @@ var StagehandContext = class _StagehandContext {
|
|
|
4808
4897
|
});
|
|
4809
4898
|
}
|
|
4810
4899
|
};
|
|
4900
|
+
_StagehandContext.contextsWithInitScript = /* @__PURE__ */ new WeakSet();
|
|
4901
|
+
var StagehandContext = _StagehandContext;
|
|
4811
4902
|
|
|
4812
4903
|
// lib/api.ts
|
|
4813
4904
|
var import_zod_to_json_schema = __toESM(require("zod-to-json-schema"));
|
|
@@ -20273,9 +20364,7 @@ var modelToProviderMap = {
|
|
|
20273
20364
|
"gpt-4o-2024-08-06": "openai",
|
|
20274
20365
|
"gpt-4.5-preview": "openai",
|
|
20275
20366
|
"o1-preview": "openai",
|
|
20276
|
-
"claude-
|
|
20277
|
-
"claude-3-5-sonnet-20240620": "anthropic",
|
|
20278
|
-
"claude-3-5-sonnet-20241022": "anthropic",
|
|
20367
|
+
"claude-haiku-4-5": "anthropic",
|
|
20279
20368
|
"claude-3-7-sonnet-20250219": "anthropic",
|
|
20280
20369
|
"claude-3-7-sonnet-latest": "anthropic",
|
|
20281
20370
|
"cerebras-llama-3.3-70b": "cerebras",
|
|
@@ -24073,9 +24162,7 @@ var AvailableModelSchema = import_v315.z.enum([
|
|
|
24073
24162
|
"gpt-4o-2024-08-06",
|
|
24074
24163
|
"gpt-4.5-preview",
|
|
24075
24164
|
"o1-preview",
|
|
24076
|
-
"claude-
|
|
24077
|
-
"claude-3-5-sonnet-20241022",
|
|
24078
|
-
"claude-3-5-sonnet-20240620",
|
|
24165
|
+
"claude-haiku-4-5",
|
|
24079
24166
|
"claude-3-7-sonnet-latest",
|
|
24080
24167
|
"claude-3-7-sonnet-20250219",
|
|
24081
24168
|
"cerebras-llama-3.3-70b",
|
|
@@ -8,6 +8,7 @@ export declare class StagehandContext {
|
|
|
8
8
|
private pageMap;
|
|
9
9
|
private activeStagehandPage;
|
|
10
10
|
private readonly frameIdMap;
|
|
11
|
+
private static readonly contextsWithInitScript;
|
|
11
12
|
private constructor();
|
|
12
13
|
private createStagehandPage;
|
|
13
14
|
static init(context: PlaywrightContext, stagehand: Stagehand): Promise<StagehandContext>;
|
|
@@ -30,7 +30,6 @@ export declare class StagehandPage {
|
|
|
30
30
|
ordinalForFrameId(fid: string | undefined): number;
|
|
31
31
|
encodeWithFrameId(fid: string | undefined, backendId: number): EncodedId;
|
|
32
32
|
resetFrameOrdinals(): void;
|
|
33
|
-
private ensureStagehandScript;
|
|
34
33
|
/** Register the custom selector engine that pierces open/closed shadow roots. */
|
|
35
34
|
private ensureStagehandSelectorEngine;
|
|
36
35
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const scriptContent = "(() => {\n // lib/dom/elementCheckUtils.ts\n function isElementNode(node) {\n return node.nodeType === Node.ELEMENT_NODE;\n }\n function isTextNode(node) {\n return node.nodeType === Node.TEXT_NODE && Boolean(node.textContent?.trim());\n }\n\n // lib/dom/xpathUtils.ts\n function getParentElement(node) {\n return isElementNode(node) ? node.parentElement : node.parentNode;\n }\n function getCombinations(attributes, size) {\n const results = [];\n function helper(start, combo) {\n if (combo.length === size) {\n results.push([...combo]);\n return;\n }\n for (let i = start; i < attributes.length; i++) {\n combo.push(attributes[i]);\n helper(i + 1, combo);\n combo.pop();\n }\n }\n helper(0, []);\n return results;\n }\n function isXPathFirstResultElement(xpath, target) {\n try {\n const result = document.evaluate(\n xpath,\n document.documentElement,\n null,\n XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,\n null\n );\n return result.snapshotItem(0) === target;\n } catch (error) {\n console.warn(`Invalid XPath expression: ${xpath}`, error);\n return false;\n }\n }\n function escapeXPathString(value) {\n if (value.includes(\"'\")) {\n if (value.includes('\"')) {\n return \"concat(\" + value.split(/('+)/).map((part) => {\n if (part === \"'\") {\n return `\"'\"`;\n } else if (part.startsWith(\"'\") && part.endsWith(\"'\")) {\n return `\"${part}\"`;\n } else {\n return `'${part}'`;\n }\n }).join(\",\") + \")\";\n } else {\n return `\"${value}\"`;\n }\n } else {\n return `'${value}'`;\n }\n }\n async function generateXPathsForElement(element) {\n if (!element) return [];\n const [complexXPath, standardXPath, idBasedXPath] = await Promise.all([\n generateComplexXPath(element),\n generateStandardXPath(element),\n generatedIdBasedXPath(element)\n ]);\n return [standardXPath, ...idBasedXPath ? [idBasedXPath] : [], complexXPath];\n }\n async function generateComplexXPath(element) {\n const parts = [];\n let currentElement = element;\n while (currentElement && (isTextNode(currentElement) || isElementNode(currentElement))) {\n if (isElementNode(currentElement)) {\n const el = currentElement;\n let selector = el.tagName.toLowerCase();\n const attributePriority = [\n \"data-qa\",\n \"data-component\",\n \"data-role\",\n \"role\",\n \"aria-role\",\n \"type\",\n \"name\",\n \"aria-label\",\n \"placeholder\",\n \"title\",\n \"alt\"\n ];\n const attributes = attributePriority.map((attr) => {\n let value = el.getAttribute(attr);\n if (attr === \"href-full\" && value) {\n value = el.getAttribute(\"href\");\n }\n return value ? { attr: attr === \"href-full\" ? \"href\" : attr, value } : null;\n }).filter((attr) => attr !== null);\n let uniqueSelector = \"\";\n for (let i = 1; i <= attributes.length; i++) {\n const combinations = getCombinations(attributes, i);\n for (const combo of combinations) {\n const conditions = combo.map((a) => `@${a.attr}=${escapeXPathString(a.value)}`).join(\" and \");\n const xpath2 = `//${selector}[${conditions}]`;\n if (isXPathFirstResultElement(xpath2, el)) {\n uniqueSelector = xpath2;\n break;\n }\n }\n if (uniqueSelector) break;\n }\n if (uniqueSelector) {\n parts.unshift(uniqueSelector.replace(\"//\", \"\"));\n break;\n } else {\n const parent = getParentElement(el);\n if (parent) {\n const siblings = Array.from(parent.children).filter(\n (sibling) => sibling.tagName === el.tagName\n );\n const index = siblings.indexOf(el) + 1;\n selector += siblings.length > 1 ? `[${index}]` : \"\";\n }\n parts.unshift(selector);\n }\n }\n currentElement = getParentElement(currentElement);\n }\n const xpath = \"//\" + parts.join(\"/\");\n return xpath;\n }\n async function generateStandardXPath(element) {\n const parts = [];\n while (element && (isTextNode(element) || isElementNode(element))) {\n let index = 0;\n let hasSameTypeSiblings = false;\n const siblings = element.parentElement ? Array.from(element.parentElement.childNodes) : [];\n for (let i = 0; i < siblings.length; i++) {\n const sibling = siblings[i];\n if (sibling.nodeType === element.nodeType && sibling.nodeName === element.nodeName) {\n index = index + 1;\n hasSameTypeSiblings = true;\n if (sibling.isSameNode(element)) {\n break;\n }\n }\n }\n if (element.nodeName !== \"#text\") {\n const tagName = element.nodeName.toLowerCase();\n const pathIndex = hasSameTypeSiblings ? `[${index}]` : \"\";\n parts.unshift(`${tagName}${pathIndex}`);\n }\n element = element.parentElement;\n }\n return parts.length ? `/${parts.join(\"/\")}` : \"\";\n }\n async function generatedIdBasedXPath(element) {\n if (isElementNode(element) && element.id) {\n return `//*[@id='${element.id}']`;\n }\n return null;\n }\n\n // types/stagehandErrors.ts\n var StagehandError = class extends Error {\n constructor(message) {\n super(message);\n this.name = this.constructor.name;\n }\n };\n var StagehandDomProcessError = class extends StagehandError {\n constructor(message) {\n super(`Error Processing Dom: ${message}`);\n }\n };\n\n // lib/dom/utils.ts\n function canElementScroll(elem) {\n if (typeof elem.scrollTo !== \"function\") {\n console.warn(\"canElementScroll: .scrollTo is not a function.\");\n return false;\n }\n try {\n const originalTop = elem.scrollTop;\n elem.scrollTo({\n top: originalTop + 100,\n left: 0,\n behavior: \"instant\"\n });\n if (elem.scrollTop === originalTop) {\n throw new StagehandDomProcessError(\"scrollTop did not change\");\n }\n elem.scrollTo({\n top: originalTop,\n left: 0,\n behavior: \"instant\"\n });\n return true;\n } catch (error) {\n console.warn(\"canElementScroll error:\", error.message || error);\n return false;\n }\n }\n function getNodeFromXpath(xpath) {\n return document.evaluate(\n xpath,\n document.documentElement,\n null,\n XPathResult.FIRST_ORDERED_NODE_TYPE,\n null\n ).singleNodeValue;\n }\n function waitForElementScrollEnd(element, idleMs = 100) {\n return new Promise((resolve) => {\n let scrollEndTimer;\n const handleScroll = () => {\n clearTimeout(scrollEndTimer);\n scrollEndTimer = window.setTimeout(() => {\n element.removeEventListener(\"scroll\", handleScroll);\n resolve();\n }, idleMs);\n };\n element.addEventListener(\"scroll\", handleScroll, { passive: true });\n handleScroll();\n });\n }\n\n // lib/dom/process.ts\n function getScrollableElements(topN) {\n const docEl = document.documentElement;\n const scrollableElements = [docEl];\n const allElements = document.querySelectorAll(\"*\");\n for (const elem of allElements) {\n const style = window.getComputedStyle(elem);\n const overflowY = style.overflowY;\n const isPotentiallyScrollable = overflowY === \"auto\" || overflowY === \"scroll\" || overflowY === \"overlay\";\n if (isPotentiallyScrollable) {\n const candidateScrollDiff = elem.scrollHeight - elem.clientHeight;\n if (candidateScrollDiff > 0 && canElementScroll(elem)) {\n scrollableElements.push(elem);\n }\n }\n }\n scrollableElements.sort((a, b) => b.scrollHeight - a.scrollHeight);\n if (topN !== void 0) {\n return scrollableElements.slice(0, topN);\n }\n return scrollableElements;\n }\n async function getScrollableElementXpaths(topN) {\n const scrollableElems = getScrollableElements(topN);\n const xpaths = [];\n for (const elem of scrollableElems) {\n const allXPaths = await generateXPathsForElement(elem);\n const firstXPath = allXPaths?.[0] || \"\";\n xpaths.push(firstXPath);\n }\n return xpaths;\n }\n (() => {\n const closedRoots = /* @__PURE__ */ new WeakMap();\n const nativeAttachShadow = Element.prototype.attachShadow;\n Element.prototype.attachShadow = function(init) {\n const root = nativeAttachShadow.call(this, init);\n if (init.mode === \"closed\") closedRoots.set(this, root);\n return root;\n };\n const backdoor = {\n getClosedRoot: (host) => closedRoots.get(host),\n queryClosed: (host, selector) => {\n const root = closedRoots.get(host);\n return root ? Array.from(root.querySelectorAll(selector)) : [];\n },\n xpathClosed: (host, xp) => {\n const root = closedRoots.get(host);\n if (!root) return [];\n const it = document.evaluate(\n xp,\n root,\n null,\n XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,\n null\n );\n const out = [];\n for (let i = 0; i < it.snapshotLength; ++i) {\n const n = it.snapshotItem(i);\n if (n) out.push(n);\n }\n return out;\n }\n };\n if (!(\"__stagehand__\" in window)) {\n Object.defineProperty(window, \"__stagehand__\", {\n value: backdoor,\n enumerable: false,\n writable: false,\n configurable: false\n });\n }\n })();\n window.getScrollableElementXpaths = getScrollableElementXpaths;\n window.getNodeFromXpath = getNodeFromXpath;\n window.waitForElementScrollEnd = waitForElementScrollEnd;\n})();\n";
|
|
1
|
+
export declare const scriptContent = "(() => {\n // lib/dom/process.ts\n (() => {\n const closedRoots = /* @__PURE__ */ new WeakMap();\n const nativeAttachShadow = Element.prototype.attachShadow;\n Element.prototype.attachShadow = function(init) {\n const root = nativeAttachShadow.call(this, init);\n if (init.mode === \"closed\") closedRoots.set(this, root);\n return root;\n };\n const backdoor = {\n getClosedRoot: (host) => closedRoots.get(host),\n queryClosed: (host, selector) => {\n const root = closedRoots.get(host);\n return root ? Array.from(root.querySelectorAll(selector)) : [];\n },\n xpathClosed: (host, xp) => {\n const root = closedRoots.get(host);\n if (!root) return [];\n const it = document.evaluate(\n xp,\n root,\n null,\n XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,\n null\n );\n const out = [];\n for (let i = 0; i < it.snapshotLength; ++i) {\n const n = it.snapshotItem(i);\n if (n) out.push(n);\n }\n return out;\n }\n };\n if (!(\"__stagehand__\" in window)) {\n Object.defineProperty(window, \"__stagehand__\", {\n value: backdoor,\n enumerable: false,\n writable: false,\n configurable: false\n });\n }\n })();\n})();\n";
|
package/dist/lib/dom/index.d.ts
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export * from "./utils";
|
|
1
|
+
import "./process";
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Finds and returns a list of scrollable elements on the page,
|
|
3
|
-
* ordered from the element with the largest scrollHeight to the smallest.
|
|
4
|
-
*
|
|
5
|
-
* @param topN Optional maximum number of scrollable elements to return.
|
|
6
|
-
* If not provided, all found scrollable elements are returned.
|
|
7
|
-
* @returns An array of HTMLElements sorted by descending scrollHeight.
|
|
8
|
-
*/
|
|
9
|
-
export declare function getScrollableElements(topN?: number): HTMLElement[];
|
|
10
|
-
/**
|
|
11
|
-
* Calls getScrollableElements, then for each element calls generateXPaths,
|
|
12
|
-
* and returns the first XPath for each.
|
|
13
|
-
*
|
|
14
|
-
* @param topN (optional) integer limit on how many scrollable elements to process
|
|
15
|
-
* @returns string[] list of XPaths (1 for each scrollable element)
|
|
16
|
-
*/
|
|
17
|
-
export declare function getScrollableElementXpaths(topN?: number): Promise<string[]>;
|
package/dist/lib/version.d.ts
CHANGED
package/dist/types/context.d.ts
CHANGED
|
@@ -51,6 +51,7 @@ export interface TreeResult {
|
|
|
51
51
|
xpathMap: Record<EncodedId, string>;
|
|
52
52
|
}
|
|
53
53
|
export type DOMNode = {
|
|
54
|
+
nodeId?: number;
|
|
54
55
|
backendNodeId?: number;
|
|
55
56
|
nodeName?: string;
|
|
56
57
|
children?: DOMNode[];
|
|
@@ -59,6 +60,7 @@ export type DOMNode = {
|
|
|
59
60
|
nodeType: number;
|
|
60
61
|
frameId?: string;
|
|
61
62
|
isScrollable?: boolean;
|
|
63
|
+
childNodeCount?: number;
|
|
62
64
|
};
|
|
63
65
|
export type BackendIdMaps = {
|
|
64
66
|
tagNameMap: Record<number, string>;
|
package/dist/types/model.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ClientOptions as AnthropicClientOptions } from "@anthropic-ai/sdk";
|
|
2
2
|
import type { ClientOptions as OpenAIClientOptions } from "openai";
|
|
3
3
|
import { z } from "zod/v3";
|
|
4
|
-
export declare const AvailableModelSchema: z.ZodEnum<["gpt-4.1", "gpt-4.1-mini", "gpt-4.1-nano", "o4-mini", "o3", "o3-mini", "o1", "o1-mini", "gpt-4o", "gpt-4o-mini", "gpt-4o-2024-08-06", "gpt-4.5-preview", "o1-preview", "claude-
|
|
4
|
+
export declare const AvailableModelSchema: z.ZodEnum<["gpt-4.1", "gpt-4.1-mini", "gpt-4.1-nano", "o4-mini", "o3", "o3-mini", "o1", "o1-mini", "gpt-4o", "gpt-4o-mini", "gpt-4o-2024-08-06", "gpt-4.5-preview", "o1-preview", "claude-haiku-4-5", "claude-3-7-sonnet-latest", "claude-3-7-sonnet-20250219", "cerebras-llama-3.3-70b", "cerebras-llama-3.1-8b", "groq-llama-3.3-70b-versatile", "groq-llama-3.3-70b-specdec", "gemini-1.5-flash", "gemini-1.5-pro", "gemini-1.5-flash-8b", "gemini-2.0-flash-lite", "gemini-2.0-flash", "gemini-2.5-flash-preview-04-17", "gemini-2.5-pro-preview-03-25"]>;
|
|
5
5
|
export type AvailableModel = z.infer<typeof AvailableModelSchema> | string;
|
|
6
6
|
export type ModelProvider = "openai" | "anthropic" | "cerebras" | "groq" | "google" | "aisdk";
|
|
7
7
|
export type ClientOptions = OpenAIClientOptions | AnthropicClientOptions;
|
package/package.json
CHANGED
package/dist/lib/dom/utils.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests if the element actually responds to .scrollTo(...)
|
|
3
|
-
* and that scrollTop changes as expected.
|
|
4
|
-
*/
|
|
5
|
-
export declare function canElementScroll(elem: HTMLElement): boolean;
|
|
6
|
-
export declare function getNodeFromXpath(xpath: string): Node;
|
|
7
|
-
export declare function waitForElementScrollEnd(element: HTMLElement, idleMs?: number): Promise<void>;
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Escapes a string for use in an XPath expression.
|
|
3
|
-
* Handles special characters, including single and double quotes.
|
|
4
|
-
* @param value - The string to escape.
|
|
5
|
-
* @returns The escaped string safe for XPath.
|
|
6
|
-
*/
|
|
7
|
-
export declare function escapeXPathString(value: string): string;
|
|
8
|
-
/**
|
|
9
|
-
* Generates both a complicated XPath and a standard XPath for a given DOM element.
|
|
10
|
-
* @param element - The target DOM element.
|
|
11
|
-
* @param documentOverride - Optional document override.
|
|
12
|
-
* @returns An object containing both XPaths.
|
|
13
|
-
*/
|
|
14
|
-
export declare function generateXPathsForElement(element: ChildNode): Promise<string[]>;
|