├── .editorconfig ├── .eslintrc.js ├── .eslintrc.prepublish.js ├── .gitignore ├── .npmignore ├── .prettierrc.js ├── .vscode └── extensions.json ├── CODE_OF_CONDUCT.md ├── LICENSE.md ├── README.md ├── README_TEMPLATE.md ├── credentials ├── BrowserUseCloudApi.credentials.ts ├── BrowserUseLocalBridgeApi.credentials.ts ├── ExampleCredentialsApi.credentials.ts └── HttpBinApi.credentials.ts ├── gulpfile.js ├── index.js ├── nodes ├── BrowserUse │ ├── BrowserUse.node.ts │ └── icons │ │ └── browseruse.svg ├── ExampleNode │ └── ExampleNode.node.ts └── HttpBin │ ├── HttpBin.node.json │ ├── HttpBin.node.ts │ ├── HttpVerbDescription.ts │ └── httpbin.svg ├── package-lock.json ├── package.json ├── pnpm-lock.yaml ├── test-browser-use.js └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = tab 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [package.json] 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | 18 | [*.yml] 19 | indent_style = space 20 | indent_size = 2 21 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import('@types/eslint').ESLint.ConfigData} 3 | */ 4 | module.exports = { 5 | root: true, 6 | 7 | env: { 8 | browser: true, 9 | es6: true, 10 | node: true, 11 | }, 12 | 13 | parser: '@typescript-eslint/parser', 14 | 15 | parserOptions: { 16 | project: ['./tsconfig.json'], 17 | sourceType: 'module', 18 | extraFileExtensions: ['.json'], 19 | }, 20 | 21 | ignorePatterns: ['.eslintrc.js', '**/*.js', '**/node_modules/**', '**/dist/**'], 22 | 23 | overrides: [ 24 | { 25 | files: ['package.json'], 26 | plugins: ['eslint-plugin-n8n-nodes-base'], 27 | extends: ['plugin:n8n-nodes-base/community'], 28 | rules: { 29 | 'n8n-nodes-base/community-package-json-name-still-default': 'off', 30 | }, 31 | }, 32 | { 33 | files: ['./credentials/**/*.ts'], 34 | plugins: ['eslint-plugin-n8n-nodes-base'], 35 | extends: ['plugin:n8n-nodes-base/credentials'], 36 | rules: { 37 | 'n8n-nodes-base/cred-class-field-documentation-url-missing': 'off', 38 | 'n8n-nodes-base/cred-class-field-documentation-url-miscased': 'off', 39 | }, 40 | }, 41 | { 42 | files: ['./nodes/**/*.ts'], 43 | plugins: ['eslint-plugin-n8n-nodes-base'], 44 | extends: ['plugin:n8n-nodes-base/nodes'], 45 | rules: { 46 | 'n8n-nodes-base/node-execute-block-missing-continue-on-fail': 'off', 47 | 'n8n-nodes-base/node-resource-description-filename-against-convention': 'off', 48 | 'n8n-nodes-base/node-param-fixed-collection-type-unsorted-items': 'off', 49 | }, 50 | }, 51 | ], 52 | }; 53 | -------------------------------------------------------------------------------- /.eslintrc.prepublish.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import('@types/eslint').ESLint.ConfigData} 3 | */ 4 | module.exports = { 5 | extends: "./.eslintrc.js", 6 | 7 | overrides: [ 8 | { 9 | files: ['package.json'], 10 | plugins: ['eslint-plugin-n8n-nodes-base'], 11 | rules: { 12 | 'n8n-nodes-base/community-package-json-name-still-default': 'error', 13 | }, 14 | }, 15 | ], 16 | }; 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | .tmp 4 | tmp 5 | dist 6 | npm-debug.log* 7 | yarn.lock 8 | .vscode/launch.json 9 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.tsbuildinfo 3 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | /** 3 | * https://prettier.io/docs/en/options.html#semicolons 4 | */ 5 | semi: true, 6 | 7 | /** 8 | * https://prettier.io/docs/en/options.html#trailing-commas 9 | */ 10 | trailingComma: 'all', 11 | 12 | /** 13 | * https://prettier.io/docs/en/options.html#bracket-spacing 14 | */ 15 | bracketSpacing: true, 16 | 17 | /** 18 | * https://prettier.io/docs/en/options.html#tabs 19 | */ 20 | useTabs: true, 21 | 22 | /** 23 | * https://prettier.io/docs/en/options.html#tab-width 24 | */ 25 | tabWidth: 2, 26 | 27 | /** 28 | * https://prettier.io/docs/en/options.html#arrow-function-parentheses 29 | */ 30 | arrowParens: 'always', 31 | 32 | /** 33 | * https://prettier.io/docs/en/options.html#quotes 34 | */ 35 | singleQuote: true, 36 | 37 | /** 38 | * https://prettier.io/docs/en/options.html#quote-props 39 | */ 40 | quoteProps: 'as-needed', 41 | 42 | /** 43 | * https://prettier.io/docs/en/options.html#end-of-line 44 | */ 45 | endOfLine: 'lf', 46 | 47 | /** 48 | * https://prettier.io/docs/en/options.html#print-width 49 | */ 50 | printWidth: 100, 51 | }; 52 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "EditorConfig.EditorConfig", 5 | "esbenp.prettier-vscode", 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at jan@n8n.io. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2022 n8n 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Banner image](https://user-images.githubusercontent.com/10284570/173569848-c624317f-42b1-45a6-ab09-f0ea3c247648.png) 2 | 3 | # n8n-nodes-browser-use 4 | 5 | This is an n8n community node. It lets you use [Browser Use](https://browser-use.com) in your n8n workflows. 6 | 7 | Browser Use is an AI-powered browser automation tool that allows you to create and manage browser automation tasks using natural language instructions. 8 | 9 | [n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/reference/license/) workflow automation platform. 10 | 11 | [Installation](#installation) 12 | [Operations](#operations) 13 | [Credentials](#credentials) 14 | [Compatibility](#compatibility) 15 | [Usage](#usage) 16 | [Resources](#resources) 17 | 18 | ## Installation 19 | 20 | Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation. 21 | 22 | ### In n8n Desktop or Cloud: 23 | 24 | 1. Go to **Settings > Community Nodes** 25 | 2. Click on **Install** 26 | 3. Enter `n8n-nodes-browser-use` in the **Name** field 27 | 4. Click **Install** 28 | 29 | ### In a self-hosted n8n instance: 30 | 31 | ```bash 32 | npm install n8n-nodes-browser-use 33 | ``` 34 | 35 | ### For local development: 36 | 37 | 1. Clone this repository 38 | 2. Install dependencies: `npm install` 39 | 3. Build the code: `npm run build` 40 | 4. Link to your n8n installation: `npm link` 41 | 5. In your n8n installation directory: `npm link n8n-nodes-browser-use` 42 | 43 | ## Operations 44 | 45 | ### Run Task 46 | 47 | Execute a new browser automation task with natural language instructions. 48 | 49 | **Parameters:** 50 | - **Instructions**: Natural language description of what you want the browser to do. 51 | - **Save Browser Data**: Whether to save browser cookies and other data (safely encrypted). 52 | 53 | **Returns:** Task ID, status, and a live preview URL. 54 | 55 | ### Get Task 56 | 57 | Retrieve full details of a specific task. 58 | 59 | **Parameters:** 60 | - **Task ID**: The ID of the task to retrieve. 61 | 62 | **Returns:** Complete task information including status, instructions, and timestamps. 63 | 64 | ### Get Task Status 65 | 66 | Check the status of a running task. 67 | 68 | **Parameters:** 69 | - **Task ID**: The ID of the task to check. 70 | 71 | **Returns:** Current status, completion percentage, and any error messages. 72 | 73 | ### Pause Task 74 | 75 | Temporarily pause a running task. 76 | 77 | **Parameters:** 78 | - **Task ID**: The ID of the task to pause. 79 | 80 | **Returns:** Confirmation of task being paused. 81 | 82 | ### Resume Task 83 | 84 | Resume a previously paused task. 85 | 86 | **Parameters:** 87 | - **Task ID**: The ID of the task to resume. 88 | 89 | **Returns:** Confirmation of task resumption. 90 | 91 | ### Stop Task 92 | 93 | Stop a running task. 94 | 95 | **Parameters:** 96 | - **Task ID**: The ID of the task to stop. 97 | 98 | **Returns:** Confirmation of task termination. 99 | 100 | ### Get Task Media 101 | 102 | Retrieve media (screenshots, video, PDF) from a task. 103 | 104 | **Parameters:** 105 | - **Task ID**: The ID of the task. 106 | - **Media Type**: Type of media to retrieve (Screenshot, Video, or PDF). 107 | 108 | **Returns:** URL or binary data of the requested media. 109 | 110 | ### List Tasks 111 | 112 | Retrieve a list of tasks with optional filtering. 113 | 114 | **Parameters:** 115 | - **Limit**: Maximum number of tasks to return (1-100, default 20). 116 | - **Status Filter**: Filter tasks by their status (optional). 117 | 118 | **Returns:** Array of task records matching the criteria. 119 | 120 | ## Credentials 121 | 122 | ### Browser Use Cloud API 123 | 124 | To use the Browser Use Cloud API, you need to obtain an API key: 125 | 126 | 1. Sign up for Browser Use at [Browser Use Cloud](https://cloud.browser-use.com) 127 | 2. Navigate to the billing section to find your API key 128 | 3. Create a new credential of type "Browser Use Cloud API" in n8n 129 | 4. Enter your API key in the credential configuration 130 | 131 | The node automatically validates your API key by sending a ping request to the Browser Use Cloud API. If the API key is invalid or the service is unavailable, you'll receive a clear error message. 132 | 133 | ### Browser Use Local Bridge API (🚧 Work In Progress) 134 | 135 | ⚠️ **Note: The Local Bridge feature is currently under development and may not be fully functional.** 136 | 137 | To use the Local Bridge connection: 138 | 139 | 1. Set up the Browser Use bridge service (documentation coming soon) 140 | 2. Create a new credential of type "Browser Use Local Bridge API" in n8n 141 | 3. Configure with: 142 | - URL of your local bridge service (e.g., `http://localhost:8000`) 143 | - Authentication token (if enabled) 144 | 145 | ## Compatibility 146 | 147 | This node has been tested with n8n version 0.209.4 and later. 148 | 149 | ## Usage 150 | 151 | ### Cloud API Connection 152 | 153 | 1. Add the Browser Use node to your workflow 154 | 2. Select "Cloud API" as the connection type 155 | 3. Choose your credentials or create new ones 156 | 4. Select an operation (Run Task, Get Task Status, etc.) 157 | 5. Configure the operation parameters 158 | 6. Run your workflow 159 | 160 | ### Local Bridge Connection (🚧 Work In Progress) 161 | 162 | The Local Bridge option allows you to connect to a locally running Browser Use instance, which can be useful for development, testing, or when you need to keep your automation entirely on-premise. 163 | 164 | ⚠️ **Setup Requirements (Coming Soon):** 165 | 166 | 1. Clone the repository for the bridge service (under development) 167 | 2. Install the required dependencies 168 | 3. Run the bridge service 169 | 170 | Then in n8n: 171 | 172 | 1. Add the Browser Use node to your workflow 173 | 2. Select "Local Bridge" as the connection type 174 | 3. Choose your credentials or create new ones 175 | 4. Configure as you would with the Cloud API connection 176 | 177 | ## Resources 178 | 179 | * [n8n community nodes documentation](https://docs.n8n.io/integrations/community-nodes/) 180 | * [Browser Use Documentation](https://docs.browser-use.com) 181 | * [Browser Use Cloud API Documentation](https://docs.browser-use.com/cloud/quickstart) 182 | 183 | ## License 184 | 185 | [MIT](LICENSE.md) 186 | -------------------------------------------------------------------------------- /README_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # n8n-nodes-_node-name_ 2 | 3 | This is an n8n community node. It lets you use _app/service name_ in your n8n workflows. 4 | 5 | _App/service name_ is _one or two sentences describing the service this node integrates with_. 6 | 7 | [n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/reference/license/) workflow automation platform. 8 | 9 | [Installation](#installation) 10 | [Operations](#operations) 11 | [Credentials](#credentials) 12 | [Compatibility](#compatibility) 13 | [Usage](#usage) 14 | [Resources](#resources) 15 | [Version history](#version-history) 16 | 17 | ## Installation 18 | 19 | Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation. 20 | 21 | ## Operations 22 | 23 | _List the operations supported by your node._ 24 | 25 | ## Credentials 26 | 27 | _If users need to authenticate with the app/service, provide details here. You should include prerequisites (such as signing up with the service), available authentication methods, and how to set them up._ 28 | 29 | ## Compatibility 30 | 31 | _State the minimum n8n version, as well as which versions you test against. You can also include any known version incompatibility issues._ 32 | 33 | ## Usage 34 | 35 | _This is an optional section. Use it to help users with any difficult or confusing aspects of the node._ 36 | 37 | _By the time users are looking for community nodes, they probably already know n8n basics. But if you expect new users, you can link to the [Try it out](https://docs.n8n.io/try-it-out/) documentation to help them get started._ 38 | 39 | ## Resources 40 | 41 | * [n8n community nodes documentation](https://docs.n8n.io/integrations/community-nodes/) 42 | * _Link to app/service documentation._ 43 | 44 | ## Version history 45 | 46 | _This is another optional section. If your node has multiple versions, include a short description of available versions and what changed, as well as any compatibility impact._ 47 | 48 | 49 | -------------------------------------------------------------------------------- /credentials/BrowserUseCloudApi.credentials.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IAuthenticateGeneric, 3 | ICredentialTestRequest, 4 | ICredentialType, 5 | INodeProperties, 6 | } from 'n8n-workflow'; 7 | 8 | export class BrowserUseCloudApi implements ICredentialType { 9 | name = 'browserUseCloudApi'; 10 | displayName = 'Browser Use Cloud API'; 11 | documentationUrl = 'https://docs.browser-use.com/cloud/quickstart'; 12 | properties: INodeProperties[] = [ 13 | { 14 | displayName: 'API Key', 15 | name: 'apiKey', 16 | type: 'string', 17 | typeOptions: { 18 | password: true, 19 | }, 20 | default: '', 21 | required: true, 22 | description: 'API key from Browser Use Cloud', 23 | }, 24 | ]; 25 | 26 | authenticate: IAuthenticateGeneric = { 27 | type: 'generic', 28 | properties: { 29 | headers: { 30 | Authorization: '={{`Bearer ${$credentials.apiKey}`}}', 31 | }, 32 | }, 33 | }; 34 | 35 | test: ICredentialTestRequest = { 36 | request: { 37 | baseURL: 'https://api.browser-use.com/api/v1', 38 | url: '/ping', 39 | method: 'GET', 40 | headers: { 41 | Authorization: '={{`Bearer ${$credentials.apiKey}`}}', 42 | }, 43 | }, 44 | }; 45 | } -------------------------------------------------------------------------------- /credentials/BrowserUseLocalBridgeApi.credentials.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IAuthenticateGeneric, 3 | ICredentialTestRequest, 4 | ICredentialType, 5 | INodeProperties, 6 | } from 'n8n-workflow'; 7 | 8 | export class BrowserUseLocalBridgeApi implements ICredentialType { 9 | name = 'browserUseLocalBridgeApi'; 10 | displayName = 'Browser Use Local Bridge API'; 11 | documentationUrl = 'https://github.com/yourusername/n8n-nodes-browser-use'; 12 | properties: INodeProperties[] = [ 13 | { 14 | displayName: 'URL', 15 | name: 'url', 16 | type: 'string', 17 | default: 'http://localhost:8000', 18 | required: true, 19 | description: 'URL of the local Bridge service', 20 | }, 21 | { 22 | displayName: 'Authentication Token', 23 | name: 'token', 24 | type: 'string', 25 | typeOptions: { 26 | password: true, 27 | }, 28 | default: '', 29 | required: false, 30 | description: 'Token for authenticating with the local Bridge service (if enabled)', 31 | }, 32 | ]; 33 | 34 | authenticate: IAuthenticateGeneric = { 35 | type: 'generic', 36 | properties: { 37 | headers: { 38 | Authorization: '={{$credentials.token ? `Bearer ${$credentials.token}` : undefined}}', 39 | }, 40 | }, 41 | }; 42 | 43 | test: ICredentialTestRequest = { 44 | request: { 45 | baseURL: '={{$credentials.url}}', 46 | url: '/api/v1/ping', 47 | method: 'GET', 48 | headers: { 49 | Authorization: '={{$credentials.token ? `Bearer ${$credentials.token}` : undefined}}', 50 | }, 51 | }, 52 | }; 53 | } -------------------------------------------------------------------------------- /credentials/ExampleCredentialsApi.credentials.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IAuthenticateGeneric, 3 | ICredentialTestRequest, 4 | ICredentialType, 5 | INodeProperties, 6 | } from 'n8n-workflow'; 7 | 8 | export class ExampleCredentialsApi implements ICredentialType { 9 | name = 'exampleCredentialsApi'; 10 | displayName = 'Example Credentials API'; 11 | properties: INodeProperties[] = [ 12 | // The credentials to get from user and save encrypted. 13 | // Properties can be defined exactly in the same way 14 | // as node properties. 15 | { 16 | displayName: 'User Name', 17 | name: 'username', 18 | type: 'string', 19 | default: '', 20 | }, 21 | { 22 | displayName: 'Password', 23 | name: 'password', 24 | type: 'string', 25 | typeOptions: { 26 | password: true, 27 | }, 28 | default: '', 29 | }, 30 | ]; 31 | 32 | // This credential is currently not used by any node directly 33 | // but the HTTP Request node can use it to make requests. 34 | // The credential is also testable due to the `test` property below 35 | authenticate: IAuthenticateGeneric = { 36 | type: 'generic', 37 | properties: { 38 | auth: { 39 | username: '={{ $credentials.username }}', 40 | password: '={{ $credentials.password }}', 41 | }, 42 | qs: { 43 | // Send this as part of the query string 44 | n8n: 'rocks', 45 | }, 46 | }, 47 | }; 48 | 49 | // The block below tells how this credential can be tested 50 | test: ICredentialTestRequest = { 51 | request: { 52 | baseURL: 'https://example.com/', 53 | url: '', 54 | }, 55 | }; 56 | } 57 | -------------------------------------------------------------------------------- /credentials/HttpBinApi.credentials.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IAuthenticateGeneric, 3 | ICredentialTestRequest, 4 | ICredentialType, 5 | INodeProperties, 6 | } from 'n8n-workflow'; 7 | 8 | export class HttpBinApi implements ICredentialType { 9 | name = 'httpbinApi'; 10 | displayName = 'HttpBin API'; 11 | documentationUrl = 'https://httpbin.org/docs/auth'; 12 | properties: INodeProperties[] = [ 13 | { 14 | displayName: 'Token', 15 | name: 'token', 16 | type: 'string', 17 | default: '', 18 | typeOptions: { 19 | password: true, 20 | } 21 | }, 22 | { 23 | displayName: 'Domain', 24 | name: 'domain', 25 | type: 'string', 26 | default: 'https://httpbin.org', 27 | }, 28 | ]; 29 | 30 | // This allows the credential to be used by other parts of n8n 31 | // stating how this credential is injected as part of the request 32 | // An example is the Http Request node that can make generic calls 33 | // reusing this credential 34 | authenticate: IAuthenticateGeneric = { 35 | type: 'generic', 36 | properties: { 37 | headers: { 38 | Authorization: '={{"Bearer " + $credentials.token}}', 39 | }, 40 | }, 41 | }; 42 | 43 | // The block below tells how this credential can be tested 44 | test: ICredentialTestRequest = { 45 | request: { 46 | baseURL: '={{$credentials?.domain}}', 47 | url: '/bearer', 48 | }, 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { task, src, dest } = require('gulp'); 3 | 4 | task('build:icons', copyIcons); 5 | 6 | function copyIcons() { 7 | const nodeSource = path.resolve('nodes', '**', '*.{png,svg}'); 8 | const nodeDestination = path.resolve('dist', 'nodes'); 9 | 10 | src(nodeSource).pipe(dest(nodeDestination)); 11 | 12 | const credSource = path.resolve('credentials', '**', '*.{png,svg}'); 13 | const credDestination = path.resolve('dist', 'credentials'); 14 | 15 | return src(credSource).pipe(dest(credDestination)); 16 | } 17 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/draphonix/n8n-nodes-browser-use/7bae20e775fccf2b2a4f37f035c6cd80d0f21086/index.js -------------------------------------------------------------------------------- /nodes/BrowserUse/BrowserUse.node.ts: -------------------------------------------------------------------------------- 1 | import type { IExecuteFunctions } from 'n8n-workflow'; 2 | import { INodeExecutionData, INodeType, INodeTypeDescription, NodeApiError, NodeOperationError } from 'n8n-workflow'; 3 | 4 | export class BrowserUse implements INodeType { 5 | description: INodeTypeDescription = { 6 | displayName: 'Browser Use', 7 | name: 'browserUse', 8 | icon: 'file:browseruse.svg', 9 | group: ['transform'], 10 | version: 1, 11 | description: 'Automate browser interactions using AI', 12 | defaults: { 13 | name: 'Browser Use', 14 | }, 15 | inputs: ['main'], 16 | outputs: ['main'], 17 | credentials: [ 18 | { 19 | name: 'browserUseCloudApi', 20 | required: true, 21 | displayOptions: { 22 | show: { 23 | connectionType: ['cloud'], 24 | }, 25 | }, 26 | }, 27 | { 28 | name: 'browserUseLocalBridgeApi', 29 | required: true, 30 | displayOptions: { 31 | show: { 32 | connectionType: ['local'], 33 | }, 34 | }, 35 | }, 36 | ], 37 | properties: [ 38 | // Connection Type selector 39 | { 40 | displayName: 'Connection Type', 41 | name: 'connectionType', 42 | type: 'options', 43 | options: [ 44 | { 45 | name: 'Cloud API', 46 | value: 'cloud', 47 | description: 'Connect to Browser Use Cloud API (simplest setup)', 48 | }, 49 | { 50 | name: 'Local Bridge', 51 | value: 'local', 52 | description: 'Connect to locally running Browser Use bridge (requires additional setup)', 53 | }, 54 | ], 55 | default: 'cloud', 56 | description: 'Choose how to connect to Browser Use', 57 | }, 58 | 59 | // Local Bridge Notice 60 | { 61 | displayName: 'Local Bridge Setup Required', 62 | name: 'localBridgeNotice', 63 | type: 'notice', 64 | default: 'The Local Bridge option requires setting up a Python service separately. Please refer to the documentation for installation instructions.', 65 | displayOptions: { 66 | show: { 67 | connectionType: ['local'], 68 | }, 69 | }, 70 | }, 71 | 72 | // Operation selector 73 | { 74 | displayName: 'Operation', 75 | name: 'operation', 76 | type: 'options', 77 | noDataExpression: true, 78 | options: [ 79 | { 80 | name: 'Run Task', 81 | value: 'runTask', 82 | }, 83 | { 84 | name: 'Get Task', 85 | value: 'getTask', 86 | }, 87 | { 88 | name: 'Get Task Status', 89 | value: 'getTaskStatus', 90 | }, 91 | { 92 | name: 'Get Task Media', 93 | value: 'getTaskMedia', 94 | }, 95 | { 96 | name: 'Pause Task', 97 | value: 'pauseTask', 98 | }, 99 | { 100 | name: 'Resume Task', 101 | value: 'resumeTask', 102 | }, 103 | { 104 | name: 'Stop Task', 105 | value: 'stopTask', 106 | }, 107 | { 108 | name: 'List Tasks', 109 | value: 'listTasks', 110 | }, 111 | ], 112 | default: 'runTask', 113 | }, 114 | 115 | // Task instruction (for runTask) 116 | { 117 | displayName: 'Instructions', 118 | name: 'instructions', 119 | type: 'string', 120 | typeOptions: { 121 | rows: 5, 122 | }, 123 | default: '', 124 | placeholder: 'e.g., Go to google.com and search for "n8n automation"', 125 | displayOptions: { 126 | show: { 127 | operation: ['runTask'], 128 | }, 129 | }, 130 | description: 'Natural language instructions for the browser automation', 131 | required: true, 132 | }, 133 | 134 | // Save Browser Data (for runTask) 135 | { 136 | displayName: 'Save Browser Data', 137 | name: 'saveBrowserData', 138 | type: 'boolean', 139 | default: false, 140 | displayOptions: { 141 | show: { 142 | operation: ['runTask'], 143 | }, 144 | }, 145 | description: 'Whether to save browser cookies and other data. Cookies are safely encrypted before storing in the database.', 146 | }, 147 | 148 | // Task ID (for getTaskStatus, stopTask, and getTaskMedia) 149 | { 150 | displayName: 'Task ID', 151 | name: 'taskId', 152 | type: 'string', 153 | default: '', 154 | displayOptions: { 155 | show: { 156 | operation: ['getTaskStatus', 'stopTask', 'getTaskMedia', 'getTask', 'pauseTask', 'resumeTask'], 157 | }, 158 | }, 159 | description: 'ID of the task to interact with', 160 | required: true, 161 | }, 162 | 163 | // Media Type (for getTaskMedia) 164 | { 165 | displayName: 'Media Type', 166 | name: 'mediaType', 167 | type: 'options', 168 | options: [ 169 | { name: 'Screenshot', value: 'screenshot' }, 170 | { name: 'Video', value: 'video' }, 171 | { name: 'PDF', value: 'pdf' }, 172 | ], 173 | default: 'screenshot', 174 | displayOptions: { 175 | show: { 176 | operation: ['getTaskMedia'], 177 | }, 178 | }, 179 | description: 'Type of media to retrieve from the task', 180 | }, 181 | 182 | // List Tasks Limit 183 | { 184 | displayName: 'Limit', 185 | name: 'limit', 186 | type: 'number', 187 | typeOptions: { 188 | minValue: 1, 189 | maxValue: 100, 190 | }, 191 | default: 20, 192 | displayOptions: { 193 | show: { 194 | operation: ['listTasks'], 195 | }, 196 | }, 197 | description: 'Maximum number of tasks to return', 198 | } 199 | ], 200 | }; 201 | 202 | async execute(this: IExecuteFunctions): Promise { 203 | const items = this.getInputData(); 204 | const returnData: INodeExecutionData[] = []; 205 | const operation = this.getNodeParameter('operation', 0) as string; 206 | const connectionType = this.getNodeParameter('connectionType', 0) as string; 207 | 208 | // Get the appropriate credentials based on connection type 209 | let credentials; 210 | let baseUrl; 211 | 212 | try { 213 | if (connectionType === 'cloud') { 214 | credentials = await this.getCredentials('browserUseCloudApi'); 215 | 216 | if (!credentials.apiKey) { 217 | throw new NodeOperationError(this.getNode(), 'API Key is required for Browser Use Cloud API'); 218 | } 219 | 220 | baseUrl = 'https://api.browser-use.com/api/v1'; 221 | 222 | // Validate credentials by making a test ping request 223 | try { 224 | await this.helpers.request({ 225 | method: 'GET', 226 | url: `${baseUrl}/ping`, 227 | headers: { 228 | 'Authorization': `Bearer ${credentials.apiKey}`, 229 | }, 230 | json: true, 231 | }); 232 | } catch (error) { 233 | throw new NodeApiError(this.getNode(), error, { message: `Failed to connect to Browser Use Cloud API: ${error.message}` }); 234 | } 235 | } else { 236 | credentials = await this.getCredentials('browserUseLocalBridgeApi'); 237 | 238 | if (!credentials.url) { 239 | throw new NodeOperationError(this.getNode(), 'URL is required for Browser Use Local Bridge'); 240 | } 241 | 242 | baseUrl = credentials.url as string; 243 | if (!baseUrl.endsWith('/api/v1')) { 244 | baseUrl = `${baseUrl}/api/v1`.replace(/\/+/g, '/'); 245 | } 246 | 247 | // Validate credentials by making a test ping request 248 | try { 249 | await this.helpers.request({ 250 | method: 'GET', 251 | url: `${baseUrl}/ping`, 252 | headers: { 253 | 'Authorization': credentials.token ? `Bearer ${credentials.token}` : undefined, 254 | }, 255 | json: true, 256 | }); 257 | } catch (error) { 258 | throw new NodeApiError(this.getNode(), error, { message: `Failed to connect to Browser Use Local Bridge at ${baseUrl}: ${error.message}` }); 259 | } 260 | } 261 | 262 | for (let i = 0; i < items.length; i++) { 263 | try { 264 | if (operation === 'runTask') { 265 | const instructions = this.getNodeParameter('instructions', i) as string; 266 | const saveBrowserData = this.getNodeParameter('saveBrowserData', i) as boolean; 267 | 268 | // Make API call to Browser Use (Cloud or Local) 269 | const response = await this.helpers.request({ 270 | method: 'POST', 271 | url: `${baseUrl}/run-task`, 272 | headers: { 273 | 'Authorization': connectionType === 'cloud' 274 | ? `Bearer ${credentials.apiKey}` 275 | : credentials.token ? `Bearer ${credentials.token}` : undefined, 276 | 'Content-Type': 'application/json', 277 | }, 278 | body: { 279 | task: instructions, 280 | save_browser_data: saveBrowserData, 281 | }, 282 | json: true, 283 | }); 284 | 285 | returnData.push({ 286 | json: response, 287 | }); 288 | } else if (operation === 'getTaskStatus') { 289 | const taskId = this.getNodeParameter('taskId', i) as string; 290 | 291 | const response = await this.helpers.request({ 292 | method: 'GET', 293 | url: `${baseUrl}/task/${taskId}/status`, 294 | headers: { 295 | 'Authorization': connectionType === 'cloud' 296 | ? `Bearer ${credentials.apiKey}` 297 | : credentials.token ? `Bearer ${credentials.token}` : undefined, 298 | }, 299 | json: true, 300 | }); 301 | 302 | returnData.push({ 303 | json: response, 304 | }); 305 | } else if (operation === 'stopTask') { 306 | const taskId = this.getNodeParameter('taskId', i) as string; 307 | 308 | const response = await this.helpers.request({ 309 | method: 'PUT', 310 | url: `${baseUrl}/stop-task/${taskId}`, 311 | headers: { 312 | 'Authorization': connectionType === 'cloud' 313 | ? `Bearer ${credentials.apiKey}` 314 | : credentials.token ? `Bearer ${credentials.token}` : undefined, 315 | }, 316 | json: true, 317 | }); 318 | 319 | returnData.push({ 320 | json: response, 321 | }); 322 | } else if (operation === 'getTaskMedia') { 323 | const taskId = this.getNodeParameter('taskId', i) as string; 324 | const mediaType = this.getNodeParameter('mediaType', i) as string; 325 | 326 | const response = await this.helpers.request({ 327 | method: 'GET', 328 | url: `${baseUrl}/task/${taskId}/media?type=${mediaType}`, 329 | headers: { 330 | 'Authorization': connectionType === 'cloud' 331 | ? `Bearer ${credentials.apiKey}` 332 | : credentials.token ? `Bearer ${credentials.token}` : undefined, 333 | }, 334 | json: true, 335 | }); 336 | 337 | returnData.push({ 338 | json: response, 339 | }); 340 | } else if (operation === 'getTask') { 341 | const taskId = this.getNodeParameter('taskId', i) as string; 342 | 343 | const response = await this.helpers.request({ 344 | method: 'GET', 345 | url: `${baseUrl}/task/${taskId}`, 346 | headers: { 347 | 'Authorization': connectionType === 'cloud' 348 | ? `Bearer ${credentials.apiKey}` 349 | : credentials.token ? `Bearer ${credentials.token}` : undefined, 350 | }, 351 | json: true, 352 | }); 353 | 354 | returnData.push({ 355 | json: response, 356 | }); 357 | } else if (operation === 'pauseTask') { 358 | const taskId = this.getNodeParameter('taskId', i) as string; 359 | 360 | const response = await this.helpers.request({ 361 | method: 'PUT', 362 | url: `${baseUrl}/pause-task/${taskId}`, 363 | headers: { 364 | 'Authorization': connectionType === 'cloud' 365 | ? `Bearer ${credentials.apiKey}` 366 | : credentials.token ? `Bearer ${credentials.token}` : undefined, 367 | }, 368 | json: true, 369 | }); 370 | 371 | returnData.push({ 372 | json: response, 373 | }); 374 | } else if (operation === 'resumeTask') { 375 | const taskId = this.getNodeParameter('taskId', i) as string; 376 | 377 | const response = await this.helpers.request({ 378 | method: 'PUT', 379 | url: `${baseUrl}/resume-task/${taskId}`, 380 | headers: { 381 | 'Authorization': connectionType === 'cloud' 382 | ? `Bearer ${credentials.apiKey}` 383 | : credentials.token ? `Bearer ${credentials.token}` : undefined, 384 | }, 385 | json: true, 386 | }); 387 | 388 | returnData.push({ 389 | json: response, 390 | }); 391 | } else if (operation === 'listTasks') { 392 | const limit = this.getNodeParameter('limit', i) as number; 393 | 394 | // Build query parameters 395 | let queryString = `limit=${limit}`; 396 | 397 | const response = await this.helpers.request({ 398 | method: 'GET', 399 | url: `${baseUrl}/tasks?${queryString}`, 400 | headers: { 401 | 'Authorization': connectionType === 'cloud' 402 | ? `Bearer ${credentials.apiKey}` 403 | : credentials.token ? `Bearer ${credentials.token}` : undefined, 404 | }, 405 | json: true, 406 | }); 407 | 408 | returnData.push({ 409 | json: response, 410 | }); 411 | } 412 | } catch (error) { 413 | if (this.continueOnFail()) { 414 | returnData.push({ json: { error: error.message } }); 415 | continue; 416 | } 417 | throw error; 418 | } 419 | } 420 | } catch (error) { 421 | if (this.continueOnFail()) { 422 | return [[{ json: { error: error.message } }]]; 423 | } 424 | throw error; 425 | } 426 | 427 | return [returnData]; 428 | } 429 | } -------------------------------------------------------------------------------- /nodes/BrowserUse/icons/browseruse.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Browser Use 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /nodes/ExampleNode/ExampleNode.node.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | IExecuteFunctions, 3 | INodeExecutionData, 4 | INodeType, 5 | INodeTypeDescription, 6 | } from 'n8n-workflow'; 7 | import { NodeOperationError } from 'n8n-workflow'; 8 | 9 | export class ExampleNode implements INodeType { 10 | description: INodeTypeDescription = { 11 | displayName: 'Example Node', 12 | name: 'exampleNode', 13 | group: ['transform'], 14 | version: 1, 15 | description: 'Basic Example Node', 16 | defaults: { 17 | name: 'Example Node', 18 | }, 19 | inputs: ['main'], 20 | outputs: ['main'], 21 | properties: [ 22 | // Node properties which the user gets displayed and 23 | // can change on the node. 24 | { 25 | displayName: 'My String', 26 | name: 'myString', 27 | type: 'string', 28 | default: '', 29 | placeholder: 'Placeholder value', 30 | description: 'The description text', 31 | }, 32 | ], 33 | }; 34 | 35 | // The function below is responsible for actually doing whatever this node 36 | // is supposed to do. In this case, we're just appending the `myString` property 37 | // with whatever the user has entered. 38 | // You can make async calls and use `await`. 39 | async execute(this: IExecuteFunctions): Promise { 40 | const items = this.getInputData(); 41 | 42 | let item: INodeExecutionData; 43 | let myString: string; 44 | 45 | // Iterates over all input items and add the key "myString" with the 46 | // value the parameter "myString" resolves to. 47 | // (This could be a different value for each item in case it contains an expression) 48 | for (let itemIndex = 0; itemIndex < items.length; itemIndex++) { 49 | try { 50 | myString = this.getNodeParameter('myString', itemIndex, '') as string; 51 | item = items[itemIndex]; 52 | 53 | item.json.myString = myString; 54 | } catch (error) { 55 | // This node should never fail but we want to showcase how 56 | // to handle errors. 57 | if (this.continueOnFail()) { 58 | items.push({ json: this.getInputData(itemIndex)[0].json, error, pairedItem: itemIndex }); 59 | } else { 60 | // Adding `itemIndex` allows other workflows to handle this error 61 | if (error.context) { 62 | // If the error thrown already contains the context property, 63 | // only append the itemIndex 64 | error.context.itemIndex = itemIndex; 65 | throw error; 66 | } 67 | throw new NodeOperationError(this.getNode(), error, { 68 | itemIndex, 69 | }); 70 | } 71 | } 72 | } 73 | 74 | return [items]; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /nodes/HttpBin/HttpBin.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "node": "n8n-nodes-base.httpbin", 3 | "nodeVersion": "1.0", 4 | "codexVersion": "1.0", 5 | "categories": ["Development", "Developer Tools"], 6 | "resources": { 7 | "credentialDocumentation": [ 8 | { 9 | "url": "http://httpbin.org/#/Auth/get_bearer" 10 | } 11 | ], 12 | "primaryDocumentation": [ 13 | { 14 | "url": "http://httpbin.org/" 15 | } 16 | ] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /nodes/HttpBin/HttpBin.node.ts: -------------------------------------------------------------------------------- 1 | import { INodeType, INodeTypeDescription } from 'n8n-workflow'; 2 | import { httpVerbFields, httpVerbOperations } from './HttpVerbDescription'; 3 | 4 | export class HttpBin implements INodeType { 5 | description: INodeTypeDescription = { 6 | displayName: 'HttpBin', 7 | name: 'httpBin', 8 | icon: 'file:httpbin.svg', 9 | group: ['transform'], 10 | version: 1, 11 | subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', 12 | description: 'Interact with HttpBin API', 13 | defaults: { 14 | name: 'HttpBin', 15 | }, 16 | inputs: ['main'], 17 | outputs: ['main'], 18 | credentials: [ 19 | { 20 | name: 'httpbinApi', 21 | required: false, 22 | }, 23 | ], 24 | requestDefaults: { 25 | baseURL: 'https://httpbin.org', 26 | url: '', 27 | headers: { 28 | Accept: 'application/json', 29 | 'Content-Type': 'application/json', 30 | }, 31 | }, 32 | /** 33 | * In the properties array we have two mandatory options objects required 34 | * 35 | * [Resource & Operation] 36 | * 37 | * https://docs.n8n.io/integrations/creating-nodes/code/create-first-node/#resources-and-operations 38 | * 39 | * In our example, the operations are separated into their own file (HTTPVerbDescription.ts) 40 | * to keep this class easy to read. 41 | * 42 | */ 43 | properties: [ 44 | { 45 | displayName: 'Resource', 46 | name: 'resource', 47 | type: 'options', 48 | noDataExpression: true, 49 | options: [ 50 | { 51 | name: 'HTTP Verb', 52 | value: 'httpVerb', 53 | }, 54 | ], 55 | default: 'httpVerb', 56 | }, 57 | 58 | ...httpVerbOperations, 59 | ...httpVerbFields, 60 | ], 61 | }; 62 | } 63 | -------------------------------------------------------------------------------- /nodes/HttpBin/HttpVerbDescription.ts: -------------------------------------------------------------------------------- 1 | import { INodeProperties } from 'n8n-workflow'; 2 | 3 | // When the resource `httpVerb` is selected, this `operation` parameter will be shown. 4 | export const httpVerbOperations: INodeProperties[] = [ 5 | { 6 | displayName: 'Operation', 7 | name: 'operation', 8 | type: 'options', 9 | noDataExpression: true, 10 | 11 | displayOptions: { 12 | show: { 13 | resource: ['httpVerb'], 14 | }, 15 | }, 16 | options: [ 17 | { 18 | name: 'GET', 19 | value: 'get', 20 | description: 'Perform a GET request', 21 | action: 'Get a http verb', 22 | routing: { 23 | request: { 24 | method: 'GET', 25 | url: '/get', 26 | }, 27 | }, 28 | }, 29 | { 30 | name: 'DELETE', 31 | value: 'delete', 32 | description: 'Perform a DELETE request', 33 | action: 'Delete a http verb', 34 | routing: { 35 | request: { 36 | method: 'DELETE', 37 | url: '/delete', 38 | }, 39 | }, 40 | }, 41 | ], 42 | default: 'get', 43 | }, 44 | ]; 45 | 46 | // Here we define what to show when the `get` operation is selected. 47 | // We do that by adding `operation: ["get"]` to `displayOptions.show` 48 | const getOperation: INodeProperties[] = [ 49 | { 50 | displayName: 'Type of Data', 51 | name: 'typeofData', 52 | default: 'queryParameter', 53 | description: 'Select type of data to send [Query Parameters]', 54 | displayOptions: { 55 | show: { 56 | resource: ['httpVerb'], 57 | operation: ['get'], 58 | }, 59 | }, 60 | type: 'options', 61 | options: [ 62 | { 63 | name: 'Query', 64 | value: 'queryParameter', 65 | }, 66 | ], 67 | required: true, 68 | }, 69 | { 70 | displayName: 'Query Parameters', 71 | name: 'arguments', 72 | default: {}, 73 | description: "The request's query parameters", 74 | displayOptions: { 75 | show: { 76 | resource: ['httpVerb'], 77 | operation: ['get'], 78 | }, 79 | }, 80 | options: [ 81 | { 82 | name: 'keyvalue', 83 | displayName: 'Key:Value', 84 | values: [ 85 | { 86 | displayName: 'Key', 87 | name: 'key', 88 | type: 'string', 89 | default: '', 90 | required: true, 91 | description: 'Key of query parameter', 92 | }, 93 | { 94 | displayName: 'Value', 95 | name: 'value', 96 | type: 'string', 97 | default: '', 98 | routing: { 99 | send: { 100 | property: '={{$parent.key}}', 101 | type: 'query', 102 | }, 103 | }, 104 | required: true, 105 | description: 'Value of query parameter', 106 | }, 107 | ], 108 | }, 109 | ], 110 | type: 'fixedCollection', 111 | typeOptions: { 112 | multipleValues: true, 113 | }, 114 | }, 115 | ]; 116 | 117 | // Here we define what to show when the DELETE Operation is selected. 118 | // We do that by adding `operation: ["delete"]` to `displayOptions.show` 119 | const deleteOperation: INodeProperties[] = [ 120 | { 121 | displayName: 'Type of Data', 122 | name: 'typeofData', 123 | default: 'queryParameter', 124 | description: 'Select type of data to send [Query Parameter Arguments, JSON-Body]', 125 | displayOptions: { 126 | show: { 127 | resource: ['httpVerb'], 128 | operation: ['delete'], 129 | }, 130 | }, 131 | options: [ 132 | { 133 | name: 'Query', 134 | value: 'queryParameter', 135 | }, 136 | { 137 | name: 'JSON', 138 | value: 'jsonData', 139 | }, 140 | ], 141 | required: true, 142 | type: 'options', 143 | }, 144 | { 145 | displayName: 'Query Parameters', 146 | name: 'arguments', 147 | default: {}, 148 | description: "The request's query parameters", 149 | displayOptions: { 150 | show: { 151 | resource: ['httpVerb'], 152 | operation: ['delete'], 153 | typeofData: ['queryParameter'], 154 | }, 155 | }, 156 | options: [ 157 | { 158 | name: 'keyvalue', 159 | displayName: 'Key:Value', 160 | values: [ 161 | { 162 | displayName: 'Key', 163 | name: 'key', 164 | type: 'string', 165 | default: '', 166 | required: true, 167 | description: 'Key of query parameter', 168 | }, 169 | { 170 | displayName: 'Value', 171 | name: 'value', 172 | type: 'string', 173 | default: '', 174 | routing: { 175 | send: { 176 | property: '={{$parent.key}}', 177 | type: 'query', 178 | }, 179 | }, 180 | required: true, 181 | description: 'Value of query parameter', 182 | }, 183 | ], 184 | }, 185 | ], 186 | type: 'fixedCollection', 187 | typeOptions: { 188 | multipleValues: true, 189 | }, 190 | }, 191 | { 192 | displayName: 'JSON Object', 193 | name: 'arguments', 194 | default: {}, 195 | description: "The request's JSON properties", 196 | displayOptions: { 197 | show: { 198 | resource: ['httpVerb'], 199 | operation: ['delete'], 200 | typeofData: ['jsonData'], 201 | }, 202 | }, 203 | options: [ 204 | { 205 | name: 'keyvalue', 206 | displayName: 'Key:Value', 207 | values: [ 208 | { 209 | displayName: 'Key', 210 | name: 'key', 211 | type: 'string', 212 | default: '', 213 | required: true, 214 | description: 'Key of JSON property', 215 | }, 216 | { 217 | displayName: 'Value', 218 | name: 'value', 219 | type: 'string', 220 | default: '', 221 | routing: { 222 | send: { 223 | property: '={{$parent.key}}', 224 | type: 'body', 225 | }, 226 | }, 227 | required: true, 228 | description: 'Value of JSON property', 229 | }, 230 | ], 231 | }, 232 | ], 233 | type: 'fixedCollection', 234 | typeOptions: { 235 | multipleValues: true, 236 | }, 237 | }, 238 | ]; 239 | 240 | export const httpVerbFields: INodeProperties[] = [ 241 | /* -------------------------------------------------------------------------- */ 242 | /* httpVerb:get */ 243 | /* -------------------------------------------------------------------------- */ 244 | ...getOperation, 245 | 246 | /* -------------------------------------------------------------------------- */ 247 | /* httpVerb:delete */ 248 | /* -------------------------------------------------------------------------- */ 249 | ...deleteOperation, 250 | ]; 251 | -------------------------------------------------------------------------------- /nodes/HttpBin/httpbin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "n8n-nodes-browser-use", 3 | "version": "0.1.1", 4 | "description": "n8n node to control browser-use AI-powered browser automation", 5 | "keywords": [ 6 | "n8n-community-node-package", 7 | "browser-use", 8 | "browser-automation", 9 | "web-scraping", 10 | "ai-automation" 11 | ], 12 | "license": "MIT", 13 | "homepage": "https://github.com/draphonix/n8n-nodes-browser-use.git", 14 | "author": { 15 | "name": "Hoang Nguyen Bang", 16 | "email": "hoangmrb@gmail.com" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/draphonix/n8n-nodes-browser-use.git" 21 | }, 22 | "engines": { 23 | "node": ">=18.10", 24 | "pnpm": ">=9.1" 25 | }, 26 | "packageManager": "pnpm@9.1.4", 27 | "main": "index.js", 28 | "scripts": { 29 | "preinstall": "npx only-allow pnpm", 30 | "build": "tsc && gulp build:icons", 31 | "dev": "tsc --watch", 32 | "format": "prettier nodes credentials --write", 33 | "lint": "eslint nodes credentials package.json", 34 | "lintfix": "eslint nodes credentials package.json --fix", 35 | "prepublishOnly": "pnpm build && pnpm lint -c .eslintrc.prepublish.js nodes credentials package.json" 36 | }, 37 | "files": [ 38 | "dist" 39 | ], 40 | "n8n": { 41 | "n8nNodesApiVersion": 1, 42 | "credentials": [ 43 | "dist/credentials/BrowserUseCloudApi.credentials.js", 44 | "dist/credentials/BrowserUseLocalBridgeApi.credentials.js" 45 | ], 46 | "nodes": [ 47 | "dist/nodes/BrowserUse/BrowserUse.node.js" 48 | ] 49 | }, 50 | "devDependencies": { 51 | "@typescript-eslint/parser": "^7.15.0", 52 | "eslint": "^8.56.0", 53 | "eslint-plugin-n8n-nodes-base": "^1.16.1", 54 | "gulp": "^4.0.2", 55 | "n8n-core": "^1.14.1", 56 | "prettier": "^3.3.2", 57 | "typescript": "^5.5.3" 58 | }, 59 | "peerDependencies": { 60 | "n8n-workflow": "*" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /test-browser-use.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a simple test script for the Browser Use node. 3 | * It demonstrates how to configure the node in your n8n workflow. 4 | * 5 | * To use in n8n: 6 | * 1. Install the n8n-nodes-browser-use package 7 | * 2. Set up credentials for either Cloud API or Local Bridge 8 | * 3. Configure a workflow similar to the example below 9 | */ 10 | 11 | // Example n8n workflow JSON: 12 | const exampleWorkflow = { 13 | "nodes": [ 14 | { 15 | "parameters": {}, 16 | "name": "Start", 17 | "type": "n8n-nodes-base.start", 18 | "typeVersion": 1, 19 | "position": [ 20 | 250, 21 | 300 22 | ] 23 | }, 24 | { 25 | "parameters": { 26 | "connectionType": "cloud", // or "local" 27 | "operation": "runTask", 28 | "aiProvider": "default", 29 | "instructions": "Go to google.com and search for 'n8n automation'" 30 | }, 31 | "name": "Browser Use", 32 | "type": "n8n-nodes-browser-use.browserUse", 33 | "typeVersion": 1, 34 | "position": [ 35 | 450, 36 | 300 37 | ], 38 | "credentials": { 39 | "browserUseCloudApi": { 40 | "id": "1", 41 | "name": "Browser Use Cloud account" 42 | } 43 | // OR for local bridge: 44 | // "browserUseLocalBridgeApi": { 45 | // "id": "2", 46 | // "name": "Browser Use Local Bridge" 47 | // } 48 | } 49 | } 50 | ], 51 | "connections": { 52 | "Start": { 53 | "main": [ 54 | [ 55 | { 56 | "node": "Browser Use", 57 | "type": "main", 58 | "index": 0 59 | } 60 | ] 61 | ] 62 | } 63 | } 64 | }; 65 | 66 | console.log("To test the Browser Use node:"); 67 | console.log("1. Install the node with: npm install n8n-nodes-browser-use"); 68 | console.log("2. Start n8n: npx n8n start"); 69 | console.log("3. Create a new workflow and add the Browser Use node"); 70 | console.log("4. Configure credentials for either Cloud API or Local Bridge"); 71 | console.log("5. Set the operation and parameters as needed"); 72 | console.log("6. Run the workflow and check the results!"); -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "target": "es2019", 7 | "lib": ["es2019", "es2020", "es2022.error"], 8 | "removeComments": true, 9 | "useUnknownInCatchVariables": false, 10 | "forceConsistentCasingInFileNames": true, 11 | "noImplicitAny": true, 12 | "noImplicitReturns": true, 13 | "noUnusedLocals": true, 14 | "strictNullChecks": true, 15 | "preserveConstEnums": true, 16 | "esModuleInterop": true, 17 | "resolveJsonModule": true, 18 | "incremental": true, 19 | "declaration": true, 20 | "sourceMap": true, 21 | "skipLibCheck": true, 22 | "outDir": "./dist/", 23 | }, 24 | "include": [ 25 | "credentials/**/*", 26 | "nodes/**/*", 27 | "nodes/**/*.json", 28 | "package.json", 29 | ], 30 | } 31 | --------------------------------------------------------------------------------