diff --git a/examples/example.ts b/examples/example.ts index 41d8d861..35204dd5 100644 --- a/examples/example.ts +++ b/examples/example.ts @@ -5,15 +5,20 @@ * npx create-browser-app@latest my-browser-app */ -import { Stagehand } from "@/dist"; -import StagehandConfig from "@/stagehand.config"; +import { Stagehand } from "@/lib/index"; async function example() { const stagehand = new Stagehand({ - ...StagehandConfig, + env: "LOCAL", + localDebugPort: 9222, }); await stagehand.init(); - await stagehand.page.goto("https://docs.stagehand.dev"); + const page = stagehand.page; + await page.act( + `type 'if you're seeing this tweet, get excited -- i'm using stagehand on arc!'`, + ); + await page.act("click the post button"); + await stagehand.close(); } (async () => { diff --git a/lib/index.ts b/lib/index.ts index 09a47379..ff2f15a5 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -53,6 +53,7 @@ async function getBrowser( browserbaseSessionCreateParams?: Browserbase.Sessions.SessionCreateParams, browserbaseSessionID?: string, localBrowserLaunchOptions?: LocalBrowserLaunchOptions, + localDebugPort?: number, ): Promise { if (env === "BROWSERBASE") { if (!apiKey) { @@ -200,6 +201,14 @@ async function getBrowser( const context = browser.contexts()[0]; return { browser, context, debugUrl, sessionUrl, sessionId, env }; + } else if (localDebugPort) { + const browser = await chromium.connectOverCDP( + `http://localhost:${localDebugPort}`, + ); + const context = browser.contexts()[0]; + await applyStealthScripts(context); + + return { context, contextPath: undefined, env: "LOCAL" }; } else { logger({ category: "init", @@ -377,6 +386,7 @@ export class Stagehand { private waitForCaptchaSolves: boolean; private localBrowserLaunchOptions?: LocalBrowserLaunchOptions; public readonly selfHeal: boolean; + private readonly localDebugPort?: number; private cleanupCalled = false; constructor( @@ -399,6 +409,7 @@ export class Stagehand { systemPrompt, useAPI, localBrowserLaunchOptions, + localDebugPort, selfHeal = true, waitForCaptchaSolves = false, }: ConstructorParams = { @@ -437,6 +448,7 @@ export class Stagehand { this.userProvidedInstructions = systemPrompt; this.usingAPI = useAPI ?? false; this.modelName = modelName ?? DEFAULT_MODEL_NAME; + this.localDebugPort = localDebugPort; if (this.usingAPI && env === "LOCAL") { throw new Error("API mode can only be used with BROWSERBASE environment"); @@ -559,6 +571,7 @@ export class Stagehand { this.browserbaseSessionCreateParams, this.browserbaseSessionID, this.localBrowserLaunchOptions, + this.localDebugPort, ).catch((e) => { console.error("Error in init:", e); const br: BrowserResult = { @@ -573,7 +586,11 @@ export class Stagehand { this.intEnv = env; this.contextPath = contextPath; this.stagehandContext = await StagehandContext.init(context, this); - const defaultPage = this.context.pages()[0]; + const pages = this.context.pages(); + let defaultPage = pages[0]; + if (!defaultPage) { + defaultPage = await this.context.newPage(); + } this.stagehandPage = await new StagehandPage( defaultPage, this, diff --git a/types/stagehand.ts b/types/stagehand.ts index fdc6f0b9..60971485 100644 --- a/types/stagehand.ts +++ b/types/stagehand.ts @@ -40,6 +40,12 @@ export interface ConstructorParams { */ waitForCaptchaSolves?: boolean; localBrowserLaunchOptions?: LocalBrowserLaunchOptions; + /** + * The port to use for local debugging. Do not set this if you are using localBrowserLaunchOptions. + * + * @default 9222 + */ + localDebugPort?: number; } export interface InitOptions {