@meetopenbot/stagehand 1.0.2 → 1.0.3

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.
Files changed (2) hide show
  1. package/dist/index.js +75 -121
  2. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -1,134 +1,88 @@
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
+
7
+ // src/config.ts
3
8
  import {
4
- agentOutput,
5
- definePlugin,
6
- shouldHandleInvoke,
7
- uiWidget
9
+ lookupSecret,
10
+ readConfigModel,
11
+ trimmedString
8
12
  } from "@meetopenbot/plugin-sdk";
9
- var PROVIDER_CONFIG = {
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 = {
13
+ var pluginConfigSchema = {
15
14
  type: "object",
16
15
  properties: {
17
- apiKey: { type: "string", format: "password" },
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"] }
16
+ env: { type: "string", enum: ["LOCAL", "BROWSERBASE"], description: "Browser environment." }
26
17
  }
27
18
  };
28
- var plugin = definePlugin({
29
- name: "Stagehand",
30
- description: "Stagehand AI browser automation",
31
- configSchema,
32
- factory: (context) => (builder) => {
33
- const env = process.env ?? {};
34
- builder.on("agent:invoke", async function* (event) {
35
- if (!shouldHandleInvoke(event, context.agentId)) return;
36
- const content = event.data?.content;
37
- const threadId = event.meta?.threadId;
38
- if (!content) return;
39
- const config = context.config;
40
- const requestedEnv = config.env || (env.BROWSERBASE_API_KEY ? "BROWSERBASE" : "LOCAL");
41
- const provider = config.provider || "openai";
42
- const pConfig = PROVIDER_CONFIG[provider] || PROVIDER_CONFIG.openai;
43
- const apiKey = config.apiKey || env[pConfig.envVar];
44
- const model = config.model || pConfig.defaultModel;
45
- if (!apiKey && requestedEnv === "LOCAL") {
46
- yield uiWidget({
47
- agentId: context.agentId,
48
- threadId,
49
- widget: {
50
- widgetId: `stagehand_api_key_${Date.now()}`,
51
- kind: "form",
52
- title: "API Key Required",
53
- description: `Stagehand needs an API key for ${pConfig.label}.`,
54
- fields: [
55
- { id: "provider", label: "Provider", type: "select", options: Object.entries(PROVIDER_CONFIG).map(([k, v]) => ({ value: k, label: v.label })), defaultValue: provider, required: true },
56
- { id: "apiKey", label: "API Key", type: "text", required: true }
57
- ],
58
- submitLabel: "Save Key",
59
- metadata: { type: "api_key_request" }
60
- }
61
- });
62
- return;
63
- }
64
- let stagehand = null;
65
- try {
66
- yield agentOutput({ agentId: context.agentId, threadId, content: `Starting Stagehand (${requestedEnv.toLowerCase()})...` });
67
- stagehand = new Stagehand({
68
- env: requestedEnv,
69
- model: apiKey ? { modelName: model, apiKey } : model,
70
- apiKey: config.browserbaseApiKey ?? env.BROWSERBASE_API_KEY,
71
- projectId: config.browserbaseProjectId ?? env.BROWSERBASE_PROJECT_ID,
72
- localBrowserLaunchOptions: { headless: config.headless ?? true },
73
- experimental: true
74
- });
75
- await stagehand.init();
76
- const agent = stagehand.agent({
77
- systemPrompt: context.agentDetails?.instructions ?? void 0,
78
- mode: config.mode,
79
- stream: true
80
- });
81
- const streamResult = await agent.execute({
82
- instruction: content,
83
- maxSteps: config.maxSteps ?? 25
84
- });
85
- for await (const part of streamResult.fullStream) {
86
- if (part.type === "tool-call") {
87
- yield agentOutput({
88
- agentId: context.agentId,
89
- threadId,
90
- content: `
91
- > [Tool Call] ${part.toolName}: ${JSON.stringify(part.input, null, 2)}
92
- `
93
- });
94
- }
95
- if (part.type === "tool-result") {
96
- yield agentOutput({
97
- agentId: context.agentId,
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
- }
19
+ function resolveStagehandConfig(config) {
20
+ const browserbaseApiKey = lookupSecret({ envKeys: ["BROWSERBASE_API_KEY"] });
21
+ const requestedEnv = trimmedString(config.env) || (browserbaseApiKey ? "BROWSERBASE" : "LOCAL");
22
+ return {
23
+ requestedEnv,
24
+ model: readConfigModel(config, "openai/gpt-4o-mini"),
25
+ apiKey: lookupSecret({ envKeys: ["OPENAI_API_KEY"] }),
26
+ browserbaseApiKey,
27
+ browserbaseProjectId: lookupSecret({ envKeys: ["BROWSERBASE_PROJECT_ID"] }),
28
+ headless: true,
29
+ maxSteps: 25
30
+ };
31
+ }
32
+
33
+ // src/agent.ts
34
+ async function runStagehandTurn({ prompt, context }) {
35
+ const config = resolveStagehandConfig(context.config);
36
+ if (config.requestedEnv === "BROWSERBASE" && (!config.browserbaseApiKey || !config.browserbaseProjectId)) {
37
+ return "Stagehand Browserbase setup is incomplete. Set `BROWSERBASE_API_KEY` and `BROWSERBASE_PROJECT_ID` in workspace settings.";
38
+ }
39
+ if (config.requestedEnv === "LOCAL" && !config.apiKey) {
40
+ return "Stagehand local setup is incomplete. Set `OPENAI_API_KEY` in workspace settings.";
41
+ }
42
+ let stagehand = null;
43
+ try {
44
+ stagehand = new Stagehand({
45
+ env: config.requestedEnv,
46
+ model: config.apiKey ? { modelName: config.model, apiKey: config.apiKey } : config.model,
47
+ apiKey: config.browserbaseApiKey,
48
+ projectId: config.browserbaseProjectId,
49
+ localBrowserLaunchOptions: { headless: config.headless },
50
+ experimental: true
116
51
  });
117
- builder.on("client:ui:widget:response", async function* (event) {
118
- const { metadata, values, widgetId } = event.data ?? {};
119
- if (metadata?.type !== "api_key_request") return;
120
- const provider = values?.provider;
121
- const apiKey = values?.apiKey;
122
- const pConfig = PROVIDER_CONFIG[provider];
123
- if (pConfig && apiKey) {
124
- await context.storage.createVariable({ key: pConfig.envVar, value: apiKey, secret: true });
125
- env[pConfig.envVar] = apiKey;
126
- yield uiWidget({ agentId: context.agentId, widget: { widgetId, kind: "message", title: "Success", body: "API key saved. Please retry your request.", state: "submitted", actions: [{ id: "ok", label: "OK" }] } });
127
- }
52
+ await stagehand.init();
53
+ const agent = stagehand.agent({
54
+ systemPrompt: context.agentDetails?.instructions ?? void 0,
55
+ stream: true
56
+ });
57
+ const streamResult = await agent.execute({
58
+ instruction: prompt,
59
+ maxSteps: config.maxSteps
60
+ });
61
+ for await (const _part of streamResult.fullStream) {
62
+ }
63
+ const result = await streamResult.result;
64
+ const parts = [];
65
+ if (result.message) parts.push(result.message);
66
+ if (result.output && Object.keys(result.output).length > 0) {
67
+ parts.push(JSON.stringify(result.output, null, 2));
68
+ }
69
+ return parts.join("\n\n");
70
+ } finally {
71
+ if (stagehand) await stagehand.close().catch(() => {
128
72
  });
129
73
  }
74
+ }
75
+
76
+ // src/index.ts
77
+ var plugin = await defineAgentPlugin({
78
+ name: "Stagehand",
79
+ description: "Stagehand AI browser automation",
80
+ models: { providers: ["openai"], default: "openai/gpt-4o-mini" },
81
+ configSchema: pluginConfigSchema,
82
+ run: runStagehandTurn
130
83
  });
131
- var plugin_stagehand_default = plugin;
84
+ var src_default = plugin;
132
85
  export {
133
- plugin_stagehand_default as default
86
+ src_default as default,
87
+ plugin
134
88
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meetopenbot/stagehand",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
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.2.0"
27
+ "@meetopenbot/plugin-sdk": "^0.3.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 --noEmit --allowImportingTsExtensions --module ESNext --moduleResolution Bundler --target ES2022 --skipLibCheck index.ts"
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
  }