diff --git a/packages/backend/src/apps/brave-search/actions/index.js b/packages/backend/src/apps/brave-search/actions/index.js new file mode 100644 index 00000000..b7bb1459 --- /dev/null +++ b/packages/backend/src/apps/brave-search/actions/index.js @@ -0,0 +1,3 @@ +import webSearch from './web-search/index.js'; + +export default [webSearch]; diff --git a/packages/backend/src/apps/brave-search/actions/web-search/index.js b/packages/backend/src/apps/brave-search/actions/web-search/index.js new file mode 100644 index 00000000..10f3dfd6 --- /dev/null +++ b/packages/backend/src/apps/brave-search/actions/web-search/index.js @@ -0,0 +1,52 @@ +import defineAction from '../../../../helpers/define-action.js'; + +export default defineAction({ + name: 'Web search', + key: 'webSearch', + description: 'Queries Brave Search and get back search results from the web.', + arguments: [ + { + label: 'Query', + key: 'q', + type: 'string', + required: true, + variables: true, + description: 'The search query term.', + }, + { + label: 'Safe search', + key: 'safesearch', + type: 'dropdown', + required: true, + description: 'Add or remove messages as needed', + value: 'moderate', + options: [ + { + label: 'Off', + value: 'off', + }, + { + label: 'Moderate', + value: 'moderate', + }, + { + label: 'Strict', + value: 'strict', + }, + ], + }, + ], + + async run($) { + const params = { + q: $.step.parameters.q, + safesearch: $.step.parameters.safesearch, + }; + + const { data } = await $.http.get('/v1/web/search', { params }); + + $.setActionItem({ + raw: data, + }); + }, +}); diff --git a/packages/backend/src/apps/brave-search/assets/favicon.svg b/packages/backend/src/apps/brave-search/assets/favicon.svg new file mode 100644 index 00000000..8f953988 --- /dev/null +++ b/packages/backend/src/apps/brave-search/assets/favicon.svg @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/packages/backend/src/apps/brave-search/auth/index.js b/packages/backend/src/apps/brave-search/auth/index.js new file mode 100644 index 00000000..859a5832 --- /dev/null +++ b/packages/backend/src/apps/brave-search/auth/index.js @@ -0,0 +1,34 @@ +import verifyCredentials from './verify-credentials.js'; +import isStillVerified from './is-still-verified.js'; + +export default { + fields: [ + { + key: 'screenName', + label: 'Screen Name', + type: 'string', + required: true, + readOnly: false, + value: null, + placeholder: null, + description: + 'Screen name of your connection to be used on Automatisch UI.', + clickToCopy: false, + }, + { + key: 'apiKey', + label: 'API Key', + type: 'string', + required: true, + readOnly: false, + value: null, + placeholder: null, + description: 'Brave Search API key of your account.', + docUrl: 'https://automatisch.io/docs/brave-search#api-key', + clickToCopy: false, + }, + ], + + verifyCredentials, + isStillVerified, +}; diff --git a/packages/backend/src/apps/brave-search/auth/is-still-verified.js b/packages/backend/src/apps/brave-search/auth/is-still-verified.js new file mode 100644 index 00000000..3f853952 --- /dev/null +++ b/packages/backend/src/apps/brave-search/auth/is-still-verified.js @@ -0,0 +1,5 @@ +const isStillVerified = async () => { + return true; +}; + +export default isStillVerified; diff --git a/packages/backend/src/apps/brave-search/auth/verify-credentials.js b/packages/backend/src/apps/brave-search/auth/verify-credentials.js new file mode 100644 index 00000000..07e4f027 --- /dev/null +++ b/packages/backend/src/apps/brave-search/auth/verify-credentials.js @@ -0,0 +1,5 @@ +const verifyCredentials = async () => { + return true; +}; + +export default verifyCredentials; diff --git a/packages/backend/src/apps/brave-search/common/add-accept-header.js b/packages/backend/src/apps/brave-search/common/add-accept-header.js new file mode 100644 index 00000000..47c033a4 --- /dev/null +++ b/packages/backend/src/apps/brave-search/common/add-accept-header.js @@ -0,0 +1,7 @@ +const addContentTypeHeader = ($, requestConfig) => { + requestConfig.headers.accept = 'application/json'; + + return requestConfig; +}; + +export default addContentTypeHeader; diff --git a/packages/backend/src/apps/brave-search/common/add-auth-header.js b/packages/backend/src/apps/brave-search/common/add-auth-header.js new file mode 100644 index 00000000..14b60d88 --- /dev/null +++ b/packages/backend/src/apps/brave-search/common/add-auth-header.js @@ -0,0 +1,9 @@ +const addAuthHeader = ($, requestConfig) => { + if ($.auth.data?.apiKey) { + requestConfig.headers['X-Subscription-Token'] = $.auth.data.apiKey; + } + + return requestConfig; +}; + +export default addAuthHeader; diff --git a/packages/backend/src/apps/brave-search/dynamic-data/index.js b/packages/backend/src/apps/brave-search/dynamic-data/index.js new file mode 100644 index 00000000..6db48046 --- /dev/null +++ b/packages/backend/src/apps/brave-search/dynamic-data/index.js @@ -0,0 +1,3 @@ +import listModels from './list-models/index.js'; + +export default [listModels]; diff --git a/packages/backend/src/apps/brave-search/dynamic-data/list-models/index.js b/packages/backend/src/apps/brave-search/dynamic-data/list-models/index.js new file mode 100644 index 00000000..f47f5fb9 --- /dev/null +++ b/packages/backend/src/apps/brave-search/dynamic-data/list-models/index.js @@ -0,0 +1,31 @@ +export default { + name: 'List models', + key: 'listModels', + + async run($) { + const models = { + data: [], + }; + + const params = { + limit: 999, + }; + + let hasMore = false; + + do { + const { data } = await $.http.get('/v1/models', { params }); + params.after_id = data.last_id; + hasMore = data.has_more; + + for (const base of data.data) { + models.data.push({ + value: base.id, + name: base.display_name, + }); + } + } while (hasMore); + + return models; + }, +}; diff --git a/packages/backend/src/apps/brave-search/index.js b/packages/backend/src/apps/brave-search/index.js new file mode 100644 index 00000000..f85615e8 --- /dev/null +++ b/packages/backend/src/apps/brave-search/index.js @@ -0,0 +1,21 @@ +import defineApp from '../../helpers/define-app.js'; +import addAuthHeader from './common/add-auth-header.js'; +import addAcceptHeader from './common/add-accept-header.js'; +import auth from './auth/index.js'; +import actions from './actions/index.js'; +import dynamicData from './dynamic-data/index.js'; + +export default defineApp({ + name: 'Brave Search', + key: 'brave-search', + baseUrl: 'https://search.brave.com', + apiBaseUrl: 'https://api.search.brave.com/res', + iconUrl: '{BASE_URL}/apps/brave-search/assets/favicon.svg', + authDocUrl: '{DOCS_URL}/apps/brave-search/connection', + primaryColor: '#181818', + supportsConnections: true, + beforeRequest: [addAuthHeader, addAcceptHeader], + auth, + actions, + dynamicData, +}); diff --git a/packages/backend/src/models/__snapshots__/app.test.js.snap b/packages/backend/src/models/__snapshots__/app.test.js.snap index 18b951e1..624194aa 100644 --- a/packages/backend/src/models/__snapshots__/app.test.js.snap +++ b/packages/backend/src/models/__snapshots__/app.test.js.snap @@ -6,6 +6,7 @@ exports[`App model > list should have list of applications keys 1`] = ` "anthropic", "appwrite", "azure-openai", + "brave-search", "carbone", "clickup", "code", diff --git a/packages/docs/pages/.vitepress/config.js b/packages/docs/pages/.vitepress/config.js index 63a996a1..c842386e 100644 --- a/packages/docs/pages/.vitepress/config.js +++ b/packages/docs/pages/.vitepress/config.js @@ -59,6 +59,15 @@ export default defineConfig({ { text: 'Connection', link: '/apps/appwrite/connection' }, ], }, + { + text: 'Brave Search', + collapsible: true, + collapsed: true, + items: [ + { text: 'Actions', link: '/apps/brave-search/actions' }, + { text: 'Connection', link: '/apps/brave-search/connection' }, + ], + }, { text: 'Carbone', collapsible: true, diff --git a/packages/docs/pages/apps/brave-search/actions.md b/packages/docs/pages/apps/brave-search/actions.md new file mode 100644 index 00000000..9c9da7b5 --- /dev/null +++ b/packages/docs/pages/apps/brave-search/actions.md @@ -0,0 +1,12 @@ +--- +favicon: /favicons/brave-search.svg +items: + - name: Web search + desc: Queries Brave Search and get back search results from the web. +--- + + + + diff --git a/packages/docs/pages/apps/brave-search/connection.md b/packages/docs/pages/apps/brave-search/connection.md new file mode 100644 index 00000000..eadab30b --- /dev/null +++ b/packages/docs/pages/apps/brave-search/connection.md @@ -0,0 +1,8 @@ +# Brave Search + +1. Go to [API Keys page](https://api.search.brave.com/app/keys) on Brave Search. +2. Create a new API key. +3. Paste the key into the `API Key` field in Automatisch. +4. Write any screen name to be displayed in Automatisch. +5. Click `Save`. +6. Start using Brave Search integration with Automatisch! diff --git a/packages/docs/pages/guide/available-apps.md b/packages/docs/pages/guide/available-apps.md index 0eb08bea..5f0271ad 100644 --- a/packages/docs/pages/guide/available-apps.md +++ b/packages/docs/pages/guide/available-apps.md @@ -5,6 +5,7 @@ The following integrations are currently supported by Automatisch. - [Airtable](/apps/airtable/actions) - [Anthropic](/apps/anthropic/actions) - [Appwrite](/apps/appwrite/triggers) +- [Brave Search](/apps/brave-search/actions) - [Carbone](/apps/carbone/actions) - [ClickUp](/apps/clickup/triggers) - [Datastore](/apps/datastore/actions) diff --git a/packages/docs/pages/public/favicons/brave-search.svg b/packages/docs/pages/public/favicons/brave-search.svg new file mode 100644 index 00000000..8f953988 --- /dev/null +++ b/packages/docs/pages/public/favicons/brave-search.svg @@ -0,0 +1,5 @@ + \ No newline at end of file