diff --git a/examples/package.json b/examples/package.json index 71053f409..13de3cce0 100644 --- a/examples/package.json +++ b/examples/package.json @@ -4,6 +4,7 @@ "private": true, "description": "Example scripts for Stagehand", "main": "./", + "type": "module", "scripts": { "build": "pnpm --filter @browserbasehq/stagehand run build", "start": "pnpm run build && sh -c 'if [ -n \"$1\" ]; then tsx \"$1.ts\"; else tsx example.ts; fi' --" diff --git a/examples/v3_example.ts b/examples/v3_example.ts index a2bef7165..3bd55dc98 100644 --- a/examples/v3_example.ts +++ b/examples/v3_example.ts @@ -1,36 +1,33 @@ import { V3 } from "../lib/v3/v3"; import puppeteer from "puppeteer-core"; -async function example(v3: V3) { - const puppeteerBrowser = await puppeteer.connect({ - browserWSEndpoint: v3.connectURL(), - }); - const puppeteerPages = await puppeteerBrowser.pages(); +const v3 = new V3({ + env: "LOCAL", + headless: false, + verbose: 0, +}); +await v3.init(); +const puppeteerBrowser = await puppeteer.connect({ + browserWSEndpoint: v3.connectURL(), +}); +const puppeteerPages = await puppeteerBrowser.pages(); - const page = puppeteerPages[0]; +const page = puppeteerPages[0]; - await page.goto( - "https://browserbase.github.io/stagehand-eval-sites/sites/closed-shadow-root-in-oopif/", - ); +await page.goto( + "https://browserbase.github.io/stagehand-eval-sites/sites/closed-shadow-root-in-oopif/", +); - const observeResult = { - selector: - "xpath=/html/body/main/section/iframe/html/body/shadow-demo//div/button", - method: "click", - description: "nunya", - arguments: [""], - }; +const observeResult = { + selector: + "xpath=/html/body/main/section/iframe/html/body/shadow-demo//div/button", + method: "click", + description: "nunya", + arguments: [""], +}; - await new Promise((resolve) => setTimeout(resolve, 200)); - await v3.act(observeResult, page); -} +await new Promise((resolve) => setTimeout(resolve, 200)); +await v3.act(observeResult, page); -(async () => { - const v3 = new V3({ - env: "LOCAL", - headless: false, - verbose: 0, - }); - await v3.init(); - await example(v3); -})(); +await v3.close(); +await puppeteerBrowser.close(); diff --git a/lib/dom/genDomScripts.ts b/lib/dom/genDomScripts.ts index d3c3edc1c..b4f72001b 100644 --- a/lib/dom/genDomScripts.ts +++ b/lib/dom/genDomScripts.ts @@ -8,9 +8,12 @@ * We can't rely on the normal build process for stagehand, because we need our script content as a string so that the import *just works* */ import fs from "fs"; -import path from "path"; +import path, { dirname } from "path"; +import { fileURLToPath } from "url"; import esbuild from "esbuild"; +const __dirname = dirname(fileURLToPath(import.meta.url)); + fs.mkdirSync(path.join(__dirname, "./build"), { recursive: true }); esbuild.buildSync({ diff --git a/lib/index.ts b/lib/index.ts index 04cd4a5bf..e2e6080d3 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -1028,4 +1028,5 @@ export * from "../types/stagehand"; export * from "../types/stagehandApiErrors"; export * from "../types/stagehandErrors"; export * from "./llm/LLMClient"; +export * from "./v3/v3"; export { connectToMCPServer }; diff --git a/lib/llm/AnthropicClient.ts b/lib/llm/AnthropicClient.ts index 27d8f4c7c..448436a63 100644 --- a/lib/llm/AnthropicClient.ts +++ b/lib/llm/AnthropicClient.ts @@ -21,7 +21,6 @@ export class AnthropicClient extends LLMClient { private client: Anthropic; private cache: LLMCache | undefined; private enableCaching: boolean; - public clientOptions: ClientOptions; constructor({ enableCaching = false, diff --git a/lib/llm/CerebrasClient.ts b/lib/llm/CerebrasClient.ts index 4b12c0380..fdec43c9e 100644 --- a/lib/llm/CerebrasClient.ts +++ b/lib/llm/CerebrasClient.ts @@ -17,7 +17,6 @@ export class CerebrasClient extends LLMClient { private client: OpenAI; private cache: LLMCache | undefined; private enableCaching: boolean; - public clientOptions: ClientOptions; public hasVision = false; constructor({ diff --git a/lib/llm/GoogleClient.ts b/lib/llm/GoogleClient.ts index 5460bea73..83d6de9fe 100644 --- a/lib/llm/GoogleClient.ts +++ b/lib/llm/GoogleClient.ts @@ -60,8 +60,6 @@ export class GoogleClient extends LLMClient { private client: GoogleGenAI; private cache: LLMCache | undefined; private enableCaching: boolean; - public clientOptions: ClientOptions; - public hasVision: boolean; private logger: (message: LogLine) => void; constructor({ diff --git a/lib/llm/GroqClient.ts b/lib/llm/GroqClient.ts index d512ed6f9..78dbe0ebc 100644 --- a/lib/llm/GroqClient.ts +++ b/lib/llm/GroqClient.ts @@ -17,7 +17,6 @@ export class GroqClient extends LLMClient { private client: OpenAI; private cache: LLMCache | undefined; private enableCaching: boolean; - public clientOptions: ClientOptions; public hasVision = false; constructor({ diff --git a/lib/llm/OpenAIClient.ts b/lib/llm/OpenAIClient.ts index 008215bd2..0a3a5c350 100644 --- a/lib/llm/OpenAIClient.ts +++ b/lib/llm/OpenAIClient.ts @@ -32,7 +32,6 @@ export class OpenAIClient extends LLMClient { private client: OpenAI; private cache: LLMCache | undefined; private enableCaching: boolean; - public clientOptions: ClientOptions; constructor({ enableCaching = false, diff --git a/lib/package.json b/lib/package.json index cfedfd8cd..0df32522c 100644 --- a/lib/package.json +++ b/lib/package.json @@ -2,6 +2,7 @@ "name": "@browserbasehq/stagehand-lib", "version": "2.4.1", "private": true, + "type": "module", "description": "Core Stagehand library sources", "main": "../dist/index.js", "module": "../dist/index.js", diff --git a/lib/v3/dom/build/scriptV3Content.ts b/lib/v3/dom/build/scriptV3Content.ts new file mode 100644 index 000000000..9ea5eab91 --- /dev/null +++ b/lib/v3/dom/build/scriptV3Content.ts @@ -0,0 +1 @@ +export const v3ScriptContent = "(()=>{function b(_={}){let S=n=>{let{hostToRoot:l}=n,m=t=>{let o=[];if(t instanceof Document)return t.documentElement&&o.push(t.documentElement),o;if(t instanceof ShadowRoot||t instanceof DocumentFragment)return o.push(...Array.from(t.children)),o;if(t instanceof Element){o.push(...Array.from(t.children));let a=t.shadowRoot;a&&o.push(...Array.from(a.children));let r=l.get(t);return r&&o.push(...Array.from(r.children)),o}return o},v=t=>{let o=[],a=[...m(t)];for(;a.length;){let r=a.shift();o.push(r),a.push(...m(r))}return o},y=t=>{let o=String(t||\"\").trim();if(!o)return null;let a=o.replace(/^xpath=/i,\"\"),r=[];{let e=0;for(;e({axis:e.axis,raw:e.raw,tag:e.tag,index:e.index}))});let g=[document];for(let e of r){let d=e.index,h=null;for(let u of g){let p=e.axis===\"child\"?m(u):v(u),i=[];for(let c of p)(e.tag===\"*\"||c.localName===e.tag)&&i.push(c);if(n.debug&&console.info(\"[v3-piercer][resolve] step\",{axis:e.axis,tag:e.tag,index:d,poolCount:p.length,matchesCount:i.length}),!!i.length){if(d!=null){let c=d-1;h=c>=0&&cl.get(t),stats:()=>({installed:!0,url:location.href,isTop:window.top===window,open:n.openCount,closed:n.closedCount}),resolveSimpleXPath:y}},f=Element.prototype.attachShadow;if(f.__v3Patched&&f.__v3State){f.__v3State.debug=!0,S(f.__v3State);return}let s={hostToRoot:new WeakMap,openCount:0,closedCount:0,debug:!0},x=f,w=function(n){let l=n?.mode??\"open\",m=x.call(this,n);try{s.hostToRoot.set(this,m),l===\"closed\"?s.closedCount++:s.openCount++,s.debug&&console.info(\"[v3-piercer] attachShadow\",{tag:this.tagName?.toLowerCase()??\"\",mode:l,url:location.href})}catch{}return m};if(w.__v3Patched=!0,w.__v3State=s,Object.defineProperty(Element.prototype,\"attachShadow\",{configurable:!0,writable:!0,value:w}),_.tagExisting)try{let n=document.createTreeWalker(document,NodeFilter.SHOW_ELEMENT);for(;n.nextNode();){let l=n.currentNode;l.shadowRoot&&(s.hostToRoot.set(l,l.shadowRoot),s.openCount++)}}catch{}window.__stagehandV3Injected=!0,S(s),s.debug&&console.info(\"[v3-piercer] installed\",{url:location.href,isTop:window.top===window,readyState:document.readyState})}b({debug:!0,tagExisting:!1});})();\n"; \ No newline at end of file diff --git a/lib/v3/dom/build/v3-index.js b/lib/v3/dom/build/v3-index.js new file mode 100644 index 000000000..2796dbcfc --- /dev/null +++ b/lib/v3/dom/build/v3-index.js @@ -0,0 +1 @@ +(()=>{function b(_={}){let S=n=>{let{hostToRoot:l}=n,m=t=>{let o=[];if(t instanceof Document)return t.documentElement&&o.push(t.documentElement),o;if(t instanceof ShadowRoot||t instanceof DocumentFragment)return o.push(...Array.from(t.children)),o;if(t instanceof Element){o.push(...Array.from(t.children));let a=t.shadowRoot;a&&o.push(...Array.from(a.children));let r=l.get(t);return r&&o.push(...Array.from(r.children)),o}return o},v=t=>{let o=[],a=[...m(t)];for(;a.length;){let r=a.shift();o.push(r),a.push(...m(r))}return o},y=t=>{let o=String(t||"").trim();if(!o)return null;let a=o.replace(/^xpath=/i,""),r=[];{let e=0;for(;e({axis:e.axis,raw:e.raw,tag:e.tag,index:e.index}))});let g=[document];for(let e of r){let d=e.index,h=null;for(let u of g){let p=e.axis==="child"?m(u):v(u),i=[];for(let c of p)(e.tag==="*"||c.localName===e.tag)&&i.push(c);if(n.debug&&console.info("[v3-piercer][resolve] step",{axis:e.axis,tag:e.tag,index:d,poolCount:p.length,matchesCount:i.length}),!!i.length){if(d!=null){let c=d-1;h=c>=0&&cl.get(t),stats:()=>({installed:!0,url:location.href,isTop:window.top===window,open:n.openCount,closed:n.closedCount}),resolveSimpleXPath:y}},f=Element.prototype.attachShadow;if(f.__v3Patched&&f.__v3State){f.__v3State.debug=!0,S(f.__v3State);return}let s={hostToRoot:new WeakMap,openCount:0,closedCount:0,debug:!0},x=f,w=function(n){let l=n?.mode??"open",m=x.call(this,n);try{s.hostToRoot.set(this,m),l==="closed"?s.closedCount++:s.openCount++,s.debug&&console.info("[v3-piercer] attachShadow",{tag:this.tagName?.toLowerCase()??"",mode:l,url:location.href})}catch{}return m};if(w.__v3Patched=!0,w.__v3State=s,Object.defineProperty(Element.prototype,"attachShadow",{configurable:!0,writable:!0,value:w}),_.tagExisting)try{let n=document.createTreeWalker(document,NodeFilter.SHOW_ELEMENT);for(;n.nextNode();){let l=n.currentNode;l.shadowRoot&&(s.hostToRoot.set(l,l.shadowRoot),s.openCount++)}}catch{}window.__stagehandV3Injected=!0,S(s),s.debug&&console.info("[v3-piercer] installed",{url:location.href,isTop:window.top===window,readyState:document.readyState})}b({debug:!0,tagExisting:!1});})(); diff --git a/lib/v3/dom/genDomScripts.ts b/lib/v3/dom/genDomScripts.ts index 1e6b2d1f0..bebba4dd0 100644 --- a/lib/v3/dom/genDomScripts.ts +++ b/lib/v3/dom/genDomScripts.ts @@ -3,9 +3,11 @@ * as a string constant (`v3ScriptContent`) for CDP injection (document-start). */ import fs from "node:fs"; -import path from "node:path"; +import path, { dirname } from "node:path"; +import { fileURLToPath } from "node:url"; import esbuild from "esbuild"; +const __dirname = dirname(fileURLToPath(import.meta.url)); const here = __dirname; const outDir = path.join(here, "./build"); fs.mkdirSync(outDir, { recursive: true }); diff --git a/package.json b/package.json index 50d90edb5..a5644751c 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,18 @@ { "name": "@browserbasehq/stagehand", "version": "2.5.0", + "type": "module", "description": "An AI web browsing framework focused on simplicity and extensibility.", - "main": "./dist/index.js", + "main": "./dist/index.cjs", "module": "./dist/index.js", "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "require": "./dist/index.cjs" + } + }, "bin": { "evals": "./dist/evals/cli.js" }, @@ -24,7 +32,7 @@ "build-v3-dom-scripts": "tsx lib/v3/dom/genDomScripts.ts", "build-dom-scripts": "tsx lib/dom/genDomScripts.ts", "build-types": "tsc --emitDeclarationOnly --outDir dist", - "build-js": "tsup lib/index.ts --dts", + "build-js": "tsup lib/index.ts --format esm,cjs --dts --target es2022", "build:cli": "tsup evals/cli.ts --outDir dist/evals --format cjs && cp evals/evals.config.json dist/evals/ && chmod +x dist/evals/cli.js && npm link", "build": "pnpm run lint && pnpm run gen-version && pnpm run build-dom-scripts && pnpm run build-v3-dom-scripts && pnpm run build-js && pnpm run build-types", "gen-version": "tsx scripts/gen-version.ts", diff --git a/scripts/gen-version.ts b/scripts/gen-version.ts index 722ec5e99..925ce1d65 100644 --- a/scripts/gen-version.ts +++ b/scripts/gen-version.ts @@ -1,8 +1,10 @@ import { readFileSync, writeFileSync } from "node:fs"; -import { join } from "node:path"; +import { join, dirname } from "node:path"; +import { fileURLToPath } from "node:url"; type PackageJson = { version: string }; +const __dirname = dirname(fileURLToPath(import.meta.url)); const pkgPath = join(__dirname, "..", "package.json"); const pkg: PackageJson = JSON.parse(readFileSync(pkgPath, "utf8")); diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 000000000..981fa170b --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,31 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "ESNext", + "target": "ES2022", + "moduleResolution": "bundler", + "outDir": "dist", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "allowImportingTsExtensions": false, + "noEmit": true + }, + "include": [ + "lib/**/*", + "types/**/*" + ], + "exclude": [ + "node_modules", + "dist", + "**/*.test.ts", + "**/*.spec.ts", + "evals/**/*", + "examples/**/*" + ] +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index d97b9cb50..e50174f23 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,11 @@ { "compilerOptions": { - "module": "commonjs", + "module": "ES2022", + "moduleResolution": "Node", "esModuleInterop": true, "allowSyntheticDefaultImports": true, - "target": "es6", + "target": "ES2022", "noImplicitAny": true, - "moduleResolution": "node", "sourceMap": true, "outDir": "dist", "baseUrl": ".",