@elizaos/plugin-browser 2.0.0-alpha.5 → 2.0.0-alpha.7
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.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/services/browser-service.d.ts +2 -0
- package/dist/services/browser-service.d.ts.map +1 -1
- package/dist/services/browser-service.js +50 -6
- package/dist/services/browser-service.js.map +1 -1
- package/dist/services/process-manager.d.ts +10 -1
- package/dist/services/process-manager.d.ts.map +1 -1
- package/dist/services/process-manager.js +143 -59
- package/dist/services/process-manager.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAyB,MAAM,EAAmC,MAAM,eAAe,CAAC;AAGpG,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAExE,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,CAAC;AAClF,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,uBAAuB,GACxB,CAAC;AA6EF,eAAO,MAAM,aAAa,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAyB,MAAM,EAAmC,MAAM,eAAe,CAAC;AAGpG,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAExE,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,CAAC;AAClF,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,uBAAuB,GACxB,CAAC;AA6EF,eAAO,MAAM,aAAa,EAAE,MAoD3B,CAAC;AAEF,eAAe,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -102,6 +102,10 @@ export const browserPlugin = {
|
|
|
102
102
|
process.env[key] = String(value);
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
|
+
// Auto-enable browser actions when the plugin is loaded.
|
|
106
|
+
// The plugin being present in the runtime means the user toggled it on.
|
|
107
|
+
process.env.ENABLE_BROWSER = "true";
|
|
108
|
+
process.env.BROWSER_ENABLED = "true";
|
|
105
109
|
logger.info("Browser plugin initialized successfully");
|
|
106
110
|
}
|
|
107
111
|
catch (error) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IAgentRuntime, Memory, Plugin, Provider, ProviderResult, State } from \"@elizaos/core\";\nimport { logger, ServiceType } from \"@elizaos/core\";\nimport { z } from \"zod\";\nimport { browserClickAction } from \"./actions/click.js\";\nimport { browserExtractAction } from \"./actions/extract.js\";\nimport { browserNavigateAction } from \"./actions/navigate.js\";\nimport { browserScreenshotAction } from \"./actions/screenshot.js\";\nimport { browserSelectAction } from \"./actions/select.js\";\nimport { browserTypeAction } from \"./actions/type.js\";\nimport { BrowserService, Session } from \"./services/browser-service.js\";\nimport { BrowserProcessManager } from \"./services/process-manager.js\";\nimport { BrowserWebSocketClient } from \"./services/websocket-client.js\";\n\nexport * from \"./types.js\";\nexport * from \"./utils/index.js\";\nexport { BrowserService, Session, BrowserWebSocketClient, BrowserProcessManager };\nexport {\n browserNavigateAction,\n browserClickAction,\n browserTypeAction,\n browserSelectAction,\n browserExtractAction,\n browserScreenshotAction,\n};\nconst configSchema = z.object({\n BROWSERBASE_API_KEY: z.string().optional(),\n BROWSERBASE_PROJECT_ID: z.string().optional(),\n OPENAI_API_KEY: z.string().optional(),\n ANTHROPIC_API_KEY: z.string().optional(),\n OLLAMA_BASE_URL: z.string().optional(),\n OLLAMA_MODEL: z.string().optional(),\n BROWSER_HEADLESS: z\n .string()\n .transform((val: string) => val === \"true\")\n .optional()\n .default(true),\n CAPSOLVER_API_KEY: z.string().optional(),\n BROWSER_SERVER_PORT: z.string().optional().default(\"3456\"),\n});\n\nconst browserStateProvider: Provider = {\n name: \"BROWSER_STATE\",\n description: \"Provides current browser state information\",\n\n get: async (\n runtime: IAgentRuntime,\n _message: Memory,\n _state?: State\n ): Promise<ProviderResult> => {\n const service = runtime.getService<BrowserService>(ServiceType.BROWSER);\n const session = await service?.getCurrentSession();\n\n if (!session || !service) {\n return {\n text: \"No active browser session\",\n values: {\n hasSession: false,\n },\n data: {},\n };\n }\n\n try {\n const client = service.getClient();\n const state = await client.getState(session.id);\n\n return {\n text: `Current browser page: \"${state.title}\" at ${state.url}`,\n values: {\n hasSession: true,\n url: state.url,\n title: state.title,\n },\n data: {\n sessionId: session.id,\n createdAt: session.createdAt.toISOString(),\n },\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.error(`Error getting browser state: ${errorMessage}`);\n return {\n text: \"Error getting browser state\",\n values: {\n hasSession: true,\n error: true,\n },\n data: {},\n };\n }\n },\n};\n\n// CLI self-registration - importing this module triggers CLI command registration.\n// Wrapped in a dynamic import so the plugin still loads when @elizaos/plugin-cli\n// is unavailable (e.g. when consumed outside the monorepo).\nimport(\"./cli/index.js\").catch(() => {\n // plugin-cli not available — skip CLI registration silently\n});\n\nexport const browserPlugin: Plugin = {\n name: \"plugin-browser\",\n description: \"Browser automation plugin\",\n config: {\n BROWSERBASE_API_KEY: process.env.BROWSERBASE_API_KEY ?? null,\n BROWSERBASE_PROJECT_ID: process.env.BROWSERBASE_PROJECT_ID ?? null,\n OPENAI_API_KEY: process.env.OPENAI_API_KEY ?? null,\n ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY ?? null,\n OLLAMA_BASE_URL: process.env.OLLAMA_BASE_URL ?? null,\n OLLAMA_MODEL: process.env.OLLAMA_MODEL ?? null,\n BROWSER_HEADLESS: process.env.BROWSER_HEADLESS ?? \"true\",\n CAPSOLVER_API_KEY: process.env.CAPSOLVER_API_KEY ?? null,\n BROWSER_SERVER_PORT: process.env.BROWSER_SERVER_PORT ?? \"3456\",\n },\n async init(config: Record<string, string | null>, _runtime: IAgentRuntime) {\n logger.info(\"Initializing browser automation plugin\");\n try {\n const validatedConfig = await configSchema.parseAsync(config);\n\n for (const [key, value] of Object.entries(validatedConfig)) {\n if (value !== undefined && value !== null) {\n process.env[key] = String(value);\n }\n }\n\n logger.info(\"Browser plugin initialized successfully\");\n } catch (error) {\n if (error && typeof error === \"object\" && \"issues\" in error) {\n const zodError = error as { issues?: Array<{ message: string }> };\n if (Array.isArray(zodError.issues)) {\n const errorMessages = zodError.issues.map((e) => e.message).join(\", \");\n throw new Error(`Invalid plugin configuration: ${errorMessages}`);\n }\n }\n throw error;\n }\n },\n services: [BrowserService],\n actions: [\n browserNavigateAction,\n browserClickAction,\n browserTypeAction,\n browserSelectAction,\n browserExtractAction,\n browserScreenshotAction,\n ],\n providers: [browserStateProvider],\n};\n\nexport default browserPlugin;\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IAgentRuntime, Memory, Plugin, Provider, ProviderResult, State } from \"@elizaos/core\";\nimport { logger, ServiceType } from \"@elizaos/core\";\nimport { z } from \"zod\";\nimport { browserClickAction } from \"./actions/click.js\";\nimport { browserExtractAction } from \"./actions/extract.js\";\nimport { browserNavigateAction } from \"./actions/navigate.js\";\nimport { browserScreenshotAction } from \"./actions/screenshot.js\";\nimport { browserSelectAction } from \"./actions/select.js\";\nimport { browserTypeAction } from \"./actions/type.js\";\nimport { BrowserService, Session } from \"./services/browser-service.js\";\nimport { BrowserProcessManager } from \"./services/process-manager.js\";\nimport { BrowserWebSocketClient } from \"./services/websocket-client.js\";\n\nexport * from \"./types.js\";\nexport * from \"./utils/index.js\";\nexport { BrowserService, Session, BrowserWebSocketClient, BrowserProcessManager };\nexport {\n browserNavigateAction,\n browserClickAction,\n browserTypeAction,\n browserSelectAction,\n browserExtractAction,\n browserScreenshotAction,\n};\nconst configSchema = z.object({\n BROWSERBASE_API_KEY: z.string().optional(),\n BROWSERBASE_PROJECT_ID: z.string().optional(),\n OPENAI_API_KEY: z.string().optional(),\n ANTHROPIC_API_KEY: z.string().optional(),\n OLLAMA_BASE_URL: z.string().optional(),\n OLLAMA_MODEL: z.string().optional(),\n BROWSER_HEADLESS: z\n .string()\n .transform((val: string) => val === \"true\")\n .optional()\n .default(true),\n CAPSOLVER_API_KEY: z.string().optional(),\n BROWSER_SERVER_PORT: z.string().optional().default(\"3456\"),\n});\n\nconst browserStateProvider: Provider = {\n name: \"BROWSER_STATE\",\n description: \"Provides current browser state information\",\n\n get: async (\n runtime: IAgentRuntime,\n _message: Memory,\n _state?: State\n ): Promise<ProviderResult> => {\n const service = runtime.getService<BrowserService>(ServiceType.BROWSER);\n const session = await service?.getCurrentSession();\n\n if (!session || !service) {\n return {\n text: \"No active browser session\",\n values: {\n hasSession: false,\n },\n data: {},\n };\n }\n\n try {\n const client = service.getClient();\n const state = await client.getState(session.id);\n\n return {\n text: `Current browser page: \"${state.title}\" at ${state.url}`,\n values: {\n hasSession: true,\n url: state.url,\n title: state.title,\n },\n data: {\n sessionId: session.id,\n createdAt: session.createdAt.toISOString(),\n },\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.error(`Error getting browser state: ${errorMessage}`);\n return {\n text: \"Error getting browser state\",\n values: {\n hasSession: true,\n error: true,\n },\n data: {},\n };\n }\n },\n};\n\n// CLI self-registration - importing this module triggers CLI command registration.\n// Wrapped in a dynamic import so the plugin still loads when @elizaos/plugin-cli\n// is unavailable (e.g. when consumed outside the monorepo).\nimport(\"./cli/index.js\").catch(() => {\n // plugin-cli not available — skip CLI registration silently\n});\n\nexport const browserPlugin: Plugin = {\n name: \"plugin-browser\",\n description: \"Browser automation plugin\",\n config: {\n BROWSERBASE_API_KEY: process.env.BROWSERBASE_API_KEY ?? null,\n BROWSERBASE_PROJECT_ID: process.env.BROWSERBASE_PROJECT_ID ?? null,\n OPENAI_API_KEY: process.env.OPENAI_API_KEY ?? null,\n ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY ?? null,\n OLLAMA_BASE_URL: process.env.OLLAMA_BASE_URL ?? null,\n OLLAMA_MODEL: process.env.OLLAMA_MODEL ?? null,\n BROWSER_HEADLESS: process.env.BROWSER_HEADLESS ?? \"true\",\n CAPSOLVER_API_KEY: process.env.CAPSOLVER_API_KEY ?? null,\n BROWSER_SERVER_PORT: process.env.BROWSER_SERVER_PORT ?? \"3456\",\n },\n async init(config: Record<string, string | null>, _runtime: IAgentRuntime) {\n logger.info(\"Initializing browser automation plugin\");\n try {\n const validatedConfig = await configSchema.parseAsync(config);\n\n for (const [key, value] of Object.entries(validatedConfig)) {\n if (value !== undefined && value !== null) {\n process.env[key] = String(value);\n }\n }\n\n // Auto-enable browser actions when the plugin is loaded.\n // The plugin being present in the runtime means the user toggled it on.\n process.env.ENABLE_BROWSER = \"true\";\n process.env.BROWSER_ENABLED = \"true\";\n\n logger.info(\"Browser plugin initialized successfully\");\n } catch (error) {\n if (error && typeof error === \"object\" && \"issues\" in error) {\n const zodError = error as { issues?: Array<{ message: string }> };\n if (Array.isArray(zodError.issues)) {\n const errorMessages = zodError.issues.map((e) => e.message).join(\", \");\n throw new Error(`Invalid plugin configuration: ${errorMessages}`);\n }\n }\n throw error;\n }\n },\n services: [BrowserService],\n actions: [\n browserNavigateAction,\n browserClickAction,\n browserTypeAction,\n browserSelectAction,\n browserExtractAction,\n browserScreenshotAction,\n ],\n providers: [browserStateProvider],\n};\n\nexport default browserPlugin;\n"]}
|
|
@@ -14,6 +14,8 @@ export declare class BrowserService extends Service {
|
|
|
14
14
|
private processManager;
|
|
15
15
|
private client;
|
|
16
16
|
private isInitialized;
|
|
17
|
+
/** Whether this service is operating in sandbox remote mode. */
|
|
18
|
+
private sandboxMode;
|
|
17
19
|
constructor(runtime?: IAgentRuntime);
|
|
18
20
|
static start(runtime: IAgentRuntime): Promise<BrowserService>;
|
|
19
21
|
static stopRuntime(runtime: IAgentRuntime): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser-service.d.ts","sourceRoot":"","sources":["../../src/services/browser-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAU,OAAO,EAAe,MAAM,eAAe,CAAC;AACjF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAE/D,qBAAa,OAAQ,YAAW,cAAc;IAEnC,EAAE,EAAE,MAAM;IACV,SAAS,EAAE,IAAI;gBADf,EAAE,EAAE,MAAM,EACV,SAAS,GAAE,IAAiB;CAEtC;AAED,qBAAa,cAAe,SAAQ,OAAO;IACzC,MAAM,CAAC,WAAW,YAAuB;IACzC,qBAAqB,SAAgC;IAErD,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,cAAc,CAAwB;IAC9C,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,aAAa,CAAS;
|
|
1
|
+
{"version":3,"file":"browser-service.d.ts","sourceRoot":"","sources":["../../src/services/browser-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAU,OAAO,EAAe,MAAM,eAAe,CAAC;AACjF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAE/D,qBAAa,OAAQ,YAAW,cAAc;IAEnC,EAAE,EAAE,MAAM;IACV,SAAS,EAAE,IAAI;gBADf,EAAE,EAAE,MAAM,EACV,SAAS,GAAE,IAAiB;CAEtC;AAED,qBAAa,cAAe,SAAQ,OAAO;IACzC,MAAM,CAAC,WAAW,YAAuB;IACzC,qBAAqB,SAAgC;IAErD,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,cAAc,CAAwB;IAC9C,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,aAAa,CAAS;IAE9B,gEAAgE;IAChE,OAAO,CAAC,WAAW,CAAS;gBAEhB,OAAO,CAAC,EAAE,aAAa;WA6BtB,KAAK,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC;WAgDtD,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IASzD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAYrB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAkC3B,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAkBlD,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;IAI3D,iBAAiB,IAAI,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;IAOjD,kBAAkB,IAAI,OAAO,CAAC,OAAO,CAAC;IAUtC,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWtD,SAAS,IAAI,sBAAsB;YAOrB,YAAY;CAyB3B"}
|
|
@@ -17,24 +17,54 @@ export class BrowserService extends Service {
|
|
|
17
17
|
processManager;
|
|
18
18
|
client;
|
|
19
19
|
isInitialized = false;
|
|
20
|
+
/** Whether this service is operating in sandbox remote mode. */
|
|
21
|
+
sandboxMode = false;
|
|
20
22
|
constructor(runtime) {
|
|
21
23
|
super(runtime);
|
|
22
24
|
if (!runtime) {
|
|
23
25
|
throw new Error("BrowserService requires a runtime");
|
|
24
26
|
}
|
|
25
27
|
this.runtime = runtime;
|
|
28
|
+
// Detect sandbox mode from runtime
|
|
29
|
+
this.sandboxMode = Boolean(runtime.sandboxMode);
|
|
26
30
|
const portSetting = runtime.getSetting("BROWSER_SERVER_PORT");
|
|
27
31
|
const port = typeof portSetting === "string" ? parseInt(portSetting, 10) : 3456;
|
|
28
|
-
|
|
29
|
-
this.
|
|
32
|
+
// In sandbox mode, connect to remote endpoint instead of local process
|
|
33
|
+
if (this.sandboxMode) {
|
|
34
|
+
const remoteWsUrl = runtime.getSetting("SANDBOX_BROWSER_WS_URL") ??
|
|
35
|
+
`ws://localhost:${port}`;
|
|
36
|
+
this.processManager = new BrowserProcessManager(port);
|
|
37
|
+
this.client = new BrowserWebSocketClient(remoteWsUrl);
|
|
38
|
+
logger.info(`Browser service: sandbox mode, remote endpoint: ${remoteWsUrl}`);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
this.processManager = new BrowserProcessManager(port);
|
|
42
|
+
this.client = new BrowserWebSocketClient(`ws://localhost:${port}`);
|
|
43
|
+
}
|
|
30
44
|
}
|
|
31
45
|
static async start(runtime) {
|
|
32
46
|
logger.info("Starting browser automation service");
|
|
33
47
|
try {
|
|
34
48
|
const service = new BrowserService(runtime);
|
|
49
|
+
// In sandbox mode, do NOT start a local browser server process
|
|
50
|
+
if (service.sandboxMode) {
|
|
51
|
+
logger.info("Browser service: sandbox mode — skipping local server spawn");
|
|
52
|
+
logger.info("Connecting to remote browser server...");
|
|
53
|
+
try {
|
|
54
|
+
await service.initialize();
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
58
|
+
logger.warn(`Remote browser not reachable: ${errorMessage}`);
|
|
59
|
+
logger.warn("Browser plugin will be in degraded state");
|
|
60
|
+
}
|
|
61
|
+
return service;
|
|
62
|
+
}
|
|
35
63
|
logger.info("Starting browser server process...");
|
|
64
|
+
let serverStarted = false;
|
|
36
65
|
try {
|
|
37
66
|
await service.processManager.start();
|
|
67
|
+
serverStarted = true;
|
|
38
68
|
logger.info("Browser server started successfully");
|
|
39
69
|
}
|
|
40
70
|
catch (error) {
|
|
@@ -43,8 +73,13 @@ export class BrowserService extends Service {
|
|
|
43
73
|
logger.warn("Browser plugin will be available but automation will not work");
|
|
44
74
|
logger.warn("To fix this, run: cd packages/plugin-browser && npm run build");
|
|
45
75
|
}
|
|
46
|
-
|
|
47
|
-
|
|
76
|
+
// Only attempt WebSocket initialization if the server actually started.
|
|
77
|
+
// Otherwise we'd retry processManager.start() inside initialize() and
|
|
78
|
+
// throw again, which crashes service registration.
|
|
79
|
+
if (serverStarted) {
|
|
80
|
+
logger.info("Initializing WebSocket client...");
|
|
81
|
+
await service.initialize();
|
|
82
|
+
}
|
|
48
83
|
return service;
|
|
49
84
|
}
|
|
50
85
|
catch (error) {
|
|
@@ -77,8 +112,17 @@ export class BrowserService extends Service {
|
|
|
77
112
|
try {
|
|
78
113
|
if (!this.processManager.isServerRunning()) {
|
|
79
114
|
logger.warn("Browser server is not running, attempting to start...");
|
|
80
|
-
|
|
81
|
-
|
|
115
|
+
try {
|
|
116
|
+
await this.processManager.start();
|
|
117
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
118
|
+
}
|
|
119
|
+
catch (startError) {
|
|
120
|
+
const msg = startError instanceof Error ? startError.message : String(startError);
|
|
121
|
+
logger.error(`Failed to initialize browser service: ${msg}`);
|
|
122
|
+
// Don't throw — allow the service to exist in a degraded state
|
|
123
|
+
// so other plugins can still function.
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
82
126
|
}
|
|
83
127
|
logger.info("Connecting to browser server...");
|
|
84
128
|
await this.client.connect();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser-service.js","sourceRoot":"","sources":["../../src/services/browser-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjF,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAE/D,MAAM,OAAO,OAAO;IAET;IACA;IAFT,YACS,EAAU,EACV,YAAkB,IAAI,IAAI,EAAE;QAD5B,OAAE,GAAF,EAAE,CAAQ;QACV,cAAS,GAAT,SAAS,CAAmB;IAClC,CAAC;CACL;AAED,MAAM,OAAO,cAAe,SAAQ,OAAO;IACzC,MAAM,CAAC,WAAW,GAAG,WAAW,CAAC,OAAO,CAAC;IACzC,qBAAqB,GAAG,4BAA4B,CAAC;IAE7C,QAAQ,GAAG,IAAI,GAAG,EAAmB,CAAC;IACtC,gBAAgB,GAAkB,IAAI,CAAC;IACvC,cAAc,CAAwB;IACtC,MAAM,CAAyB;IAC/B,aAAa,GAAG,KAAK,CAAC;IAE9B,YAAY,OAAuB;QACjC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;QACvD,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,MAAM,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,qBAAqB,CAAC,CAAC;QAC9D,MAAM,IAAI,GAAG,OAAO,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAChF,IAAI,CAAC,cAAc,GAAG,IAAI,qBAAqB,CAAC,IAAI,CAAC,CAAC;QACtD,IAAI,CAAC,MAAM,GAAG,IAAI,sBAAsB,CAAC,kBAAkB,IAAI,EAAE,CAAC,CAAC;IACrE,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,OAAsB;QACvC,MAAM,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;QACnD,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,IAAI,cAAc,CAAC,OAAO,CAAC,CAAC;YAE5C,MAAM,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;YAClD,IAAI,CAAC;gBACH,MAAM,OAAO,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;gBACrC,MAAM,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;YACrD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC5E,MAAM,CAAC,KAAK,CAAC,mCAAmC,YAAY,EAAE,CAAC,CAAC;gBAChE,MAAM,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAC;gBAC7E,MAAM,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAC;YAC/E,CAAC;YAED,MAAM,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;YAChD,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC;YAE3B,OAAO,OAAO,CAAC;QACjB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC5E,MAAM,CAAC,KAAK,CAAC,oCAAoC,YAAY,EAAE,CAAC,CAAC;YACjE,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,OAAsB;QAC7C,MAAM,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;QACnD,MAAM,OAAO,GAAG,OAAO,CAAC,UAAU,CAAiB,cAAc,CAAC,WAAW,CAAC,CAAC;QAC/E,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAC/C,CAAC;QACD,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,IAAI;QACR,MAAM,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;QAE5C,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;YAC7C,MAAM,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;QACvC,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;QACzB,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;QACjC,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,UAAU;QACd,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,eAAe,EAAE,EAAE,CAAC;gBAC3C,MAAM,CAAC,IAAI,CAAC,uDAAuD,CAAC,CAAC;gBACrE,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;gBAClC,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;YAC5D,CAAC;YAED,MAAM,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;YAC/C,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YAE5B,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;YAE1B,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;YAC1B,MAAM,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAC;QAC1D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC5E,MAAM,CAAC,KAAK,CAAC,yCAAyC,YAAY,EAAE,CAAC,CAAC;YACtE,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,SAAiB;QACnC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACrD,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;QACpE,MAAM,eAAe,GAAI,QAAQ,CAAC,IAA+B,EAAE,SAAS,CAAC;QAC7E,IAAI,CAAC,eAAe,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;QACxD,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,eAAe,CAAC,CAAC;QAC7C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACtC,IAAI,CAAC,gBAAgB,GAAG,SAAS,CAAC;QAElC,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,SAAiB;QAChC,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,iBAAiB;QACrB,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC3B,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAClD,CAAC;IAED,KAAK,CAAC,kBAAkB;QACtB,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACtD,IAAI,cAAc,EAAE,CAAC;YACnB,OAAO,cAAc,CAAC;QACxB,CAAC;QAED,MAAM,SAAS,GAAG,WAAW,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;QACrF,OAAO,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,SAAiB;QACpC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC7C,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,gBAAgB,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;YAC3E,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAChC,IAAI,IAAI,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;gBACxC,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;YAC/B,CAAC;QACH,CAAC;IACH,CAAC;IAED,SAAS;QACP,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACrD,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAEO,KAAK,CAAC,YAAY,CAAC,WAAW,GAAG,EAAE,EAAE,OAAO,GAAG,IAAI;QACzD,MAAM,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAC;QAEzD,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;YACxD,IAAI,CAAC;gBACH,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;gBAC7C,IAAI,SAAS,EAAE,CAAC;oBACd,MAAM,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;oBACvC,OAAO;gBACT,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC5E,MAAM,CAAC,KAAK,CAAC,wBAAwB,OAAO,IAAI,WAAW,YAAY,YAAY,EAAE,CAAC,CAAC;YACzF,CAAC;YAED,IAAI,OAAO,GAAG,WAAW,EAAE,CAAC;gBAC1B,MAAM,CAAC,IAAI,CACT,qCAAqC,OAAO,GAAG,IAAI,iBAAiB,OAAO,IAAI,WAAW,GAAG,CAC9F,CAAC;gBACF,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;YAC/D,CAAC;QACH,CAAC;QAED,MAAM,IAAI,KAAK,CAAC,6CAA6C,WAAW,WAAW,CAAC,CAAC;IACvF,CAAC","sourcesContent":["import { type IAgentRuntime, logger, Service, ServiceType } from \"@elizaos/core\";\nimport type { BrowserSession } from \"../types.js\";\nimport { BrowserProcessManager } from \"./process-manager.js\";\nimport { BrowserWebSocketClient } from \"./websocket-client.js\";\n\nexport class Session implements BrowserSession {\n constructor(\n public id: string,\n public createdAt: Date = new Date()\n ) {}\n}\n\nexport class BrowserService extends Service {\n static serviceType = ServiceType.BROWSER;\n capabilityDescription = \"Browser automation service\";\n\n private sessions = new Map<string, Session>();\n private currentSessionId: string | null = null;\n private processManager: BrowserProcessManager;\n private client: BrowserWebSocketClient;\n private isInitialized = false;\n\n constructor(runtime?: IAgentRuntime) {\n super(runtime);\n if (!runtime) {\n throw new Error(\"BrowserService requires a runtime\");\n }\n this.runtime = runtime;\n const portSetting = runtime.getSetting(\"BROWSER_SERVER_PORT\");\n const port = typeof portSetting === \"string\" ? parseInt(portSetting, 10) : 3456;\n this.processManager = new BrowserProcessManager(port);\n this.client = new BrowserWebSocketClient(`ws://localhost:${port}`);\n }\n\n static async start(runtime: IAgentRuntime): Promise<BrowserService> {\n logger.info(\"Starting browser automation service\");\n try {\n const service = new BrowserService(runtime);\n\n logger.info(\"Starting browser server process...\");\n try {\n await service.processManager.start();\n logger.info(\"Browser server started successfully\");\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.error(`Failed to start browser server: ${errorMessage}`);\n logger.warn(\"Browser plugin will be available but automation will not work\");\n logger.warn(\"To fix this, run: cd packages/plugin-browser && npm run build\");\n }\n\n logger.info(\"Initializing WebSocket client...\");\n await service.initialize();\n\n return service;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.error(`Failed to start browser service: ${errorMessage}`);\n throw error;\n }\n }\n\n static async stopRuntime(runtime: IAgentRuntime): Promise<void> {\n logger.info(\"Stopping browser automation service\");\n const service = runtime.getService<BrowserService>(BrowserService.serviceType);\n if (!service) {\n throw new Error(\"Browser service not found\");\n }\n await service.stop();\n }\n\n async stop(): Promise<void> {\n logger.info(\"Cleaning up browser sessions\");\n\n for (const sessionId of this.sessions.keys()) {\n await this.destroySession(sessionId);\n }\n\n this.client.disconnect();\n await this.processManager.stop();\n this.isInitialized = false;\n }\n\n async initialize(): Promise<void> {\n if (this.isInitialized) {\n return;\n }\n\n try {\n if (!this.processManager.isServerRunning()) {\n logger.warn(\"Browser server is not running, attempting to start...\");\n await this.processManager.start();\n await new Promise((resolve) => setTimeout(resolve, 2000));\n }\n\n logger.info(\"Connecting to browser server...\");\n await this.client.connect();\n\n await this.waitForReady();\n\n this.isInitialized = true;\n logger.info(\"Browser service initialized successfully\");\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.error(`Failed to initialize browser service: ${errorMessage}`);\n throw error;\n }\n }\n\n async createSession(sessionId: string): Promise<Session> {\n if (!this.isInitialized) {\n throw new Error(\"Browser service not initialized\");\n }\n\n const response = await this.client.sendMessage(\"createSession\", {});\n const serverSessionId = (response.data as { sessionId?: string })?.sessionId;\n if (!serverSessionId) {\n throw new Error(\"Failed to create session on server\");\n }\n\n const session = new Session(serverSessionId);\n this.sessions.set(sessionId, session);\n this.currentSessionId = sessionId;\n\n return session;\n }\n\n async getSession(sessionId: string): Promise<Session | undefined> {\n return this.sessions.get(sessionId);\n }\n\n async getCurrentSession(): Promise<Session | undefined> {\n if (!this.currentSessionId) {\n return undefined;\n }\n return this.sessions.get(this.currentSessionId);\n }\n\n async getOrCreateSession(): Promise<Session> {\n const currentSession = await this.getCurrentSession();\n if (currentSession) {\n return currentSession;\n }\n\n const sessionId = `session-${Date.now()}-${Math.random().toString(36).substring(7)}`;\n return this.createSession(sessionId);\n }\n\n async destroySession(sessionId: string): Promise<void> {\n const session = this.sessions.get(sessionId);\n if (session) {\n await this.client.sendMessage(\"destroySession\", { sessionId: session.id });\n this.sessions.delete(sessionId);\n if (this.currentSessionId === sessionId) {\n this.currentSessionId = null;\n }\n }\n }\n\n getClient(): BrowserWebSocketClient {\n if (!this.isInitialized) {\n throw new Error(\"Browser service not initialized\");\n }\n return this.client;\n }\n\n private async waitForReady(maxAttempts = 60, delayMs = 3000): Promise<void> {\n logger.info(\"Waiting for browser server to be ready...\");\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n const isHealthy = await this.client.health();\n if (isHealthy) {\n logger.info(\"Browser server is ready\");\n return;\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.debug(`Health check attempt ${attempt}/${maxAttempts} failed: ${errorMessage}`);\n }\n\n if (attempt < maxAttempts) {\n logger.info(\n `Server not ready yet, retrying in ${delayMs / 1000}s... (attempt ${attempt}/${maxAttempts})`\n );\n await new Promise((resolve) => setTimeout(resolve, delayMs));\n }\n }\n\n throw new Error(`Browser server did not become ready after ${maxAttempts} attempts`);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"browser-service.js","sourceRoot":"","sources":["../../src/services/browser-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { type IAgentRuntime, logger, Service, ServiceType } from \"@elizaos/core\";\nimport type { BrowserSession } from \"../types.js\";\nimport { BrowserProcessManager } from \"./process-manager.js\";\nimport { BrowserWebSocketClient } from \"./websocket-client.js\";\n\nexport class Session implements BrowserSession {\n constructor(\n public id: string,\n public createdAt: Date = new Date()\n ) {}\n}\n\nexport class BrowserService extends Service {\n static serviceType = ServiceType.BROWSER;\n capabilityDescription = \"Browser automation service\";\n\n private sessions = new Map<string, Session>();\n private currentSessionId: string | null = null;\n private processManager: BrowserProcessManager;\n private client: BrowserWebSocketClient;\n private isInitialized = false;\n\n /** Whether this service is operating in sandbox remote mode. */\n private sandboxMode = false;\n\n constructor(runtime?: IAgentRuntime) {\n super(runtime);\n if (!runtime) {\n throw new Error(\"BrowserService requires a runtime\");\n }\n this.runtime = runtime;\n\n // Detect sandbox mode from runtime\n this.sandboxMode = Boolean(\n (runtime as Record<string, unknown>).sandboxMode,\n );\n\n const portSetting = runtime.getSetting(\"BROWSER_SERVER_PORT\");\n const port = typeof portSetting === \"string\" ? parseInt(portSetting, 10) : 3456;\n\n // In sandbox mode, connect to remote endpoint instead of local process\n if (this.sandboxMode) {\n const remoteWsUrl =\n (runtime.getSetting(\"SANDBOX_BROWSER_WS_URL\") as string | null) ??\n `ws://localhost:${port}`;\n this.processManager = new BrowserProcessManager(port);\n this.client = new BrowserWebSocketClient(remoteWsUrl);\n logger.info(`Browser service: sandbox mode, remote endpoint: ${remoteWsUrl}`);\n } else {\n this.processManager = new BrowserProcessManager(port);\n this.client = new BrowserWebSocketClient(`ws://localhost:${port}`);\n }\n }\n\n static async start(runtime: IAgentRuntime): Promise<BrowserService> {\n logger.info(\"Starting browser automation service\");\n try {\n const service = new BrowserService(runtime);\n\n // In sandbox mode, do NOT start a local browser server process\n if (service.sandboxMode) {\n logger.info(\"Browser service: sandbox mode — skipping local server spawn\");\n logger.info(\"Connecting to remote browser server...\");\n try {\n await service.initialize();\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.warn(`Remote browser not reachable: ${errorMessage}`);\n logger.warn(\"Browser plugin will be in degraded state\");\n }\n return service;\n }\n\n logger.info(\"Starting browser server process...\");\n let serverStarted = false;\n try {\n await service.processManager.start();\n serverStarted = true;\n logger.info(\"Browser server started successfully\");\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.error(`Failed to start browser server: ${errorMessage}`);\n logger.warn(\"Browser plugin will be available but automation will not work\");\n logger.warn(\"To fix this, run: cd packages/plugin-browser && npm run build\");\n }\n\n // Only attempt WebSocket initialization if the server actually started.\n // Otherwise we'd retry processManager.start() inside initialize() and\n // throw again, which crashes service registration.\n if (serverStarted) {\n logger.info(\"Initializing WebSocket client...\");\n await service.initialize();\n }\n\n return service;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.error(`Failed to start browser service: ${errorMessage}`);\n throw error;\n }\n }\n\n static async stopRuntime(runtime: IAgentRuntime): Promise<void> {\n logger.info(\"Stopping browser automation service\");\n const service = runtime.getService<BrowserService>(BrowserService.serviceType);\n if (!service) {\n throw new Error(\"Browser service not found\");\n }\n await service.stop();\n }\n\n async stop(): Promise<void> {\n logger.info(\"Cleaning up browser sessions\");\n\n for (const sessionId of this.sessions.keys()) {\n await this.destroySession(sessionId);\n }\n\n this.client.disconnect();\n await this.processManager.stop();\n this.isInitialized = false;\n }\n\n async initialize(): Promise<void> {\n if (this.isInitialized) {\n return;\n }\n\n try {\n if (!this.processManager.isServerRunning()) {\n logger.warn(\"Browser server is not running, attempting to start...\");\n try {\n await this.processManager.start();\n await new Promise((resolve) => setTimeout(resolve, 2000));\n } catch (startError) {\n const msg = startError instanceof Error ? startError.message : String(startError);\n logger.error(`Failed to initialize browser service: ${msg}`);\n // Don't throw — allow the service to exist in a degraded state\n // so other plugins can still function.\n return;\n }\n }\n\n logger.info(\"Connecting to browser server...\");\n await this.client.connect();\n\n await this.waitForReady();\n\n this.isInitialized = true;\n logger.info(\"Browser service initialized successfully\");\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.error(`Failed to initialize browser service: ${errorMessage}`);\n throw error;\n }\n }\n\n async createSession(sessionId: string): Promise<Session> {\n if (!this.isInitialized) {\n throw new Error(\"Browser service not initialized\");\n }\n\n const response = await this.client.sendMessage(\"createSession\", {});\n const serverSessionId = (response.data as { sessionId?: string })?.sessionId;\n if (!serverSessionId) {\n throw new Error(\"Failed to create session on server\");\n }\n\n const session = new Session(serverSessionId);\n this.sessions.set(sessionId, session);\n this.currentSessionId = sessionId;\n\n return session;\n }\n\n async getSession(sessionId: string): Promise<Session | undefined> {\n return this.sessions.get(sessionId);\n }\n\n async getCurrentSession(): Promise<Session | undefined> {\n if (!this.currentSessionId) {\n return undefined;\n }\n return this.sessions.get(this.currentSessionId);\n }\n\n async getOrCreateSession(): Promise<Session> {\n const currentSession = await this.getCurrentSession();\n if (currentSession) {\n return currentSession;\n }\n\n const sessionId = `session-${Date.now()}-${Math.random().toString(36).substring(7)}`;\n return this.createSession(sessionId);\n }\n\n async destroySession(sessionId: string): Promise<void> {\n const session = this.sessions.get(sessionId);\n if (session) {\n await this.client.sendMessage(\"destroySession\", { sessionId: session.id });\n this.sessions.delete(sessionId);\n if (this.currentSessionId === sessionId) {\n this.currentSessionId = null;\n }\n }\n }\n\n getClient(): BrowserWebSocketClient {\n if (!this.isInitialized) {\n throw new Error(\"Browser service not initialized\");\n }\n return this.client;\n }\n\n private async waitForReady(maxAttempts = 60, delayMs = 3000): Promise<void> {\n logger.info(\"Waiting for browser server to be ready...\");\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n const isHealthy = await this.client.health();\n if (isHealthy) {\n logger.info(\"Browser server is ready\");\n return;\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.debug(`Health check attempt ${attempt}/${maxAttempts} failed: ${errorMessage}`);\n }\n\n if (attempt < maxAttempts) {\n logger.info(\n `Server not ready yet, retrying in ${delayMs / 1000}s... (attempt ${attempt}/${maxAttempts})`\n );\n await new Promise((resolve) => setTimeout(resolve, delayMs));\n }\n }\n\n throw new Error(`Browser server did not become ready after ${maxAttempts} attempts`);\n }\n}\n"]}
|
|
@@ -6,8 +6,17 @@ export declare class BrowserProcessManager {
|
|
|
6
6
|
constructor(serverPort?: number);
|
|
7
7
|
private getBinaryName;
|
|
8
8
|
private findBinary;
|
|
9
|
+
/**
|
|
10
|
+
* Probe the port for an existing stagehand-server.
|
|
11
|
+
* Returns `true` if a WebSocket server is already listening and responds.
|
|
12
|
+
*/
|
|
13
|
+
private probeExistingServer;
|
|
14
|
+
/**
|
|
15
|
+
* Kill whatever process is listening on `this.serverPort`.
|
|
16
|
+
* Uses `lsof` on Unix / `netstat` on Windows. Best-effort.
|
|
17
|
+
*/
|
|
18
|
+
private freePort;
|
|
9
19
|
start(): Promise<void>;
|
|
10
|
-
private waitForServer;
|
|
11
20
|
stop(): Promise<void>;
|
|
12
21
|
isServerRunning(): boolean;
|
|
13
22
|
getServerUrl(): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process-manager.d.ts","sourceRoot":"","sources":["../../src/services/process-manager.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"process-manager.d.ts","sourceRoot":"","sources":["../../src/services/process-manager.ts"],"names":[],"mappings":"AAQA,qBAAa,qBAAqB;IAKpB,OAAO,CAAC,UAAU;IAJ9B,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAAuB;gBAErB,UAAU,GAAE,MAAa;IAI7C,OAAO,CAAC,aAAa;IAWrB,OAAO,CAAC,UAAU;IAyDlB;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAuC3B;;;OAGG;YACW,QAAQ;IA4BhB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA2HtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAqB3B,eAAe,IAAI,OAAO;IAI1B,YAAY,IAAI,MAAM;CAGvB"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
1
|
+
import { execSync, spawn } from "node:child_process";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { platform } from "node:os";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { logger } from "@elizaos/core";
|
|
7
|
+
import WebSocket from "ws";
|
|
7
8
|
export class BrowserProcessManager {
|
|
8
9
|
serverPort;
|
|
9
10
|
process = null;
|
|
@@ -44,13 +45,16 @@ export class BrowserProcessManager {
|
|
|
44
45
|
join(moduleDir, "../../.bin", "browser-server"),
|
|
45
46
|
join(moduleDir, "../server/dist/index.js"),
|
|
46
47
|
...(isDocker
|
|
47
|
-
? [
|
|
48
|
+
? [
|
|
49
|
+
"/app/packages/plugin-browser/server/dist/index.js",
|
|
50
|
+
"/app/browser-server/dist/index.js",
|
|
51
|
+
]
|
|
48
52
|
: []),
|
|
49
53
|
];
|
|
50
|
-
for (const
|
|
51
|
-
if (existsSync(
|
|
52
|
-
logger.info(`Found browser server at: ${
|
|
53
|
-
return
|
|
54
|
+
for (const p of possiblePaths) {
|
|
55
|
+
if (existsSync(p)) {
|
|
56
|
+
logger.info(`Found browser server at: ${p}`);
|
|
57
|
+
return p;
|
|
54
58
|
}
|
|
55
59
|
}
|
|
56
60
|
const srcPath = join(moduleDir, "../server/src/index.ts");
|
|
@@ -62,29 +66,124 @@ export class BrowserProcessManager {
|
|
|
62
66
|
logger.error(`Searched paths: ${possiblePaths.join(", ")}`);
|
|
63
67
|
return null;
|
|
64
68
|
}
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
// Port management
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
/**
|
|
73
|
+
* Probe the port for an existing stagehand-server.
|
|
74
|
+
* Returns `true` if a WebSocket server is already listening and responds.
|
|
75
|
+
*/
|
|
76
|
+
probeExistingServer() {
|
|
77
|
+
return new Promise((resolve) => {
|
|
78
|
+
const timeout = setTimeout(() => {
|
|
79
|
+
conn.close();
|
|
80
|
+
resolve(false);
|
|
81
|
+
}, 2_000);
|
|
82
|
+
const conn = new WebSocket(`ws://localhost:${this.serverPort}`);
|
|
83
|
+
conn.on("message", (data) => {
|
|
84
|
+
try {
|
|
85
|
+
const msg = JSON.parse(data.toString());
|
|
86
|
+
// The stagehand-server sends { type: "connected", ... } on connect.
|
|
87
|
+
if (msg.type === "connected") {
|
|
88
|
+
clearTimeout(timeout);
|
|
89
|
+
conn.close();
|
|
90
|
+
resolve(true);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// Not valid JSON — not our server.
|
|
96
|
+
}
|
|
97
|
+
clearTimeout(timeout);
|
|
98
|
+
conn.close();
|
|
99
|
+
resolve(false);
|
|
100
|
+
});
|
|
101
|
+
conn.on("open", () => {
|
|
102
|
+
// Connection opened — wait for the "connected" message (handled above).
|
|
103
|
+
// If no message arrives within the timeout, we'll resolve(false).
|
|
104
|
+
});
|
|
105
|
+
conn.on("error", () => {
|
|
106
|
+
clearTimeout(timeout);
|
|
107
|
+
resolve(false);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Kill whatever process is listening on `this.serverPort`.
|
|
113
|
+
* Uses `lsof` on Unix / `netstat` on Windows. Best-effort.
|
|
114
|
+
*/
|
|
115
|
+
async freePort() {
|
|
116
|
+
const port = this.serverPort;
|
|
117
|
+
try {
|
|
118
|
+
if (process.platform === "win32") {
|
|
119
|
+
// netstat → find PID → taskkill
|
|
120
|
+
const out = execSync(`netstat -ano | findstr :${port} | findstr LISTENING`, { encoding: "utf8", stdio: ["pipe", "pipe", "ignore"] });
|
|
121
|
+
const pid = out.trim().split(/\s+/).pop();
|
|
122
|
+
if (pid && /^\d+$/.test(pid)) {
|
|
123
|
+
execSync(`taskkill /F /PID ${pid}`, { stdio: "ignore" });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
execSync(`lsof -ti :${port} | xargs kill -9`, { stdio: "ignore" });
|
|
128
|
+
}
|
|
129
|
+
// Give the OS a moment to release the port.
|
|
130
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
131
|
+
logger.info(`Freed port ${port}`);
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// Nothing on the port, or kill failed — either way it's fine.
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
// Start / stop
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
65
140
|
async start() {
|
|
66
141
|
if (this.isRunning) {
|
|
67
142
|
logger.warn("Browser server is already running");
|
|
68
143
|
return;
|
|
69
144
|
}
|
|
145
|
+
// ── 1. Check for an existing stagehand-server on the port ──────────────
|
|
146
|
+
if (await this.probeExistingServer()) {
|
|
147
|
+
logger.info(`Reusing existing browser server on port ${this.serverPort}`);
|
|
148
|
+
this.isRunning = true;
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
// ── 2. Something else is on the port — try to free it ──────────────────
|
|
152
|
+
await this.freePort();
|
|
153
|
+
// ── 3. Spawn a new server ──────────────────────────────────────────────
|
|
70
154
|
if (!this.binaryPath) {
|
|
71
155
|
throw new Error("Browser server binary not found - please ensure server is built");
|
|
72
156
|
}
|
|
73
157
|
const binaryPath = this.binaryPath;
|
|
74
158
|
return new Promise((resolve, reject) => {
|
|
159
|
+
let settled = false;
|
|
160
|
+
const startupTimeout = setTimeout(() => {
|
|
161
|
+
if (!settled) {
|
|
162
|
+
settled = true;
|
|
163
|
+
if (this.process)
|
|
164
|
+
this.process.kill("SIGTERM");
|
|
165
|
+
reject(new Error("Browser server startup timed out after 30s"));
|
|
166
|
+
}
|
|
167
|
+
}, 30_000);
|
|
168
|
+
const ok = () => {
|
|
169
|
+
if (!settled) {
|
|
170
|
+
settled = true;
|
|
171
|
+
clearTimeout(startupTimeout);
|
|
172
|
+
resolve();
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
const fail = (err) => {
|
|
176
|
+
if (!settled) {
|
|
177
|
+
settled = true;
|
|
178
|
+
clearTimeout(startupTimeout);
|
|
179
|
+
reject(err);
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
// Pass through the parent env as-is; only override port + NODE_ENV.
|
|
75
183
|
const env = {
|
|
76
184
|
...process.env,
|
|
77
185
|
BROWSER_SERVER_PORT: this.serverPort.toString(),
|
|
78
186
|
NODE_ENV: process.env.NODE_ENV ?? "production",
|
|
79
|
-
BROWSERBASE_API_KEY: process.env.BROWSERBASE_API_KEY,
|
|
80
|
-
BROWSERBASE_PROJECT_ID: process.env.BROWSERBASE_PROJECT_ID,
|
|
81
|
-
OPENAI_API_KEY: process.env.OPENAI_API_KEY,
|
|
82
|
-
ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY,
|
|
83
|
-
BROWSER_HEADLESS: process.env.BROWSER_HEADLESS,
|
|
84
|
-
CAPSOLVER_API_KEY: process.env.CAPSOLVER_API_KEY,
|
|
85
|
-
OLLAMA_BASE_URL: process.env.OLLAMA_BASE_URL ?? "http://ollama:11434",
|
|
86
|
-
OLLAMA_MODEL: process.env.OLLAMA_MODEL ?? "llama3.2-vision",
|
|
87
|
-
DISPLAY: process.env.DISPLAY ?? ":99",
|
|
88
187
|
};
|
|
89
188
|
const isBinary = !binaryPath.endsWith(".js") && !binaryPath.endsWith(".ts");
|
|
90
189
|
const isTypeScript = binaryPath.endsWith(".ts");
|
|
@@ -92,8 +191,18 @@ export class BrowserProcessManager {
|
|
|
92
191
|
this.process = spawn(binaryPath, [], { env });
|
|
93
192
|
}
|
|
94
193
|
else if (isTypeScript) {
|
|
95
|
-
|
|
96
|
-
|
|
194
|
+
try {
|
|
195
|
+
const tsxPath = require.resolve("tsx/cli", {
|
|
196
|
+
paths: [process.cwd()],
|
|
197
|
+
});
|
|
198
|
+
this.process = spawn("node", [tsxPath, binaryPath], { env });
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
logger.warn("tsx not found, falling back to node --import tsx");
|
|
202
|
+
this.process = spawn("node", ["--import", "tsx", binaryPath], {
|
|
203
|
+
env,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
97
206
|
}
|
|
98
207
|
else {
|
|
99
208
|
this.process = spawn("node", [binaryPath], { env });
|
|
@@ -103,62 +212,37 @@ export class BrowserProcessManager {
|
|
|
103
212
|
logger.debug(`[BrowserServer] ${message}`);
|
|
104
213
|
if (message.includes("listening on port")) {
|
|
105
214
|
this.isRunning = true;
|
|
106
|
-
|
|
215
|
+
ok();
|
|
107
216
|
}
|
|
108
217
|
});
|
|
109
218
|
this.process.stderr?.on("data", (data) => {
|
|
110
|
-
|
|
219
|
+
const text = data.toString().trim();
|
|
220
|
+
if (text.includes("Error") ||
|
|
221
|
+
text.includes("EADDRINUSE") ||
|
|
222
|
+
text.includes("FATAL")) {
|
|
223
|
+
logger.error(`[BrowserServer] ${text}`);
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
logger.debug(`[BrowserServer stderr] ${text}`);
|
|
227
|
+
}
|
|
111
228
|
});
|
|
112
229
|
this.process.on("error", (error) => {
|
|
113
230
|
logger.error(`Failed to start browser server: ${error.message}`);
|
|
114
231
|
this.isRunning = false;
|
|
115
|
-
|
|
232
|
+
fail(error);
|
|
116
233
|
});
|
|
117
234
|
this.process.on("exit", (code) => {
|
|
118
|
-
logger.info(`Browser server exited with code ${code}`);
|
|
119
235
|
this.isRunning = false;
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
this.process.kill("SIGTERM");
|
|
236
|
+
if (code !== 0 && code !== null) {
|
|
237
|
+
logger.warn(`Browser server exited with code ${code}`);
|
|
238
|
+
fail(new Error(`Browser server exited with code ${code}`));
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
logger.info("Browser server stopped");
|
|
127
242
|
}
|
|
128
|
-
reject(error);
|
|
129
243
|
});
|
|
130
244
|
});
|
|
131
245
|
}
|
|
132
|
-
async waitForServer() {
|
|
133
|
-
const maxAttempts = 30;
|
|
134
|
-
const delay = 1000;
|
|
135
|
-
for (let i = 0; i < maxAttempts; i++) {
|
|
136
|
-
try {
|
|
137
|
-
const ws = require("ws");
|
|
138
|
-
const wsConnection = new ws(`ws://localhost:${this.serverPort}`);
|
|
139
|
-
await new Promise((resolve, reject) => {
|
|
140
|
-
const timeout = setTimeout(() => {
|
|
141
|
-
wsConnection.close();
|
|
142
|
-
reject(new Error("Connection timeout"));
|
|
143
|
-
}, 5000);
|
|
144
|
-
wsConnection.on("open", () => {
|
|
145
|
-
clearTimeout(timeout);
|
|
146
|
-
wsConnection.close();
|
|
147
|
-
resolve();
|
|
148
|
-
});
|
|
149
|
-
wsConnection.on("error", (error) => {
|
|
150
|
-
clearTimeout(timeout);
|
|
151
|
-
reject(error);
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
logger.info("Browser server is ready");
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
catch { }
|
|
158
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
159
|
-
}
|
|
160
|
-
throw new Error("Browser server failed to start");
|
|
161
|
-
}
|
|
162
246
|
async stop() {
|
|
163
247
|
if (!this.process || !this.isRunning) {
|
|
164
248
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process-manager.js","sourceRoot":"","sources":["../../src/services/process-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { type ChildProcess, spawn } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { platform } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { logger } from \"@elizaos/core\";\n\nexport class BrowserProcessManager {\n private process: ChildProcess | null = null;\n private isRunning = false;\n private binaryPath: string | null = null;\n\n constructor(private serverPort: number = 3456) {\n this.binaryPath = this.findBinary();\n }\n\n private getBinaryName(): { primary: string; fallback: string } {\n const platformName = platform();\n const arch = process.arch;\n const ext = platformName === \"win32\" ? \".exe\" : \"\";\n\n return {\n primary: `browser-server-${platformName}-${arch}${ext}`,\n fallback: `browser-server-${platformName}${ext}`,\n };\n }\n\n private findBinary(): string | null {\n const moduleDir = dirname(fileURLToPath(import.meta.url));\n const isDocker = process.env.DOCKER_CONTAINER === \"true\" || existsSync(\"/.dockerenv\");\n const binaryNames = this.getBinaryName();\n\n const possiblePaths = [\n ...(isDocker\n ? [\n \"/usr/local/bin/browser-server\",\n \"/usr/local/bin/browser-server-linux\",\n \"/app/browser-server\",\n `/app/binaries/${binaryNames.primary}`,\n `/app/binaries/${binaryNames.fallback}`,\n ]\n : []),\n\n ...(!isDocker ? [join(moduleDir, \"../server/dist/index.js\")] : []),\n join(moduleDir, \"../server/binaries\", binaryNames.primary),\n join(moduleDir, \"../server/binaries\", binaryNames.fallback),\n join(moduleDir, \"../../../browser-server\", binaryNames.primary),\n join(moduleDir, \"../../../browser-server\", binaryNames.fallback),\n join(moduleDir, \"../../.bin\", \"browser-server\"),\n join(moduleDir, \"../server/dist/index.js\"),\n\n ...(isDocker\n ? [\"/app/packages/plugin-browser/server/dist/index.js\", \"/app/browser-server/dist/index.js\"]\n : []),\n ];\n\n for (const path of possiblePaths) {\n if (existsSync(path)) {\n logger.info(`Found browser server at: ${path}`);\n return path;\n }\n }\n\n const srcPath = join(moduleDir, \"../server/src/index.ts\");\n if (existsSync(srcPath)) {\n logger.warn(\"No compiled binary found, will try to run from source with tsx\");\n return srcPath;\n }\n\n logger.error(\"Could not find browser server binary or source files\");\n logger.error(`Searched paths: ${possiblePaths.join(\", \")}`);\n return null;\n }\n\n async start(): Promise<void> {\n if (this.isRunning) {\n logger.warn(\"Browser server is already running\");\n return;\n }\n\n if (!this.binaryPath) {\n throw new Error(\"Browser server binary not found - please ensure server is built\");\n }\n\n const binaryPath = this.binaryPath;\n\n return new Promise((resolve, reject) => {\n const env = {\n ...process.env,\n BROWSER_SERVER_PORT: this.serverPort.toString(),\n NODE_ENV: process.env.NODE_ENV ?? \"production\",\n BROWSERBASE_API_KEY: process.env.BROWSERBASE_API_KEY,\n BROWSERBASE_PROJECT_ID: process.env.BROWSERBASE_PROJECT_ID,\n OPENAI_API_KEY: process.env.OPENAI_API_KEY,\n ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY,\n BROWSER_HEADLESS: process.env.BROWSER_HEADLESS,\n CAPSOLVER_API_KEY: process.env.CAPSOLVER_API_KEY,\n OLLAMA_BASE_URL: process.env.OLLAMA_BASE_URL ?? \"http://ollama:11434\",\n OLLAMA_MODEL: process.env.OLLAMA_MODEL ?? \"llama3.2-vision\",\n DISPLAY: process.env.DISPLAY ?? \":99\",\n };\n\n const isBinary = !binaryPath.endsWith(\".js\") && !binaryPath.endsWith(\".ts\");\n const isTypeScript = binaryPath.endsWith(\".ts\");\n\n if (isBinary) {\n this.process = spawn(binaryPath, [], { env });\n } else if (isTypeScript) {\n const tsxPath = require.resolve(\"tsx/cli\", { paths: [process.cwd()] });\n this.process = spawn(\"node\", [tsxPath, binaryPath], { env });\n } else {\n this.process = spawn(\"node\", [binaryPath], { env });\n }\n\n this.process.stdout?.on(\"data\", (data: Buffer) => {\n const message = data.toString().trim();\n logger.debug(`[BrowserServer] ${message}`);\n\n if (message.includes(\"listening on port\")) {\n this.isRunning = true;\n resolve();\n }\n });\n\n this.process.stderr?.on(\"data\", (data: Buffer) => {\n logger.error(`[BrowserServer Error] ${data.toString()}`);\n });\n\n this.process.on(\"error\", (error: Error) => {\n logger.error(`Failed to start browser server: ${error.message}`);\n this.isRunning = false;\n reject(error);\n });\n\n this.process.on(\"exit\", (code) => {\n logger.info(`Browser server exited with code ${code}`);\n this.isRunning = false;\n });\n\n this.waitForServer()\n .then(() => resolve())\n .catch((error) => {\n this.isRunning = false;\n if (this.process) {\n this.process.kill(\"SIGTERM\");\n }\n reject(error);\n });\n });\n }\n\n private async waitForServer(): Promise<void> {\n const maxAttempts = 30;\n const delay = 1000;\n\n for (let i = 0; i < maxAttempts; i++) {\n try {\n const ws = require(\"ws\");\n const wsConnection = new ws(`ws://localhost:${this.serverPort}`);\n\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n wsConnection.close();\n reject(new Error(\"Connection timeout\"));\n }, 5000);\n\n wsConnection.on(\"open\", () => {\n clearTimeout(timeout);\n wsConnection.close();\n resolve();\n });\n\n wsConnection.on(\"error\", (error: Error) => {\n clearTimeout(timeout);\n reject(error);\n });\n });\n\n logger.info(\"Browser server is ready\");\n return;\n } catch {}\n\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n\n throw new Error(\"Browser server failed to start\");\n }\n\n async stop(): Promise<void> {\n if (!this.process || !this.isRunning) {\n return;\n }\n\n return new Promise((resolve) => {\n this.process?.on(\"exit\", () => {\n this.isRunning = false;\n resolve();\n });\n\n this.process?.kill(\"SIGTERM\");\n\n setTimeout(() => {\n if (this.isRunning && this.process) {\n this.process.kill(\"SIGKILL\");\n }\n }, 5000);\n });\n }\n\n isServerRunning(): boolean {\n return this.isRunning;\n }\n\n getServerUrl(): string {\n return `ws://localhost:${this.serverPort}`;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"process-manager.js","sourceRoot":"","sources":["../../src/services/process-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { type ChildProcess, execSync, spawn } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { platform } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { logger } from \"@elizaos/core\";\nimport WebSocket from \"ws\";\n\nexport class BrowserProcessManager {\n private process: ChildProcess | null = null;\n private isRunning = false;\n private binaryPath: string | null = null;\n\n constructor(private serverPort: number = 3456) {\n this.binaryPath = this.findBinary();\n }\n\n private getBinaryName(): { primary: string; fallback: string } {\n const platformName = platform();\n const arch = process.arch;\n const ext = platformName === \"win32\" ? \".exe\" : \"\";\n\n return {\n primary: `browser-server-${platformName}-${arch}${ext}`,\n fallback: `browser-server-${platformName}${ext}`,\n };\n }\n\n private findBinary(): string | null {\n const moduleDir = dirname(fileURLToPath(import.meta.url));\n const isDocker =\n process.env.DOCKER_CONTAINER === \"true\" || existsSync(\"/.dockerenv\");\n const binaryNames = this.getBinaryName();\n\n const possiblePaths = [\n ...(isDocker\n ? [\n \"/usr/local/bin/browser-server\",\n \"/usr/local/bin/browser-server-linux\",\n \"/app/browser-server\",\n `/app/binaries/${binaryNames.primary}`,\n `/app/binaries/${binaryNames.fallback}`,\n ]\n : []),\n\n ...(!isDocker ? [join(moduleDir, \"../server/dist/index.js\")] : []),\n join(moduleDir, \"../server/binaries\", binaryNames.primary),\n join(moduleDir, \"../server/binaries\", binaryNames.fallback),\n join(moduleDir, \"../../../browser-server\", binaryNames.primary),\n join(moduleDir, \"../../../browser-server\", binaryNames.fallback),\n join(moduleDir, \"../../.bin\", \"browser-server\"),\n join(moduleDir, \"../server/dist/index.js\"),\n\n ...(isDocker\n ? [\n \"/app/packages/plugin-browser/server/dist/index.js\",\n \"/app/browser-server/dist/index.js\",\n ]\n : []),\n ];\n\n for (const p of possiblePaths) {\n if (existsSync(p)) {\n logger.info(`Found browser server at: ${p}`);\n return p;\n }\n }\n\n const srcPath = join(moduleDir, \"../server/src/index.ts\");\n if (existsSync(srcPath)) {\n logger.warn(\n \"No compiled binary found, will try to run from source with tsx\",\n );\n return srcPath;\n }\n\n logger.error(\"Could not find browser server binary or source files\");\n logger.error(`Searched paths: ${possiblePaths.join(\", \")}`);\n return null;\n }\n\n // ---------------------------------------------------------------------------\n // Port management\n // ---------------------------------------------------------------------------\n\n /**\n * Probe the port for an existing stagehand-server.\n * Returns `true` if a WebSocket server is already listening and responds.\n */\n private probeExistingServer(): Promise<boolean> {\n return new Promise((resolve) => {\n const timeout = setTimeout(() => {\n conn.close();\n resolve(false);\n }, 2_000);\n\n const conn = new WebSocket(`ws://localhost:${this.serverPort}`);\n\n conn.on(\"message\", (data) => {\n try {\n const msg = JSON.parse(data.toString());\n // The stagehand-server sends { type: \"connected\", ... } on connect.\n if (msg.type === \"connected\") {\n clearTimeout(timeout);\n conn.close();\n resolve(true);\n return;\n }\n } catch {\n // Not valid JSON — not our server.\n }\n clearTimeout(timeout);\n conn.close();\n resolve(false);\n });\n\n conn.on(\"open\", () => {\n // Connection opened — wait for the \"connected\" message (handled above).\n // If no message arrives within the timeout, we'll resolve(false).\n });\n\n conn.on(\"error\", () => {\n clearTimeout(timeout);\n resolve(false);\n });\n });\n }\n\n /**\n * Kill whatever process is listening on `this.serverPort`.\n * Uses `lsof` on Unix / `netstat` on Windows. Best-effort.\n */\n private async freePort(): Promise<void> {\n const port = this.serverPort;\n try {\n if (process.platform === \"win32\") {\n // netstat → find PID → taskkill\n const out = execSync(\n `netstat -ano | findstr :${port} | findstr LISTENING`,\n { encoding: \"utf8\", stdio: [\"pipe\", \"pipe\", \"ignore\"] },\n );\n const pid = out.trim().split(/\\s+/).pop();\n if (pid && /^\\d+$/.test(pid)) {\n execSync(`taskkill /F /PID ${pid}`, { stdio: \"ignore\" });\n }\n } else {\n execSync(`lsof -ti :${port} | xargs kill -9`, { stdio: \"ignore\" });\n }\n // Give the OS a moment to release the port.\n await new Promise((r) => setTimeout(r, 500));\n logger.info(`Freed port ${port}`);\n } catch {\n // Nothing on the port, or kill failed — either way it's fine.\n }\n }\n\n // ---------------------------------------------------------------------------\n // Start / stop\n // ---------------------------------------------------------------------------\n\n async start(): Promise<void> {\n if (this.isRunning) {\n logger.warn(\"Browser server is already running\");\n return;\n }\n\n // ── 1. Check for an existing stagehand-server on the port ──────────────\n if (await this.probeExistingServer()) {\n logger.info(\n `Reusing existing browser server on port ${this.serverPort}`,\n );\n this.isRunning = true;\n return;\n }\n\n // ── 2. Something else is on the port — try to free it ──────────────────\n await this.freePort();\n\n // ── 3. Spawn a new server ──────────────────────────────────────────────\n if (!this.binaryPath) {\n throw new Error(\n \"Browser server binary not found - please ensure server is built\",\n );\n }\n\n const binaryPath = this.binaryPath;\n\n return new Promise((resolve, reject) => {\n let settled = false;\n const startupTimeout = setTimeout(() => {\n if (!settled) {\n settled = true;\n if (this.process) this.process.kill(\"SIGTERM\");\n reject(new Error(\"Browser server startup timed out after 30s\"));\n }\n }, 30_000);\n const ok = () => {\n if (!settled) {\n settled = true;\n clearTimeout(startupTimeout);\n resolve();\n }\n };\n const fail = (err: Error) => {\n if (!settled) {\n settled = true;\n clearTimeout(startupTimeout);\n reject(err);\n }\n };\n\n // Pass through the parent env as-is; only override port + NODE_ENV.\n const env: Record<string, string | undefined> = {\n ...process.env,\n BROWSER_SERVER_PORT: this.serverPort.toString(),\n NODE_ENV: process.env.NODE_ENV ?? \"production\",\n };\n\n const isBinary =\n !binaryPath.endsWith(\".js\") && !binaryPath.endsWith(\".ts\");\n const isTypeScript = binaryPath.endsWith(\".ts\");\n\n if (isBinary) {\n this.process = spawn(binaryPath, [], { env });\n } else if (isTypeScript) {\n try {\n const tsxPath = require.resolve(\"tsx/cli\", {\n paths: [process.cwd()],\n });\n this.process = spawn(\"node\", [tsxPath, binaryPath], { env });\n } catch {\n logger.warn(\n \"tsx not found, falling back to node --import tsx\",\n );\n this.process = spawn(\"node\", [\"--import\", \"tsx\", binaryPath], {\n env,\n });\n }\n } else {\n this.process = spawn(\"node\", [binaryPath], { env });\n }\n\n this.process.stdout?.on(\"data\", (data: Buffer) => {\n const message = data.toString().trim();\n logger.debug(`[BrowserServer] ${message}`);\n\n if (message.includes(\"listening on port\")) {\n this.isRunning = true;\n ok();\n }\n });\n\n this.process.stderr?.on(\"data\", (data: Buffer) => {\n const text = data.toString().trim();\n if (\n text.includes(\"Error\") ||\n text.includes(\"EADDRINUSE\") ||\n text.includes(\"FATAL\")\n ) {\n logger.error(`[BrowserServer] ${text}`);\n } else {\n logger.debug(`[BrowserServer stderr] ${text}`);\n }\n });\n\n this.process.on(\"error\", (error: Error) => {\n logger.error(`Failed to start browser server: ${error.message}`);\n this.isRunning = false;\n fail(error);\n });\n\n this.process.on(\"exit\", (code) => {\n this.isRunning = false;\n if (code !== 0 && code !== null) {\n logger.warn(`Browser server exited with code ${code}`);\n fail(new Error(`Browser server exited with code ${code}`));\n } else {\n logger.info(\"Browser server stopped\");\n }\n });\n });\n }\n\n async stop(): Promise<void> {\n if (!this.process || !this.isRunning) {\n return;\n }\n\n return new Promise((resolve) => {\n this.process?.on(\"exit\", () => {\n this.isRunning = false;\n resolve();\n });\n\n this.process?.kill(\"SIGTERM\");\n\n setTimeout(() => {\n if (this.isRunning && this.process) {\n this.process.kill(\"SIGKILL\");\n }\n }, 5000);\n });\n }\n\n isServerRunning(): boolean {\n return this.isRunning;\n }\n\n getServerUrl(): string {\n return `ws://localhost:${this.serverPort}`;\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elizaos/plugin-browser",
|
|
3
3
|
"description": "Browser automation plugin for ElizaOS - enables AI agents to browse websites, interact with elements, and extract data",
|
|
4
|
-
"version": "2.0.0-alpha.
|
|
4
|
+
"version": "2.0.0-alpha.7",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"module": "dist/index.js",
|