@browserbasehq/eve 0.1.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/README.md +117 -0
- package/dist/extension/_manifest.json +13 -0
- package/dist/extension/extension.d.ts +8 -0
- package/dist/extension/extension.mjs +14 -0
- package/dist/extension/instructions/browser.md +9 -0
- package/dist/extension/lib/browserbase.d.ts +2 -0
- package/dist/extension/lib/browserbase.mjs +11 -0
- package/dist/extension/lib/disconnect.d.ts +5 -0
- package/dist/extension/lib/disconnect.mjs +9 -0
- package/dist/extension/lib/release-session.d.ts +14 -0
- package/dist/extension/lib/release-session.mjs +38 -0
- package/dist/extension/lib/session-lock.d.ts +1 -0
- package/dist/extension/lib/session-lock.mjs +22 -0
- package/dist/extension/lib/session-state.d.ts +5 -0
- package/dist/extension/lib/session-state.mjs +11 -0
- package/dist/extension/lib/stagehand.d.ts +10 -0
- package/dist/extension/lib/stagehand.mjs +118 -0
- package/dist/extension/tools/act.d.ts +4 -0
- package/dist/extension/tools/act.mjs +16 -0
- package/dist/extension/tools/agent.d.ts +5 -0
- package/dist/extension/tools/agent.mjs +24 -0
- package/dist/extension/tools/extract.d.ts +5 -0
- package/dist/extension/tools/extract.mjs +21 -0
- package/dist/extension/tools/fetch.d.ts +9 -0
- package/dist/extension/tools/fetch.mjs +37 -0
- package/dist/extension/tools/navigate.d.ts +7 -0
- package/dist/extension/tools/navigate.mjs +23 -0
- package/dist/extension/tools/observe.d.ts +4 -0
- package/dist/extension/tools/observe.mjs +16 -0
- package/dist/extension/tools/search.d.ts +5 -0
- package/dist/extension/tools/search.mjs +22 -0
- package/dist/extension/tools/session.d.ts +9 -0
- package/dist/extension/tools/session.mjs +29 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.mjs +7 -0
- package/dist/tools/index.d.ts +10 -0
- package/dist/tools/index.mjs +14 -0
- package/package.json +57 -0
package/README.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Browserbase for Eve
|
|
2
|
+
|
|
3
|
+
An [Eve extension](https://eve.dev/docs/extensions) that gives an agent
|
|
4
|
+
Browserbase Search and Fetch plus a persistent Browserbase browser powered by
|
|
5
|
+
[Stagehand](https://docs.stagehand.dev/). It exposes Stagehand's focused
|
|
6
|
+
primitives (`act`, `observe`, and `extract`) plus navigation and autonomous agent
|
|
7
|
+
tools.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm add @browserbasehq/eve
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Add your credentials to the consuming Eve app:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
BROWSERBASE_API_KEY=bb_live_...
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Mount the extension
|
|
22
|
+
|
|
23
|
+
The filename under `agent/extensions/` becomes the tool namespace. Mounting the
|
|
24
|
+
extension as `browserbase.ts` creates tools such as `browserbase__search`,
|
|
25
|
+
`browserbase__fetch`, `browserbase__create_session`, and
|
|
26
|
+
`browserbase__navigate`.
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
// agent/extensions/browserbase.ts
|
|
30
|
+
import browserbase from '@browserbasehq/eve';
|
|
31
|
+
|
|
32
|
+
export default browserbase({
|
|
33
|
+
apiKey: process.env.BROWSERBASE_API_KEY!,
|
|
34
|
+
model: 'openai/gpt-5.4-mini',
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Start Eve and give the agent a browser task:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pnpm exec eve dev
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```text
|
|
45
|
+
Open https://news.ycombinator.com and extract the titles and URLs of the first
|
|
46
|
+
five stories.
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The extension keeps the Browserbase session ID in Eve's durable per-session
|
|
50
|
+
state. Each browser tool reconnects to that browser, performs one operation,
|
|
51
|
+
and disconnects without terminating it. `browserbase__create_session` creates
|
|
52
|
+
or reconnects the browser explicitly, and `browserbase__stop_session`
|
|
53
|
+
terminates it.
|
|
54
|
+
|
|
55
|
+
## Tools
|
|
56
|
+
|
|
57
|
+
| Tool | Purpose |
|
|
58
|
+
| ---------------- | ------------------------------------------------------- |
|
|
59
|
+
| `search` | Find relevant public web pages with Browserbase Search. |
|
|
60
|
+
| `fetch` | Retrieve raw, markdown, or structured page content. |
|
|
61
|
+
| `create_session` | Create or reconnect the Browserbase session. |
|
|
62
|
+
| `stop_session` | Stop the Browserbase session and release resources. |
|
|
63
|
+
| `navigate` | Open a URL in the current browser. |
|
|
64
|
+
| `observe` | Find relevant elements and candidate actions. |
|
|
65
|
+
| `act` | Perform one natural-language page interaction. |
|
|
66
|
+
| `extract` | Return data validated against a supplied JSON Schema. |
|
|
67
|
+
| `agent` | Run a multi-step autonomous Stagehand task. |
|
|
68
|
+
|
|
69
|
+
Use Search → Fetch → browser as an escalation path: discover sources cheaply,
|
|
70
|
+
retrieve straightforward content without a session, and create a browser only
|
|
71
|
+
when the page requires JavaScript or interaction. Fetch supports Browserbase's
|
|
72
|
+
`raw`, `markdown`, and schema-driven `json` formats.
|
|
73
|
+
|
|
74
|
+
For predictable and efficient runs, use `create_session` → `navigate` →
|
|
75
|
+
`observe` → `act` or `extract`, then `stop_session`. Reserve `agent` for
|
|
76
|
+
workflows that need Stagehand to plan several steps on its own.
|
|
77
|
+
|
|
78
|
+
## Configuration
|
|
79
|
+
|
|
80
|
+
| Option | Default | Description |
|
|
81
|
+
| ----------------------- | --------------------- | --------------------------------------------------- |
|
|
82
|
+
| `apiKey` | required | Browserbase API key for browsers and Model Gateway. |
|
|
83
|
+
| `model` | `openai/gpt-5.4-mini` | Stagehand Model Gateway model identifier. |
|
|
84
|
+
| `sessionTimeoutSeconds` | `900` | Session timeout, from 60 to 21,600 seconds. |
|
|
85
|
+
| `proxies` | `false` | Enable Browserbase proxies for new sessions. |
|
|
86
|
+
|
|
87
|
+
Stagehand runs through Browserbase Model Gateway, so consumers do not need an
|
|
88
|
+
OpenAI or other model-provider API key. The Browserbase API key covers both the
|
|
89
|
+
browser session and Stagehand inference.
|
|
90
|
+
|
|
91
|
+
The extension uses Browserbase `keepAlive` sessions so it can reconnect across
|
|
92
|
+
Eve workflow steps and Vercel function invocations. Parallel browser calls made
|
|
93
|
+
inside one Eve workflow step are queued in that step's managed runtime; durable
|
|
94
|
+
state reconnects later steps and invocations. Close the session after the task
|
|
95
|
+
to avoid leaving billable browser time running. Keep-alive availability depends
|
|
96
|
+
on your Browserbase plan.
|
|
97
|
+
|
|
98
|
+
## Build
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
nvm use
|
|
102
|
+
pnpm install
|
|
103
|
+
pnpm check
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Eve requires Node.js 24 or newer. This directory's `.nvmrc` pins Node 24.16.0;
|
|
107
|
+
run `nvm install 24.16.0` first if needed.
|
|
108
|
+
|
|
109
|
+
`eve extension build` writes the publishable extension and type declarations to
|
|
110
|
+
`dist/`.
|
|
111
|
+
|
|
112
|
+
## Example agent
|
|
113
|
+
|
|
114
|
+
The [`eve-example`](../../examples/integrations/vercel/eve-example/) directory
|
|
115
|
+
contains a runnable Eve agent that mounts this package locally. It includes the
|
|
116
|
+
agent instructions, environment template, and a Browserbase research prompt for
|
|
117
|
+
a complete smoke test.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
declare const _default: import("eve/extension").ExtensionHandle<z.ZodObject<{
|
|
3
|
+
apiKey: z.ZodString;
|
|
4
|
+
model: z.ZodDefault<z.ZodString>;
|
|
5
|
+
sessionTimeoutSeconds: z.ZodDefault<z.ZodNumber>;
|
|
6
|
+
proxies: z.ZodDefault<z.ZodBoolean>;
|
|
7
|
+
}, z.core.$strip>>;
|
|
8
|
+
export default _default;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { fileURLToPath as __eveFileURLToPath } from "node:url";
|
|
2
|
+
import { dirname as __eveDirname } from "node:path";
|
|
3
|
+
import { createRequire as __eveCreateRequire } from "node:module";
|
|
4
|
+
__eveDirname(__eveFileURLToPath(import.meta.url));
|
|
5
|
+
__eveCreateRequire(import.meta.url);
|
|
6
|
+
import { defineExtension } from "eve/extension";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
var extension_default = defineExtension({ config: z.object({
|
|
9
|
+
apiKey: z.string().min(1),
|
|
10
|
+
model: z.string().min(1).default("openai/gpt-5.4-mini"),
|
|
11
|
+
sessionTimeoutSeconds: z.number().int().min(60).max(21600).default(900),
|
|
12
|
+
proxies: z.boolean().default(false)
|
|
13
|
+
}) });
|
|
14
|
+
export { extension_default as default };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Use Browserbase Search to discover relevant URLs and Browserbase Fetch for quick
|
|
2
|
+
retrievals that do not need JavaScript or interaction. Escalate to a browser
|
|
3
|
+
session when a page needs rendering or interaction: create a session, navigate,
|
|
4
|
+
then use observe to plan, act for one interaction, and extract for structured
|
|
5
|
+
results. Use agent only for genuinely multi-step work. Browser tools share one
|
|
6
|
+
Browserbase session per Eve session. Parallel browser calls from the same Eve
|
|
7
|
+
workflow step are queued within that step; later steps reconnect through durable
|
|
8
|
+
state. Prefer calling tools sequentially, and stop the browser session when the
|
|
9
|
+
task is complete.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { fileURLToPath as __eveFileURLToPath } from "node:url";
|
|
2
|
+
import { dirname as __eveDirname } from "node:path";
|
|
3
|
+
import { createRequire as __eveCreateRequire } from "node:module";
|
|
4
|
+
__eveDirname(__eveFileURLToPath(import.meta.url));
|
|
5
|
+
__eveCreateRequire(import.meta.url);
|
|
6
|
+
import extension_default from "../extension.mjs";
|
|
7
|
+
import Browserbase from "@browserbasehq/sdk";
|
|
8
|
+
function createBrowserbaseClient() {
|
|
9
|
+
return new Browserbase({ apiKey: extension_default.config.apiKey });
|
|
10
|
+
}
|
|
11
|
+
export { createBrowserbaseClient };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { fileURLToPath as __eveFileURLToPath } from "node:url";
|
|
2
|
+
import { dirname as __eveDirname } from "node:path";
|
|
3
|
+
import { createRequire as __eveCreateRequire } from "node:module";
|
|
4
|
+
__eveDirname(__eveFileURLToPath(import.meta.url));
|
|
5
|
+
__eveCreateRequire(import.meta.url);
|
|
6
|
+
async function disconnectStagehand(stagehand) {
|
|
7
|
+
await stagehand.close().catch(() => {});
|
|
8
|
+
}
|
|
9
|
+
export { disconnectStagehand };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
type BrowserbaseSessionStatus = 'PENDING' | 'RUNNING' | 'ERROR' | 'TIMED_OUT' | 'COMPLETED';
|
|
2
|
+
interface BrowserbaseSessionRecord {
|
|
3
|
+
status: BrowserbaseSessionStatus;
|
|
4
|
+
}
|
|
5
|
+
interface BrowserbaseSessionApi {
|
|
6
|
+
retrieve(sessionId: string): Promise<BrowserbaseSessionRecord>;
|
|
7
|
+
update(sessionId: string, body: {
|
|
8
|
+
status: 'REQUEST_RELEASE';
|
|
9
|
+
}): Promise<BrowserbaseSessionRecord>;
|
|
10
|
+
}
|
|
11
|
+
export type RemoteSessionState = 'active' | 'terminal' | 'unknown';
|
|
12
|
+
export declare function getRemoteSessionState(sessions: Pick<BrowserbaseSessionApi, 'retrieve'>, sessionId: string): Promise<RemoteSessionState>;
|
|
13
|
+
export declare function releaseBrowserbaseSession(sessions: BrowserbaseSessionApi, sessionId: string, wait?: (milliseconds: number) => Promise<void>): Promise<void>;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { fileURLToPath as __eveFileURLToPath } from "node:url";
|
|
2
|
+
import { dirname as __eveDirname } from "node:path";
|
|
3
|
+
import { createRequire as __eveCreateRequire } from "node:module";
|
|
4
|
+
__eveDirname(__eveFileURLToPath(import.meta.url));
|
|
5
|
+
__eveCreateRequire(import.meta.url);
|
|
6
|
+
import { APIError } from "@browserbasehq/sdk";
|
|
7
|
+
const RELEASE_POLL_DELAYS_MS = [
|
|
8
|
+
100,
|
|
9
|
+
250,
|
|
10
|
+
500,
|
|
11
|
+
1e3
|
|
12
|
+
];
|
|
13
|
+
function isActiveSessionStatus(status) {
|
|
14
|
+
return status === "PENDING" || status === "RUNNING";
|
|
15
|
+
}
|
|
16
|
+
async function getRemoteSessionState(sessions, sessionId) {
|
|
17
|
+
try {
|
|
18
|
+
return isActiveSessionStatus((await sessions.retrieve(sessionId)).status) ? "active" : "terminal";
|
|
19
|
+
} catch (error) {
|
|
20
|
+
return error instanceof APIError && error.status === 404 ? "terminal" : "unknown";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async function releaseBrowserbaseSession(sessions, sessionId, wait = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds))) {
|
|
24
|
+
try {
|
|
25
|
+
if (!isActiveSessionStatus((await sessions.update(sessionId, { status: "REQUEST_RELEASE" })).status)) return;
|
|
26
|
+
} catch (error) {
|
|
27
|
+
if (error instanceof APIError && error.status === 404) return;
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
for (const delay of RELEASE_POLL_DELAYS_MS) {
|
|
31
|
+
await wait(delay);
|
|
32
|
+
const state = await getRemoteSessionState(sessions, sessionId);
|
|
33
|
+
if (state === "terminal") return;
|
|
34
|
+
if (state === "unknown") throw new Error(`Unable to confirm Browserbase session ${sessionId} was released.`);
|
|
35
|
+
}
|
|
36
|
+
throw new Error(`Browserbase session ${sessionId} is still active after requesting release.`);
|
|
37
|
+
}
|
|
38
|
+
export { getRemoteSessionState, releaseBrowserbaseSession };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function withSessionLock<T>(sessionId: string, operation: () => Promise<T>): Promise<T>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { fileURLToPath as __eveFileURLToPath } from "node:url";
|
|
2
|
+
import { dirname as __eveDirname } from "node:path";
|
|
3
|
+
import { createRequire as __eveCreateRequire } from "node:module";
|
|
4
|
+
__eveDirname(__eveFileURLToPath(import.meta.url));
|
|
5
|
+
__eveCreateRequire(import.meta.url);
|
|
6
|
+
const sessionTails = new Map();
|
|
7
|
+
async function withSessionLock(sessionId, operation) {
|
|
8
|
+
const previous = sessionTails.get(sessionId) ?? Promise.resolve();
|
|
9
|
+
let release = () => {};
|
|
10
|
+
const current = new Promise((resolve) => {
|
|
11
|
+
release = resolve;
|
|
12
|
+
});
|
|
13
|
+
sessionTails.set(sessionId, current);
|
|
14
|
+
await previous;
|
|
15
|
+
try {
|
|
16
|
+
return await operation();
|
|
17
|
+
} finally {
|
|
18
|
+
release();
|
|
19
|
+
if (sessionTails.get(sessionId) === current) sessionTails.delete(sessionId);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export { withSessionLock };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { fileURLToPath as __eveFileURLToPath } from "node:url";
|
|
2
|
+
import { dirname as __eveDirname } from "node:path";
|
|
3
|
+
import { createRequire as __eveCreateRequire } from "node:module";
|
|
4
|
+
__eveDirname(__eveFileURLToPath(import.meta.url));
|
|
5
|
+
__eveCreateRequire(import.meta.url);
|
|
6
|
+
import { defineState } from "eve/context";
|
|
7
|
+
const browserSession = defineState("browser-session", () => ({
|
|
8
|
+
id: null,
|
|
9
|
+
url: null
|
|
10
|
+
}));
|
|
11
|
+
export { browserSession };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Stagehand } from '@browserbasehq/stagehand';
|
|
2
|
+
import { type BrowserSessionState } from './session-state';
|
|
3
|
+
type StagehandOperation<T> = (stagehand: Stagehand) => Promise<T>;
|
|
4
|
+
export interface BrowserSessionResult extends BrowserSessionState {
|
|
5
|
+
created: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function withStagehand<T>(sessionId: string, operation: StagehandOperation<T>): Promise<T>;
|
|
8
|
+
export declare function createBrowserSession(sessionId: string): Promise<BrowserSessionResult>;
|
|
9
|
+
export declare function closeBrowserSession(sessionId: string): Promise<BrowserSessionState>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { fileURLToPath as __eveFileURLToPath } from "node:url";
|
|
2
|
+
import { dirname as __eveDirname } from "node:path";
|
|
3
|
+
import { createRequire as __eveCreateRequire } from "node:module";
|
|
4
|
+
__eveDirname(__eveFileURLToPath(import.meta.url));
|
|
5
|
+
__eveCreateRequire(import.meta.url);
|
|
6
|
+
import extension_default from "../extension.mjs";
|
|
7
|
+
import { createBrowserbaseClient } from "./browserbase.mjs";
|
|
8
|
+
import { disconnectStagehand } from "./disconnect.mjs";
|
|
9
|
+
import { getRemoteSessionState, releaseBrowserbaseSession } from "./release-session.mjs";
|
|
10
|
+
import { withSessionLock } from "./session-lock.mjs";
|
|
11
|
+
import { browserSession } from "./session-state.mjs";
|
|
12
|
+
import { Stagehand } from "@browserbasehq/stagehand";
|
|
13
|
+
function createStagehand() {
|
|
14
|
+
const { apiKey, model, proxies, sessionTimeoutSeconds } = extension_default.config;
|
|
15
|
+
return new Stagehand({
|
|
16
|
+
env: "BROWSERBASE",
|
|
17
|
+
apiKey,
|
|
18
|
+
model,
|
|
19
|
+
disablePino: true,
|
|
20
|
+
keepAlive: true,
|
|
21
|
+
browserbaseSessionCreateParams: {
|
|
22
|
+
keepAlive: true,
|
|
23
|
+
timeout: sessionTimeoutSeconds,
|
|
24
|
+
proxies
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function resumeStagehand(sessionId) {
|
|
29
|
+
const { apiKey, model } = extension_default.config;
|
|
30
|
+
return new Stagehand({
|
|
31
|
+
env: "BROWSERBASE",
|
|
32
|
+
apiKey,
|
|
33
|
+
model,
|
|
34
|
+
disablePino: true,
|
|
35
|
+
keepAlive: true,
|
|
36
|
+
browserbaseSessionID: sessionId
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
async function connect() {
|
|
40
|
+
const current = browserSession.get();
|
|
41
|
+
if (current.id) {
|
|
42
|
+
const resumed = resumeStagehand(current.id);
|
|
43
|
+
try {
|
|
44
|
+
await resumed.init();
|
|
45
|
+
return {
|
|
46
|
+
stagehand: resumed,
|
|
47
|
+
created: false
|
|
48
|
+
};
|
|
49
|
+
} catch (error) {
|
|
50
|
+
await disconnectStagehand(resumed);
|
|
51
|
+
const sessions = createBrowserbaseClient().sessions;
|
|
52
|
+
if (await getRemoteSessionState(sessions, current.id) !== "terminal") throw error;
|
|
53
|
+
browserSession.update(() => ({
|
|
54
|
+
id: null,
|
|
55
|
+
url: null
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const created = createStagehand();
|
|
60
|
+
try {
|
|
61
|
+
await created.init();
|
|
62
|
+
} catch (error) {
|
|
63
|
+
await disconnectStagehand(created);
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
browserSession.update(() => ({
|
|
67
|
+
id: created.browserbaseSessionID ?? null,
|
|
68
|
+
url: created.browserbaseSessionURL ?? null
|
|
69
|
+
}));
|
|
70
|
+
return {
|
|
71
|
+
stagehand: created,
|
|
72
|
+
created: true
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
async function withStagehand(sessionId, operation) {
|
|
76
|
+
return withSessionLock(sessionId, async () => {
|
|
77
|
+
const { stagehand } = await connect();
|
|
78
|
+
try {
|
|
79
|
+
return await operation(stagehand);
|
|
80
|
+
} finally {
|
|
81
|
+
await disconnectStagehand(stagehand);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
async function createBrowserSession(sessionId) {
|
|
86
|
+
return withSessionLock(sessionId, async () => {
|
|
87
|
+
const { stagehand, created } = await connect();
|
|
88
|
+
try {
|
|
89
|
+
return {
|
|
90
|
+
...browserSession.get(),
|
|
91
|
+
created
|
|
92
|
+
};
|
|
93
|
+
} finally {
|
|
94
|
+
await disconnectStagehand(stagehand);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
async function closeBrowserSession(sessionId) {
|
|
99
|
+
return withSessionLock(sessionId, async () => {
|
|
100
|
+
const current = browserSession.get();
|
|
101
|
+
if (!current.id) return current;
|
|
102
|
+
const sessions = createBrowserbaseClient().sessions;
|
|
103
|
+
if (await getRemoteSessionState(sessions, current.id) === "terminal") {
|
|
104
|
+
browserSession.update(() => ({
|
|
105
|
+
id: null,
|
|
106
|
+
url: null
|
|
107
|
+
}));
|
|
108
|
+
return current;
|
|
109
|
+
}
|
|
110
|
+
await releaseBrowserbaseSession(sessions, current.id);
|
|
111
|
+
browserSession.update(() => ({
|
|
112
|
+
id: null,
|
|
113
|
+
url: null
|
|
114
|
+
}));
|
|
115
|
+
return current;
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
export { closeBrowserSession, createBrowserSession, withStagehand };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { fileURLToPath as __eveFileURLToPath } from "node:url";
|
|
2
|
+
import { dirname as __eveDirname } from "node:path";
|
|
3
|
+
import { createRequire as __eveCreateRequire } from "node:module";
|
|
4
|
+
__eveDirname(__eveFileURLToPath(import.meta.url));
|
|
5
|
+
__eveCreateRequire(import.meta.url);
|
|
6
|
+
import { withStagehand } from "../lib/stagehand.mjs";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { defineTool } from "eve/tools";
|
|
9
|
+
var act_default = defineTool({
|
|
10
|
+
description: "Perform one natural-language action in the current Browserbase page with Stagehand.",
|
|
11
|
+
inputSchema: z.object({ instruction: z.string().min(1).describe("A precise single action, such as clicking or filling a field.") }),
|
|
12
|
+
async execute({ instruction }, ctx) {
|
|
13
|
+
return withStagehand(ctx.session.id, (stagehand) => stagehand.act(instruction));
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
export { act_default as default };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { fileURLToPath as __eveFileURLToPath } from "node:url";
|
|
2
|
+
import { dirname as __eveDirname } from "node:path";
|
|
3
|
+
import { createRequire as __eveCreateRequire } from "node:module";
|
|
4
|
+
__eveDirname(__eveFileURLToPath(import.meta.url));
|
|
5
|
+
__eveCreateRequire(import.meta.url);
|
|
6
|
+
import { withStagehand } from "../lib/stagehand.mjs";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { defineTool } from "eve/tools";
|
|
9
|
+
var agent_default = defineTool({
|
|
10
|
+
description: "Run an autonomous multi-step Stagehand browser task in the current Browserbase session. Prefer act, observe, and extract for targeted work.",
|
|
11
|
+
inputSchema: z.object({
|
|
12
|
+
instruction: z.string().min(1).describe("The browser task to complete."),
|
|
13
|
+
maxSteps: z.number().int().min(1).max(50).default(20)
|
|
14
|
+
}),
|
|
15
|
+
async execute({ instruction, maxSteps }, ctx) {
|
|
16
|
+
return withStagehand(ctx.session.id, async (stagehand) => {
|
|
17
|
+
return stagehand.agent().execute({
|
|
18
|
+
instruction,
|
|
19
|
+
maxSteps
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
export { agent_default as default };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { fileURLToPath as __eveFileURLToPath } from "node:url";
|
|
2
|
+
import { dirname as __eveDirname } from "node:path";
|
|
3
|
+
import { createRequire as __eveCreateRequire } from "node:module";
|
|
4
|
+
__eveDirname(__eveFileURLToPath(import.meta.url));
|
|
5
|
+
__eveCreateRequire(import.meta.url);
|
|
6
|
+
import { withStagehand } from "../lib/stagehand.mjs";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { defineTool } from "eve/tools";
|
|
9
|
+
const jsonSchema = z.record(z.string(), z.unknown()).describe("A JSON Schema describing the exact data shape to return.");
|
|
10
|
+
var extract_default = defineTool({
|
|
11
|
+
description: "Extract structured data from the current Browserbase page with Stagehand and validate it against a JSON Schema.",
|
|
12
|
+
inputSchema: z.object({
|
|
13
|
+
instruction: z.string().min(1).describe("What data to extract."),
|
|
14
|
+
schema: jsonSchema
|
|
15
|
+
}),
|
|
16
|
+
async execute({ instruction, schema }, ctx) {
|
|
17
|
+
const outputSchema = z.fromJSONSchema(schema);
|
|
18
|
+
return withStagehand(ctx.session.id, (stagehand) => stagehand.extract(instruction, outputSchema));
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
export { extract_default as default };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
declare const _default: import("eve/tools").ToolDefinition<{
|
|
2
|
+
url: string;
|
|
3
|
+
format: "json" | "markdown" | "raw";
|
|
4
|
+
schema?: Record<string, unknown> | undefined;
|
|
5
|
+
allowRedirects: boolean;
|
|
6
|
+
allowInsecureSsl: boolean;
|
|
7
|
+
proxies: boolean;
|
|
8
|
+
}, import("@browserbasehq/sdk/resources/fetch-api.mjs").FetchAPICreateResponse>;
|
|
9
|
+
export default _default;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { fileURLToPath as __eveFileURLToPath } from "node:url";
|
|
2
|
+
import { dirname as __eveDirname } from "node:path";
|
|
3
|
+
import { createRequire as __eveCreateRequire } from "node:module";
|
|
4
|
+
__eveDirname(__eveFileURLToPath(import.meta.url));
|
|
5
|
+
__eveCreateRequire(import.meta.url);
|
|
6
|
+
import { createBrowserbaseClient } from "../lib/browserbase.mjs";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { defineTool } from "eve/tools";
|
|
9
|
+
const jsonSchema = z.record(z.string(), z.unknown()).optional().describe("A JSON Schema for structured extraction. Required with the json format and invalid with other formats.");
|
|
10
|
+
var fetch_default = defineTool({
|
|
11
|
+
description: "Retrieve a URL through Browserbase Fetch without starting a browser session. Use raw for the original response, markdown for agent-friendly content, or json with a schema for structured extraction.",
|
|
12
|
+
inputSchema: z.object({
|
|
13
|
+
url: z.url().describe("The absolute URL to retrieve."),
|
|
14
|
+
format: z.enum([
|
|
15
|
+
"raw",
|
|
16
|
+
"markdown",
|
|
17
|
+
"json"
|
|
18
|
+
]).default("raw").describe("The response content format."),
|
|
19
|
+
schema: jsonSchema,
|
|
20
|
+
allowRedirects: z.boolean().default(false).describe("Whether to follow HTTP redirects."),
|
|
21
|
+
allowInsecureSsl: z.boolean().default(false).describe("Whether to bypass TLS certificate verification."),
|
|
22
|
+
proxies: z.boolean().default(false).describe("Whether to route the request through Browserbase proxies.")
|
|
23
|
+
}),
|
|
24
|
+
async execute({ url, format, schema, allowRedirects, allowInsecureSsl, proxies }) {
|
|
25
|
+
if (format === "json" && !schema) throw new Error("schema is required when format is json.");
|
|
26
|
+
if (format !== "json" && schema) throw new Error("schema can only be used when format is json.");
|
|
27
|
+
return createBrowserbaseClient().fetchAPI.create({
|
|
28
|
+
url,
|
|
29
|
+
format,
|
|
30
|
+
schema,
|
|
31
|
+
allowRedirects,
|
|
32
|
+
allowInsecureSsl,
|
|
33
|
+
proxies
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
export { fetch_default as default };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { fileURLToPath as __eveFileURLToPath } from "node:url";
|
|
2
|
+
import { dirname as __eveDirname } from "node:path";
|
|
3
|
+
import { createRequire as __eveCreateRequire } from "node:module";
|
|
4
|
+
__eveDirname(__eveFileURLToPath(import.meta.url));
|
|
5
|
+
__eveCreateRequire(import.meta.url);
|
|
6
|
+
import { withStagehand } from "../lib/stagehand.mjs";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { defineTool } from "eve/tools";
|
|
9
|
+
var navigate_default = defineTool({
|
|
10
|
+
description: "Navigate the Browserbase browser to a URL. Call this before using other browser tools on a new site.",
|
|
11
|
+
inputSchema: z.object({ url: z.url().describe("The absolute URL to open.") }),
|
|
12
|
+
async execute({ url }, ctx) {
|
|
13
|
+
return withStagehand(ctx.session.id, async (stagehand) => {
|
|
14
|
+
const page = await stagehand.context.awaitActivePage();
|
|
15
|
+
await page.goto(url);
|
|
16
|
+
return {
|
|
17
|
+
url: page.url(),
|
|
18
|
+
title: await page.title()
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
export { navigate_default as default };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { fileURLToPath as __eveFileURLToPath } from "node:url";
|
|
2
|
+
import { dirname as __eveDirname } from "node:path";
|
|
3
|
+
import { createRequire as __eveCreateRequire } from "node:module";
|
|
4
|
+
__eveDirname(__eveFileURLToPath(import.meta.url));
|
|
5
|
+
__eveCreateRequire(import.meta.url);
|
|
6
|
+
import { withStagehand } from "../lib/stagehand.mjs";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { defineTool } from "eve/tools";
|
|
9
|
+
var observe_default = defineTool({
|
|
10
|
+
description: "Inspect the current Browserbase page with Stagehand and return candidate elements and actions.",
|
|
11
|
+
inputSchema: z.object({ instruction: z.string().min(1).describe("The elements or actions to find on the page.") }),
|
|
12
|
+
async execute({ instruction }, ctx) {
|
|
13
|
+
return withStagehand(ctx.session.id, (stagehand) => stagehand.observe(instruction));
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
export { observe_default as default };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { fileURLToPath as __eveFileURLToPath } from "node:url";
|
|
2
|
+
import { dirname as __eveDirname } from "node:path";
|
|
3
|
+
import { createRequire as __eveCreateRequire } from "node:module";
|
|
4
|
+
__eveDirname(__eveFileURLToPath(import.meta.url));
|
|
5
|
+
__eveCreateRequire(import.meta.url);
|
|
6
|
+
import { createBrowserbaseClient } from "../lib/browserbase.mjs";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { defineTool } from "eve/tools";
|
|
9
|
+
var search_default = defineTool({
|
|
10
|
+
description: "Search the public web with Browserbase Search. Use this to discover relevant URLs before fetching content or opening a browser session.",
|
|
11
|
+
inputSchema: z.object({
|
|
12
|
+
query: z.string().min(1).max(200).describe("The web search query, from 1 to 200 characters."),
|
|
13
|
+
numResults: z.number().int().min(1).max(25).default(10).describe("The number of results to return, from 1 to 25.")
|
|
14
|
+
}),
|
|
15
|
+
async execute({ query, numResults }) {
|
|
16
|
+
return createBrowserbaseClient().search.web({
|
|
17
|
+
query,
|
|
18
|
+
numResults
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
export { search_default as default };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
declare const _default: import("eve/tools").DynamicSentinel<{
|
|
2
|
+
create_session: import("eve/tools").ToolDefinition<Record<string, never>, import("../lib/stagehand").BrowserSessionResult>;
|
|
3
|
+
stop_session: import("eve/tools").ToolDefinition<Record<string, never>, {
|
|
4
|
+
id: string | null;
|
|
5
|
+
url: string | null;
|
|
6
|
+
stopped: boolean;
|
|
7
|
+
}>;
|
|
8
|
+
}>;
|
|
9
|
+
export default _default;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { fileURLToPath as __eveFileURLToPath } from "node:url";
|
|
2
|
+
import { dirname as __eveDirname } from "node:path";
|
|
3
|
+
import { createRequire as __eveCreateRequire } from "node:module";
|
|
4
|
+
__eveDirname(__eveFileURLToPath(import.meta.url));
|
|
5
|
+
__eveCreateRequire(import.meta.url);
|
|
6
|
+
import { closeBrowserSession, createBrowserSession } from "../lib/stagehand.mjs";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { defineDynamic, defineTool } from "eve/tools";
|
|
9
|
+
var session_default = defineDynamic({ events: { "session.started": () => ({
|
|
10
|
+
create_session: defineTool({
|
|
11
|
+
description: "Create a Browserbase browser session for this Eve conversation, or reconnect if one already exists.",
|
|
12
|
+
inputSchema: z.object({}),
|
|
13
|
+
async execute(_input, ctx) {
|
|
14
|
+
return createBrowserSession(ctx.session.id);
|
|
15
|
+
}
|
|
16
|
+
}),
|
|
17
|
+
stop_session: defineTool({
|
|
18
|
+
description: "Stop the Browserbase browser session for this Eve conversation and release its resources.",
|
|
19
|
+
inputSchema: z.object({}),
|
|
20
|
+
async execute(_input, ctx) {
|
|
21
|
+
const session = await closeBrowserSession(ctx.session.id);
|
|
22
|
+
return {
|
|
23
|
+
...session,
|
|
24
|
+
stopped: Boolean(session.id)
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
}) } });
|
|
29
|
+
export { session_default as default };
|
package/dist/index.d.ts
ADDED
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { fileURLToPath as __eveFileURLToPath } from "node:url";
|
|
2
|
+
import { dirname as __eveDirname } from "node:path";
|
|
3
|
+
import { createRequire as __eveCreateRequire } from "node:module";
|
|
4
|
+
__eveDirname(__eveFileURLToPath(import.meta.url));
|
|
5
|
+
__eveCreateRequire(import.meta.url);
|
|
6
|
+
import extension_default from "./extension/extension.mjs";
|
|
7
|
+
export { extension_default as default, extension_default as eve };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Generated by eve. Do not edit by hand.
|
|
2
|
+
|
|
3
|
+
export { default as act } from "../extension/tools/act.js";
|
|
4
|
+
export { default as agent } from "../extension/tools/agent.js";
|
|
5
|
+
export { default as extract } from "../extension/tools/extract.js";
|
|
6
|
+
export { default as fetch } from "../extension/tools/fetch.js";
|
|
7
|
+
export { default as navigate } from "../extension/tools/navigate.js";
|
|
8
|
+
export { default as observe } from "../extension/tools/observe.js";
|
|
9
|
+
export { default as search } from "../extension/tools/search.js";
|
|
10
|
+
export { default as session } from "../extension/tools/session.js";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { fileURLToPath as __eveFileURLToPath } from "node:url";
|
|
2
|
+
import { dirname as __eveDirname } from "node:path";
|
|
3
|
+
import { createRequire as __eveCreateRequire } from "node:module";
|
|
4
|
+
__eveDirname(__eveFileURLToPath(import.meta.url));
|
|
5
|
+
__eveCreateRequire(import.meta.url);
|
|
6
|
+
import act_default from "../extension/tools/act.mjs";
|
|
7
|
+
import agent_default from "../extension/tools/agent.mjs";
|
|
8
|
+
import extract_default from "../extension/tools/extract.mjs";
|
|
9
|
+
import fetch_default from "../extension/tools/fetch.mjs";
|
|
10
|
+
import navigate_default from "../extension/tools/navigate.mjs";
|
|
11
|
+
import observe_default from "../extension/tools/observe.mjs";
|
|
12
|
+
import search_default from "../extension/tools/search.mjs";
|
|
13
|
+
import session_default from "../extension/tools/session.mjs";
|
|
14
|
+
export { act_default as act, agent_default as agent, extract_default as extract, fetch_default as fetch, navigate_default as navigate, observe_default as observe, search_default as search, session_default as session };
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@browserbasehq/eve",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "An Eve extension that gives agents Browserbase-powered Stagehand browser tools.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"packageManager": "pnpm@10.9.0",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"browserbase",
|
|
10
|
+
"eve",
|
|
11
|
+
"stagehand",
|
|
12
|
+
"browser-automation",
|
|
13
|
+
"ai-agents"
|
|
14
|
+
],
|
|
15
|
+
"eve": {
|
|
16
|
+
"extension": {
|
|
17
|
+
"source": "./extension",
|
|
18
|
+
"dist": "./dist/extension"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"README.md"
|
|
24
|
+
],
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"default": "./dist/index.mjs"
|
|
29
|
+
},
|
|
30
|
+
"./tools": {
|
|
31
|
+
"types": "./dist/tools/index.d.ts",
|
|
32
|
+
"default": "./dist/tools/index.mjs"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "eve extension build",
|
|
37
|
+
"prepare": "eve extension build",
|
|
38
|
+
"typecheck": "tsc",
|
|
39
|
+
"check": "pnpm typecheck && pnpm build"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@browserbasehq/sdk": "2.16.0",
|
|
43
|
+
"@browserbasehq/stagehand": "3.7.0",
|
|
44
|
+
"zod": "4.4.3"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/node": "24.10.1",
|
|
48
|
+
"eve": "0.25.3",
|
|
49
|
+
"typescript": "7.0.2"
|
|
50
|
+
},
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"eve": "*"
|
|
53
|
+
},
|
|
54
|
+
"engines": {
|
|
55
|
+
"node": ">=24"
|
|
56
|
+
}
|
|
57
|
+
}
|