@browserbasehq/stagehand
Advanced tools
Comparing version 1.7.0 to 1.7.1-alpha-6c831de6640117820532bbd55689b378ff92f9f3
@@ -0,1 +1,7 @@ | ||
/** | ||
* This file is meant to be used as a scratchpad for developing new evals. | ||
* To create a Stagehand project with best practices and configuration, run: | ||
* | ||
* npx create-browser-app@latest my-browser-app | ||
*/ | ||
export {}; |
@@ -198,2 +198,3 @@ import { Page, BrowserContext, Browser } from '@playwright/test'; | ||
interface BrowserResult { | ||
env: "LOCAL" | "BROWSERBASE"; | ||
browser?: Browser; | ||
@@ -222,14 +223,14 @@ context: BrowserContext; | ||
private llmClient; | ||
page: Page; | ||
context: BrowserContext; | ||
private stagehandPage; | ||
private stagehandContext; | ||
browserbaseSessionID?: string; | ||
private env; | ||
private intEnv; | ||
readonly domSettleTimeoutMs: number; | ||
readonly debugDom: boolean; | ||
readonly headless: boolean; | ||
private logger; | ||
private apiKey; | ||
private projectId; | ||
private verbose; | ||
private debugDom; | ||
private headless; | ||
private logger; | ||
private externalLogger?; | ||
private domSettleTimeoutMs; | ||
private browserbaseSessionCreateParams?; | ||
@@ -243,2 +244,5 @@ private enableCaching; | ||
constructor({ env, apiKey, projectId, verbose, debugDom, llmProvider, headless, logger, browserbaseSessionCreateParams, domSettleTimeoutMs, enableCaching, browserbaseSessionID, modelName, modelClientOptions, }?: ConstructorParams); | ||
get page(): Page; | ||
get env(): "LOCAL" | "BROWSERBASE"; | ||
get context(): BrowserContext; | ||
init( | ||
@@ -254,5 +258,2 @@ /** @deprecated Use constructor options instead */ | ||
private _log_to_browserbase; | ||
private _waitForSettledDom; | ||
private startDomDebug; | ||
private cleanupDomDebug; | ||
act({ action, modelName, modelClientOptions, useVision, variables, domSettleTimeoutMs, }: ActOptions): Promise<ActResult>; | ||
@@ -259,0 +260,0 @@ extract<T extends z.AnyZodObject>({ instruction, schema, modelName, modelClientOptions, domSettleTimeoutMs, useTextExtract, }: ExtractOptions<T>): Promise<ExtractResult<T>>; |
@@ -5,4 +5,6 @@ import { LogLine } from "../../types/log"; | ||
import { LLMProvider } from "../llm/LLMProvider"; | ||
import { StagehandPage } from "../StagehandPage"; | ||
export declare class StagehandActHandler { | ||
private readonly stagehand; | ||
private readonly stagehandPage; | ||
private readonly verbose; | ||
@@ -12,8 +14,5 @@ private readonly llmProvider; | ||
private readonly logger; | ||
private readonly waitForSettledDom; | ||
private readonly actionCache; | ||
private readonly startDomDebug; | ||
private readonly cleanupDomDebug; | ||
private actions; | ||
constructor({ stagehand, verbose, llmProvider, enableCaching, logger, waitForSettledDom, startDomDebug, cleanupDomDebug, }: { | ||
private readonly actions; | ||
constructor({ stagehand, verbose, llmProvider, enableCaching, logger, stagehandPage, }: { | ||
stagehand: Stagehand; | ||
@@ -24,6 +23,4 @@ verbose: 0 | 1 | 2; | ||
logger: (logLine: LogLine) => void; | ||
waitForSettledDom: (domSettleTimeoutMs?: number) => Promise<void>; | ||
llmClient: LLMClient; | ||
startDomDebug: () => Promise<void>; | ||
cleanupDomDebug: () => Promise<void>; | ||
stagehandPage: StagehandPage; | ||
}); | ||
@@ -30,0 +27,0 @@ private _recordAction; |
@@ -1,5 +0,5 @@ | ||
import { LLMProvider } from "../llm/LLMProvider"; | ||
import { Stagehand } from "../index"; | ||
import { z } from "zod"; | ||
import { LLMClient } from "../llm/LLMClient"; | ||
import { StagehandPage } from "../StagehandPage"; | ||
import { Stagehand } from "../index"; | ||
/** | ||
@@ -76,10 +76,5 @@ * The `StagehandExtractHandler` class is responsible for extracting structured data from a webpage. | ||
private readonly stagehand; | ||
private readonly stagehandPage; | ||
private readonly logger; | ||
private readonly waitForSettledDom; | ||
private readonly startDomDebug; | ||
private readonly cleanupDomDebug; | ||
private readonly llmProvider; | ||
private readonly llmClient; | ||
private readonly verbose; | ||
constructor({ stagehand, logger, waitForSettledDom, startDomDebug, cleanupDomDebug, llmProvider, llmClient, verbose, }: { | ||
constructor({ stagehand, logger, stagehandPage, }: { | ||
stagehand: Stagehand; | ||
@@ -97,8 +92,3 @@ logger: (message: { | ||
}) => void; | ||
waitForSettledDom: (domSettleTimeoutMs?: number) => Promise<void>; | ||
startDomDebug: () => Promise<void>; | ||
cleanupDomDebug: () => Promise<void>; | ||
llmProvider: LLMProvider; | ||
llmClient: LLMClient; | ||
verbose: 0 | 1 | 2; | ||
stagehandPage: StagehandPage; | ||
}); | ||
@@ -105,0 +95,0 @@ extract<T extends z.AnyZodObject>({ instruction, schema, content, chunksSeen, llmClient, requestId, domSettleTimeoutMs, useTextExtract, }: { |
import { LogLine } from "../../types/log"; | ||
import { Stagehand } from "../index"; | ||
import { LLMClient } from "../llm/LLMClient"; | ||
import { LLMProvider } from "../llm/LLMProvider"; | ||
import { StagehandPage } from "../StagehandPage"; | ||
export declare class StagehandObserveHandler { | ||
private readonly stagehand; | ||
private readonly logger; | ||
private readonly waitForSettledDom; | ||
private readonly startDomDebug; | ||
private readonly cleanupDomDebug; | ||
private readonly llmProvider; | ||
private readonly stagehandPage; | ||
private readonly verbose; | ||
private readonly llmClient; | ||
private observations; | ||
constructor({ stagehand, logger, waitForSettledDom, startDomDebug, cleanupDomDebug, llmProvider, verbose, llmClient, }: { | ||
constructor({ stagehand, logger, stagehandPage, }: { | ||
stagehand: Stagehand; | ||
logger: (logLine: LogLine) => void; | ||
waitForSettledDom: (domSettleTimeoutMs?: number) => Promise<void>; | ||
startDomDebug: () => Promise<void>; | ||
cleanupDomDebug: () => Promise<void>; | ||
llmProvider: LLMProvider; | ||
verbose: 0 | 1 | 2; | ||
llmClient: LLMClient; | ||
stagehandPage: StagehandPage; | ||
}); | ||
@@ -25,0 +16,0 @@ private _recordObservation; |
@@ -1,2 +0,2 @@ | ||
import { type BrowserContext, type Page } from "@playwright/test"; | ||
import { type BrowserContext, Page } from "@playwright/test"; | ||
import { z } from "zod"; | ||
@@ -8,14 +8,14 @@ import { LogLine } from "../types/log"; | ||
private llmClient; | ||
page: Page; | ||
context: BrowserContext; | ||
private stagehandPage; | ||
private stagehandContext; | ||
browserbaseSessionID?: string; | ||
private env; | ||
private intEnv; | ||
readonly domSettleTimeoutMs: number; | ||
readonly debugDom: boolean; | ||
readonly headless: boolean; | ||
private logger; | ||
private apiKey; | ||
private projectId; | ||
private verbose; | ||
private debugDom; | ||
private headless; | ||
private logger; | ||
private externalLogger?; | ||
private domSettleTimeoutMs; | ||
private browserbaseSessionCreateParams?; | ||
@@ -29,2 +29,5 @@ private enableCaching; | ||
constructor({ env, apiKey, projectId, verbose, debugDom, llmProvider, headless, logger, browserbaseSessionCreateParams, domSettleTimeoutMs, enableCaching, browserbaseSessionID, modelName, modelClientOptions, }?: ConstructorParams); | ||
get page(): Page; | ||
get env(): "LOCAL" | "BROWSERBASE"; | ||
get context(): BrowserContext; | ||
init( | ||
@@ -40,5 +43,2 @@ /** @deprecated Use constructor options instead */ | ||
private _log_to_browserbase; | ||
private _waitForSettledDom; | ||
private startDomDebug; | ||
private cleanupDomDebug; | ||
act({ action, modelName, modelClientOptions, useVision, variables, domSettleTimeoutMs, }: ActOptions): Promise<ActResult>; | ||
@@ -45,0 +45,0 @@ extract<T extends z.AnyZodObject>({ instruction, schema, modelName, modelClientOptions, domSettleTimeoutMs, useTextExtract, }: ExtractOptions<T>): Promise<ExtractResult<T>>; |
import { Browser, BrowserContext } from "@playwright/test"; | ||
export interface BrowserResult { | ||
env: "LOCAL" | "BROWSERBASE"; | ||
browser?: Browser; | ||
@@ -4,0 +5,0 @@ context: BrowserContext; |
@@ -14,5 +14,6 @@ import { Locator, Page } from "@playwright/test"; | ||
import { ScreenshotService } from "../vision"; | ||
import { StagehandPage } from "../StagehandPage"; | ||
export class StagehandActHandler { | ||
private readonly stagehand: Stagehand; | ||
private readonly stagehandPage: StagehandPage; | ||
private readonly verbose: 0 | 1 | 2; | ||
@@ -22,9 +23,6 @@ private readonly llmProvider: LLMProvider; | ||
private readonly logger: (logLine: LogLine) => void; | ||
private readonly waitForSettledDom: ( | ||
domSettleTimeoutMs?: number, | ||
) => Promise<void>; | ||
private readonly actionCache: ActionCache | undefined; | ||
private readonly startDomDebug: () => Promise<void>; | ||
private readonly cleanupDomDebug: () => Promise<void>; | ||
private actions: { [key: string]: { result: string; action: string } }; | ||
private readonly actions: { | ||
[key: string]: { result: string; action: string }; | ||
}; | ||
@@ -37,5 +35,3 @@ constructor({ | ||
logger, | ||
waitForSettledDom, | ||
startDomDebug, | ||
cleanupDomDebug, | ||
stagehandPage, | ||
}: { | ||
@@ -47,6 +43,4 @@ stagehand: Stagehand; | ||
logger: (logLine: LogLine) => void; | ||
waitForSettledDom: (domSettleTimeoutMs?: number) => Promise<void>; | ||
llmClient: LLMClient; | ||
startDomDebug: () => Promise<void>; | ||
cleanupDomDebug: () => Promise<void>; | ||
stagehandPage: StagehandPage; | ||
}) { | ||
@@ -58,7 +52,5 @@ this.stagehand = stagehand; | ||
this.logger = logger; | ||
this.waitForSettledDom = waitForSettledDom; | ||
this.actionCache = enableCaching ? new ActionCache(this.logger) : undefined; | ||
this.startDomDebug = startDomDebug; | ||
this.cleanupDomDebug = cleanupDomDebug; | ||
this.actions = {}; | ||
this.stagehandPage = stagehandPage; | ||
} | ||
@@ -91,3 +83,3 @@ | ||
}): Promise<boolean> { | ||
await this.waitForSettledDom(domSettleTimeoutMs); | ||
await this.stagehandPage._waitForSettledDom(domSettleTimeoutMs); | ||
@@ -107,3 +99,3 @@ // o1 is overkill for this task + this task uses a lot of tokens. So we switch it 4o | ||
const { selectorMap } = await this.stagehand.page.evaluate(() => { | ||
const { selectorMap } = await this.stagehandPage.page.evaluate(() => { | ||
return window.processAllOfDom(); | ||
@@ -451,11 +443,9 @@ }); | ||
await newOpenedTab.close(); | ||
await this.stagehand.page.goto(newOpenedTab.url()); | ||
await this.stagehand.page.waitForLoadState("domcontentloaded"); | ||
await this.waitForSettledDom(domSettleTimeoutMs); | ||
await this.stagehandPage.page.goto(newOpenedTab.url()); | ||
await this.stagehandPage.page.waitForLoadState("domcontentloaded"); | ||
await this.stagehandPage._waitForSettledDom(domSettleTimeoutMs); | ||
} | ||
// Wait for the network to be idle with timeout of 5s (will only wait if loading a new page) | ||
// await this.waitForSettledDom(domSettleTimeoutMs); | ||
await Promise.race([ | ||
this.stagehand.page.waitForLoadState("networkidle"), | ||
this.stagehandPage.page.waitForLoadState("networkidle"), | ||
new Promise((resolve) => setTimeout(resolve, 5_000)), | ||
@@ -486,3 +476,3 @@ ]).catch((e) => { | ||
if (this.stagehand.page.url() !== initialUrl) { | ||
if (this.stagehandPage.page.url() !== initialUrl) { | ||
this.logger({ | ||
@@ -494,3 +484,3 @@ category: "action", | ||
url: { | ||
value: this.stagehand.page.url(), | ||
value: this.stagehandPage.page.url(), | ||
type: "string", | ||
@@ -520,3 +510,3 @@ }, | ||
await this.waitForSettledDom(domSettleTimeoutMs); | ||
await this.stagehandPage._waitForSettledDom(domSettleTimeoutMs); | ||
} | ||
@@ -1002,4 +992,4 @@ | ||
try { | ||
await this.waitForSettledDom(domSettleTimeoutMs); | ||
await this.startDomDebug(); | ||
await this.stagehandPage._waitForSettledDom(domSettleTimeoutMs); | ||
await this.stagehandPage.startDomDebug(); | ||
@@ -1168,3 +1158,3 @@ if (this.enableCaching && !skipActionCacheForThisStep) { | ||
await this.cleanupDomDebug(); | ||
await this.stagehandPage.cleanupDomDebug(); | ||
@@ -1171,0 +1161,0 @@ if (!response) { |
@@ -1,3 +0,1 @@ | ||
import { LLMProvider } from "../llm/LLMProvider"; | ||
import { Stagehand } from "../index"; | ||
import { z } from "zod"; | ||
@@ -9,2 +7,4 @@ import { LogLine } from "../../types/log"; | ||
import { formatText } from "../utils"; | ||
import { StagehandPage } from "../StagehandPage"; | ||
import { Stagehand } from "../index"; | ||
@@ -85,12 +85,4 @@ const PROXIMITY_THRESHOLD = 15; | ||
private readonly stagehand: Stagehand; | ||
private readonly stagehandPage: StagehandPage; | ||
private readonly logger: (logLine: LogLine) => void; | ||
private readonly waitForSettledDom: ( | ||
domSettleTimeoutMs?: number, | ||
) => Promise<void>; | ||
private readonly startDomDebug: () => Promise<void>; | ||
private readonly cleanupDomDebug: () => Promise<void>; | ||
private readonly llmProvider: LLMProvider; | ||
private readonly llmClient: LLMClient; | ||
private readonly verbose: 0 | 1 | 2; | ||
@@ -100,8 +92,3 @@ constructor({ | ||
logger, | ||
waitForSettledDom, | ||
startDomDebug, | ||
cleanupDomDebug, | ||
llmProvider, | ||
llmClient, | ||
verbose, | ||
stagehandPage, | ||
}: { | ||
@@ -115,17 +102,7 @@ stagehand: Stagehand; | ||
}) => void; | ||
waitForSettledDom: (domSettleTimeoutMs?: number) => Promise<void>; | ||
startDomDebug: () => Promise<void>; | ||
cleanupDomDebug: () => Promise<void>; | ||
llmProvider: LLMProvider; | ||
llmClient: LLMClient; | ||
verbose: 0 | 1 | 2; | ||
stagehandPage: StagehandPage; | ||
}) { | ||
this.stagehand = stagehand; | ||
this.logger = logger; | ||
this.waitForSettledDom = waitForSettledDom; | ||
this.startDomDebug = startDomDebug; | ||
this.cleanupDomDebug = cleanupDomDebug; | ||
this.llmProvider = llmProvider; | ||
this.llmClient = llmClient; | ||
this.verbose = verbose; | ||
this.stagehandPage = stagehandPage; | ||
} | ||
@@ -202,4 +179,4 @@ | ||
// **1:** Wait for the DOM to settle and start DOM debugging | ||
await this.waitForSettledDom(domSettleTimeoutMs); | ||
await this.startDomDebug(); | ||
await this.stagehandPage._waitForSettledDom(domSettleTimeoutMs); | ||
await this.stagehandPage.startDomDebug(); | ||
@@ -209,3 +186,3 @@ // **2:** Store the original DOM before any mutations | ||
// will mutate the DOM by adding spans around every word | ||
const originalDOM = await this.stagehand.page.evaluate(() => | ||
const originalDOM = await this.stagehandPage.page.evaluate(() => | ||
window.storeDOM(), | ||
@@ -253,3 +230,3 @@ ); | ||
height: number; | ||
}> = await this.stagehand.page.evaluate( | ||
}> = await this.stagehandPage.page.evaluate( | ||
(xpath) => window.getElementBoundingBoxes(xpath), | ||
@@ -321,3 +298,3 @@ xpath, | ||
// **7:** Restore the original DOM after mutations | ||
await this.stagehand.page.evaluate( | ||
await this.stagehandPage.page.evaluate( | ||
(dom) => window.restoreDOM(dom), | ||
@@ -346,3 +323,3 @@ originalDOM, | ||
} = extractionResponse; | ||
await this.cleanupDomDebug(); | ||
await this.stagehandPage.cleanupDomDebug(); | ||
@@ -420,4 +397,4 @@ // **10:** Handle the extraction response and log the results | ||
// This ensures the page is stable before extracting any data. | ||
await this.waitForSettledDom(domSettleTimeoutMs); | ||
await this.startDomDebug(); | ||
await this.stagehandPage._waitForSettledDom(domSettleTimeoutMs); | ||
await this.stagehandPage.startDomDebug(); | ||
@@ -477,3 +454,3 @@ // **2:** Call processDom() to handle chunk-based extraction | ||
await this.cleanupDomDebug(); | ||
await this.stagehandPage.cleanupDomDebug(); | ||
@@ -521,3 +498,3 @@ this.logger({ | ||
}); | ||
await this.waitForSettledDom(domSettleTimeoutMs); | ||
await this.stagehandPage._waitForSettledDom(domSettleTimeoutMs); | ||
@@ -524,0 +501,0 @@ // Recursively continue with the next chunk |
@@ -5,5 +5,5 @@ import { LogLine } from "../../types/log"; | ||
import { LLMClient } from "../llm/LLMClient"; | ||
import { LLMProvider } from "../llm/LLMProvider"; | ||
import { generateId } from "../utils"; | ||
import { ScreenshotService } from "../vision"; | ||
import { StagehandPage } from "../StagehandPage"; | ||
@@ -13,10 +13,4 @@ export class StagehandObserveHandler { | ||
private readonly logger: (logLine: LogLine) => void; | ||
private readonly waitForSettledDom: ( | ||
domSettleTimeoutMs?: number, | ||
) => Promise<void>; | ||
private readonly startDomDebug: () => Promise<void>; | ||
private readonly cleanupDomDebug: () => Promise<void>; | ||
private readonly llmProvider: LLMProvider; | ||
private readonly stagehandPage: StagehandPage; | ||
private readonly verbose: 0 | 1 | 2; | ||
private readonly llmClient: LLMClient; | ||
private observations: { | ||
@@ -32,26 +26,11 @@ [key: string]: { | ||
logger, | ||
waitForSettledDom, | ||
startDomDebug, | ||
cleanupDomDebug, | ||
llmProvider, | ||
verbose, | ||
llmClient, | ||
stagehandPage, | ||
}: { | ||
stagehand: Stagehand; | ||
logger: (logLine: LogLine) => void; | ||
waitForSettledDom: (domSettleTimeoutMs?: number) => Promise<void>; | ||
startDomDebug: () => Promise<void>; | ||
cleanupDomDebug: () => Promise<void>; | ||
llmProvider: LLMProvider; | ||
verbose: 0 | 1 | 2; | ||
llmClient: LLMClient; | ||
stagehandPage: StagehandPage; | ||
}) { | ||
this.stagehand = stagehand; | ||
this.logger = logger; | ||
this.waitForSettledDom = waitForSettledDom; | ||
this.startDomDebug = startDomDebug; | ||
this.cleanupDomDebug = cleanupDomDebug; | ||
this.llmProvider = llmProvider; | ||
this.verbose = verbose; | ||
this.llmClient = llmClient; | ||
this.stagehandPage = stagehandPage; | ||
this.observations = {}; | ||
@@ -101,4 +80,4 @@ } | ||
await this.waitForSettledDom(domSettleTimeoutMs); | ||
await this.startDomDebug(); | ||
await this.stagehandPage._waitForSettledDom(domSettleTimeoutMs); | ||
await this.stagehandPage.startDomDebug(); | ||
const evalResult = await this.stagehand.page.evaluate( | ||
@@ -161,3 +140,3 @@ (fullPage: boolean) => | ||
await this.cleanupDomDebug(); | ||
await this.stagehandPage.cleanupDomDebug(); | ||
@@ -164,0 +143,0 @@ this.logger({ |
201
lib/index.ts
import { Browserbase } from "@browserbasehq/sdk"; | ||
import { type BrowserContext, chromium, type Page } from "@playwright/test"; | ||
import { type BrowserContext, chromium, Page } from "@playwright/test"; | ||
import { randomUUID } from "crypto"; | ||
@@ -32,2 +32,4 @@ import dotenv from "dotenv"; | ||
import { logLineToString } from "./utils"; | ||
import { StagehandPage } from "./StagehandPage"; | ||
import { StagehandContext } from "./StagehandContext"; | ||
@@ -61,3 +63,3 @@ dotenv.config({ path: ".env" }); | ||
}); | ||
env = "LOCAL"; | ||
this.env = "LOCAL"; | ||
} | ||
@@ -199,3 +201,3 @@ if (!projectId) { | ||
return { browser, context, debugUrl, sessionUrl, sessionId }; | ||
return { browser, context, debugUrl, sessionUrl, sessionId, env }; | ||
} else { | ||
@@ -266,3 +268,3 @@ logger({ | ||
return { context, contextPath: tmpDir }; | ||
return { context, contextPath: tmpDir, env: "LOCAL" }; | ||
} | ||
@@ -311,15 +313,15 @@ } | ||
private llmClient: LLMClient; | ||
public page: Page; | ||
public context: BrowserContext; | ||
private stagehandPage!: StagehandPage; | ||
private stagehandContext!: StagehandContext; | ||
public browserbaseSessionID?: string; | ||
private env: "LOCAL" | "BROWSERBASE"; | ||
private intEnv: "LOCAL" | "BROWSERBASE"; | ||
public readonly domSettleTimeoutMs: number; | ||
public readonly debugDom: boolean; | ||
public readonly headless: boolean; | ||
private logger: (logLine: LogLine) => void; | ||
private apiKey: string | undefined; | ||
private projectId: string | undefined; | ||
private verbose: 0 | 1 | 2; | ||
private debugDom: boolean; | ||
private headless: boolean; | ||
private logger: (logLine: LogLine) => void; | ||
private externalLogger?: (logLine: LogLine) => void; | ||
private domSettleTimeoutMs: number; | ||
private browserbaseSessionCreateParams?: Browserbase.Sessions.SessionCreateParams; | ||
@@ -361,3 +363,3 @@ private enableCaching: boolean; | ||
llmProvider || new LLMProvider(this.logger, this.enableCaching); | ||
this.env = env; | ||
this.intEnv = env; | ||
this.apiKey = apiKey ?? process.env.BROWSERBASE_API_KEY; | ||
@@ -377,2 +379,21 @@ this.projectId = projectId ?? process.env.BROWSERBASE_PROJECT_ID; | ||
public get page(): Page { | ||
// End users should not be able to access the StagehandPage directly | ||
// This is a proxy to the underlying Playwright Page | ||
if (!this.stagehandPage) { | ||
throw new Error( | ||
"Stagehand not initialized. Make sure to await stagehand.init() first.", | ||
); | ||
} | ||
return this.stagehandPage.page; | ||
} | ||
public get env(): "LOCAL" | "BROWSERBASE" { | ||
return this.intEnv; | ||
} | ||
public get context(): BrowserContext { | ||
return this.stagehandContext.context; | ||
} | ||
async init( | ||
@@ -387,3 +408,3 @@ /** @deprecated Use constructor options instead */ | ||
} | ||
const { context, debugUrl, sessionUrl, contextPath, sessionId } = | ||
const { context, debugUrl, sessionUrl, contextPath, sessionId, env } = | ||
await getBrowser( | ||
@@ -404,24 +425,15 @@ this.apiKey, | ||
sessionId: undefined, | ||
env: this.env, | ||
}; | ||
return br; | ||
}); | ||
this.intEnv = env; | ||
this.contextPath = contextPath; | ||
this.context = context; | ||
this.page = context.pages()[0]; | ||
// Redundant but needed for users who are re-connecting to a previously-created session | ||
await this.page.waitForLoadState("domcontentloaded"); | ||
await this._waitForSettledDom(); | ||
this.stagehandContext = await StagehandContext.init(context, this); | ||
const defaultPage = this.context.pages()[0]; | ||
this.stagehandPage = await new StagehandPage(defaultPage, this).init( | ||
defaultPage, | ||
this, | ||
); | ||
// Overload the page.goto method | ||
const originalGoto = this.page.goto.bind(this.page); | ||
this.page.goto = async (url: string, options: GotoOptions) => { | ||
const result = await originalGoto(url, options); | ||
if (this.debugDom) { | ||
await this.page.evaluate(() => (window.showChunks = this.debugDom)); | ||
} | ||
await this.page.waitForLoadState("domcontentloaded"); | ||
await this._waitForSettledDom(); | ||
return result; | ||
}; | ||
// Set the browser to headless mode if specified | ||
@@ -442,5 +454,3 @@ if (this.headless) { | ||
logger: this.logger, | ||
waitForSettledDom: this._waitForSettledDom.bind(this), | ||
startDomDebug: this.startDomDebug.bind(this), | ||
cleanupDomDebug: this.cleanupDomDebug.bind(this), | ||
stagehandPage: this.stagehandPage, | ||
llmClient: this.llmClient, | ||
@@ -452,8 +462,3 @@ }); | ||
logger: this.logger, | ||
waitForSettledDom: this._waitForSettledDom.bind(this), | ||
startDomDebug: this.startDomDebug.bind(this), | ||
cleanupDomDebug: this.cleanupDomDebug.bind(this), | ||
llmProvider: this.llmProvider, | ||
verbose: this.verbose, | ||
llmClient: this.llmClient, | ||
stagehandPage: this.stagehandPage, | ||
}); | ||
@@ -464,9 +469,5 @@ | ||
logger: this.logger, | ||
waitForSettledDom: this._waitForSettledDom.bind(this), | ||
startDomDebug: this.startDomDebug.bind(this), | ||
cleanupDomDebug: this.cleanupDomDebug.bind(this), | ||
llmProvider: this.llmProvider, | ||
verbose: this.verbose, | ||
llmClient: this.llmClient, | ||
stagehandPage: this.stagehandPage, | ||
}); | ||
this.browserbaseSessionID = sessionId; | ||
@@ -484,4 +485,4 @@ | ||
); | ||
this.page = page; | ||
this.context = page.context(); | ||
this.stagehandPage = await new StagehandPage(page, this).init(page, this); | ||
this.stagehandContext = await StagehandContext.init(page.context(), this); | ||
@@ -495,3 +496,3 @@ const originalGoto = this.page.goto.bind(this.page); | ||
await this.page.waitForLoadState("domcontentloaded"); | ||
await this._waitForSettledDom(); | ||
await this.stagehandPage._waitForSettledDom(); | ||
return result; | ||
@@ -551,3 +552,3 @@ }; | ||
if (!this.page) { | ||
if (!this.stagehandPage) { | ||
return; | ||
@@ -598,104 +599,2 @@ } | ||
private async _waitForSettledDom(timeoutMs?: number) { | ||
try { | ||
const timeout = timeoutMs ?? this.domSettleTimeoutMs; | ||
let timeoutHandle: NodeJS.Timeout; | ||
const timeoutPromise = new Promise<void>((resolve) => { | ||
timeoutHandle = setTimeout(() => { | ||
this.log({ | ||
category: "dom", | ||
message: "DOM settle timeout exceeded, continuing anyway", | ||
level: 1, | ||
auxiliary: { | ||
timeout_ms: { | ||
value: timeout.toString(), | ||
type: "integer", | ||
}, | ||
}, | ||
}); | ||
resolve(); | ||
}, timeout); | ||
}); | ||
try { | ||
await Promise.race([ | ||
this.page.evaluate(() => { | ||
return new Promise<void>((resolve) => { | ||
if (typeof window.waitForDomSettle === "function") { | ||
window.waitForDomSettle().then(resolve); | ||
} else { | ||
console.warn( | ||
"waitForDomSettle is not defined, considering DOM as settled", | ||
); | ||
resolve(); | ||
} | ||
}); | ||
}), | ||
this.page.waitForLoadState("domcontentloaded"), | ||
this.page.waitForSelector("body"), | ||
timeoutPromise, | ||
]); | ||
} finally { | ||
clearTimeout(timeoutHandle!); | ||
} | ||
} catch (e) { | ||
this.log({ | ||
category: "dom", | ||
message: "Error in waitForSettledDom", | ||
level: 1, | ||
auxiliary: { | ||
error: { | ||
value: e.message, | ||
type: "string", | ||
}, | ||
trace: { | ||
value: e.stack, | ||
type: "string", | ||
}, | ||
}, | ||
}); | ||
} | ||
} | ||
private async startDomDebug() { | ||
try { | ||
await this.page | ||
.evaluate(() => { | ||
if (typeof window.debugDom === "function") { | ||
window.debugDom(); | ||
} else { | ||
this.log({ | ||
category: "dom", | ||
message: "debugDom is not defined", | ||
level: 1, | ||
}); | ||
} | ||
}) | ||
.catch(() => {}); | ||
} catch (e) { | ||
this.log({ | ||
category: "dom", | ||
message: "Error in startDomDebug", | ||
level: 1, | ||
auxiliary: { | ||
error: { | ||
value: e.message, | ||
type: "string", | ||
}, | ||
trace: { | ||
value: e.stack, | ||
type: "string", | ||
}, | ||
}, | ||
}); | ||
} | ||
} | ||
private async cleanupDomDebug() { | ||
if (this.debugDom) { | ||
await this.page.evaluate(() => window.cleanupDebug()).catch(() => {}); | ||
} | ||
} | ||
async act({ | ||
@@ -702,0 +601,0 @@ action, |
{ | ||
"name": "@browserbasehq/stagehand", | ||
"version": "1.7.0", | ||
"version": "1.7.1-alpha-6c831de6640117820532bbd55689b378ff92f9f3", | ||
"description": "An AI web browsing framework focused on simplicity and extensibility.", | ||
@@ -18,2 +18,3 @@ "main": "./dist/index.js", | ||
"evals": "npm run build-dom-scripts && tsx evals/index.eval.ts", | ||
"e2e": "npm run build-dom-scripts && cd evals/deterministic && npx playwright test", | ||
"build-dom-scripts": "tsx lib/dom/genDomScripts.ts", | ||
@@ -32,3 +33,3 @@ "build-types": "tsc --emitDeclarationOnly --outDir dist", | ||
"keywords": [], | ||
"author": "Paul Klein IV", | ||
"author": "Browserbase", | ||
"license": "MIT", | ||
@@ -39,5 +40,7 @@ "devDependencies": { | ||
"@eslint/js": "^9.16.0", | ||
"@types/adm-zip": "^0.5.7", | ||
"@types/cheerio": "^0.22.35", | ||
"@types/express": "^4.17.21", | ||
"@types/node": "^20.11.30", | ||
"adm-zip": "^0.5.16", | ||
"autoevals": "^0.0.64", | ||
@@ -44,0 +47,0 @@ "braintrust": "^0.0.171", |
Sorry, the diff of this file is too big to display
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
551356
128
14019
23
1