Add Activepieces integration for workflow automation

- Add Activepieces fork with SmoothSchedule custom piece
- Create integrations app with Activepieces service layer
- Add embed token endpoint for iframe integration
- Create Automations page with embedded workflow builder
- Add sidebar visibility fix for embed mode
- Add list inactive customers endpoint to Public API
- Include SmoothSchedule triggers: event created/updated/cancelled
- Include SmoothSchedule actions: create/update/cancel events, list resources/services/customers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
poduck
2025-12-18 22:59:37 -05:00
parent 9848268d34
commit 3aa7199503
16292 changed files with 1284892 additions and 4708 deletions

View File

@@ -0,0 +1,18 @@
{
"extends": ["../../../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}

View File

@@ -0,0 +1,7 @@
# pieces-browse-ai
This library was generated with [Nx](https://nx.dev).
## Building
Run `nx build pieces-browse-ai` to build the library.

View File

@@ -0,0 +1,10 @@
{
"name": "@activepieces/piece-browse-ai",
"version": "0.0.6",
"type": "commonjs",
"main": "./src/index.js",
"types": "./src/index.d.ts",
"dependencies": {
"tslib": "^2.3.0"
}
}

View File

@@ -0,0 +1,65 @@
{
"name": "pieces-browse-ai",
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "packages/pieces/community/browse-ai/src",
"projectType": "library",
"release": {
"version": {
"manifestRootsToUpdate": [
"dist/{projectRoot}"
],
"currentVersionResolver": "git-tag",
"fallbackCurrentVersionResolver": "disk"
}
},
"tags": [],
"targets": {
"build": {
"executor": "@nx/js:tsc",
"outputs": [
"{options.outputPath}"
],
"options": {
"outputPath": "dist/packages/pieces/community/browse-ai",
"tsConfig": "packages/pieces/community/browse-ai/tsconfig.lib.json",
"packageJson": "packages/pieces/community/browse-ai/package.json",
"main": "packages/pieces/community/browse-ai/src/index.ts",
"assets": [
"packages/pieces/community/browse-ai/*.md",
{
"input": "packages/pieces/community/browse-ai/src/i18n",
"output": "./src/i18n",
"glob": "**/!(i18n.json)"
}
],
"buildableProjectDepsInPackageJsonType": "dependencies",
"updateBuildableProjectDepsInPackageJson": true
},
"dependsOn": [
"^build",
"prebuild"
]
},
"nx-release-publish": {
"options": {
"packageRoot": "dist/{projectRoot}"
}
},
"lint": {
"executor": "@nx/eslint:lint",
"outputs": [
"{options.outputFile}"
]
},
"prebuild": {
"executor": "nx:run-commands",
"options": {
"cwd": "packages/pieces/community/browse-ai",
"command": "bun install --no-save --silent"
},
"dependsOn": [
"^build"
]
}
}
}

View File

@@ -0,0 +1,37 @@
{
"You can find your Browse AI API key on the dashboard under Settings → API Key.": "Unter Einstellungen → API-Schlüssel finden Sie den AI-API-Schlüssel auf dem Dashboard.",
"Get Task Details": "Aufgabendetails abrufen",
"List Robots": "List Robots",
"Run a Robot": "Roboter ausführen",
"Custom API Call": "Eigener API-Aufruf",
"Retrieves the details of a specific task executed by a Browse AI robot.": "Ruft die Details einer bestimmten Aufgabe ab, die von einem Browse AI Roboter ausgeführt wird.",
"Retrieves all robots available in your account.": "Ruft alle Roboter in deinem Account ab.",
"Runs a robot on-demand with custom input parameters.": "Führt einen Roboter auf Abruf mit benutzerdefinierten Eingabeparametern aus.",
"Make a custom API call to a specific endpoint": "Einen benutzerdefinierten API-Aufruf an einen bestimmten Endpunkt machen",
"Robot": "Robot",
"Task": "Aufgabe",
"Record Video": "Video aufnehmen",
"Input Parameters": "Eingabeparameter",
"Method": "Methode",
"Headers": "Kopfzeilen",
"Query Parameters": "Abfrageparameter",
"Body": "Körper",
"Response is Binary ?": "Antwort ist binär?",
"No Error on Failure": "Kein Fehler bei Fehler",
"Timeout (in seconds)": "Timeout (in Sekunden)",
"Select a robot from your Browse AI account": "Wählen Sie einen Roboter aus Ihrem Browse KI Konto",
"Select a task associated with the selected robot": "Wählen Sie eine Aufgabe, die mit dem ausgewählten Roboter verknüpft ist",
"Try to record a video while running the task.": "Versuchen Sie, ein Video aufzuzeichnen, während Sie die Aufgabe ausführen.",
"Authorization headers are injected automatically from your connection.": "Autorisierungs-Header werden automatisch von Ihrer Verbindung injiziert.",
"Enable for files like PDFs, images, etc..": "Aktivieren für Dateien wie PDFs, Bilder, etc..",
"GET": "ERHALTEN",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "LÖSCHEN",
"HEAD": "HEAD",
"Task Finished with Error": "Aufgabe abgeschlossen mit Fehler",
"Task Finished Successfully": "Aufgabe erfolgreich beendet",
"Triggers when a robot task run fails with an error.": "Wird ausgelöst, wenn ein Roboter-Task mit einem Fehler fehlschlägt.",
"Triggers when a robot finishes a task successfully.": "Wird ausgelöst, wenn ein Roboter eine Aufgabe erfolgreich erledigt."
}

View File

@@ -0,0 +1,37 @@
{
"You can find your Browse AI API key on the dashboard under Settings → API Key.": "Puede encontrar su clave API de navegación en el panel de control en Configuración → Clave API.",
"Get Task Details": "Obtener Detalles de Tarea",
"List Robots": "List Robots",
"Run a Robot": "Ejecutar un Robot",
"Custom API Call": "Llamada API personalizada",
"Retrieves the details of a specific task executed by a Browse AI robot.": "Devuelve los detalles de una tarea específica ejecutada por un robot de exploración de IA.",
"Retrieves all robots available in your account.": "Recupera todos los robots disponibles en tu cuenta.",
"Runs a robot on-demand with custom input parameters.": "Ejecuta un robot bajo demanda con parámetros de entrada personalizados.",
"Make a custom API call to a specific endpoint": "Hacer una llamada API personalizada a un extremo específico",
"Robot": "Robot",
"Task": "Tarea",
"Record Video": "Grabar vídeo",
"Input Parameters": "Parámetros de entrada",
"Method": "Método",
"Headers": "Encabezados",
"Query Parameters": "Parámetros de consulta",
"Body": "Cuerpo",
"Response is Binary ?": "¿Respuesta es binaria?",
"No Error on Failure": "No hay ningún error en fallo",
"Timeout (in seconds)": "Tiempo de espera (en segundos)",
"Select a robot from your Browse AI account": "Selecciona un robot de tu cuenta de exploración de IA",
"Select a task associated with the selected robot": "Seleccione una tarea asociada con el robot seleccionado",
"Try to record a video while running the task.": "Intenta grabar un vídeo mientras ejecutas la tarea.",
"Authorization headers are injected automatically from your connection.": "Las cabeceras de autorización se inyectan automáticamente desde tu conexión.",
"Enable for files like PDFs, images, etc..": "Activar para archivos como PDFs, imágenes, etc.",
"GET": "RECOGER",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "BORRAR",
"HEAD": "LIMPIO",
"Task Finished with Error": "Tarea Terminada con Error",
"Task Finished Successfully": "Tarea finalizada con éxito",
"Triggers when a robot task run fails with an error.": "Dispara cuando una tarea de robot falle con un error.",
"Triggers when a robot finishes a task successfully.": "Dispara cuando un robot finaliza una tarea con éxito."
}

View File

@@ -0,0 +1,37 @@
{
"You can find your Browse AI API key on the dashboard under Settings → API Key.": "Vous pouvez trouver votre clé API Parcourir l'IA dans le tableau de bord sous Paramètres → Clé API.",
"Get Task Details": "Obtenir les détails de la tâche",
"List Robots": "List Robots",
"Run a Robot": "Exécuter un robot",
"Custom API Call": "Appel API personnalisé",
"Retrieves the details of a specific task executed by a Browse AI robot.": "Récupère les détails d'une tâche spécifique exécutée par un robot Parcourir les IA.",
"Retrieves all robots available in your account.": "Récupère tous les robots disponibles dans votre compte.",
"Runs a robot on-demand with custom input parameters.": "Exécute un robot à la demande avec des paramètres d'entrée personnalisés.",
"Make a custom API call to a specific endpoint": "Passez un appel API personnalisé à un point de terminaison spécifique",
"Robot": "Robot",
"Task": "Tâche",
"Record Video": "Enregistrer la vidéo",
"Input Parameters": "Paramètres d'entrée",
"Method": "Méthode",
"Headers": "En-têtes",
"Query Parameters": "Paramètres de requête",
"Body": "Corps",
"Response is Binary ?": "La réponse est Binaire ?",
"No Error on Failure": "Aucune erreur en cas d'échec",
"Timeout (in seconds)": "Délai d'attente (en secondes)",
"Select a robot from your Browse AI account": "Sélectionnez un robot dans votre compte Parcourir les AI",
"Select a task associated with the selected robot": "Sélectionnez une tâche associée au robot sélectionné",
"Try to record a video while running the task.": "Essayez d'enregistrer une vidéo pendant l'exécution de la tâche.",
"Authorization headers are injected automatically from your connection.": "Les en-têtes d'autorisation sont injectés automatiquement à partir de votre connexion.",
"Enable for files like PDFs, images, etc..": "Activer pour les fichiers comme les PDFs, les images, etc.",
"GET": "OBTENIR",
"POST": "POSTER",
"PATCH": "PATCH",
"PUT": "EFFACER",
"DELETE": "SUPPRIMER",
"HEAD": "TÊTE",
"Task Finished with Error": "Tâche terminée avec erreur",
"Task Finished Successfully": "Tâche terminée avec succès",
"Triggers when a robot task run fails with an error.": "Déclenche quand une tâche de robot échoue avec une erreur.",
"Triggers when a robot finishes a task successfully.": "Déclenche quand un robot termine une tâche avec succès."
}

View File

@@ -0,0 +1,37 @@
{
"You can find your Browse AI API key on the dashboard under Settings → API Key.": "ダッシュボードの format@@0 → format@@1 キーで、AI API キーを参照できます。",
"Get Task Details": "タスクの詳細を取得する",
"List Robots": "List Robots",
"Run a Robot": "ロボットを実行する",
"Custom API Call": "カスタムAPI通話",
"Retrieves the details of a specific task executed by a Browse AI robot.": "ブラウズAIロボットで実行される特定のタスクの詳細を取得します。",
"Retrieves all robots available in your account.": "アカウントで利用可能なすべてのロボットを取得します。",
"Runs a robot on-demand with custom input parameters.": "カスタム入力パラメータでロボットをオンデマンドで実行します。",
"Make a custom API call to a specific endpoint": "特定のエンドポイントへのカスタム API コールを実行します。",
"Robot": "Robot",
"Task": "タスク",
"Record Video": "ビデオを録画する",
"Input Parameters": "入力パラメータ",
"Method": "方法",
"Headers": "ヘッダー",
"Query Parameters": "クエリパラメータ",
"Body": "本文",
"Response is Binary ?": "応答はバイナリですか?",
"No Error on Failure": "失敗時にエラーはありません",
"Timeout (in seconds)": "タイムアウト(秒)",
"Select a robot from your Browse AI account": "ブラウズAIアカウントからロボットを選択します",
"Select a task associated with the selected robot": "選択したロボットに関連付けられているタスクを選択します",
"Try to record a video while running the task.": "タスクの実行中にビデオを録画してみてください。",
"Authorization headers are injected automatically from your connection.": "認証ヘッダは接続から自動的に注入されます。",
"Enable for files like PDFs, images, etc..": "PDF、画像などのファイルを有効にします。",
"GET": "取得",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "削除",
"HEAD": "頭",
"Task Finished with Error": "タスクがエラーで終了しました",
"Task Finished Successfully": "タスクが完了しました",
"Triggers when a robot task run fails with an error.": "ロボットタスクの実行がエラーで失敗したときにトリガーします。",
"Triggers when a robot finishes a task successfully.": "ロボットがタスクを正常に終了したときにトリガーします。"
}

View File

@@ -0,0 +1,37 @@
{
"You can find your Browse AI API key on the dashboard under Settings → API Key.": "Je kunt je Blader API-sleutel vinden op het dashboard onder Instellingen → API Key.",
"Get Task Details": "Verkrijg Taakgegevens",
"List Robots": "List Robots",
"Run a Robot": "Voer een robot uit",
"Custom API Call": "Custom API Call",
"Retrieves the details of a specific task executed by a Browse AI robot.": "Haal de details op van een specifieke taak uitgevoerd door een Browse AI robot.",
"Retrieves all robots available in your account.": "Haal alle robots op die in je account beschikbaar zijn.",
"Runs a robot on-demand with custom input parameters.": "Voer een robot op aanvraag uit met aangepaste invoerparameters.",
"Make a custom API call to a specific endpoint": "Maak een aangepaste API call naar een specifiek eindpunt",
"Robot": "Robot",
"Task": "Opdracht",
"Record Video": "Video opnemen",
"Input Parameters": "Invoer parameters",
"Method": "Methode",
"Headers": "Kopteksten",
"Query Parameters": "Query parameters",
"Body": "Lichaam",
"Response is Binary ?": "Antwoord is binair?",
"No Error on Failure": "Geen fout bij fout",
"Timeout (in seconds)": "Time-out (in seconden)",
"Select a robot from your Browse AI account": "Selecteer een robot in uw account Blader AI",
"Select a task associated with the selected robot": "Selecteer een taak gekoppeld aan de geselecteerde robot",
"Try to record a video while running the task.": "Probeer een video op te nemen tijdens het uitvoeren van de taak.",
"Authorization headers are injected automatically from your connection.": "Autorisatie headers worden automatisch geïnjecteerd vanuit uw verbinding.",
"Enable for files like PDFs, images, etc..": "Inschakelen voor bestanden zoals PDF's, afbeeldingen etc..",
"GET": "KRIJG",
"POST": "POSTE",
"PATCH": "BEKIJK",
"PUT": "PUT",
"DELETE": "VERWIJDEREN",
"HEAD": "HOOFD",
"Task Finished with Error": "Taak beëindigd met een fout",
"Task Finished Successfully": "Taak Succesvol Voltooid",
"Triggers when a robot task run fails with an error.": "Taak van een robot wordt uitgevoerd met een fout.",
"Triggers when a robot finishes a task successfully.": "Triggert een robot met succes een taak af."
}

View File

@@ -0,0 +1,37 @@
{
"You can find your Browse AI API key on the dashboard under Settings → API Key.": "Você pode encontrar sua chave de Navegação API no painel em Configurações → Chave de API.",
"Get Task Details": "Obter Detalhes da Tarefa",
"List Robots": "List Robots",
"Run a Robot": "Executar um robô",
"Custom API Call": "Chamada de API personalizada",
"Retrieves the details of a specific task executed by a Browse AI robot.": "Obtém detalhes de uma tarefa específica executada por um robô AI.",
"Retrieves all robots available in your account.": "Recupera todos os robôs disponíveis em sua conta.",
"Runs a robot on-demand with custom input parameters.": "Executa um robô sob demanda com parâmetros de entrada personalizados.",
"Make a custom API call to a specific endpoint": "Faça uma chamada de API personalizada para um ponto de extremidade específico",
"Robot": "Robot",
"Task": "Tarefas",
"Record Video": "Gravar Vídeo",
"Input Parameters": "Parâmetros de entrada",
"Method": "Método",
"Headers": "Cabeçalhos",
"Query Parameters": "Parâmetros da consulta",
"Body": "Conteúdo",
"Response is Binary ?": "A resposta é binária ?",
"No Error on Failure": "Nenhum erro no Failure",
"Timeout (in seconds)": "Tempo limite (em segundos)",
"Select a robot from your Browse AI account": "Selecione um robô de sua conta de Navegação IA",
"Select a task associated with the selected robot": "Selecione uma tarefa associada com o robô selecionado",
"Try to record a video while running the task.": "Tente gravar um vídeo durante a execução da tarefa.",
"Authorization headers are injected automatically from your connection.": "Os cabeçalhos de autorização são inseridos automaticamente a partir da sua conexão.",
"Enable for files like PDFs, images, etc..": "Habilitar para arquivos como PDFs, imagens, etc..",
"GET": "OBTER",
"POST": "POSTAR",
"PATCH": "COMPRAR",
"PUT": "COLOCAR",
"DELETE": "EXCLUIR",
"HEAD": "CABEÇA",
"Task Finished with Error": "Tarefa concluída com erro",
"Task Finished Successfully": "Tarefa concluída com sucesso",
"Triggers when a robot task run fails with an error.": "Aciona quando uma tarefa do robô executada falha com um erro.",
"Triggers when a robot finishes a task successfully.": "Aciona quando um robô termina uma tarefa com sucesso."
}

View File

@@ -0,0 +1,36 @@
{
"Browse AI": "Просмотр ИИ",
"You can find your Browse AI API key on the dashboard under Settings → API Key.": "Вы можете найти ваш Browse AI API ключ на приборной панели в меню Настройки → API Key.",
"Get Task Details": "Получить детали задачи",
"List Robots": "List Robots",
"Run a Robot": "Запустить робота",
"Custom API Call": "Пользовательский вызов API",
"Retrieves the details of a specific task executed by a Browse AI robot.": "Возвращает детали конкретной задачи, выполненной роботом Обзор ИИ.",
"Retrieves all robots available in your account.": "Возвращает все роботы, доступные в вашей учетной записи.",
"Runs a robot on-demand with custom input parameters.": "Выполняет робот по требованию с пользовательскими параметрами ввода.",
"Make a custom API call to a specific endpoint": "Сделать пользовательский API вызов к определенной конечной точке",
"Robot": "Robot",
"Task": "Задача",
"Record Video": "Запись видео",
"Input Parameters": "Входные параметры",
"Method": "Метод",
"Headers": "Заголовки",
"Query Parameters": "Параметры запроса",
"Body": "Тело",
"No Error on Failure": "Нет ошибок при ошибке",
"Timeout (in seconds)": "Таймаут (в секундах)",
"Select a robot from your Browse AI account": "Выберите робота из Browse AI аккаунта",
"Select a task associated with the selected robot": "Выберите задачу, связанную с выбранным роботом",
"Try to record a video while running the task.": "Попробуйте записать видео во время выполнения задачи.",
"Authorization headers are injected automatically from your connection.": "Заголовки авторизации включаются автоматически из вашего соединения.",
"GET": "ПОЛУЧИТЬ",
"POST": "ПОСТ",
"PATCH": "ПАТЧ",
"PUT": "ПОКУПИТЬ",
"DELETE": "УДАЛИТЬ",
"HEAD": "HEAD",
"Task Finished with Error": "Задача завершена с ошибкой",
"Task Finished Successfully": "Задание успешно завершено",
"Triggers when a robot task run fails with an error.": "Срабатывает при запуске задачи робота с ошибкой.",
"Triggers when a robot finishes a task successfully.": "Триггеры, когда робот успешно завершил задачу."
}

View File

@@ -0,0 +1,37 @@
{
"You can find your Browse AI API key on the dashboard under Settings → API Key.": "You can find your Browse AI API key on the dashboard under Settings → API Key.",
"Get Task Details": "Get Task Details",
"List Robots": "List Robots",
"Run a Robot": "Run a Robot",
"Custom API Call": "Custom API Call",
"Retrieves the details of a specific task executed by a Browse AI robot.": "Retrieves the details of a specific task executed by a Browse AI robot.",
"Retrieves all robots available in your account.": "Retrieves all robots available in your account.",
"Runs a robot on-demand with custom input parameters.": "Runs a robot on-demand with custom input parameters.",
"Make a custom API call to a specific endpoint": "Make a custom API call to a specific endpoint",
"Robot": "Robot",
"Task": "Task",
"Record Video": "Record Video",
"Input Parameters": "Input Parameters",
"Method": "Method",
"Headers": "Headers",
"Query Parameters": "Query Parameters",
"Body": "Body",
"Response is Binary ?": "Response is Binary ?",
"No Error on Failure": "No Error on Failure",
"Timeout (in seconds)": "Timeout (in seconds)",
"Select a robot from your Browse AI account": "Select a robot from your Browse AI account",
"Select a task associated with the selected robot": "Select a task associated with the selected robot",
"Try to record a video while running the task.": "Try to record a video while running the task.",
"Authorization headers are injected automatically from your connection.": "Authorization headers are injected automatically from your connection.",
"Enable for files like PDFs, images, etc..": "Enable for files like PDFs, images, etc..",
"GET": "GET",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "DELETE",
"HEAD": "HEAD",
"Task Finished with Error": "Task Finished with Error",
"Task Finished Successfully": "Task Finished Successfully",
"Triggers when a robot task run fails with an error.": "Triggers when a robot task run fails with an error.",
"Triggers when a robot finishes a task successfully.": "Triggers when a robot finishes a task successfully."
}

View File

@@ -0,0 +1,36 @@
{
"Browse AI": "Browse AI",
"You can find your Browse AI API key on the dashboard under Settings → API Key.": "You can find your Browse AI API key on the dashboard under Settings → API Key.",
"Get Task Details": "Get Task Details",
"List Robots": "List Robots",
"Run a Robot": "Run a Robot",
"Custom API Call": "Custom API Call",
"Retrieves the details of a specific task executed by a Browse AI robot.": "Retrieves the details of a specific task executed by a Browse AI robot.",
"Retrieves all robots available in your account.": "Retrieves all robots available in your account.",
"Runs a robot on-demand with custom input parameters.": "Runs a robot on-demand with custom input parameters.",
"Make a custom API call to a specific endpoint": "Make a custom API call to a specific endpoint",
"Robot": "Robot",
"Task": "Task",
"Record Video": "Record Video",
"Input Parameters": "Input Parameters",
"Method": "Method",
"Headers": "Headers",
"Query Parameters": "Query Parameters",
"Body": "Body",
"No Error on Failure": "No Error on Failure",
"Timeout (in seconds)": "Timeout (in seconds)",
"Select a robot from your Browse AI account": "Select a robot from your Browse AI account",
"Select a task associated with the selected robot": "Select a task associated with the selected robot",
"Try to record a video while running the task.": "Try to record a video while running the task.",
"Authorization headers are injected automatically from your connection.": "Authorization headers are injected automatically from your connection.",
"GET": "GET",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "DELETE",
"HEAD": "HEAD",
"Task Finished with Error": "Task Finished with Error",
"Task Finished Successfully": "Task Finished Successfully",
"Triggers when a robot task run fails with an error.": "Triggers when a robot task run fails with an error.",
"Triggers when a robot finishes a task successfully.": "Triggers when a robot finishes a task successfully."
}

View File

@@ -0,0 +1,37 @@
{
"You can find your Browse AI API key on the dashboard under Settings → API Key.": "You can find your Browse AI API key on the dashboard under Settings → API Key.",
"Get Task Details": "Get Task Details",
"List Robots": "List Robots",
"Run a Robot": "Run a Robot",
"Custom API Call": "自定义 API 呼叫",
"Retrieves the details of a specific task executed by a Browse AI robot.": "Retrieves the details of a specific task executed by a Browse AI robot.",
"Retrieves all robots available in your account.": "Retrieves all robots available in your account.",
"Runs a robot on-demand with custom input parameters.": "Runs a robot on-demand with custom input parameters.",
"Make a custom API call to a specific endpoint": "将一个自定义 API 调用到一个特定的终点",
"Robot": "Robot",
"Task": "Task",
"Record Video": "Record Video",
"Input Parameters": "Input Parameters",
"Method": "方法",
"Headers": "信头",
"Query Parameters": "查询参数",
"Body": "正文内容",
"Response is Binary ?": "Response is Binary ?",
"No Error on Failure": "失败时没有错误",
"Timeout (in seconds)": "超时(秒)",
"Select a robot from your Browse AI account": "Select a robot from your Browse AI account",
"Select a task associated with the selected robot": "Select a task associated with the selected robot",
"Try to record a video while running the task.": "Try to record a video while running the task.",
"Authorization headers are injected automatically from your connection.": "授权头自动从您的连接中注入。",
"Enable for files like PDFs, images, etc..": "Enable for files like PDFs, images, etc..",
"GET": "获取",
"POST": "帖子",
"PATCH": "PATCH",
"PUT": "弹出",
"DELETE": "删除",
"HEAD": "黑色",
"Task Finished with Error": "Task Finished with Error",
"Task Finished Successfully": "Task Finished Successfully",
"Triggers when a robot task run fails with an error.": "Triggers when a robot task run fails with an error.",
"Triggers when a robot finishes a task successfully.": "Triggers when a robot finishes a task successfully."
}

View File

@@ -0,0 +1,34 @@
import { createCustomApiCallAction } from '@activepieces/pieces-common';
import { createPiece } from '@activepieces/pieces-framework';
import { browseAiAuth } from './lib/common/auth';
import { getTaskDetailsAction } from './lib/actions/get-task-details';
import { listRobotsAction } from './lib/actions/list-robots';
import { runRobotAction } from './lib/actions/run-robot';
import { taskFinishedWithErrorTrigger } from './lib/triggers/task-finished-with-error';
import { taskFinishedSuccessfullyTrigger } from './lib/triggers/task-finished-successfully';
import { PieceCategory } from '@activepieces/shared';
export const browseAi = createPiece({
displayName: 'Browse AI',
auth: browseAiAuth,
minimumSupportedRelease: '0.36.1',
logoUrl: 'https://cdn.activepieces.com/pieces/browse-ai.png',
categories:[PieceCategory.PRODUCTIVITY],
authors: ['aryel780'],
actions: [
getTaskDetailsAction,
listRobotsAction,
runRobotAction,
createCustomApiCallAction({
auth: browseAiAuth,
baseUrl: () => 'https://api.browse.ai/v2',
authMapping: async (auth) => ({
Authorization: `Bearer ${auth.secret_text}`,
}),
}),
],
triggers: [
taskFinishedWithErrorTrigger,
taskFinishedSuccessfullyTrigger,
],
});

View File

@@ -0,0 +1,46 @@
import { createAction } from '@activepieces/pieces-framework';
import { HttpMethod } from '@activepieces/pieces-common';
import { browseAiApiCall } from '../common/client';
import { browseAiAuth } from '../common/auth';
import { robotIdDropdown, taskIdDropdown } from '../common/props';
export const getTaskDetailsAction = createAction({
name: 'get-task-details',
auth: browseAiAuth,
displayName: 'Get Task Details',
description:
'Retrieves the details of a specific task executed by a Browse AI robot.',
props: {
robotId: robotIdDropdown,
taskId: taskIdDropdown,
},
async run(context) {
const { robotId, taskId } = context.propsValue;
try {
const response = await browseAiApiCall({
auth: { apiKey: context.auth.secret_text },
method: HttpMethod.GET,
resourceUri: `/robots/${robotId}/tasks/${taskId}`,
});
return response;
} catch (error: any) {
if (error.response?.status === 404) {
throw new Error(
'Task not found. Please verify the Robot ID and Task ID.'
);
}
if (error.response?.status === 401) {
throw new Error('Authentication failed. Please check your API key.');
}
throw new Error(
`Failed to fetch task details: ${
error.message || 'Unknown error occurred'
}`
);
}
},
});

View File

@@ -0,0 +1,35 @@
import { createAction } from '@activepieces/pieces-framework';
import { HttpMethod } from '@activepieces/pieces-common';
import { browseAiApiCall } from '../common/client';
import { browseAiAuth } from '../common/auth';
export const listRobotsAction = createAction({
name: 'list-robots',
auth: browseAiAuth,
displayName: 'List Robots',
description: 'Retrieves all robots available in your account.',
props: {},
async run(context) {
try {
const response = await browseAiApiCall({
auth: { apiKey: context.auth.secret_text },
method: HttpMethod.GET,
resourceUri: '/robots',
});
return response;
} catch (error: any) {
if (error.response?.status === 401) {
throw new Error('Authentication failed. Please check your API key.');
}
if (error.response?.status === 429) {
throw new Error('Rate limit exceeded. Please wait before retrying.');
}
throw new Error(
`Failed to fetch robots: ${error.message || 'Unknown error occurred'}`
);
}
},
});

View File

@@ -0,0 +1,59 @@
import { HttpMethod } from '@activepieces/pieces-common';
import { createAction, Property } from '@activepieces/pieces-framework';
import { browseAiApiCall } from '../common/client';
import { browseAiAuth } from '../common/auth';
import { robotIdDropdown, robotParameters } from '../common/props';
export const runRobotAction = createAction({
name: 'run-robot',
auth: browseAiAuth,
displayName: 'Run a Robot',
description:
'Runs a robot on-demand with custom input parameters.',
props: {
robotId: robotIdDropdown,
recordVideo: Property.Checkbox({
displayName: 'Record Video',
description:
'Try to record a video while running the task.',
required: false,
defaultValue: false,
}),
robotParams: robotParameters,
},
async run(context) {
const { robotId, recordVideo } = context.propsValue;
const inputParameters = context.propsValue.robotParams ?? {};
try {
const response = await browseAiApiCall({
method: HttpMethod.POST,
resourceUri: `/robots/${robotId}/tasks`,
auth: { apiKey: context.auth.secret_text },
body: {
recordVideo: recordVideo || false,
inputParameters,
},
});
return response;
} catch (error: any) {
if (error.response?.status === 401) {
throw new Error('Authentication failed. Please check your API key.');
}
if (error.response?.status === 404) {
throw new Error('Robot not found. Please verify the robot ID.');
}
if (error.response?.status === 429) {
throw new Error('Rate limit exceeded. Please wait before retrying.');
}
throw new Error(
`Failed to run robot: ${error.message || 'Unknown error occurred'}`
);
}
},
});

View File

@@ -0,0 +1,24 @@
import { PieceAuth } from '@activepieces/pieces-framework';
import { HttpMethod } from '@activepieces/pieces-common';
import { browseAiApiCall } from './client';
export const browseAiAuth = PieceAuth.SecretText({
displayName: 'API Key',
description: 'You can find your Browse AI API key on the dashboard under Settings → API Key.',
required: true,
validate: async ({ auth }) => {
try {
await browseAiApiCall({
method: HttpMethod.GET,
resourceUri: '/status',
auth: { apiKey: auth },
});
return { valid: true };
} catch {
return {
valid: false,
error: 'Invalid API Key. Please check your Browse AI credentials.',
};
}
},
});

View File

@@ -0,0 +1,85 @@
import {
httpClient,
HttpMethod,
HttpRequest,
HttpMessageBody,
QueryParams,
} from '@activepieces/pieces-common';
export type BrowseAiAuthProps = {
apiKey: string;
};
export type BrowseAiApiCallParams = {
method: HttpMethod;
resourceUri: string;
query?: Record<string, string | number | string[] | undefined>;
body?: unknown;
auth: BrowseAiAuthProps;
};
export async function browseAiApiCall<T extends HttpMessageBody>({
method,
resourceUri,
query,
body,
auth,
}: BrowseAiApiCallParams): Promise<T> {
const { apiKey } = auth;
if (!apiKey) {
throw new Error('Browse AI API key is required for authentication');
}
const queryParams: QueryParams = {};
if (query) {
for (const [key, value] of Object.entries(query)) {
if (value !== null && value !== undefined) {
queryParams[key] = String(value);
}
}
}
const baseUrl = `https://api.browse.ai/v2`;
const request: HttpRequest = {
method,
url: `${baseUrl}${resourceUri}`,
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
queryParams,
body,
};
try {
const response = await httpClient.sendRequest<T>(request);
return response.body;
} catch (error: any) {
const statusCode = error.response?.status;
const errorData = error.response?.data;
switch (statusCode) {
case 400:
throw new Error(`Bad Request: ${errorData?.message || 'Invalid parameters'}`);
case 401:
throw new Error('Unauthorized: Invalid API key. Please check your credentials.');
case 403:
throw new Error('Forbidden: You do not have permission to access this resource.');
case 404:
throw new Error('Not Found: The requested resource does not exist.');
case 429:
throw new Error('Rate Limit Exceeded: Please slow down your requests.');
case 500:
throw new Error('Internal Server Error: Something went wrong on Browse AIs side.');
default:
{
const message = errorData?.message || error.message || 'Unknown error';
throw new Error(`Browse AI API Error (${statusCode || 'Unknown'}): ${message}`);
}
}
}
}

View File

@@ -0,0 +1,213 @@
import { DynamicPropsValue, Property } from '@activepieces/pieces-framework';
import { HttpMethod } from '@activepieces/pieces-common';
import { browseAiApiCall } from './client';
import { browseAiAuth } from './auth';
interface BrowseAiRobot {
id: string;
name: string;
}
interface BrowseAiTask {
id: string;
status: string;
createdAt?: number;
}
interface BrowseAiTasksResponse {
statusCode: number;
messageCode: string;
result: {
robotTasks: {
totalCount: number;
pageNumber: number;
hasMore: boolean;
items: BrowseAiTask[];
};
};
}
interface BrowseAiRobotResponse {
robot: {
id: string;
name: string;
inputParameters: {
type: string;
name: string;
label: string;
required: boolean;
options?: { label: string; value: string }[];
}[];
};
}
export const robotIdDropdown = Property.Dropdown({
auth: browseAiAuth,
displayName: 'Robot',
description: 'Select a robot from your Browse AI account',
required: true,
refreshers: [],
options: async ({ auth }) => {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please connect your Browse AI account first.',
};
}
try {
const response = await browseAiApiCall<{
robots: { items: BrowseAiRobot[] };
}>({
method: HttpMethod.GET,
resourceUri: '/robots',
auth: { apiKey: auth.secret_text },
});
const robots = response?.robots?.items ?? [];
if (robots.length === 0) {
return {
disabled: true,
options: [],
placeholder: 'No robots found in your account.',
};
}
return {
disabled: false,
options: robots.map((robot) => ({
label: robot.name,
value: robot.id,
})),
};
} catch (error: any) {
return {
disabled: true,
options: [],
placeholder:
'Failed to load robots. Please check your API key and try again.',
};
}
},
});
export const taskIdDropdown = Property.Dropdown({
auth: browseAiAuth,
displayName: 'Task',
description: 'Select a task associated with the selected robot',
required: true,
refreshers: ['robotId'],
options: async ({ auth, robotId }) => {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please connect your Browse AI account.',
};
}
if (!robotId) {
return {
disabled: true,
options: [],
placeholder: 'Please select a robot first.',
};
}
try {
const response = await browseAiApiCall<BrowseAiTasksResponse>({
method: HttpMethod.GET,
resourceUri: `/robots/${robotId}/tasks`,
auth: { apiKey: auth.secret_text },
});
const tasks = response.result?.robotTasks?.items ?? [];
if (tasks.length === 0) {
return {
disabled: true,
options: [],
placeholder: 'No tasks found for the selected robot.',
};
}
return {
disabled: false,
options: tasks.map((task) => {
const createdDate = task.createdAt
? new Date(task.createdAt).toLocaleDateString()
: 'Unknown date';
return {
label: `${task.id} - ${task.status} (${createdDate})`,
value: task.id,
};
}),
};
} catch (e) {
return {
disabled: true,
options: [],
placeholder: `Error fetching tasks: ${
e instanceof Error ? e.message : 'Unknown error'
}`,
};
}
},
});
export const robotParameters = Property.DynamicProperties({
auth: browseAiAuth,
displayName: 'Input Parameters',
refreshers: ['robotId'],
required: true,
props: async ({ auth, robotId }) => {
if (!auth || !robotId) return {};
try {
const response = await browseAiApiCall<BrowseAiRobotResponse>({
method: HttpMethod.GET,
resourceUri: `/robots/${robotId}`,
auth: { apiKey: auth.secret_text },
});
const props: DynamicPropsValue = {};
const params = response.robot.inputParameters ?? [];
for (const param of params) {
switch (param.type) {
case 'number':
props[param.name] = Property.Number({
displayName: param.label,
required: param.required,
});
break;
case 'url':
case 'string':
props[param.name] = Property.ShortText({
displayName: param.label,
required: param.required,
});
break;
case 'select':
props[param.name] = Property.StaticDropdown({
displayName: param.label,
required: param.required,
options: {
disabled: false,
options: param.options ? param.options : [],
},
});
break;
default:
break;
}
}
return props;
} catch {
return {};
}
},
});

View File

@@ -0,0 +1,135 @@
import { HttpMethod } from '@activepieces/pieces-common';
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
import { isNil } from '@activepieces/shared';
import { browseAiAuth } from '../common/auth';
import { browseAiApiCall } from '../common/client';
import { robotIdDropdown } from '../common/props';
const TRIGGER_KEY = 'browse-ai-task_finished_successfully';
export const taskFinishedSuccessfullyTrigger = createTrigger({
auth: browseAiAuth,
name: 'task_finished_successfully',
displayName: 'Task Finished Successfully',
description:
'Triggers when a robot finishes a task successfully.',
type: TriggerStrategy.WEBHOOK,
props: {
robotId: robotIdDropdown,
},
async onEnable(context) {
const { robotId } = context.propsValue;
const apiKey = context.auth.secret_text;
try {
// Verify robot exists and we have access
await browseAiApiCall({
method: HttpMethod.GET,
auth: { apiKey },
resourceUri: `/robots/${robotId}`,
});
const response = await browseAiApiCall<{
webhook: { id: string; url: string; status: string };
}>({
method: HttpMethod.POST,
auth: { apiKey },
resourceUri: `/robots/${robotId}/webhooks`,
body: {
hookUrl: context.webhookUrl,
eventType: 'taskFinishedSuccessfully',
},
});
await context.store.put<string>(TRIGGER_KEY, response.webhook.id);
} catch (error: any) {
if (error.response?.status === 404) {
throw new Error(
`Robot not found: The robot with ID "${robotId}" does not exist or you do not have access to it. Please verify the robot ID and your permissions.`
);
}
if (error.response?.status === 403) {
throw new Error(
'Access denied: You do not have permission to set up webhooks for this robot. Please check your Browse AI account permissions and ensure you have webhook access.'
);
}
if (error.response?.status === 400) {
throw new Error(
`Invalid webhook configuration: ${
error.response?.data?.message || error.message
}. Please check your webhook URL and robot ID.`
);
}
if (error.response?.status === 429) {
throw new Error(
'Rate limit exceeded: Too many webhook requests. Please wait a moment and try again.'
);
}
throw new Error(
`Failed to set up webhook: ${
error.message || 'Unknown error occurred'
}. Please check your robot ID and try again.`
);
}
},
async onDisable(context) {
const { robotId } = context.propsValue;
const webhookId = await context.store.get<string>(TRIGGER_KEY);
const apiKey = context.auth.secret_text;
if (!isNil(webhookId)) {
try {
await browseAiApiCall({
method: HttpMethod.DELETE,
auth: { apiKey },
resourceUri: `/robots/${robotId}/webhooks/${webhookId}`,
});
} catch (error: any) {
console.warn(
`Warning: Failed to clean up webhook ${webhookId}:`,
error.message
);
// Clean up the stored webhook ID even if deletion failed
await context.store.delete(TRIGGER_KEY);
}
}
},
async run(context) {
const payload = context.payload.body as {
task: Record<string, any>;
event: string;
};
if (payload.event !== 'task.finishedSuccessfully') return [];
return [payload.task];
},
async test(context) {
const { robotId } = context.propsValue;
const apiKey = context.auth.secret_text;
const response = await browseAiApiCall<{
result: { robotTasks: { items: { id: string }[] } };
}>({
method: HttpMethod.GET,
auth: { apiKey },
resourceUri: `/robots/${robotId}/tasks`,
query: { status: 'successful', sort: '-createdAt' },
});
return response.result.robotTasks.items;
},
sampleData: {},
});

View File

@@ -0,0 +1,134 @@
import { HttpMethod } from '@activepieces/pieces-common';
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
import { isNil } from '@activepieces/shared';
import { browseAiAuth } from '../common/auth';
import { browseAiApiCall } from '../common/client';
import { robotIdDropdown } from '../common/props';
const TRIGGER_KEY = 'browse-ai-task_finished_with_error';
export const taskFinishedWithErrorTrigger = createTrigger({
auth: browseAiAuth,
name: 'task_finished_with_error',
displayName: 'Task Finished with Error',
description: 'Triggers when a robot task run fails with an error.',
type: TriggerStrategy.WEBHOOK,
props: {
robotId: robotIdDropdown,
},
async onEnable(context) {
const { robotId } = context.propsValue;
const apiKey = context.auth.secret_text;
try {
// Verify robot exists and we have access
await browseAiApiCall({
method: HttpMethod.GET,
auth: { apiKey },
resourceUri: `/robots/${robotId}`,
});
const response = await browseAiApiCall<{
webhook: { id: string; url: string; status: string };
}>({
method: HttpMethod.POST,
auth: { apiKey },
resourceUri: `/robots/${robotId}/webhooks`,
body: {
hookUrl: context.webhookUrl,
eventType: 'taskFinishedWithError',
},
});
await context.store.put<string>(TRIGGER_KEY, response.webhook.id);
} catch (error: any) {
if (error.response?.status === 404) {
throw new Error(
`Robot not found: The robot with ID "${robotId}" does not exist or you do not have access to it. Please verify the robot ID and your permissions.`
);
}
if (error.response?.status === 403) {
throw new Error(
'Access denied: You do not have permission to set up webhooks for this robot. Please check your Browse AI account permissions and ensure you have webhook access.'
);
}
if (error.response?.status === 400) {
throw new Error(
`Invalid webhook configuration: ${
error.response?.data?.message || error.message
}. Please check your webhook URL and robot ID.`
);
}
if (error.response?.status === 429) {
throw new Error(
'Rate limit exceeded: Too many webhook requests. Please wait a moment and try again.'
);
}
throw new Error(
`Failed to set up webhook: ${
error.message || 'Unknown error occurred'
}. Please check your robot ID and try again.`
);
}
},
async onDisable(context) {
const { robotId } = context.propsValue;
const webhookId = await context.store.get<string>(TRIGGER_KEY);
const apiKey = context.auth.secret_text;
if (!isNil(webhookId)) {
try {
await browseAiApiCall({
method: HttpMethod.DELETE,
auth: { apiKey },
resourceUri: `/robots/${robotId}/webhooks/${webhookId}`,
});
} catch (error: any) {
console.warn(
`Warning: Failed to clean up webhook ${webhookId}:`,
error.message
);
// Clean up the stored webhook ID even if deletion failed
await context.store.delete(TRIGGER_KEY);
}
}
},
async run(context) {
const payload = context.payload.body as {
task: Record<string, any>;
event: string;
};
if (payload.event !== 'task.finishedWithError') return [];
return [payload.task];
},
async test(context) {
const { robotId } = context.propsValue;
const apiKey = context.auth.secret_text;
const response = await browseAiApiCall<{
result: { robotTasks: { items: { id: string }[] } };
}>({
method: HttpMethod.GET,
auth: { apiKey },
resourceUri: `/robots/${robotId}/tasks`,
query: { status: 'failed', sort: '-createdAt' },
});
return response.result.robotTasks.items;
},
sampleData: {},
});

View File

@@ -0,0 +1,20 @@
{
"extends": "../../../../tsconfig.base.json",
"compilerOptions": {
"module": "commonjs",
"forceConsistentCasingInFileNames": true,
"strict": true,
"importHelpers": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noPropertyAccessFromIndexSignature": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
}
]
}

View File

@@ -0,0 +1,9 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../../dist/out-tsc",
"declaration": true,
"types": ["node"]
},
"include": ["src/**/*.ts"]
}