@meetopenbot/stagehand 1.0.2 → 1.0.4
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/index.js +118 -121
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -1,134 +1,131 @@
|
|
|
1
|
-
// index.ts
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { defineAgentPlugin } from "@meetopenbot/plugin-sdk";
|
|
3
|
+
|
|
4
|
+
// src/agent.ts
|
|
2
5
|
import { Stagehand } from "@browserbasehq/stagehand";
|
|
6
|
+
import { textReply as textReply2 } from "@meetopenbot/plugin-sdk";
|
|
7
|
+
|
|
8
|
+
// src/config.ts
|
|
3
9
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
uiWidget
|
|
10
|
+
lookupSecret,
|
|
11
|
+
readConfigModel,
|
|
12
|
+
trimmedString
|
|
8
13
|
} from "@meetopenbot/plugin-sdk";
|
|
9
|
-
var
|
|
10
|
-
openai: { label: "OpenAI", envVar: "OPENAI_API_KEY", defaultModel: "openai/gpt-4o-mini" },
|
|
11
|
-
anthropic: { label: "Anthropic", envVar: "ANTHROPIC_API_KEY", defaultModel: "anthropic/claude-sonnet-4-5-20250929" },
|
|
12
|
-
google: { label: "Google Generative AI", envVar: "GOOGLE_GENERATIVE_AI_API_KEY", defaultModel: "google/gemini-2.5-flash" }
|
|
13
|
-
};
|
|
14
|
-
var configSchema = {
|
|
14
|
+
var pluginConfigSchema = {
|
|
15
15
|
type: "object",
|
|
16
16
|
properties: {
|
|
17
|
-
|
|
18
|
-
provider: { type: "string", enum: ["openai", "anthropic", "google"] },
|
|
19
|
-
model: { type: "string" },
|
|
20
|
-
env: { type: "string", enum: ["LOCAL", "BROWSERBASE"] },
|
|
21
|
-
browserbaseApiKey: { type: "string", format: "password" },
|
|
22
|
-
browserbaseProjectId: { type: "string" },
|
|
23
|
-
headless: { type: "boolean", default: true },
|
|
24
|
-
maxSteps: { type: "integer", default: 25 },
|
|
25
|
-
mode: { type: "string", enum: ["dom", "hybrid", "cua"] }
|
|
17
|
+
env: { type: "string", enum: ["LOCAL", "BROWSERBASE"], description: "Browser environment." }
|
|
26
18
|
}
|
|
27
19
|
};
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
threadId,
|
|
99
|
-
content: `
|
|
100
|
-
> [Tool Result] ${part.toolName}: ${JSON.stringify(part.output, null, 2).slice(0, 800)}
|
|
101
|
-
`
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
const result = await streamResult.result;
|
|
106
|
-
if (result.message) yield agentOutput({ agentId: context.agentId, threadId, content: result.message });
|
|
107
|
-
if (result.output && Object.keys(result.output).length > 0) {
|
|
108
|
-
yield agentOutput({ agentId: context.agentId, threadId, content: "```json\n" + JSON.stringify(result.output, null, 2) + "\n```" });
|
|
109
|
-
}
|
|
110
|
-
} catch (error) {
|
|
111
|
-
yield agentOutput({ agentId: context.agentId, threadId, content: `Error: ${error.message}` });
|
|
112
|
-
} finally {
|
|
113
|
-
if (stagehand) await stagehand.close().catch(() => {
|
|
114
|
-
});
|
|
115
|
-
}
|
|
20
|
+
function resolveStagehandConfig(config) {
|
|
21
|
+
const browserbaseApiKey = lookupSecret({ envKeys: ["BROWSERBASE_API_KEY"] });
|
|
22
|
+
const requestedEnv = trimmedString(config.env) || (browserbaseApiKey ? "BROWSERBASE" : "LOCAL");
|
|
23
|
+
return {
|
|
24
|
+
requestedEnv,
|
|
25
|
+
model: readConfigModel(config, "openai/gpt-4o-mini"),
|
|
26
|
+
apiKey: lookupSecret({ envKeys: ["OPENAI_API_KEY"] }),
|
|
27
|
+
browserbaseApiKey,
|
|
28
|
+
browserbaseProjectId: lookupSecret({ envKeys: ["BROWSERBASE_PROJECT_ID"] }),
|
|
29
|
+
headless: true,
|
|
30
|
+
maxSteps: 25
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// src/stream.ts
|
|
35
|
+
import {
|
|
36
|
+
createTextReplyBuffer,
|
|
37
|
+
textReply
|
|
38
|
+
} from "@meetopenbot/plugin-sdk";
|
|
39
|
+
var classifyStagehandPart = (part) => {
|
|
40
|
+
if (part.type === "text-delta") {
|
|
41
|
+
const text = part.text ?? part.delta ?? "";
|
|
42
|
+
return text ? { type: "delta", text } : { type: "skip" };
|
|
43
|
+
}
|
|
44
|
+
if (part.type === "step-finish" || part.type === "finish" || part.type === "tool-call") {
|
|
45
|
+
return { type: "flush" };
|
|
46
|
+
}
|
|
47
|
+
return { type: "skip" };
|
|
48
|
+
};
|
|
49
|
+
async function* runStagehandStream(fullStream) {
|
|
50
|
+
const replies = createTextReplyBuffer();
|
|
51
|
+
for await (const part of fullStream) {
|
|
52
|
+
yield* replies.apply(classifyStagehandPart(part));
|
|
53
|
+
}
|
|
54
|
+
yield* replies.end();
|
|
55
|
+
}
|
|
56
|
+
function finalStagehandReply(args) {
|
|
57
|
+
const items = [];
|
|
58
|
+
if (!args.yielded && args.message?.trim()) items.push(textReply(args.message.trim()));
|
|
59
|
+
if (args.output && typeof args.output === "object" && Object.keys(args.output).length > 0) {
|
|
60
|
+
items.push(textReply(JSON.stringify(args.output, null, 2)));
|
|
61
|
+
}
|
|
62
|
+
return items;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// src/agent.ts
|
|
66
|
+
async function* runStagehandTurn({
|
|
67
|
+
prompt,
|
|
68
|
+
context
|
|
69
|
+
}) {
|
|
70
|
+
const config = resolveStagehandConfig(context.config);
|
|
71
|
+
if (config.requestedEnv === "BROWSERBASE" && (!config.browserbaseApiKey || !config.browserbaseProjectId)) {
|
|
72
|
+
yield textReply2(
|
|
73
|
+
"Stagehand Browserbase setup is incomplete. Set `BROWSERBASE_API_KEY` and `BROWSERBASE_PROJECT_ID` in workspace settings."
|
|
74
|
+
);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (config.requestedEnv === "LOCAL" && !config.apiKey) {
|
|
78
|
+
yield textReply2("Stagehand local setup is incomplete. Set `OPENAI_API_KEY` in workspace settings.");
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
let stagehand = null;
|
|
82
|
+
try {
|
|
83
|
+
stagehand = new Stagehand({
|
|
84
|
+
env: config.requestedEnv,
|
|
85
|
+
model: config.apiKey ? { modelName: config.model, apiKey: config.apiKey } : config.model,
|
|
86
|
+
apiKey: config.browserbaseApiKey,
|
|
87
|
+
projectId: config.browserbaseProjectId,
|
|
88
|
+
localBrowserLaunchOptions: { headless: config.headless },
|
|
89
|
+
experimental: true
|
|
116
90
|
});
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
91
|
+
await stagehand.init();
|
|
92
|
+
const agent = stagehand.agent({
|
|
93
|
+
systemPrompt: context.agentDetails?.instructions ?? void 0,
|
|
94
|
+
stream: true
|
|
95
|
+
});
|
|
96
|
+
const streamResult = await agent.execute({
|
|
97
|
+
instruction: prompt,
|
|
98
|
+
maxSteps: config.maxSteps
|
|
99
|
+
});
|
|
100
|
+
let yielded = false;
|
|
101
|
+
for await (const item of runStagehandStream(streamResult.fullStream)) {
|
|
102
|
+
yielded = true;
|
|
103
|
+
yield item;
|
|
104
|
+
}
|
|
105
|
+
const result = await streamResult.result;
|
|
106
|
+
for (const item of finalStagehandReply({
|
|
107
|
+
yielded,
|
|
108
|
+
message: result.message,
|
|
109
|
+
output: result.output
|
|
110
|
+
})) {
|
|
111
|
+
yield item;
|
|
112
|
+
}
|
|
113
|
+
} finally {
|
|
114
|
+
if (stagehand) await stagehand.close().catch(() => {
|
|
128
115
|
});
|
|
129
116
|
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// src/index.ts
|
|
120
|
+
var plugin = await defineAgentPlugin({
|
|
121
|
+
name: "Stagehand",
|
|
122
|
+
description: "Stagehand AI browser automation",
|
|
123
|
+
models: { providers: ["openai"], default: "openai/gpt-4o-mini" },
|
|
124
|
+
configSchema: pluginConfigSchema,
|
|
125
|
+
run: runStagehandTurn
|
|
130
126
|
});
|
|
131
|
-
var
|
|
127
|
+
var src_default = plugin;
|
|
132
128
|
export {
|
|
133
|
-
|
|
129
|
+
src_default as default,
|
|
130
|
+
plugin
|
|
134
131
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meetopenbot/stagehand",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Stagehand AI browser automation plugin for OpenBot",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"@browserbasehq/stagehand": "^3.3.0",
|
|
25
25
|
"playwright": "^1.52.0",
|
|
26
26
|
"zod": "^4.3.5",
|
|
27
|
-
"@meetopenbot/plugin-sdk": "^0.
|
|
27
|
+
"@meetopenbot/plugin-sdk": "^0.4.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@types/node": "^20.10.1",
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
},
|
|
33
33
|
"types": "./dist/index.d.ts",
|
|
34
34
|
"scripts": {
|
|
35
|
-
"build": "esbuild index.ts --bundle --platform=node --target=node18 --format=esm --packages=external --outfile=dist/index.js && node ../../scripts/write-plugin-declaration.mjs",
|
|
35
|
+
"build": "esbuild src/index.ts --bundle --platform=node --target=node18 --format=esm --packages=external --outfile=dist/index.js && node ../../scripts/write-plugin-declaration.mjs",
|
|
36
36
|
"setup:browser": "playwright install chromium",
|
|
37
|
-
"dev": "esbuild index.ts --bundle --platform=node --target=node18 --format=esm --packages=external --outfile=dist/index.js --watch",
|
|
38
|
-
"typecheck": "tsc
|
|
37
|
+
"dev": "esbuild src/index.ts --bundle --platform=node --target=node18 --format=esm --packages=external --outfile=dist/index.js --watch",
|
|
38
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
39
39
|
}
|
|
40
40
|
}
|