@elizaos/plugin-browser 2.0.0-alpha.9 → 2.0.3-beta.2

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 (256) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +98 -83
  3. package/auto-enable.ts +24 -0
  4. package/dist/actions/browser-autofill-login.d.ts +43 -0
  5. package/dist/actions/browser-autofill-login.d.ts.map +1 -0
  6. package/dist/actions/browser-autofill-login.js +278 -0
  7. package/dist/actions/browser-autofill-login.js.map +1 -0
  8. package/dist/actions/browser.d.ts +11 -0
  9. package/dist/actions/browser.d.ts.map +1 -0
  10. package/dist/actions/browser.js +412 -0
  11. package/dist/actions/browser.js.map +1 -0
  12. package/dist/actions/manage-browser-bridge.d.ts +34 -0
  13. package/dist/actions/manage-browser-bridge.d.ts.map +1 -0
  14. package/dist/actions/manage-browser-bridge.js +572 -0
  15. package/dist/actions/manage-browser-bridge.js.map +1 -0
  16. package/dist/bridge-policy.d.ts +10 -0
  17. package/dist/bridge-policy.d.ts.map +1 -0
  18. package/dist/bridge-policy.js +37 -0
  19. package/dist/bridge-policy.js.map +1 -0
  20. package/dist/bridge-readiness.d.ts +16 -0
  21. package/dist/bridge-readiness.d.ts.map +1 -0
  22. package/dist/bridge-readiness.js +82 -0
  23. package/dist/bridge-readiness.js.map +1 -0
  24. package/dist/bridge-records.d.ts +9 -0
  25. package/dist/bridge-records.d.ts.map +1 -0
  26. package/dist/bridge-records.js +37 -0
  27. package/dist/bridge-records.js.map +1 -0
  28. package/dist/browser-capture-hooks.d.ts +9 -0
  29. package/dist/browser-capture-hooks.d.ts.map +1 -0
  30. package/dist/browser-capture-hooks.js +15 -0
  31. package/dist/browser-capture-hooks.js.map +1 -0
  32. package/dist/browser-service.d.ts +103 -0
  33. package/dist/browser-service.d.ts.map +1 -0
  34. package/dist/browser-service.js +186 -0
  35. package/dist/browser-service.js.map +1 -0
  36. package/dist/browser-workspace-hooks.d.ts +14 -0
  37. package/dist/browser-workspace-hooks.d.ts.map +1 -0
  38. package/dist/browser-workspace-hooks.js +15 -0
  39. package/dist/browser-workspace-hooks.js.map +1 -0
  40. package/dist/companion-auth.d.ts +34 -0
  41. package/dist/companion-auth.d.ts.map +1 -0
  42. package/dist/companion-auth.js +98 -0
  43. package/dist/companion-auth.js.map +1 -0
  44. package/dist/contracts.d.ts +284 -0
  45. package/dist/contracts.d.ts.map +1 -0
  46. package/dist/contracts.js +56 -0
  47. package/dist/contracts.js.map +1 -0
  48. package/dist/index.d.ts +30 -16
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +76 -90
  51. package/dist/index.js.map +1 -1
  52. package/dist/lifeops-session-contracts.d.ts +46 -0
  53. package/dist/lifeops-session-contracts.d.ts.map +1 -0
  54. package/dist/lifeops-session-contracts.js +1 -0
  55. package/dist/lifeops-session-contracts.js.map +1 -0
  56. package/dist/message-adapter.d.ts +9 -0
  57. package/dist/message-adapter.d.ts.map +1 -0
  58. package/dist/message-adapter.js +104 -0
  59. package/dist/message-adapter.js.map +1 -0
  60. package/dist/packaging.d.ts +27 -0
  61. package/dist/packaging.d.ts.map +1 -0
  62. package/dist/packaging.js +571 -0
  63. package/dist/packaging.js.map +1 -0
  64. package/dist/password-manager-bridge.d.ts +50 -0
  65. package/dist/password-manager-bridge.d.ts.map +1 -0
  66. package/dist/password-manager-bridge.js +437 -0
  67. package/dist/password-manager-bridge.js.map +1 -0
  68. package/dist/plugin.d.ts +10 -0
  69. package/dist/plugin.d.ts.map +1 -0
  70. package/dist/plugin.js +168 -0
  71. package/dist/plugin.js.map +1 -0
  72. package/dist/providers/workspace.d.ts +13 -0
  73. package/dist/providers/workspace.d.ts.map +1 -0
  74. package/dist/providers/workspace.js +64 -0
  75. package/dist/providers/workspace.js.map +1 -0
  76. package/dist/routes/bridge.d.ts +37 -0
  77. package/dist/routes/bridge.d.ts.map +1 -0
  78. package/dist/routes/bridge.js +844 -0
  79. package/dist/routes/bridge.js.map +1 -0
  80. package/dist/routes/workspace-account-gate.d.ts +29 -0
  81. package/dist/routes/workspace-account-gate.d.ts.map +1 -0
  82. package/dist/routes/workspace-account-gate.js +147 -0
  83. package/dist/routes/workspace-account-gate.js.map +1 -0
  84. package/dist/routes/workspace-setup.d.ts +10 -0
  85. package/dist/routes/workspace-setup.d.ts.map +1 -0
  86. package/dist/routes/workspace-setup.js +65 -0
  87. package/dist/routes/workspace-setup.js.map +1 -0
  88. package/dist/routes/workspace.d.ts +20 -0
  89. package/dist/routes/workspace.d.ts.map +1 -0
  90. package/dist/routes/workspace.js +276 -0
  91. package/dist/routes/workspace.js.map +1 -0
  92. package/dist/schema.d.ts +2326 -0
  93. package/dist/schema.d.ts.map +1 -0
  94. package/dist/schema.js +133 -0
  95. package/dist/schema.js.map +1 -0
  96. package/dist/service.d.ts +30 -0
  97. package/dist/service.d.ts.map +1 -0
  98. package/dist/service.js +5 -0
  99. package/dist/service.js.map +1 -0
  100. package/dist/targets/bridge-target.d.ts +31 -0
  101. package/dist/targets/bridge-target.d.ts.map +1 -0
  102. package/dist/targets/bridge-target.js +98 -0
  103. package/dist/targets/bridge-target.js.map +1 -0
  104. package/dist/targets/stagehand-target.d.ts +3 -0
  105. package/dist/targets/stagehand-target.d.ts.map +1 -0
  106. package/dist/targets/stagehand-target.js +187 -0
  107. package/dist/targets/stagehand-target.js.map +1 -0
  108. package/dist/workspace/browser-capture.d.ts +41 -0
  109. package/dist/workspace/browser-capture.d.ts.map +1 -0
  110. package/dist/workspace/browser-capture.js +159 -0
  111. package/dist/workspace/browser-capture.js.map +1 -0
  112. package/dist/workspace/browser-workspace-desktop.d.ts +19 -0
  113. package/dist/workspace/browser-workspace-desktop.d.ts.map +1 -0
  114. package/dist/workspace/browser-workspace-desktop.js +1578 -0
  115. package/dist/workspace/browser-workspace-desktop.js.map +1 -0
  116. package/dist/workspace/browser-workspace-elements.d.ts +42 -0
  117. package/dist/workspace/browser-workspace-elements.d.ts.map +1 -0
  118. package/dist/workspace/browser-workspace-elements.js +547 -0
  119. package/dist/workspace/browser-workspace-elements.js.map +1 -0
  120. package/dist/workspace/browser-workspace-forms.d.ts +19 -0
  121. package/dist/workspace/browser-workspace-forms.d.ts.map +1 -0
  122. package/dist/workspace/browser-workspace-forms.js +277 -0
  123. package/dist/workspace/browser-workspace-forms.js.map +1 -0
  124. package/dist/workspace/browser-workspace-helpers.d.ts +32 -0
  125. package/dist/workspace/browser-workspace-helpers.d.ts.map +1 -0
  126. package/dist/workspace/browser-workspace-helpers.js +232 -0
  127. package/dist/workspace/browser-workspace-helpers.js.map +1 -0
  128. package/dist/workspace/browser-workspace-jsdom.d.ts +16 -0
  129. package/dist/workspace/browser-workspace-jsdom.d.ts.map +1 -0
  130. package/dist/workspace/browser-workspace-jsdom.js +233 -0
  131. package/dist/workspace/browser-workspace-jsdom.js.map +1 -0
  132. package/dist/workspace/browser-workspace-network.d.ts +7 -0
  133. package/dist/workspace/browser-workspace-network.d.ts.map +1 -0
  134. package/dist/workspace/browser-workspace-network.js +145 -0
  135. package/dist/workspace/browser-workspace-network.js.map +1 -0
  136. package/dist/workspace/browser-workspace-snapshots.d.ts +14 -0
  137. package/dist/workspace/browser-workspace-snapshots.d.ts.map +1 -0
  138. package/dist/workspace/browser-workspace-snapshots.js +144 -0
  139. package/dist/workspace/browser-workspace-snapshots.js.map +1 -0
  140. package/dist/workspace/browser-workspace-state.d.ts +24 -0
  141. package/dist/workspace/browser-workspace-state.d.ts.map +1 -0
  142. package/dist/workspace/browser-workspace-state.js +155 -0
  143. package/dist/workspace/browser-workspace-state.js.map +1 -0
  144. package/dist/workspace/browser-workspace-types.d.ts +345 -0
  145. package/dist/workspace/browser-workspace-types.d.ts.map +1 -0
  146. package/dist/workspace/browser-workspace-types.js +11 -0
  147. package/dist/workspace/browser-workspace-types.js.map +1 -0
  148. package/dist/workspace/browser-workspace-web.d.ts +8 -0
  149. package/dist/workspace/browser-workspace-web.d.ts.map +1 -0
  150. package/dist/workspace/browser-workspace-web.js +1342 -0
  151. package/dist/workspace/browser-workspace-web.js.map +1 -0
  152. package/dist/workspace/browser-workspace.d.ts +39 -0
  153. package/dist/workspace/browser-workspace.d.ts.map +1 -0
  154. package/dist/workspace/browser-workspace.js +958 -0
  155. package/dist/workspace/browser-workspace.js.map +1 -0
  156. package/dist/workspace/index.d.ts +26 -0
  157. package/dist/workspace/index.d.ts.map +1 -0
  158. package/dist/workspace/index.js +3 -0
  159. package/dist/workspace/index.js.map +1 -0
  160. package/dist/workspace.d.ts +2 -0
  161. package/dist/workspace.d.ts.map +1 -0
  162. package/dist/workspace.js +2 -0
  163. package/dist/workspace.js.map +1 -0
  164. package/package.json +71 -110
  165. package/dist/actions/click.d.ts +0 -3
  166. package/dist/actions/click.d.ts.map +0 -1
  167. package/dist/actions/click.js +0 -158
  168. package/dist/actions/click.js.map +0 -1
  169. package/dist/actions/extract.d.ts +0 -3
  170. package/dist/actions/extract.d.ts.map +0 -1
  171. package/dist/actions/extract.js +0 -168
  172. package/dist/actions/extract.js.map +0 -1
  173. package/dist/actions/index.d.ts +0 -7
  174. package/dist/actions/index.d.ts.map +0 -1
  175. package/dist/actions/index.js +0 -7
  176. package/dist/actions/index.js.map +0 -1
  177. package/dist/actions/navigate.d.ts +0 -3
  178. package/dist/actions/navigate.d.ts.map +0 -1
  179. package/dist/actions/navigate.js +0 -187
  180. package/dist/actions/navigate.js.map +0 -1
  181. package/dist/actions/screenshot.d.ts +0 -3
  182. package/dist/actions/screenshot.d.ts.map +0 -1
  183. package/dist/actions/screenshot.js +0 -167
  184. package/dist/actions/screenshot.js.map +0 -1
  185. package/dist/actions/select.d.ts +0 -3
  186. package/dist/actions/select.d.ts.map +0 -1
  187. package/dist/actions/select.js +0 -167
  188. package/dist/actions/select.js.map +0 -1
  189. package/dist/actions/type.d.ts +0 -3
  190. package/dist/actions/type.d.ts.map +0 -1
  191. package/dist/actions/type.js +0 -167
  192. package/dist/actions/type.js.map +0 -1
  193. package/dist/cli/index.d.ts +0 -8
  194. package/dist/cli/index.d.ts.map +0 -1
  195. package/dist/cli/index.js +0 -13
  196. package/dist/cli/index.js.map +0 -1
  197. package/dist/cli/register.d.ts +0 -20
  198. package/dist/cli/register.d.ts.map +0 -1
  199. package/dist/cli/register.js +0 -403
  200. package/dist/cli/register.js.map +0 -1
  201. package/dist/providerRelevance.d.ts +0 -4
  202. package/dist/providerRelevance.d.ts.map +0 -1
  203. package/dist/providerRelevance.js +0 -33
  204. package/dist/providerRelevance.js.map +0 -1
  205. package/dist/providers/browser-state.d.ts +0 -3
  206. package/dist/providers/browser-state.d.ts.map +0 -1
  207. package/dist/providers/browser-state.js +0 -72
  208. package/dist/providers/browser-state.js.map +0 -1
  209. package/dist/providers/index.d.ts +0 -2
  210. package/dist/providers/index.d.ts.map +0 -1
  211. package/dist/providers/index.js +0 -2
  212. package/dist/providers/index.js.map +0 -1
  213. package/dist/services/browser-service.d.ts +0 -32
  214. package/dist/services/browser-service.d.ts.map +0 -1
  215. package/dist/services/browser-service.js +0 -213
  216. package/dist/services/browser-service.js.map +0 -1
  217. package/dist/services/index.d.ts +0 -4
  218. package/dist/services/index.d.ts.map +0 -1
  219. package/dist/services/index.js +0 -4
  220. package/dist/services/index.js.map +0 -1
  221. package/dist/services/process-manager.d.ts +0 -24
  222. package/dist/services/process-manager.d.ts.map +0 -1
  223. package/dist/services/process-manager.js +0 -270
  224. package/dist/services/process-manager.js.map +0 -1
  225. package/dist/services/websocket-client.d.ts +0 -35
  226. package/dist/services/websocket-client.d.ts.map +0 -1
  227. package/dist/services/websocket-client.js +0 -221
  228. package/dist/services/websocket-client.js.map +0 -1
  229. package/dist/types.d.ts +0 -101
  230. package/dist/types.d.ts.map +0 -1
  231. package/dist/types.js +0 -2
  232. package/dist/types.js.map +0 -1
  233. package/dist/utils/captcha.d.ts +0 -33
  234. package/dist/utils/captcha.d.ts.map +0 -1
  235. package/dist/utils/captcha.js +0 -219
  236. package/dist/utils/captcha.js.map +0 -1
  237. package/dist/utils/errors.d.ts +0 -37
  238. package/dist/utils/errors.d.ts.map +0 -1
  239. package/dist/utils/errors.js +0 -81
  240. package/dist/utils/errors.js.map +0 -1
  241. package/dist/utils/index.d.ts +0 -5
  242. package/dist/utils/index.d.ts.map +0 -1
  243. package/dist/utils/index.js +0 -5
  244. package/dist/utils/index.js.map +0 -1
  245. package/dist/utils/retry.d.ts +0 -26
  246. package/dist/utils/retry.d.ts.map +0 -1
  247. package/dist/utils/retry.js +0 -55
  248. package/dist/utils/retry.js.map +0 -1
  249. package/dist/utils/security.d.ts +0 -27
  250. package/dist/utils/security.d.ts.map +0 -1
  251. package/dist/utils/security.js +0 -139
  252. package/dist/utils/security.js.map +0 -1
  253. package/dist/utils/url.d.ts +0 -12
  254. package/dist/utils/url.d.ts.map +0 -1
  255. package/dist/utils/url.js +0 -39
  256. package/dist/utils/url.js.map +0 -1
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Shaw Walters and elizaOS Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,116 +1,131 @@
1
- # Browser Automation Plugin for ElizaOS (TypeScript)
1
+ # @elizaos/plugin-browser
2
2
 
3
- Browser automation plugin enabling AI agents to browse websites, interact with elements, and extract data.
3
+ Browser automation and companion bridge plugin for elizaOS. Adds the `BROWSER` action and `MANAGE_BROWSER_BRIDGE` action to any Eliza agent, owns the Eliza browser workspace (electrobun-embedded `BrowserView` on desktop, JSDOM fallback on web/mobile), and manages the Chrome/Safari Agent Browser Bridge companion extension.
4
4
 
5
- ## Features
5
+ ## What this plugin provides
6
6
 
7
- - **Navigation**: Navigate to URLs, go back/forward, refresh pages
8
- - **AI-Powered Interactions**: Click, type, and select elements using natural language
9
- - **Data Extraction**: Extract structured data from web pages
10
- - **Screenshots**: Capture page screenshots
11
- - **CAPTCHA Solving**: Automatic CAPTCHA solving (Turnstile, reCAPTCHA, hCaptcha)
12
- - **Session Management**: Handle multiple browser sessions
13
- - **Security**: URL validation, domain filtering, rate limiting
7
+ ### Actions
14
8
 
15
- ## Installation
9
+ **BROWSER** — Controls a registered browser target. The agent picks the best available backend automatically, or you can pin a specific target with the `target` parameter. Supported operations:
16
10
 
17
- ```bash
18
- npm install @elizaos/plugin-browser
19
- ```
20
-
21
- ## Configuration
11
+ | `action` value | What it does |
12
+ |---|---|
13
+ | `open` | Open a URL in a new tab |
14
+ | `navigate` | Navigate an existing tab to a URL |
15
+ | `click` | Click a DOM element by CSS selector |
16
+ | `type` | Type text into a selector |
17
+ | `press` | Press a keyboard key |
18
+ | `get` | Get a DOM value |
19
+ | `state` | Return current tab state (URL, title) |
20
+ | `snapshot` | Capture a DOM snapshot |
21
+ | `screenshot` | Capture a screenshot |
22
+ | `reload` | Reload the current tab |
23
+ | `back` / `forward` | Browser history navigation |
24
+ | `close` | Close a tab |
25
+ | `show` / `hide` | Show or hide the browser window |
26
+ | `wait` | Wait for a selector to appear |
27
+ | `tab` | Tab management (list/new/close/switch) |
28
+ | `realistic_click` | Animated cursor click (visible to user) |
29
+ | `realistic_fill` | Animated fill with per-character delay |
30
+ | `realistic_type` | Animated typing |
31
+ | `realistic_press` | Animated key press |
32
+ | `cursor_move` | Animate cursor to a position |
33
+ | `cursor_hide` | Hide the cursor overlay |
34
+ | `autofill_login` | Fill saved credentials into a browser tab (vault-gated; requires `domain`) |
22
35
 
23
- ```bash
24
- # Optional - for cloud browser
25
- BROWSERBASE_API_KEY=your_api_key
26
- BROWSERBASE_PROJECT_ID=your_project_id
36
+ **MANAGE_BROWSER_BRIDGE** — Manages the Chrome/Safari companion extension. Subactions: `install` (build + reveal + open manager), `reveal_folder` (open the build folder in Finder/Explorer), `open_manager` (`chrome://extensions`), `refresh` (report paired companions and settings). Owner-only.
27
37
 
28
- # Optional - for AI-powered interactions
29
- OPENAI_API_KEY=your_openai_key
30
- ANTHROPIC_API_KEY=your_anthropic_key
38
+ ### Browser targets
31
39
 
32
- # Optional - for CAPTCHA solving
33
- CAPSOLVER_API_KEY=your_capsolver_key
40
+ The plugin uses a pluggable target registry in `BrowserService`. Targets are selected automatically by availability and score:
34
41
 
35
- # Browser settings
36
- BROWSER_HEADLESS=true
37
- BROWSER_ENABLED=true
38
- ```
42
+ | Target ID | Backend | When available |
43
+ |---|---|---|
44
+ | `workspace` | Electrobun `BrowserView` (desktop) or JSDOM (web) | Always |
45
+ | `bridge` | Paired Chrome/Safari via companion extension | At least one companion paired |
46
+ | `stagehand` | Playwright/Stagehand via HTTP endpoint | `ELIZA_BROWSER_STAGEHAND_COMMAND_URL` or `STAGEHAND_SERVER_URL` set |
39
47
 
40
- ## Usage
48
+ External plugins can register additional targets by calling `BrowserService.registerTarget(target)`.
41
49
 
42
- ```typescript
43
- import { browserPlugin } from '@elizaos/plugin-browser';
50
+ ### Provider
44
51
 
45
- const agent = {
46
- name: 'BrowserAgent',
47
- plugins: [browserPlugin],
48
- };
49
- ```
52
+ `browser_workspace` — Injects the current dispatch mode (`desktop` / `web`) and a capped list of open tabs into agent context. Active when the `browser` or `web` context is selected.
50
53
 
51
- ## Actions
54
+ ### Routes
52
55
 
53
- | Action | Description | Examples |
54
- | -------------------- | ------------------ | ----------------------------------- |
55
- | `BROWSER_NAVIGATE` | Navigate to URL | "Go to google.com" |
56
- | `BROWSER_BACK` | Go back in history | "Go back" |
57
- | `BROWSER_FORWARD` | Go forward | "Go forward" |
58
- | `BROWSER_REFRESH` | Refresh page | "Refresh the page" |
59
- | `BROWSER_CLICK` | Click element | "Click the search button" |
60
- | `BROWSER_TYPE` | Type text | "Type 'hello' in the search box" |
61
- | `BROWSER_SELECT` | Select option | "Select 'US' from country dropdown" |
62
- | `BROWSER_EXTRACT` | Extract data | "Extract the main heading" |
63
- | `BROWSER_SCREENSHOT` | Take screenshot | "Take a screenshot" |
56
+ `/api/browser-bridge/*` HTTP surface for the companion extension: pairing, settings, tab sync, page-context ingest, session progress, and extension package build/download.
64
57
 
65
- ## Providers
58
+ ## Requirements
66
59
 
67
- | Provider | Description |
68
- | --------------- | ----------------------------- |
69
- | `BROWSER_STATE` | Current browser session state |
60
+ ### Auto-enable
70
61
 
71
- ## API
62
+ The plugin is opt-in. It activates when `config.features.browser` is truthy in the elizaOS agent config:
72
63
 
73
- ### BrowserService
74
-
75
- ```typescript
76
- class BrowserService extends Service {
77
- async createSession(sessionId: string): Promise<BrowserSession>;
78
- async getSession(sessionId: string): Promise<BrowserSession | undefined>;
79
- async getCurrentSession(): Promise<BrowserSession | undefined>;
80
- async destroySession(sessionId: string): Promise<void>;
64
+ ```json
65
+ {
66
+ "features": {
67
+ "browser": true
68
+ }
81
69
  }
82
70
  ```
83
71
 
84
- ### BrowserSession
72
+ ### Environment variables
85
73
 
86
- ```typescript
87
- interface BrowserSession {
88
- id: string;
89
- createdAt: Date;
90
- url?: string;
91
- title?: string;
92
- }
93
- ```
74
+ | Variable | Purpose |
75
+ |---|---|
76
+ | `ELIZA_BROWSER_STAGEHAND_COMMAND_URL` | Full URL for the Stagehand command endpoint |
77
+ | `STAGEHAND_SERVER_URL` | Stagehand base URL (commands go to `<url>/api/browser-command`) |
78
+ | `ELIZA_BROWSER_STAGEHAND_URL` | Alias for `STAGEHAND_SERVER_URL` |
79
+ | `ELIZA_BROWSER_STAGEHAND_AUTO_SETUP` | Set `false` to disable automatic stagehand-server install/build |
80
+ | `ELIZA_BROWSER_ALLOW_STAGEHAND_ON_MOBILE` | Set `true` to allow stagehand target on mobile |
81
+ | `ELIZA_MOBILE_PLATFORM` / `ELIZA_PLATFORM` / `CAPACITOR_PLATFORM` | Platform hint for target scoring (`ios`/`android`/`mobile`) |
82
+
83
+ ### Vault keys (set by the user, not env vars)
94
84
 
95
- ## Development
85
+ `autofill_login` only fires when the user has pre-authorized it per domain:
96
86
 
97
- ```bash
98
- # Install dependencies
99
- npm install
87
+ - `creds.<domain>.:autoallow = "1"` — set via Settings → Vault → Logins.
100
88
 
101
- # Build
102
- npm run build
89
+ Without this flag, the action returns an error rather than prompting interactively.
103
90
 
104
- # Run tests
105
- npm test
91
+ ## Companion extension authentication
106
92
 
107
- # Type check
108
- npm run typecheck
93
+ Companion-scoped endpoints require two headers:
94
+
95
+ ```
96
+ X-Browser-Bridge-Companion-Id: <companion uuid>
97
+ Authorization: Bearer <pairing token>
109
98
  ```
110
99
 
111
- ## License
100
+ Legacy header aliases (`X-LifeOps-Browser-Companion-Id`, `x-eliza-browser-companion-id`) are not accepted.
101
+
102
+ ## Database
103
+
104
+ Drizzle tables in the `browser` PostgreSQL schema (applied by elizaOS `plugin-sql` migrator):
112
105
 
113
- MIT
106
+ - `browser_bridge_companions`
107
+ - `browser_bridge_settings`
108
+ - `browser_bridge_tabs`
109
+ - `browser_bridge_page_contexts`
114
110
 
111
+ ## Registering a custom browser target
115
112
 
113
+ Any plugin can extend the browser dispatch surface at runtime:
116
114
 
115
+ ```ts
116
+ import { BrowserService, BROWSER_SERVICE_TYPE } from "@elizaos/plugin-browser";
117
+ import type { BrowserTarget } from "@elizaos/plugin-browser";
118
+
119
+ const myTarget: BrowserTarget = {
120
+ id: "my-target",
121
+ name: "My Browser",
122
+ description: "Custom browser backend.",
123
+ kind: "external",
124
+ priority: 50,
125
+ available: async () => true,
126
+ execute: async (command) => { /* ... */ },
127
+ };
128
+
129
+ const browserService = runtime.getService<BrowserService>(BROWSER_SERVICE_TYPE);
130
+ browserService?.registerTarget(myTarget);
131
+ ```
package/auto-enable.ts ADDED
@@ -0,0 +1,24 @@
1
+ // Auto-enable check for @elizaos/plugin-browser.
2
+ //
3
+ // Plugin manifest entry-point — referenced by package.json's
4
+ // `elizaos.plugin.autoEnableModule`. Keep this module light: env reads only,
5
+ // no service init, no transitive imports of the full plugin runtime. The
6
+ // auto-enable engine loads dozens of these per boot.
7
+ import type { PluginAutoEnableContext } from "@elizaos/core";
8
+
9
+ function isFeatureEnabled(
10
+ config: PluginAutoEnableContext["config"],
11
+ key: string,
12
+ ): boolean {
13
+ const f = (config?.features as Record<string, unknown> | undefined)?.[key];
14
+ if (f === true) return true;
15
+ if (f && typeof f === "object" && f !== null) {
16
+ return (f as Record<string, unknown>).enabled !== false;
17
+ }
18
+ return false;
19
+ }
20
+
21
+ /** Enable when `config.features.browser` is truthy / not explicitly disabled. */
22
+ export function shouldEnable(ctx: PluginAutoEnableContext): boolean {
23
+ return isFeatureEnabled(ctx.config, "browser");
24
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * BROWSER autofill-login subaction — agent-driven browser login autofill.
3
+ *
4
+ * Invoked via BROWSER with `subaction: "autofill-login"` (canonical). The legacy
5
+ * planner action name `BROWSER_AUTOFILL_LOGIN` normalizes to BROWSER with this
6
+ * subaction in the planner dispatch pipeline.
7
+ *
8
+ * Lets the agent say "log into github.com for me" and have the saved
9
+ * credentials filled into an open Eliza browser tab without a per-call
10
+ * consent prompt.
11
+ *
12
+ * Authorization model (mirrors the user-driven autofill flow):
13
+ * - The user must have set `creds.<domain>.:autoallow = "1"` on the
14
+ * domain. This is the same vault key the React-side consent flow
15
+ * uses; toggling it from Settings -> Vault -> Logins is the
16
+ * SOLE way to let the agent autofill silently.
17
+ * - Without that flag, this action returns
18
+ * `{ ok: false, reason: "user has not pre-authorized agent autofill for <domain>" }`.
19
+ * The agent should NOT fall back to the user-driven flow on its own
20
+ * because the user-driven flow is gated by an interactive React
21
+ * modal that an autonomous agent cannot consent to.
22
+ *
23
+ * Tab selection:
24
+ * - Lists the live browser-workspace tabs and picks the first one
25
+ * whose URL hostname matches `domain` (registrable hostname,
26
+ * case-insensitive). Returns a clean error when no such tab exists
27
+ * so the agent can decide whether to open one first via
28
+ * BROWSER (open/navigate).
29
+ *
30
+ * Fill mechanism:
31
+ * - Injects a small JS snippet that mirrors the same form-detection
32
+ * and `setNativeInputValue` helpers the in-tab preload uses, so
33
+ * React-controlled inputs see the change.
34
+ * - When `submit: true`, the snippet also calls `form.submit()` (or
35
+ * clicks a likely submit button) after filling. Off by default —
36
+ * the safer behaviour is fill-only and let the user click submit.
37
+ */
38
+ import type { ActionResult, HandlerOptions, IAgentRuntime, Memory } from "@elizaos/core";
39
+ /**
40
+ * Executes the vault-gated workspace autofill flow for {@link AUTOFILL_SUBACTION}.
41
+ */
42
+ export declare function executeBrowserAutofillLogin(_runtime: IAgentRuntime, _message: Memory | undefined, options: HandlerOptions | undefined): Promise<ActionResult>;
43
+ //# sourceMappingURL=browser-autofill-login.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-autofill-login.d.ts","sourceRoot":"","sources":["../../src/actions/browser-autofill-login.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,cAAc,EACd,aAAa,EACb,MAAM,EACP,MAAM,eAAe,CAAC;AAyIvB;;GAEG;AACH,wBAAsB,2BAA2B,CAC/C,QAAQ,EAAE,aAAa,EACvB,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,OAAO,EAAE,cAAc,GAAG,SAAS,GAClC,OAAO,CAAC,YAAY,CAAC,CAkLvB"}
@@ -0,0 +1,278 @@
1
+ import { logger } from "@elizaos/core";
2
+ import {
3
+ createManager,
4
+ getAutofillAllowed,
5
+ getSavedLogin,
6
+ listSavedLogins
7
+ } from "@elizaos/vault";
8
+ import {
9
+ evaluateBrowserWorkspaceTab,
10
+ isBrowserWorkspaceBridgeConfigured,
11
+ listBrowserWorkspaceTabs
12
+ } from "../workspace/browser-workspace.js";
13
+ const AUTOFILL_SUBACTION = "autofill-login";
14
+ const MAX_BROWSER_TAB_SCAN = 100;
15
+ const MAX_FILL_REASON_CHARS = 240;
16
+ let cachedVault = null;
17
+ function sharedAutofillVault() {
18
+ cachedVault ??= createManager().vault;
19
+ return cachedVault;
20
+ }
21
+ function tabUrlMatchesDomain(tabUrl, domain) {
22
+ if (!tabUrl) return false;
23
+ let hostname;
24
+ try {
25
+ hostname = new URL(tabUrl).hostname;
26
+ } catch {
27
+ return false;
28
+ }
29
+ return hostname.toLowerCase() === domain.toLowerCase();
30
+ }
31
+ function buildAutofillScript(args) {
32
+ return `
33
+ (() => {
34
+ const USERNAME = ${JSON.stringify(args.username)};
35
+ const PASSWORD = ${JSON.stringify(args.password)};
36
+ const SUBMIT = ${args.submit ? "true" : "false"};
37
+
38
+ function setNativeInputValue(input, value) {
39
+ const proto = Object.getPrototypeOf(input);
40
+ const desc = Object.getOwnPropertyDescriptor(proto, "value");
41
+ if (desc && typeof desc.set === "function") {
42
+ desc.set.call(input, value);
43
+ } else {
44
+ input.value = value;
45
+ }
46
+ input.dispatchEvent(new Event("input", { bubbles: true }));
47
+ input.dispatchEvent(new Event("change", { bubbles: true }));
48
+ }
49
+
50
+ function findPrecedingTextInput(passwordInput) {
51
+ const root = passwordInput.form || document.body;
52
+ const candidates = root.querySelectorAll(
53
+ 'input[type="text"], input[type="email"], input:not([type])'
54
+ );
55
+ let lastBefore = null;
56
+ for (const el of candidates) {
57
+ if (el.compareDocumentPosition(passwordInput) & Node.DOCUMENT_POSITION_FOLLOWING) {
58
+ lastBefore = el;
59
+ }
60
+ }
61
+ return lastBefore;
62
+ }
63
+
64
+ const password = document.querySelector('input[type="password"]');
65
+ if (!password) {
66
+ return { ok: false, reason: "no_password_input" };
67
+ }
68
+ const form = password.form;
69
+ const username =
70
+ (form && form.querySelector(
71
+ 'input[type="email"], input[name*="user" i], input[name*="email" i], input[name*="login" i]'
72
+ )) || findPrecedingTextInput(password);
73
+
74
+ if (username) setNativeInputValue(username, USERNAME);
75
+ setNativeInputValue(password, PASSWORD);
76
+
77
+ if (SUBMIT) {
78
+ if (form && typeof form.requestSubmit === "function") {
79
+ form.requestSubmit();
80
+ } else if (form && typeof form.submit === "function") {
81
+ form.submit();
82
+ } else {
83
+ const button =
84
+ (form && form.querySelector('button[type="submit"], input[type="submit"]')) ||
85
+ document.querySelector('button[type="submit"], input[type="submit"]');
86
+ if (button) (button).click();
87
+ }
88
+ }
89
+
90
+ return {
91
+ ok: true,
92
+ filled: { username: !!username, password: true },
93
+ submitted: SUBMIT,
94
+ };
95
+ })();
96
+ `;
97
+ }
98
+ function narrowSnippetResult(raw) {
99
+ if (!raw || typeof raw !== "object") {
100
+ return { filled: false, fillReason: null };
101
+ }
102
+ const obj = raw;
103
+ const hasFilledProp = "filled" in obj && Boolean(obj.filled);
104
+ let fillReason = null;
105
+ const reasonVal = "reason" in obj ? obj.reason : void 0;
106
+ if (typeof reasonVal === "string") {
107
+ fillReason = reasonVal.slice(0, MAX_FILL_REASON_CHARS);
108
+ }
109
+ return { filled: hasFilledProp, fillReason };
110
+ }
111
+ async function executeBrowserAutofillLogin(_runtime, _message, options) {
112
+ const params = options?.parameters;
113
+ const domain = params?.domain?.trim().toLowerCase() ?? "";
114
+ const requestedUsername = params?.username?.trim();
115
+ const submit = params?.submit === true;
116
+ if (!domain) {
117
+ return {
118
+ text: `BROWSER requires subaction "${AUTOFILL_SUBACTION}" and a \`domain\` parameter.`,
119
+ success: false,
120
+ values: {
121
+ success: false,
122
+ error: "BROWSER_AUTOFILL_BAD_PARAMS",
123
+ subaction: AUTOFILL_SUBACTION
124
+ },
125
+ data: { actionName: "BROWSER", subaction: AUTOFILL_SUBACTION }
126
+ };
127
+ }
128
+ if (!isBrowserWorkspaceBridgeConfigured(process.env)) {
129
+ return {
130
+ text: `BROWSER ${AUTOFILL_SUBACTION} requires the desktop browser workspace bridge.`,
131
+ success: false,
132
+ values: {
133
+ success: false,
134
+ error: "BROWSER_BRIDGE_UNAVAILABLE",
135
+ subaction: AUTOFILL_SUBACTION
136
+ },
137
+ data: { actionName: "BROWSER", subaction: AUTOFILL_SUBACTION }
138
+ };
139
+ }
140
+ const vault = sharedAutofillVault();
141
+ const allowed = await getAutofillAllowed(vault, domain);
142
+ if (!allowed) {
143
+ const text = `User has not pre-authorized agent autofill for ${domain}. Toggle "Allow agent to autofill" for this domain under Settings -> Vault -> Logins.`;
144
+ return {
145
+ text,
146
+ success: false,
147
+ values: {
148
+ success: false,
149
+ error: "AGENT_AUTOFILL_NOT_AUTHORIZED",
150
+ domain,
151
+ subaction: AUTOFILL_SUBACTION
152
+ },
153
+ data: {
154
+ actionName: "BROWSER",
155
+ subaction: AUTOFILL_SUBACTION,
156
+ domain,
157
+ reason: text
158
+ }
159
+ };
160
+ }
161
+ let savedLogin = null;
162
+ if (requestedUsername) {
163
+ savedLogin = await getSavedLogin(vault, domain, requestedUsername);
164
+ if (!savedLogin) {
165
+ return {
166
+ text: `No saved login for ${requestedUsername} on ${domain}.`,
167
+ success: false,
168
+ values: {
169
+ success: false,
170
+ error: "AGENT_AUTOFILL_NO_LOGIN",
171
+ domain,
172
+ username: requestedUsername,
173
+ subaction: AUTOFILL_SUBACTION
174
+ },
175
+ data: { actionName: "BROWSER", subaction: AUTOFILL_SUBACTION }
176
+ };
177
+ }
178
+ } else {
179
+ const summaries = await listSavedLogins(vault, domain);
180
+ if (summaries.length === 0) {
181
+ return {
182
+ text: `No saved logins for ${domain}.`,
183
+ success: false,
184
+ values: {
185
+ success: false,
186
+ error: "AGENT_AUTOFILL_NO_LOGIN",
187
+ domain,
188
+ subaction: AUTOFILL_SUBACTION
189
+ },
190
+ data: { actionName: "BROWSER", subaction: AUTOFILL_SUBACTION }
191
+ };
192
+ }
193
+ const sorted = [...summaries].sort(
194
+ (a, b) => b.lastModified - a.lastModified
195
+ );
196
+ const chosen = sorted[0];
197
+ if (!chosen) {
198
+ return {
199
+ text: `No saved logins for ${domain}.`,
200
+ success: false,
201
+ values: {
202
+ success: false,
203
+ error: "AGENT_AUTOFILL_NO_LOGIN",
204
+ domain,
205
+ subaction: AUTOFILL_SUBACTION
206
+ },
207
+ data: { actionName: "BROWSER", subaction: AUTOFILL_SUBACTION }
208
+ };
209
+ }
210
+ savedLogin = await getSavedLogin(vault, domain, chosen.username);
211
+ if (!savedLogin) {
212
+ return {
213
+ text: `Saved login ${chosen.username} on ${domain} disappeared between list and reveal.`,
214
+ success: false,
215
+ values: {
216
+ success: false,
217
+ error: "AGENT_AUTOFILL_RACE",
218
+ domain,
219
+ subaction: AUTOFILL_SUBACTION
220
+ },
221
+ data: { actionName: "BROWSER", subaction: AUTOFILL_SUBACTION }
222
+ };
223
+ }
224
+ }
225
+ const tabs = await listBrowserWorkspaceTabs();
226
+ const matchingTab = tabs.slice(0, MAX_BROWSER_TAB_SCAN).find((t) => tabUrlMatchesDomain(t.url, domain));
227
+ if (!matchingTab) {
228
+ return {
229
+ text: `No open browser tab on ${domain}. Open one with BROWSER (open/navigate) first.`,
230
+ success: false,
231
+ values: {
232
+ success: false,
233
+ error: "AGENT_AUTOFILL_NO_TAB",
234
+ domain,
235
+ subaction: AUTOFILL_SUBACTION
236
+ },
237
+ data: { actionName: "BROWSER", subaction: AUTOFILL_SUBACTION }
238
+ };
239
+ }
240
+ const script = buildAutofillScript({
241
+ username: savedLogin.username,
242
+ password: savedLogin.password,
243
+ submit
244
+ });
245
+ const rawResult = await evaluateBrowserWorkspaceTab({
246
+ id: matchingTab.id,
247
+ script
248
+ });
249
+ const { filled, fillReason } = narrowSnippetResult(rawResult);
250
+ logger.info(
251
+ `[browser-autofill-login] domain=${domain} tabId=${matchingTab.id} submit=${submit} filled=${filled}`
252
+ );
253
+ return {
254
+ text: submit ? `Filled and submitted login on ${domain} (tab ${matchingTab.id}).` : `Filled login on ${domain} (tab ${matchingTab.id}). User must click submit.`,
255
+ success: true,
256
+ values: {
257
+ success: true,
258
+ domain,
259
+ tabId: matchingTab.id,
260
+ submitted: submit,
261
+ filled,
262
+ subaction: AUTOFILL_SUBACTION,
263
+ ...fillReason ? { fillReason } : {}
264
+ },
265
+ data: {
266
+ actionName: "BROWSER",
267
+ subaction: AUTOFILL_SUBACTION,
268
+ domain,
269
+ tabId: matchingTab.id,
270
+ filled,
271
+ ...fillReason ? { fillReason } : {}
272
+ }
273
+ };
274
+ }
275
+ export {
276
+ executeBrowserAutofillLogin
277
+ };
278
+ //# sourceMappingURL=browser-autofill-login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/actions/browser-autofill-login.ts"],"sourcesContent":["/**\n * BROWSER autofill-login subaction — agent-driven browser login autofill.\n *\n * Invoked via BROWSER with `subaction: \"autofill-login\"` (canonical). The legacy\n * planner action name `BROWSER_AUTOFILL_LOGIN` normalizes to BROWSER with this\n * subaction in the planner dispatch pipeline.\n *\n * Lets the agent say \"log into github.com for me\" and have the saved\n * credentials filled into an open Eliza browser tab without a per-call\n * consent prompt.\n *\n * Authorization model (mirrors the user-driven autofill flow):\n * - The user must have set `creds.<domain>.:autoallow = \"1\"` on the\n * domain. This is the same vault key the React-side consent flow\n * uses; toggling it from Settings -> Vault -> Logins is the\n * SOLE way to let the agent autofill silently.\n * - Without that flag, this action returns\n * `{ ok: false, reason: \"user has not pre-authorized agent autofill for <domain>\" }`.\n * The agent should NOT fall back to the user-driven flow on its own\n * because the user-driven flow is gated by an interactive React\n * modal that an autonomous agent cannot consent to.\n *\n * Tab selection:\n * - Lists the live browser-workspace tabs and picks the first one\n * whose URL hostname matches `domain` (registrable hostname,\n * case-insensitive). Returns a clean error when no such tab exists\n * so the agent can decide whether to open one first via\n * BROWSER (open/navigate).\n *\n * Fill mechanism:\n * - Injects a small JS snippet that mirrors the same form-detection\n * and `setNativeInputValue` helpers the in-tab preload uses, so\n * React-controlled inputs see the change.\n * - When `submit: true`, the snippet also calls `form.submit()` (or\n * clicks a likely submit button) after filling. Off by default —\n * the safer behaviour is fill-only and let the user click submit.\n */\n\nimport type {\n ActionResult,\n HandlerOptions,\n IAgentRuntime,\n Memory,\n} from \"@elizaos/core\";\nimport { logger } from \"@elizaos/core\";\nimport {\n createManager,\n getAutofillAllowed,\n getSavedLogin,\n listSavedLogins,\n type Vault,\n} from \"@elizaos/vault\";\nimport {\n evaluateBrowserWorkspaceTab,\n isBrowserWorkspaceBridgeConfigured,\n listBrowserWorkspaceTabs,\n} from \"../workspace/browser-workspace.js\";\n\ninterface BrowserAutofillLoginParameters {\n domain?: string;\n username?: string;\n /** When true, attempt to submit the form after filling. Default: false. */\n submit?: boolean;\n}\n\nconst AUTOFILL_SUBACTION = \"autofill-login\";\n\nconst MAX_BROWSER_TAB_SCAN = 100;\nconst MAX_FILL_REASON_CHARS = 240;\n\nlet cachedVault: Vault | null = null;\n\nfunction sharedAutofillVault(): Vault {\n cachedVault ??= createManager().vault;\n return cachedVault;\n}\n\nfunction tabUrlMatchesDomain(tabUrl: string, domain: string): boolean {\n if (!tabUrl) return false;\n let hostname: string;\n try {\n hostname = new URL(tabUrl).hostname;\n } catch {\n return false;\n }\n return hostname.toLowerCase() === domain.toLowerCase();\n}\n\nfunction buildAutofillScript(args: {\n username: string;\n password: string;\n submit: boolean;\n}): string {\n // Inline snippet — runs in the OOPIF (the page's content world) via\n // electrobun tab eval. Uses setNativeInputValue to bypass React's\n // value-setter override.\n return `\n(() => {\n const USERNAME = ${JSON.stringify(args.username)};\n const PASSWORD = ${JSON.stringify(args.password)};\n const SUBMIT = ${args.submit ? \"true\" : \"false\"};\n\n function setNativeInputValue(input, value) {\n const proto = Object.getPrototypeOf(input);\n const desc = Object.getOwnPropertyDescriptor(proto, \"value\");\n if (desc && typeof desc.set === \"function\") {\n desc.set.call(input, value);\n } else {\n input.value = value;\n }\n input.dispatchEvent(new Event(\"input\", { bubbles: true }));\n input.dispatchEvent(new Event(\"change\", { bubbles: true }));\n }\n\n function findPrecedingTextInput(passwordInput) {\n const root = passwordInput.form || document.body;\n const candidates = root.querySelectorAll(\n 'input[type=\"text\"], input[type=\"email\"], input:not([type])'\n );\n let lastBefore = null;\n for (const el of candidates) {\n if (el.compareDocumentPosition(passwordInput) & Node.DOCUMENT_POSITION_FOLLOWING) {\n lastBefore = el;\n }\n }\n return lastBefore;\n }\n\n const password = document.querySelector('input[type=\"password\"]');\n if (!password) {\n return { ok: false, reason: \"no_password_input\" };\n }\n const form = password.form;\n const username =\n (form && form.querySelector(\n 'input[type=\"email\"], input[name*=\"user\" i], input[name*=\"email\" i], input[name*=\"login\" i]'\n )) || findPrecedingTextInput(password);\n\n if (username) setNativeInputValue(username, USERNAME);\n setNativeInputValue(password, PASSWORD);\n\n if (SUBMIT) {\n if (form && typeof form.requestSubmit === \"function\") {\n form.requestSubmit();\n } else if (form && typeof form.submit === \"function\") {\n form.submit();\n } else {\n const button =\n (form && form.querySelector('button[type=\"submit\"], input[type=\"submit\"]')) ||\n document.querySelector('button[type=\"submit\"], input[type=\"submit\"]');\n if (button) (button).click();\n }\n }\n\n return {\n ok: true,\n filled: { username: !!username, password: true },\n submitted: SUBMIT,\n };\n})();\n`;\n}\n\nfunction narrowSnippetResult(raw: unknown): {\n filled: boolean;\n fillReason: string | null;\n} {\n if (!raw || typeof raw !== \"object\") {\n return { filled: false, fillReason: null };\n }\n const obj = raw as { filled?: { username?: boolean; password?: boolean } };\n const hasFilledProp = \"filled\" in obj && Boolean(obj.filled);\n let fillReason: string | null = null;\n const reasonVal = \"reason\" in obj ? obj.reason : undefined;\n if (typeof reasonVal === \"string\") {\n fillReason = reasonVal.slice(0, MAX_FILL_REASON_CHARS);\n }\n return { filled: hasFilledProp, fillReason };\n}\n\n/**\n * Executes the vault-gated workspace autofill flow for {@link AUTOFILL_SUBACTION}.\n */\nexport async function executeBrowserAutofillLogin(\n _runtime: IAgentRuntime,\n _message: Memory | undefined,\n options: HandlerOptions | undefined,\n): Promise<ActionResult> {\n const params = options?.parameters as\n | BrowserAutofillLoginParameters\n | undefined;\n const domain = params?.domain?.trim().toLowerCase() ?? \"\";\n const requestedUsername = params?.username?.trim();\n const submit = params?.submit === true;\n\n if (!domain) {\n return {\n text: `BROWSER requires subaction \"${AUTOFILL_SUBACTION}\" and a \\`domain\\` parameter.`,\n success: false,\n values: {\n success: false,\n error: \"BROWSER_AUTOFILL_BAD_PARAMS\",\n subaction: AUTOFILL_SUBACTION,\n },\n data: { actionName: \"BROWSER\", subaction: AUTOFILL_SUBACTION },\n };\n }\n\n if (!isBrowserWorkspaceBridgeConfigured(process.env)) {\n return {\n text: `BROWSER ${AUTOFILL_SUBACTION} requires the desktop browser workspace bridge.`,\n success: false,\n values: {\n success: false,\n error: \"BROWSER_BRIDGE_UNAVAILABLE\",\n subaction: AUTOFILL_SUBACTION,\n },\n data: { actionName: \"BROWSER\", subaction: AUTOFILL_SUBACTION },\n };\n }\n\n const vault = sharedAutofillVault();\n\n const allowed = await getAutofillAllowed(vault, domain);\n if (!allowed) {\n const text = `User has not pre-authorized agent autofill for ${domain}. Toggle \"Allow agent to autofill\" for this domain under Settings -> Vault -> Logins.`;\n return {\n text,\n success: false,\n values: {\n success: false,\n error: \"AGENT_AUTOFILL_NOT_AUTHORIZED\",\n domain,\n subaction: AUTOFILL_SUBACTION,\n },\n data: {\n actionName: \"BROWSER\",\n subaction: AUTOFILL_SUBACTION,\n domain,\n reason: text,\n },\n };\n }\n\n let savedLogin: Awaited<ReturnType<typeof getSavedLogin>> = null;\n if (requestedUsername) {\n savedLogin = await getSavedLogin(vault, domain, requestedUsername);\n if (!savedLogin) {\n return {\n text: `No saved login for ${requestedUsername} on ${domain}.`,\n success: false,\n values: {\n success: false,\n error: \"AGENT_AUTOFILL_NO_LOGIN\",\n domain,\n username: requestedUsername,\n subaction: AUTOFILL_SUBACTION,\n },\n data: { actionName: \"BROWSER\", subaction: AUTOFILL_SUBACTION },\n };\n }\n } else {\n const summaries = await listSavedLogins(vault, domain);\n if (summaries.length === 0) {\n return {\n text: `No saved logins for ${domain}.`,\n success: false,\n values: {\n success: false,\n error: \"AGENT_AUTOFILL_NO_LOGIN\",\n domain,\n subaction: AUTOFILL_SUBACTION,\n },\n data: { actionName: \"BROWSER\", subaction: AUTOFILL_SUBACTION },\n };\n }\n const sorted = [...summaries].sort(\n (a, b) => b.lastModified - a.lastModified,\n );\n const chosen = sorted[0];\n if (!chosen) {\n return {\n text: `No saved logins for ${domain}.`,\n success: false,\n values: {\n success: false,\n error: \"AGENT_AUTOFILL_NO_LOGIN\",\n domain,\n subaction: AUTOFILL_SUBACTION,\n },\n data: { actionName: \"BROWSER\", subaction: AUTOFILL_SUBACTION },\n };\n }\n savedLogin = await getSavedLogin(vault, domain, chosen.username);\n if (!savedLogin) {\n return {\n text: `Saved login ${chosen.username} on ${domain} disappeared between list and reveal.`,\n success: false,\n values: {\n success: false,\n error: \"AGENT_AUTOFILL_RACE\",\n domain,\n subaction: AUTOFILL_SUBACTION,\n },\n data: { actionName: \"BROWSER\", subaction: AUTOFILL_SUBACTION },\n };\n }\n }\n\n const tabs = await listBrowserWorkspaceTabs();\n const matchingTab = tabs\n .slice(0, MAX_BROWSER_TAB_SCAN)\n .find((t) => tabUrlMatchesDomain(t.url, domain));\n if (!matchingTab) {\n return {\n text: `No open browser tab on ${domain}. Open one with BROWSER (open/navigate) first.`,\n success: false,\n values: {\n success: false,\n error: \"AGENT_AUTOFILL_NO_TAB\",\n domain,\n subaction: AUTOFILL_SUBACTION,\n },\n data: { actionName: \"BROWSER\", subaction: AUTOFILL_SUBACTION },\n };\n }\n\n const script = buildAutofillScript({\n username: savedLogin.username,\n password: savedLogin.password,\n submit,\n });\n const rawResult = await evaluateBrowserWorkspaceTab({\n id: matchingTab.id,\n script,\n });\n const { filled, fillReason } = narrowSnippetResult(rawResult);\n\n logger.info(\n `[browser-autofill-login] domain=${domain} tabId=${matchingTab.id} submit=${submit} filled=${filled}`,\n );\n\n return {\n text: submit\n ? `Filled and submitted login on ${domain} (tab ${matchingTab.id}).`\n : `Filled login on ${domain} (tab ${matchingTab.id}). User must click submit.`,\n success: true,\n values: {\n success: true,\n domain,\n tabId: matchingTab.id,\n submitted: submit,\n filled,\n subaction: AUTOFILL_SUBACTION,\n ...(fillReason ? { fillReason } : {}),\n },\n data: {\n actionName: \"BROWSER\",\n subaction: AUTOFILL_SUBACTION,\n domain,\n tabId: matchingTab.id,\n filled,\n ...(fillReason ? { fillReason } : {}),\n },\n };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,11 @@
1
+ import type { Action } from "@elizaos/core";
2
+ /**
3
+ * Targets are the registered browser backends. The agent uses what is
4
+ * available; specifying a target overrides automatic routing. `workspace`
5
+ * is the app-owned browser surface, `bridge` is the paired Chrome/Safari
6
+ * companion, `stagehand` is the Playwright/Stagehand fallback, and other
7
+ * plugins may register additional target ids.
8
+ */
9
+ export type BrowserTarget = string;
10
+ export declare const browserAction: Action;
11
+ //# sourceMappingURL=browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/actions/browser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EAIP,MAAM,eAAe,CAAC;AAYvB;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC;AA2SnC,eAAO,MAAM,aAAa,EAAE,MAyS3B,CAAC"}