From 8fe07852ced11954c60d4b47f8baf88d974e8d18 Mon Sep 17 00:00:00 2001 From: Kylejeong2 Date: Tue, 31 Mar 2026 17:08:25 -0700 Subject: [PATCH] fix: close session after credential test and deprecate project ID field --- credentials/BrowserbaseApi.credentials.ts | 18 +---- nodes/Browserbase/Browserbase.node.ts | 85 ++++++++++++++++++++++- 2 files changed, 86 insertions(+), 17 deletions(-) diff --git a/credentials/BrowserbaseApi.credentials.ts b/credentials/BrowserbaseApi.credentials.ts index 7b2d895..952a538 100644 --- a/credentials/BrowserbaseApi.credentials.ts +++ b/credentials/BrowserbaseApi.credentials.ts @@ -1,6 +1,5 @@ import type { IAuthenticateGeneric, - ICredentialTestRequest, ICredentialType, INodeProperties, } from 'n8n-workflow'; @@ -25,12 +24,12 @@ export class BrowserbaseApi implements ICredentialType { description: 'Your Browserbase API key', }, { - displayName: 'Browserbase Project ID', + displayName: 'Browserbase Project ID (Deprecated)', name: 'browserbaseProjectId', type: 'string', default: '', - required: true, - description: 'Your Browserbase project ID', + required: false, + description: 'Optional. Your Browserbase project ID (no longer required for new setups)', }, { displayName: 'Model API Key', @@ -48,20 +47,9 @@ export class BrowserbaseApi implements ICredentialType { properties: { headers: { 'x-bb-api-key': '={{$credentials.browserbaseApiKey}}', - 'x-bb-project-id': '={{$credentials.browserbaseProjectId}}', 'x-model-api-key': '={{$credentials.modelApiKey}}', }, }, }; - test: ICredentialTestRequest = { - request: { - baseURL: 'https://api.stagehand.browserbase.com', - url: '/v1/sessions/start', - method: 'POST', - body: { - modelName: 'openai/gpt-4o', - }, - }, - }; } diff --git a/nodes/Browserbase/Browserbase.node.ts b/nodes/Browserbase/Browserbase.node.ts index 75d23c4..0170dfb 100644 --- a/nodes/Browserbase/Browserbase.node.ts +++ b/nodes/Browserbase/Browserbase.node.ts @@ -1,6 +1,10 @@ import { NodeConnectionTypes, + type ICredentialDataDecryptedObject, + type ICredentialTestFunctions, + type ICredentialsDecrypted, type IExecuteFunctions, + type INodeCredentialTestResult, type INodeExecutionData, type INodeType, type INodeTypeDescription, @@ -30,6 +34,7 @@ export class Browserbase implements INodeType { { name: 'browserbaseApi', required: true, + testedBy: 'browserbaseApiTest', }, ], properties: [ @@ -613,6 +618,79 @@ export class Browserbase implements INodeType { ], }; + methods = { + credentialTest: { + async browserbaseApiTest( + this: ICredentialTestFunctions, + credential: ICredentialsDecrypted, + ): Promise { + const { browserbaseApiKey, browserbaseProjectId, modelApiKey } = + credential.data!; + + const headers: Record = { + Accept: 'application/json', + 'Content-Type': 'application/json', + 'x-bb-api-key': browserbaseApiKey as string, + 'x-model-api-key': modelApiKey as string, + }; + if (browserbaseProjectId) { + headers['x-bb-project-id'] = browserbaseProjectId as string; + } + + let sessionId: string | undefined; + + const httpRequest = this.helpers[ + 'request' as keyof typeof this.helpers + ] as (opts: object) => Promise>; + + try { + const startResponse = await httpRequest({ + method: 'POST', + uri: `${BASE_URL}/v1/sessions/start`, + headers, + body: { modelName: 'openai/gpt-4o' }, + json: true, + }); + + const data = startResponse.data as Record | undefined; + sessionId = (data?.sessionId ?? + startResponse.sessionId ?? + startResponse.id) as string | undefined; + + if (sessionId) { + await httpRequest({ + method: 'POST', + uri: `${BASE_URL}/v1/sessions/${sessionId}/end`, + headers, + body: {}, + json: true, + }); + } + + return { status: 'OK', message: 'Connection successful' }; + } catch (error) { + if (sessionId) { + try { + await httpRequest({ + method: 'POST', + uri: `${BASE_URL}/v1/sessions/${sessionId}/end`, + headers, + body: {}, + json: true, + }); + } catch { + // Ignore cleanup errors + } + } + + const msg = + error instanceof Error ? error.message : String(error); + return { status: 'Error', message: msg }; + } + }, + }, + }; + async execute(this: IExecuteFunctions): Promise { const items = this.getInputData(); const returnData: INodeExecutionData[] = []; @@ -674,13 +752,16 @@ export class Browserbase implements INodeType { // Get credentials const credentials = await this.getCredentials('browserbaseApi'); - const headers = { + const headers: Record = { Accept: 'application/json', 'Content-Type': 'application/json', 'x-bb-api-key': credentials.browserbaseApiKey as string, - 'x-bb-project-id': credentials.browserbaseProjectId as string, 'x-model-api-key': credentials.modelApiKey as string, }; + // We no longer need to set the Project ID, but keeping this here for backwards compatibility + if (credentials.browserbaseProjectId) { + headers['x-bb-project-id'] = credentials.browserbaseProjectId as string; + } // Helper function to make API calls const apiCall = async (