@bbearai/ai-executor 0.2.0
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/cli.js +591 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.mts +233 -0
- package/dist/index.d.ts +233 -0
- package/dist/index.js +407 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +367 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +60 -0
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/runner.ts","../src/browser.ts","../src/evaluator.ts"],"sourcesContent":["/**\n * CLI entrypoint for @bbearai/ai-executor\n *\n * Usage:\n * npx @bbearai/ai-executor --url https://staging.app.com --test-case-id abc123\n *\n * Or with a local test case JSON:\n * npx @bbearai/ai-executor --url https://staging.app.com --test-file ./test.json\n */\n\nimport { createClient } from '@supabase/supabase-js';\nimport { runTest } from './runner';\nimport type { TestCaseInput, AuthConfig, BrowserConfig } from './types';\n\ninterface CliArgs {\n url: string;\n testCaseId?: string;\n testFile?: string;\n projectId?: string;\n headless: boolean;\n provider: 'local' | 'browserbase';\n cookies?: string;\n localStorage?: string;\n}\n\nfunction parseArgs(): CliArgs {\n const args = process.argv.slice(2);\n const parsed: CliArgs = {\n url: '',\n headless: true,\n provider: 'local',\n };\n\n for (let i = 0; i < args.length; i++) {\n switch (args[i]) {\n case '--url':\n parsed.url = args[++i] || '';\n break;\n case '--test-case-id':\n parsed.testCaseId = args[++i];\n break;\n case '--test-file':\n parsed.testFile = args[++i];\n break;\n case '--project-id':\n parsed.projectId = args[++i];\n break;\n case '--headless':\n parsed.headless = args[++i] !== 'false';\n break;\n case '--provider':\n parsed.provider = args[++i] as 'local' | 'browserbase';\n break;\n case '--cookies':\n parsed.cookies = args[++i];\n break;\n case '--local-storage':\n parsed.localStorage = args[++i];\n break;\n case '--help':\n printHelp();\n process.exit(0);\n }\n }\n\n return parsed;\n}\n\nfunction printHelp(): void {\n console.log(`\n@bbearai/ai-executor - AI-powered QA test executor\n\nUsage:\n bbear-execute --url <target-url> --test-case-id <id> [options]\n bbear-execute --url <target-url> --test-file <path> [options]\n\nRequired:\n --url <url> Target application URL\n\nTest Source (one required):\n --test-case-id <id> Fetch test case from BugBear (requires SUPABASE_URL, SUPABASE_ANON_KEY)\n --test-file <path> Path to a JSON test case file\n\nOptions:\n --project-id <id> BugBear project ID (for fetching test cases)\n --headless <true|false> Run browser headlessly (default: true)\n --provider <provider> Browser provider: local or browserbase (default: local)\n --cookies <json> JSON array of cookies for authentication\n --local-storage <json> JSON object of localStorage items for authentication\n --help Show this help message\n\nEnvironment Variables:\n ANTHROPIC_API_KEY Required. Claude API key for AI interpretation.\n SUPABASE_URL Required when using --test-case-id. BugBear Supabase URL.\n SUPABASE_ANON_KEY Required when using --test-case-id. BugBear Supabase anon key.\n BROWSERBASE_API_KEY Required when --provider=browserbase.\n BROWSERBASE_PROJECT_ID Required when --provider=browserbase.\n\nExamples:\n # Run with a local test file\n bbear-execute --url https://staging.myapp.com --test-file ./login-test.json --headless false\n\n # Run with a BugBear test case\n bbear-execute --url https://staging.myapp.com --test-case-id abc-123 --project-id xyz-789\n\n # Run with cookie auth\n bbear-execute --url https://staging.myapp.com --test-file ./test.json \\\\\n --cookies '[{\"name\":\"session\",\"value\":\"abc123\",\"domain\":\".myapp.com\"}]'\n`);\n}\n\nasync function fetchTestCase(testCaseId: string): Promise<TestCaseInput> {\n const supabaseUrl = process.env.SUPABASE_URL || process.env.NEXT_PUBLIC_SUPABASE_URL;\n const supabaseKey = process.env.SUPABASE_ANON_KEY || process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;\n\n if (!supabaseUrl || !supabaseKey) {\n throw new Error('SUPABASE_URL and SUPABASE_ANON_KEY are required to fetch test cases');\n }\n\n const supabase = createClient(supabaseUrl, supabaseKey);\n const { data, error } = await supabase\n .from('test_cases')\n .select('id, title, description, steps, expected_result, preconditions, target_route, estimated_minutes')\n .eq('id', testCaseId)\n .single();\n\n if (error || !data) {\n throw new Error(`Failed to fetch test case ${testCaseId}: ${error?.message ?? 'Not found'}`);\n }\n\n return {\n id: data.id,\n title: data.title,\n description: data.description ?? undefined,\n steps: data.steps as TestCaseInput['steps'],\n preconditions: data.preconditions ?? undefined,\n targetRoute: data.target_route ?? undefined,\n estimatedMinutes: data.estimated_minutes ?? undefined,\n };\n}\n\nasync function loadTestFile(path: string): Promise<TestCaseInput> {\n const fs = await import('fs');\n const content = fs.readFileSync(path, 'utf-8');\n return JSON.parse(content) as TestCaseInput;\n}\n\nfunction parseAuth(args: CliArgs): AuthConfig | undefined {\n if (args.cookies) {\n try {\n return { type: 'cookie', cookies: JSON.parse(args.cookies) };\n } catch {\n console.error('Failed to parse --cookies JSON');\n process.exit(1);\n }\n }\n if (args.localStorage) {\n try {\n return { type: 'localStorage', items: JSON.parse(args.localStorage) };\n } catch {\n console.error('Failed to parse --local-storage JSON');\n process.exit(1);\n }\n }\n return undefined;\n}\n\nasync function main(): Promise<void> {\n const args = parseArgs();\n\n if (!args.url) {\n console.error('Error: --url is required');\n printHelp();\n process.exit(1);\n }\n\n if (!args.testCaseId && !args.testFile) {\n console.error('Error: Either --test-case-id or --test-file is required');\n printHelp();\n process.exit(1);\n }\n\n const anthropicApiKey = process.env.ANTHROPIC_API_KEY;\n if (!anthropicApiKey) {\n console.error('Error: ANTHROPIC_API_KEY environment variable is required');\n process.exit(1);\n }\n\n // Load test case\n console.log('Loading test case...');\n const testCase = args.testCaseId\n ? await fetchTestCase(args.testCaseId)\n : await loadTestFile(args.testFile!);\n\n console.log(`Test: ${testCase.title}`);\n console.log(`Steps: ${testCase.steps.length}`);\n console.log(`Target: ${args.url}${testCase.targetRoute ?? ''}`);\n console.log('');\n\n // Build browser config\n const browser: BrowserConfig = {\n provider: args.provider,\n headless: args.headless,\n browserbaseApiKey: process.env.BROWSERBASE_API_KEY,\n browserbaseProjectId: process.env.BROWSERBASE_PROJECT_ID,\n };\n\n // Run the test\n const result = await runTest({\n targetUrl: args.url,\n testCase,\n auth: parseAuth(args),\n browser,\n anthropicApiKey,\n onStepComplete: (step, index, total) => {\n const icon = step.passed ? '\\u2705' : '\\u274C';\n const confidence = Math.round(step.confidence * 100);\n console.log(\n `${icon} Step ${index + 1}/${total}: ${step.action.slice(0, 60)}... ` +\n `[${step.passed ? 'PASS' : 'FAIL'} ${confidence}%] (${step.durationMs}ms)`\n );\n if (!step.passed) {\n console.log(` Expected: ${step.expectedResult.slice(0, 80)}`);\n console.log(` Actual: ${step.actualResult.slice(0, 80)}`);\n }\n if (step.error) {\n console.log(` Error: ${step.error.slice(0, 80)}`);\n }\n },\n onStatusChange: (status) => {\n if (status !== 'executing' && status !== 'evaluating') {\n console.log(`[${status}]`);\n }\n },\n });\n\n // Print summary\n console.log('');\n console.log('='.repeat(60));\n console.log(`Result: ${result.overallResult.toUpperCase()}`);\n console.log(`Duration: ${Math.round(result.totalDurationMs / 1000)}s`);\n console.log(\n `Steps: ${result.steps.filter((s) => s.passed).length} passed, ` +\n `${result.steps.filter((s) => !s.passed).length} failed`\n );\n console.log(`Tokens: ${result.tokenUsage.inputTokens} in / ${result.tokenUsage.outputTokens} out`);\n console.log('');\n console.log('Summary:');\n console.log(result.summary);\n console.log('='.repeat(60));\n\n // Exit with appropriate code\n process.exit(result.overallResult === 'passed' ? 0 : 1);\n}\n\nmain().catch((err) => {\n console.error('Fatal error:', err);\n process.exit(1);\n});\n","/**\n * Test Runner\n *\n * Orchestrates the full test execution lifecycle using Stagehand:\n * 1. Launch Stagehand browser session\n * 2. Navigate to target URL\n * 3. Inject authentication\n * 4. For each step: act() → screenshot → extract() → record\n * 5. Generate summary\n * 6. Return structured results\n */\n\nimport Anthropic from '@anthropic-ai/sdk';\nimport { z } from 'zod';\nimport { createStagehandSession, injectAuth } from './browser';\nimport { generateRunSummary } from './evaluator';\nimport type {\n TestRunConfig,\n TestRunResult,\n StepResult,\n OverallResult,\n BrowserConfig,\n ConsoleEntry,\n NetworkError,\n} from './types';\n\n/**\n * Execute a test case against a target URL using Stagehand.\n */\nexport async function runTest(config: TestRunConfig): Promise<TestRunResult> {\n const anthropic = new Anthropic({ apiKey: config.anthropicApiKey });\n const startTime = Date.now();\n\n const browserConfig: BrowserConfig = config.browser ?? {\n provider: 'local',\n headless: true,\n };\n\n config.onStatusChange?.('initializing');\n\n const session = await createStagehandSession(browserConfig, config.anthropicApiKey);\n const { stagehand, page } = session;\n\n const stepResults: StepResult[] = [];\n\n // Accumulated console logs and network errors — drained per step\n let pendingConsoleLogs: ConsoleEntry[] = [];\n let pendingNetworkErrors: NetworkError[] = [];\n let stepStartTime = Date.now();\n\n // Cast to any for event listeners — Stagehand's Page type only exposes 'console'\n // but the underlying Playwright page supports all standard events\n const rawPage = page as any;\n\n // Listen for console messages\n rawPage.on('console', (msg: any) => {\n const level = msg.type?.() ?? msg.type ?? 'log';\n const mappedLevel =\n level === 'error' ? 'error' :\n level === 'warn' || level === 'warning' ? 'warning' :\n level === 'info' ? 'info' :\n level === 'debug' ? 'debug' : 'log';\n\n pendingConsoleLogs.push({\n level: mappedLevel as ConsoleEntry['level'],\n text: (typeof msg.text === 'function' ? msg.text() : String(msg.text ?? msg)).slice(0, 2000),\n source: typeof msg.location === 'function' ? msg.location()?.url : undefined,\n timestamp: Date.now() - stepStartTime,\n });\n });\n\n // Listen for failed requests (connection errors, CORS, etc.)\n rawPage.on('requestfailed', (req: any) => {\n const url = typeof req.url === 'function' ? req.url() : String(req.url ?? '');\n const method = typeof req.method === 'function' ? req.method() : String(req.method ?? 'GET');\n const failure = typeof req.failure === 'function' ? req.failure() : req.failure;\n pendingNetworkErrors.push({\n method,\n url: url.slice(0, 500),\n status: 0,\n statusText: failure?.errorText ?? 'Request failed',\n timestamp: Date.now() - stepStartTime,\n });\n });\n\n // Listen for HTTP error responses (4xx/5xx)\n rawPage.on('response', (res: any) => {\n const status = typeof res.status === 'function' ? res.status() : Number(res.status ?? 0);\n if (status >= 400) {\n const url = typeof res.url === 'function' ? res.url() : String(res.url ?? '');\n const statusText = typeof res.statusText === 'function' ? res.statusText() : String(res.statusText ?? '');\n const req = typeof res.request === 'function' ? res.request() : res.request;\n const method = req ? (typeof req.method === 'function' ? req.method() : String(req.method ?? 'GET')) : 'GET';\n pendingNetworkErrors.push({\n method,\n url: url.slice(0, 500),\n status,\n statusText,\n timestamp: Date.now() - stepStartTime,\n });\n }\n });\n\n try {\n // Handle form-login auth first (navigates to login page, then we go to target)\n if (config.auth?.type === 'form-login') {\n config.onStatusChange?.('authenticating');\n await injectAuth(page, config.auth, stagehand);\n }\n\n // Navigate to target URL\n config.onStatusChange?.('navigating');\n const targetUrl = config.testCase.targetRoute\n ? `${config.targetUrl.replace(/\\/$/, '')}${config.testCase.targetRoute}`\n : config.targetUrl;\n\n await page.goto(targetUrl, { waitUntil: 'domcontentloaded', timeoutMs: 30_000 });\n\n // Inject non-form authentication after navigation\n if (config.auth && config.auth.type !== 'form-login') {\n config.onStatusChange?.('authenticating');\n await injectAuth(page, config.auth, stagehand);\n\n // If localStorage auth, reload to apply\n if (config.auth.type === 'localStorage') {\n await page.evaluate((items: Record<string, string>) => {\n for (const [key, value] of Object.entries(items)) {\n localStorage.setItem(key, value);\n }\n }, config.auth.items);\n await page.reload({ waitUntil: 'domcontentloaded' });\n }\n }\n\n // Wait for page to stabilize\n await page.waitForLoadState('networkidle').catch(() => {});\n\n // Clear any logs accumulated during navigation/auth\n pendingConsoleLogs = [];\n pendingNetworkErrors = [];\n\n // Execute each step using Stagehand\n config.onStatusChange?.('executing');\n const steps = config.testCase.steps;\n\n for (let i = 0; i < steps.length; i++) {\n const step = steps[i];\n stepStartTime = Date.now();\n\n // Reset per-step collectors\n pendingConsoleLogs = [];\n pendingNetworkErrors = [];\n\n // Capture pre-action screenshot\n const screenshotBefore = await page.screenshot({ type: 'png' });\n\n let error: string | undefined;\n let screenshotAfter: Buffer = screenshotBefore;\n let actSucceeded = false;\n\n // Execute the action using Stagehand\n try {\n await stagehand.act(step.action);\n actSucceeded = true;\n\n // Wait for page to settle after action\n await page.waitForLoadState('networkidle').catch(() => {});\n await page.waitForTimeout(500);\n\n // Capture post-action screenshot\n screenshotAfter = await page.screenshot({ type: 'png' });\n } catch (err) {\n error = err instanceof Error ? err.message : String(err);\n // Still capture screenshot for debugging even after failure\n screenshotAfter = await page.screenshot({ type: 'png' }).catch(() => screenshotBefore);\n }\n\n // Evaluate the result using Stagehand's extract()\n let evaluation = {\n passed: false,\n confidence: 0,\n actualResult: error ?? 'Action execution failed',\n };\n\n if (actSucceeded) {\n try {\n const verificationSchema = z.object({\n passed: z.boolean().describe('Whether the expected result was achieved'),\n confidence: z\n .number()\n .min(0)\n .max(1)\n .describe('Confidence in the assessment (0.9+ = very sure, 0.7-0.9 = likely, below 0.7 = uncertain)'),\n actualResult: z.string().describe('Description of what actually happened on the page'),\n });\n\n const verification = await stagehand.extract(\n `You are evaluating a QA test step. The action \"${step.action}\" was just performed. ` +\n `Check if this expected result was achieved: \"${step.expectedResult}\". ` +\n `Look at the current page state and describe what actually happened. ` +\n `Be precise and factual in your assessment.`,\n verificationSchema,\n );\n\n evaluation = {\n passed: verification.passed,\n confidence: verification.confidence,\n actualResult: verification.actualResult,\n };\n } catch (evalErr) {\n evaluation = {\n passed: false,\n confidence: 0.2,\n actualResult: `Verification error: ${evalErr instanceof Error ? evalErr.message : String(evalErr)}`,\n };\n }\n }\n\n // Snapshot the collected logs for this step (limit to 50 entries each to avoid bloat)\n const consoleLogs = pendingConsoleLogs.slice(0, 50);\n const networkErrors = pendingNetworkErrors.slice(0, 30);\n\n const result: StepResult = {\n stepNumber: step.stepNumber,\n action: step.action,\n expectedResult: step.expectedResult,\n actualResult: evaluation.actualResult,\n passed: evaluation.passed,\n confidence: evaluation.confidence,\n screenshotBefore,\n screenshotAfter,\n actionsTaken: [], // Stagehand handles actions internally\n error,\n durationMs: Date.now() - stepStartTime,\n consoleLogs,\n networkErrors,\n };\n\n stepResults.push(result);\n config.onStepComplete?.(result, i, steps.length);\n }\n\n // Generate summary\n config.onStatusChange?.('completed');\n const model = config.model ?? 'claude-sonnet-4-20250514';\n const summary = await generateRunSummary(anthropic, config.testCase.title, stepResults, model);\n\n // Determine overall result\n const overallResult = determineOverallResult(stepResults);\n\n return {\n testCaseId: config.testCase.id,\n testCaseTitle: config.testCase.title,\n overallResult,\n steps: stepResults,\n totalDurationMs: Date.now() - startTime,\n summary,\n screenshotUrls: [],\n tokenUsage: {\n // Stagehand tracks tokens internally; these are approximate\n inputTokens: steps.length * 3000,\n outputTokens: steps.length * 500,\n },\n browserSessionId: session.sessionId,\n };\n } catch (err) {\n // Fatal error — test couldn't complete\n return {\n testCaseId: config.testCase.id,\n testCaseTitle: config.testCase.title,\n overallResult: 'error',\n steps: stepResults,\n totalDurationMs: Date.now() - startTime,\n summary: `Test execution failed: ${err instanceof Error ? err.message : String(err)}`,\n screenshotUrls: [],\n tokenUsage: {\n inputTokens: stepResults.length * 3000,\n outputTokens: stepResults.length * 500,\n },\n browserSessionId: session.sessionId,\n };\n } finally {\n await session.close();\n }\n}\n\n/**\n * Determine the overall test result from step results.\n */\nfunction determineOverallResult(steps: StepResult[]): OverallResult {\n if (steps.length === 0) return 'error';\n\n const allPassed = steps.every((s) => s.passed);\n const allFailed = steps.every((s) => !s.passed);\n const hasErrors = steps.some((s) => s.error);\n\n if (allPassed) return 'passed';\n if (allFailed || hasErrors) return 'failed';\n return 'partial';\n}\n","/**\n * Browser Provider\n *\n * Uses Stagehand (by Browserbase) for AI-powered browser automation.\n * Supports both Browserbase hosted sessions and local Playwright.\n */\n\nimport { Stagehand } from '@browserbasehq/stagehand';\nimport type { Page } from '@browserbasehq/stagehand';\nimport type { BrowserConfig, AuthConfig } from './types';\n\nconst DEFAULT_MODEL = 'anthropic/claude-sonnet-4-20250514';\n\nexport interface StagehandSession {\n stagehand: Stagehand;\n page: Page;\n sessionId: string;\n close: () => Promise<void>;\n}\n\n/**\n * Create a Stagehand-powered browser session.\n *\n * Stagehand wraps Playwright with AI capabilities (act, extract, observe)\n * and manages Browserbase or local browser sessions.\n */\nexport async function createStagehandSession(\n config: BrowserConfig,\n anthropicApiKey: string,\n): Promise<StagehandSession> {\n const modelName = config.model ?? DEFAULT_MODEL;\n const viewport = config.viewport ?? { width: 1280, height: 720 };\n\n const stagehand = new Stagehand({\n env: config.provider === 'browserbase' ? 'BROWSERBASE' : 'LOCAL',\n apiKey: config.provider === 'browserbase' ? config.browserbaseApiKey : undefined,\n projectId: config.provider === 'browserbase' ? config.browserbaseProjectId : undefined,\n model: {\n modelName: modelName as any,\n apiKey: anthropicApiKey,\n },\n localBrowserLaunchOptions:\n config.provider === 'local'\n ? {\n headless: config.headless ?? true,\n viewport,\n }\n : undefined,\n browserbaseSessionCreateParams:\n config.provider === 'browserbase'\n ? { projectId: config.browserbaseProjectId! }\n : undefined,\n });\n\n await stagehand.init();\n\n const page = stagehand.context.activePage()!;\n await page.setViewportSize(viewport.width, viewport.height);\n\n // Extract session ID\n let sessionId = `local-${Date.now()}`;\n if (config.provider === 'browserbase' && stagehand.browserbaseSessionID) {\n sessionId = stagehand.browserbaseSessionID;\n }\n\n return {\n stagehand,\n page,\n sessionId,\n close: async () => {\n await stagehand.close().catch(() => {});\n },\n };\n}\n\n/**\n * Inject authentication into the browser session.\n * Uses Stagehand's Page API and CDP for cookie injection.\n */\nexport async function injectAuth(page: Page, auth: AuthConfig, stagehand?: Stagehand): Promise<void> {\n if (auth.type === 'cookie') {\n // Use CDP to set cookies (Stagehand's Page doesn't have context().addCookies)\n for (const c of auth.cookies) {\n await page.sendCDP('Network.setCookie', {\n name: c.name,\n value: c.value,\n domain: c.domain,\n path: c.path ?? '/',\n secure: c.secure ?? false,\n httpOnly: c.httpOnly ?? false,\n sameSite: c.sameSite ?? 'Lax',\n });\n }\n } else if (auth.type === 'localStorage') {\n const currentUrl = page.url();\n if (currentUrl === 'about:blank') {\n // We'll inject localStorage after navigation in the runner\n return;\n }\n await page.evaluate((items: Record<string, string>) => {\n for (const [key, value] of Object.entries(items)) {\n localStorage.setItem(key, value);\n }\n }, auth.items);\n } else if (auth.type === 'form-login') {\n await performFormLogin(page, auth, stagehand);\n }\n}\n\n/**\n * Automate login via a web form.\n *\n * Uses Stagehand's act() when available for AI-powered form filling,\n * falling back to manual locator-based login.\n */\nasync function performFormLogin(\n page: Page,\n auth: { loginUrl: string; email: string; password: string },\n stagehand?: Stagehand,\n): Promise<void> {\n await page.goto(auth.loginUrl, { waitUntil: 'domcontentloaded' });\n\n // Wait for the page to settle\n await page.waitForLoadState('networkidle', 15_000).catch(() => {});\n\n if (stagehand) {\n // Use Stagehand's AI to handle login — much more reliable\n await stagehand.act(\n `Fill in the email/username field with \"${auth.email}\" and the password field with \"${auth.password}\", then click the login/sign-in button to submit the form.`,\n );\n } else {\n // Fallback: manual locator-based login\n await manualFormLogin(page, auth);\n }\n\n // Wait for navigation after login\n await page.waitForLoadState('networkidle', 15_000).catch(() => {});\n}\n\n/**\n * Manual form login using locators (fallback when Stagehand instance unavailable).\n */\nasync function manualFormLogin(\n page: Page,\n auth: { email: string; password: string },\n): Promise<void> {\n // Wait for a form or input to appear\n await page\n .waitForSelector(\n 'input[type=\"email\"], input[type=\"text\"][name*=\"email\"], input[name*=\"user\"], input[type=\"text\"]',\n { timeout: 15_000 },\n )\n .catch(() => {});\n\n // Try common email/username selectors in priority order\n const emailSelectors = [\n 'input[type=\"email\"]',\n 'input[name=\"email\"]',\n 'input[name=\"username\"]',\n 'input[autocomplete=\"email\"]',\n 'input[autocomplete=\"username\"]',\n 'input[type=\"text\"][name*=\"email\"]',\n 'input[type=\"text\"][name*=\"user\"]',\n 'input[type=\"text\"]',\n ];\n\n let emailFilled = false;\n for (const sel of emailSelectors) {\n const locator = page.locator(sel);\n if ((await locator.count()) > 0 && (await locator.isVisible())) {\n await locator.fill(auth.email);\n emailFilled = true;\n break;\n }\n }\n\n if (!emailFilled) {\n throw new Error('Could not find email/username input on login page');\n }\n\n // Fill password\n const passwordLocator = page.locator('input[type=\"password\"]');\n if ((await passwordLocator.count()) > 0 && (await passwordLocator.isVisible())) {\n await passwordLocator.fill(auth.password);\n } else {\n throw new Error('Could not find password input on login page');\n }\n\n // Submit the form\n const submitSelectors = [\n 'button[type=\"submit\"]',\n 'input[type=\"submit\"]',\n ];\n\n let submitted = false;\n for (const sel of submitSelectors) {\n const locator = page.locator(sel);\n if ((await locator.count()) > 0 && (await locator.isVisible())) {\n await locator.click();\n submitted = true;\n break;\n }\n }\n\n if (!submitted) {\n // Fallback: press Enter on the password field\n await page.locator('input[type=\"password\"]').type('\\n');\n }\n}\n","/**\n * Result Evaluator\n *\n * Generates AI summaries of test run results.\n * Step-level evaluation is now handled by Stagehand's extract() in the runner.\n */\n\nimport Anthropic from '@anthropic-ai/sdk';\n\n/**\n * Generate an AI summary of the entire test run.\n */\nexport async function generateRunSummary(\n anthropic: Anthropic,\n testTitle: string,\n steps: Array<{\n stepNumber: number;\n action: string;\n expectedResult: string;\n actualResult: string;\n passed: boolean;\n confidence: number;\n error?: string;\n }>,\n model: string,\n): Promise<string> {\n const stepsText = steps\n .map(\n (s) =>\n `Step ${s.stepNumber}: ${s.action}\\n Expected: ${s.expectedResult}\\n Actual: ${s.actualResult}\\n Result: ${s.passed ? 'PASS' : 'FAIL'} (confidence: ${Math.round(s.confidence * 100)}%)${s.error ? `\\n Error: ${s.error}` : ''}`\n )\n .join('\\n\\n');\n\n const passCount = steps.filter((s) => s.passed).length;\n const failCount = steps.filter((s) => !s.passed).length;\n\n const response = await anthropic.messages.create({\n model,\n max_tokens: 512,\n messages: [\n {\n role: 'user',\n content: `Summarize this AI test execution in 2-3 sentences. Focus on what was tested, what passed, and what failed (if anything). Be concise and factual.\n\nTest: ${testTitle}\nResults: ${passCount} passed, ${failCount} failed out of ${steps.length} steps\n\n${stepsText}`,\n },\n ],\n });\n\n return response.content\n .filter((block): block is Anthropic.TextBlock => block.type === 'text')\n .map((block) => block.text)\n .join('');\n}\n"],"mappings":"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","names":["Anthropic"]}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { Stagehand, Page } from '@browserbasehq/stagehand';
|
|
2
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @bbearai/ai-executor - Type definitions
|
|
6
|
+
*
|
|
7
|
+
* AI-powered QA test executor using Stagehand and Claude
|
|
8
|
+
*/
|
|
9
|
+
type BrowserProvider = 'browserbase' | 'local';
|
|
10
|
+
interface BrowserConfig {
|
|
11
|
+
provider: BrowserProvider;
|
|
12
|
+
/** Browserbase API key (required for provider: 'browserbase') */
|
|
13
|
+
browserbaseApiKey?: string;
|
|
14
|
+
/** Browserbase project ID */
|
|
15
|
+
browserbaseProjectId?: string;
|
|
16
|
+
/** Run headless (default: true) */
|
|
17
|
+
headless?: boolean;
|
|
18
|
+
/** Viewport dimensions */
|
|
19
|
+
viewport?: {
|
|
20
|
+
width: number;
|
|
21
|
+
height: number;
|
|
22
|
+
};
|
|
23
|
+
/** Per-action timeout in ms (default: 30000) */
|
|
24
|
+
timeout?: number;
|
|
25
|
+
/** AI model for Stagehand (default: anthropic/claude-sonnet-4-20250514) */
|
|
26
|
+
model?: string;
|
|
27
|
+
}
|
|
28
|
+
interface CookieAuth {
|
|
29
|
+
type: 'cookie';
|
|
30
|
+
cookies: Array<{
|
|
31
|
+
name: string;
|
|
32
|
+
value: string;
|
|
33
|
+
domain: string;
|
|
34
|
+
path?: string;
|
|
35
|
+
secure?: boolean;
|
|
36
|
+
httpOnly?: boolean;
|
|
37
|
+
sameSite?: 'Strict' | 'Lax' | 'None';
|
|
38
|
+
}>;
|
|
39
|
+
}
|
|
40
|
+
interface LocalStorageAuth {
|
|
41
|
+
type: 'localStorage';
|
|
42
|
+
items: Record<string, string>;
|
|
43
|
+
}
|
|
44
|
+
interface FormLoginAuth {
|
|
45
|
+
type: 'form-login';
|
|
46
|
+
/** URL of the login page (navigated to before filling credentials) */
|
|
47
|
+
loginUrl: string;
|
|
48
|
+
/** Email or username to enter */
|
|
49
|
+
email: string;
|
|
50
|
+
/** Password to enter */
|
|
51
|
+
password: string;
|
|
52
|
+
}
|
|
53
|
+
type AuthConfig = CookieAuth | LocalStorageAuth | FormLoginAuth;
|
|
54
|
+
interface TestStep {
|
|
55
|
+
stepNumber: number;
|
|
56
|
+
action: string;
|
|
57
|
+
expectedResult: string;
|
|
58
|
+
}
|
|
59
|
+
interface TestCaseInput {
|
|
60
|
+
id: string;
|
|
61
|
+
title: string;
|
|
62
|
+
description?: string;
|
|
63
|
+
steps: TestStep[];
|
|
64
|
+
preconditions?: string;
|
|
65
|
+
targetRoute?: string;
|
|
66
|
+
estimatedMinutes?: number;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* @deprecated Kept for backward compatibility with dashboard.
|
|
70
|
+
* Stagehand handles actions internally — this type is no longer used for new actions.
|
|
71
|
+
*/
|
|
72
|
+
interface StepAction {
|
|
73
|
+
type: string;
|
|
74
|
+
selector?: string;
|
|
75
|
+
value?: string;
|
|
76
|
+
url?: string;
|
|
77
|
+
timeout?: number;
|
|
78
|
+
description: string;
|
|
79
|
+
}
|
|
80
|
+
/** Browser console log entry captured during step execution */
|
|
81
|
+
interface ConsoleEntry {
|
|
82
|
+
level: 'error' | 'warning' | 'log' | 'info' | 'debug';
|
|
83
|
+
text: string;
|
|
84
|
+
/** URL of the source file (if available) */
|
|
85
|
+
source?: string;
|
|
86
|
+
/** Timestamp relative to step start (ms) */
|
|
87
|
+
timestamp: number;
|
|
88
|
+
}
|
|
89
|
+
/** Failed or errored network request captured during step execution */
|
|
90
|
+
interface NetworkError {
|
|
91
|
+
/** HTTP method */
|
|
92
|
+
method: string;
|
|
93
|
+
/** Request URL */
|
|
94
|
+
url: string;
|
|
95
|
+
/** HTTP status code (0 if request failed entirely) */
|
|
96
|
+
status: number;
|
|
97
|
+
/** Status text or failure reason */
|
|
98
|
+
statusText: string;
|
|
99
|
+
/** Timestamp relative to step start (ms) */
|
|
100
|
+
timestamp: number;
|
|
101
|
+
}
|
|
102
|
+
interface StepResult {
|
|
103
|
+
stepNumber: number;
|
|
104
|
+
action: string;
|
|
105
|
+
expectedResult: string;
|
|
106
|
+
actualResult: string;
|
|
107
|
+
passed: boolean;
|
|
108
|
+
/** Confidence score 0-1 */
|
|
109
|
+
confidence: number;
|
|
110
|
+
/** Screenshot before action execution */
|
|
111
|
+
screenshotBefore: Buffer;
|
|
112
|
+
/** Screenshot after action execution */
|
|
113
|
+
screenshotAfter: Buffer;
|
|
114
|
+
/** Actions taken (empty array — Stagehand handles actions internally) */
|
|
115
|
+
actionsTaken: StepAction[];
|
|
116
|
+
/** Error message if step errored */
|
|
117
|
+
error?: string;
|
|
118
|
+
/** Duration of step execution in ms */
|
|
119
|
+
durationMs: number;
|
|
120
|
+
/** Browser console logs captured during this step */
|
|
121
|
+
consoleLogs: ConsoleEntry[];
|
|
122
|
+
/** Failed/errored network requests during this step */
|
|
123
|
+
networkErrors: NetworkError[];
|
|
124
|
+
}
|
|
125
|
+
interface TestRunConfig {
|
|
126
|
+
/** Base URL of the application under test */
|
|
127
|
+
targetUrl: string;
|
|
128
|
+
/** Test case to execute */
|
|
129
|
+
testCase: TestCaseInput;
|
|
130
|
+
/** Authentication configuration */
|
|
131
|
+
auth?: AuthConfig;
|
|
132
|
+
/** Browser configuration */
|
|
133
|
+
browser?: BrowserConfig;
|
|
134
|
+
/** Anthropic API key */
|
|
135
|
+
anthropicApiKey: string;
|
|
136
|
+
/** Claude model for summary generation (default: claude-sonnet-4-20250514) */
|
|
137
|
+
model?: string;
|
|
138
|
+
/** Supabase config for uploading screenshots */
|
|
139
|
+
supabase?: {
|
|
140
|
+
url: string;
|
|
141
|
+
anonKey: string;
|
|
142
|
+
projectId: string;
|
|
143
|
+
};
|
|
144
|
+
/** Callback for real-time progress updates */
|
|
145
|
+
onStepComplete?: (result: StepResult, index: number, total: number) => void;
|
|
146
|
+
/** Callback for status changes */
|
|
147
|
+
onStatusChange?: (status: TestRunStatus) => void;
|
|
148
|
+
}
|
|
149
|
+
type TestRunStatus = 'initializing' | 'navigating' | 'authenticating' | 'executing' | 'evaluating' | 'completed' | 'error';
|
|
150
|
+
type OverallResult = 'passed' | 'failed' | 'error' | 'partial';
|
|
151
|
+
interface TestRunResult {
|
|
152
|
+
testCaseId: string;
|
|
153
|
+
testCaseTitle: string;
|
|
154
|
+
overallResult: OverallResult;
|
|
155
|
+
steps: StepResult[];
|
|
156
|
+
totalDurationMs: number;
|
|
157
|
+
/** AI-generated summary of the entire test run */
|
|
158
|
+
summary: string;
|
|
159
|
+
/** URLs of uploaded screenshots (if Supabase configured) */
|
|
160
|
+
screenshotUrls: string[];
|
|
161
|
+
/** Claude API token usage */
|
|
162
|
+
tokenUsage: {
|
|
163
|
+
inputTokens: number;
|
|
164
|
+
outputTokens: number;
|
|
165
|
+
};
|
|
166
|
+
/** Browserbase session ID (if applicable) */
|
|
167
|
+
browserSessionId?: string;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Test Runner
|
|
172
|
+
*
|
|
173
|
+
* Orchestrates the full test execution lifecycle using Stagehand:
|
|
174
|
+
* 1. Launch Stagehand browser session
|
|
175
|
+
* 2. Navigate to target URL
|
|
176
|
+
* 3. Inject authentication
|
|
177
|
+
* 4. For each step: act() → screenshot → extract() → record
|
|
178
|
+
* 5. Generate summary
|
|
179
|
+
* 6. Return structured results
|
|
180
|
+
*/
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Execute a test case against a target URL using Stagehand.
|
|
184
|
+
*/
|
|
185
|
+
declare function runTest(config: TestRunConfig): Promise<TestRunResult>;
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Browser Provider
|
|
189
|
+
*
|
|
190
|
+
* Uses Stagehand (by Browserbase) for AI-powered browser automation.
|
|
191
|
+
* Supports both Browserbase hosted sessions and local Playwright.
|
|
192
|
+
*/
|
|
193
|
+
|
|
194
|
+
interface StagehandSession {
|
|
195
|
+
stagehand: Stagehand;
|
|
196
|
+
page: Page;
|
|
197
|
+
sessionId: string;
|
|
198
|
+
close: () => Promise<void>;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Create a Stagehand-powered browser session.
|
|
202
|
+
*
|
|
203
|
+
* Stagehand wraps Playwright with AI capabilities (act, extract, observe)
|
|
204
|
+
* and manages Browserbase or local browser sessions.
|
|
205
|
+
*/
|
|
206
|
+
declare function createStagehandSession(config: BrowserConfig, anthropicApiKey: string): Promise<StagehandSession>;
|
|
207
|
+
/**
|
|
208
|
+
* Inject authentication into the browser session.
|
|
209
|
+
* Uses Stagehand's Page API and CDP for cookie injection.
|
|
210
|
+
*/
|
|
211
|
+
declare function injectAuth(page: Page, auth: AuthConfig, stagehand?: Stagehand): Promise<void>;
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Result Evaluator
|
|
215
|
+
*
|
|
216
|
+
* Generates AI summaries of test run results.
|
|
217
|
+
* Step-level evaluation is now handled by Stagehand's extract() in the runner.
|
|
218
|
+
*/
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Generate an AI summary of the entire test run.
|
|
222
|
+
*/
|
|
223
|
+
declare function generateRunSummary(anthropic: Anthropic, testTitle: string, steps: Array<{
|
|
224
|
+
stepNumber: number;
|
|
225
|
+
action: string;
|
|
226
|
+
expectedResult: string;
|
|
227
|
+
actualResult: string;
|
|
228
|
+
passed: boolean;
|
|
229
|
+
confidence: number;
|
|
230
|
+
error?: string;
|
|
231
|
+
}>, model: string): Promise<string>;
|
|
232
|
+
|
|
233
|
+
export { type AuthConfig, type BrowserConfig, type BrowserProvider, type ConsoleEntry, type CookieAuth, type FormLoginAuth, type LocalStorageAuth, type NetworkError, type OverallResult, type StagehandSession, type StepAction, type StepResult, type TestCaseInput, type TestRunConfig, type TestRunResult, type TestRunStatus, type TestStep, createStagehandSession, generateRunSummary, injectAuth, runTest };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { Stagehand, Page } from '@browserbasehq/stagehand';
|
|
2
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @bbearai/ai-executor - Type definitions
|
|
6
|
+
*
|
|
7
|
+
* AI-powered QA test executor using Stagehand and Claude
|
|
8
|
+
*/
|
|
9
|
+
type BrowserProvider = 'browserbase' | 'local';
|
|
10
|
+
interface BrowserConfig {
|
|
11
|
+
provider: BrowserProvider;
|
|
12
|
+
/** Browserbase API key (required for provider: 'browserbase') */
|
|
13
|
+
browserbaseApiKey?: string;
|
|
14
|
+
/** Browserbase project ID */
|
|
15
|
+
browserbaseProjectId?: string;
|
|
16
|
+
/** Run headless (default: true) */
|
|
17
|
+
headless?: boolean;
|
|
18
|
+
/** Viewport dimensions */
|
|
19
|
+
viewport?: {
|
|
20
|
+
width: number;
|
|
21
|
+
height: number;
|
|
22
|
+
};
|
|
23
|
+
/** Per-action timeout in ms (default: 30000) */
|
|
24
|
+
timeout?: number;
|
|
25
|
+
/** AI model for Stagehand (default: anthropic/claude-sonnet-4-20250514) */
|
|
26
|
+
model?: string;
|
|
27
|
+
}
|
|
28
|
+
interface CookieAuth {
|
|
29
|
+
type: 'cookie';
|
|
30
|
+
cookies: Array<{
|
|
31
|
+
name: string;
|
|
32
|
+
value: string;
|
|
33
|
+
domain: string;
|
|
34
|
+
path?: string;
|
|
35
|
+
secure?: boolean;
|
|
36
|
+
httpOnly?: boolean;
|
|
37
|
+
sameSite?: 'Strict' | 'Lax' | 'None';
|
|
38
|
+
}>;
|
|
39
|
+
}
|
|
40
|
+
interface LocalStorageAuth {
|
|
41
|
+
type: 'localStorage';
|
|
42
|
+
items: Record<string, string>;
|
|
43
|
+
}
|
|
44
|
+
interface FormLoginAuth {
|
|
45
|
+
type: 'form-login';
|
|
46
|
+
/** URL of the login page (navigated to before filling credentials) */
|
|
47
|
+
loginUrl: string;
|
|
48
|
+
/** Email or username to enter */
|
|
49
|
+
email: string;
|
|
50
|
+
/** Password to enter */
|
|
51
|
+
password: string;
|
|
52
|
+
}
|
|
53
|
+
type AuthConfig = CookieAuth | LocalStorageAuth | FormLoginAuth;
|
|
54
|
+
interface TestStep {
|
|
55
|
+
stepNumber: number;
|
|
56
|
+
action: string;
|
|
57
|
+
expectedResult: string;
|
|
58
|
+
}
|
|
59
|
+
interface TestCaseInput {
|
|
60
|
+
id: string;
|
|
61
|
+
title: string;
|
|
62
|
+
description?: string;
|
|
63
|
+
steps: TestStep[];
|
|
64
|
+
preconditions?: string;
|
|
65
|
+
targetRoute?: string;
|
|
66
|
+
estimatedMinutes?: number;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* @deprecated Kept for backward compatibility with dashboard.
|
|
70
|
+
* Stagehand handles actions internally — this type is no longer used for new actions.
|
|
71
|
+
*/
|
|
72
|
+
interface StepAction {
|
|
73
|
+
type: string;
|
|
74
|
+
selector?: string;
|
|
75
|
+
value?: string;
|
|
76
|
+
url?: string;
|
|
77
|
+
timeout?: number;
|
|
78
|
+
description: string;
|
|
79
|
+
}
|
|
80
|
+
/** Browser console log entry captured during step execution */
|
|
81
|
+
interface ConsoleEntry {
|
|
82
|
+
level: 'error' | 'warning' | 'log' | 'info' | 'debug';
|
|
83
|
+
text: string;
|
|
84
|
+
/** URL of the source file (if available) */
|
|
85
|
+
source?: string;
|
|
86
|
+
/** Timestamp relative to step start (ms) */
|
|
87
|
+
timestamp: number;
|
|
88
|
+
}
|
|
89
|
+
/** Failed or errored network request captured during step execution */
|
|
90
|
+
interface NetworkError {
|
|
91
|
+
/** HTTP method */
|
|
92
|
+
method: string;
|
|
93
|
+
/** Request URL */
|
|
94
|
+
url: string;
|
|
95
|
+
/** HTTP status code (0 if request failed entirely) */
|
|
96
|
+
status: number;
|
|
97
|
+
/** Status text or failure reason */
|
|
98
|
+
statusText: string;
|
|
99
|
+
/** Timestamp relative to step start (ms) */
|
|
100
|
+
timestamp: number;
|
|
101
|
+
}
|
|
102
|
+
interface StepResult {
|
|
103
|
+
stepNumber: number;
|
|
104
|
+
action: string;
|
|
105
|
+
expectedResult: string;
|
|
106
|
+
actualResult: string;
|
|
107
|
+
passed: boolean;
|
|
108
|
+
/** Confidence score 0-1 */
|
|
109
|
+
confidence: number;
|
|
110
|
+
/** Screenshot before action execution */
|
|
111
|
+
screenshotBefore: Buffer;
|
|
112
|
+
/** Screenshot after action execution */
|
|
113
|
+
screenshotAfter: Buffer;
|
|
114
|
+
/** Actions taken (empty array — Stagehand handles actions internally) */
|
|
115
|
+
actionsTaken: StepAction[];
|
|
116
|
+
/** Error message if step errored */
|
|
117
|
+
error?: string;
|
|
118
|
+
/** Duration of step execution in ms */
|
|
119
|
+
durationMs: number;
|
|
120
|
+
/** Browser console logs captured during this step */
|
|
121
|
+
consoleLogs: ConsoleEntry[];
|
|
122
|
+
/** Failed/errored network requests during this step */
|
|
123
|
+
networkErrors: NetworkError[];
|
|
124
|
+
}
|
|
125
|
+
interface TestRunConfig {
|
|
126
|
+
/** Base URL of the application under test */
|
|
127
|
+
targetUrl: string;
|
|
128
|
+
/** Test case to execute */
|
|
129
|
+
testCase: TestCaseInput;
|
|
130
|
+
/** Authentication configuration */
|
|
131
|
+
auth?: AuthConfig;
|
|
132
|
+
/** Browser configuration */
|
|
133
|
+
browser?: BrowserConfig;
|
|
134
|
+
/** Anthropic API key */
|
|
135
|
+
anthropicApiKey: string;
|
|
136
|
+
/** Claude model for summary generation (default: claude-sonnet-4-20250514) */
|
|
137
|
+
model?: string;
|
|
138
|
+
/** Supabase config for uploading screenshots */
|
|
139
|
+
supabase?: {
|
|
140
|
+
url: string;
|
|
141
|
+
anonKey: string;
|
|
142
|
+
projectId: string;
|
|
143
|
+
};
|
|
144
|
+
/** Callback for real-time progress updates */
|
|
145
|
+
onStepComplete?: (result: StepResult, index: number, total: number) => void;
|
|
146
|
+
/** Callback for status changes */
|
|
147
|
+
onStatusChange?: (status: TestRunStatus) => void;
|
|
148
|
+
}
|
|
149
|
+
type TestRunStatus = 'initializing' | 'navigating' | 'authenticating' | 'executing' | 'evaluating' | 'completed' | 'error';
|
|
150
|
+
type OverallResult = 'passed' | 'failed' | 'error' | 'partial';
|
|
151
|
+
interface TestRunResult {
|
|
152
|
+
testCaseId: string;
|
|
153
|
+
testCaseTitle: string;
|
|
154
|
+
overallResult: OverallResult;
|
|
155
|
+
steps: StepResult[];
|
|
156
|
+
totalDurationMs: number;
|
|
157
|
+
/** AI-generated summary of the entire test run */
|
|
158
|
+
summary: string;
|
|
159
|
+
/** URLs of uploaded screenshots (if Supabase configured) */
|
|
160
|
+
screenshotUrls: string[];
|
|
161
|
+
/** Claude API token usage */
|
|
162
|
+
tokenUsage: {
|
|
163
|
+
inputTokens: number;
|
|
164
|
+
outputTokens: number;
|
|
165
|
+
};
|
|
166
|
+
/** Browserbase session ID (if applicable) */
|
|
167
|
+
browserSessionId?: string;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Test Runner
|
|
172
|
+
*
|
|
173
|
+
* Orchestrates the full test execution lifecycle using Stagehand:
|
|
174
|
+
* 1. Launch Stagehand browser session
|
|
175
|
+
* 2. Navigate to target URL
|
|
176
|
+
* 3. Inject authentication
|
|
177
|
+
* 4. For each step: act() → screenshot → extract() → record
|
|
178
|
+
* 5. Generate summary
|
|
179
|
+
* 6. Return structured results
|
|
180
|
+
*/
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Execute a test case against a target URL using Stagehand.
|
|
184
|
+
*/
|
|
185
|
+
declare function runTest(config: TestRunConfig): Promise<TestRunResult>;
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Browser Provider
|
|
189
|
+
*
|
|
190
|
+
* Uses Stagehand (by Browserbase) for AI-powered browser automation.
|
|
191
|
+
* Supports both Browserbase hosted sessions and local Playwright.
|
|
192
|
+
*/
|
|
193
|
+
|
|
194
|
+
interface StagehandSession {
|
|
195
|
+
stagehand: Stagehand;
|
|
196
|
+
page: Page;
|
|
197
|
+
sessionId: string;
|
|
198
|
+
close: () => Promise<void>;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Create a Stagehand-powered browser session.
|
|
202
|
+
*
|
|
203
|
+
* Stagehand wraps Playwright with AI capabilities (act, extract, observe)
|
|
204
|
+
* and manages Browserbase or local browser sessions.
|
|
205
|
+
*/
|
|
206
|
+
declare function createStagehandSession(config: BrowserConfig, anthropicApiKey: string): Promise<StagehandSession>;
|
|
207
|
+
/**
|
|
208
|
+
* Inject authentication into the browser session.
|
|
209
|
+
* Uses Stagehand's Page API and CDP for cookie injection.
|
|
210
|
+
*/
|
|
211
|
+
declare function injectAuth(page: Page, auth: AuthConfig, stagehand?: Stagehand): Promise<void>;
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Result Evaluator
|
|
215
|
+
*
|
|
216
|
+
* Generates AI summaries of test run results.
|
|
217
|
+
* Step-level evaluation is now handled by Stagehand's extract() in the runner.
|
|
218
|
+
*/
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Generate an AI summary of the entire test run.
|
|
222
|
+
*/
|
|
223
|
+
declare function generateRunSummary(anthropic: Anthropic, testTitle: string, steps: Array<{
|
|
224
|
+
stepNumber: number;
|
|
225
|
+
action: string;
|
|
226
|
+
expectedResult: string;
|
|
227
|
+
actualResult: string;
|
|
228
|
+
passed: boolean;
|
|
229
|
+
confidence: number;
|
|
230
|
+
error?: string;
|
|
231
|
+
}>, model: string): Promise<string>;
|
|
232
|
+
|
|
233
|
+
export { type AuthConfig, type BrowserConfig, type BrowserProvider, type ConsoleEntry, type CookieAuth, type FormLoginAuth, type LocalStorageAuth, type NetworkError, type OverallResult, type StagehandSession, type StepAction, type StepResult, type TestCaseInput, type TestRunConfig, type TestRunResult, type TestRunStatus, type TestStep, createStagehandSession, generateRunSummary, injectAuth, runTest };
|