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,33 @@
{
"extends": [
"../../../../.eslintrc.base.json"
],
"ignorePatterns": [
"!**/*"
],
"overrides": [
{
"files": [
"*.ts",
"*.tsx",
"*.js",
"*.jsx"
],
"rules": {}
},
{
"files": [
"*.ts",
"*.tsx"
],
"rules": {}
},
{
"files": [
"*.js",
"*.jsx"
],
"rules": {}
}
]
}

View File

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

View File

@@ -0,0 +1,4 @@
{
"name": "@activepieces/piece-twin-labs",
"version": "0.0.9"
}

View File

@@ -0,0 +1,65 @@
{
"name": "pieces-twin-labs",
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "packages/pieces/community/twin-labs/src",
"projectType": "library",
"release": {
"version": {
"currentVersionResolver": "git-tag",
"preserveLocalDependencyProtocols": false,
"manifestRootsToUpdate": [
"dist/{projectRoot}"
]
}
},
"tags": [],
"targets": {
"build": {
"executor": "@nx/js:tsc",
"outputs": [
"{options.outputPath}"
],
"options": {
"outputPath": "dist/packages/pieces/community/twin-labs",
"tsConfig": "packages/pieces/community/twin-labs/tsconfig.lib.json",
"packageJson": "packages/pieces/community/twin-labs/package.json",
"main": "packages/pieces/community/twin-labs/src/index.ts",
"assets": [
"packages/pieces/community/twin-labs/*.md",
{
"input": "packages/pieces/community/twin-labs/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/twin-labs",
"command": "bun install --no-save --silent"
},
"dependsOn": [
"^build"
]
}
}
}

View File

@@ -0,0 +1,9 @@
{
"Please use ***your-twin-labs-api-key*** as value for API Key": "Bitte verwenden Sie ***dein-twin-labs-api-key*** als Wert für den API-Schlüssel",
"Browse": "Durchsuchen",
"Browse the internet with an AI web navigation agent that can find information for you": "Durchsuchen Sie das Internet mit einem KI-Webnavigationsmakler, der Informationen für Sie finden kann",
"startUrl": "startUrl",
"Goal": "Ziel",
"The URL where the browsing task should begin": "Die URL, in der die Browsing-Aufgabe beginnen soll",
"The goal or objective of the browsing task": "Das Ziel oder Ziel der Browsing-Aufgabe"
}

View File

@@ -0,0 +1,9 @@
{
"Please use ***your-twin-labs-api-key*** as value for API Key": "Por favor usa ***tu-doble-labs-api-key*** como valor para la Clave API",
"Browse": "Navegar",
"Browse the internet with an AI web navigation agent that can find information for you": "Navegue por Internet con un agente de navegación web de IA que puede encontrar información para usted",
"startUrl": "startUrl",
"Goal": "Objetivo",
"The URL where the browsing task should begin": "La URL donde debe comenzar la tarea de navegación",
"The goal or objective of the browsing task": "El objetivo o objetivo de la tarea de navegación"
}

View File

@@ -0,0 +1,9 @@
{
"Please use ***your-twin-labs-api-key*** as value for API Key": "Veuillez utiliser ***your-twin-labs-api-key*** comme valeur pour la clé API",
"Browse": "Parcourir",
"Browse the internet with an AI web navigation agent that can find information for you": "Naviguez sur Internet avec un agent de navigation web IA qui peut trouver des informations pour vous",
"startUrl": "startUrl",
"Goal": "Objectif",
"The URL where the browsing task should begin": "L'URL où la tâche de navigation doit commencer",
"The goal or objective of the browsing task": "L'objectif ou l'objectif de la tâche de navigation"
}

View File

@@ -0,0 +1,9 @@
{
"Please use ***your-twin-labs-api-key*** as value for API Key": "API キーの値として ***your-twin-labs-api-key*** を使用してください",
"Browse": "検索",
"Browse the internet with an AI web navigation agent that can find information for you": "情報を見つけることができるAIウェブナビゲーションエージェントでインターネットを閲覧する",
"startUrl": "startUrl",
"Goal": "目標",
"The URL where the browsing task should begin": "ブラウジングタスクが開始されるURL",
"The goal or objective of the browsing task": "ブラウジングタスクの目標または目的"
}

View File

@@ -0,0 +1,9 @@
{
"Please use ***your-twin-labs-api-key*** as value for API Key": "Gebruik ***uw-twin-labs-api-sleutel*** als waarde voor API-sleutel",
"Browse": "Bladeren",
"Browse the internet with an AI web navigation agent that can find information for you": "Blader door het internet met een AI webnavigatieagent die informatie voor u kan vinden",
"startUrl": "startUrl",
"Goal": "Doel",
"The URL where the browsing task should begin": "De URL waar de surftaak moet beginnen",
"The goal or objective of the browsing task": "Het doel of doel van de surftaak"
}

View File

@@ -0,0 +1,9 @@
{
"Please use ***your-twin-labs-api-key*** as value for API Key": "Por favor, use ***sua-chave-duin-labs-api-*** como valor para a chave API",
"Browse": "Localizar...",
"Browse the internet with an AI web navigation agent that can find information for you": "Navegue pela internet com um agente de navegação por web AI que pode encontrar informações para você",
"startUrl": "startUrl",
"Goal": "Meta",
"The URL where the browsing task should begin": "O URL onde a tarefa de navegação deve começar",
"The goal or objective of the browsing task": "O objetivo ou objetivo da tarefa de navegação"
}

View File

@@ -0,0 +1,10 @@
{
"Twin Web Agent": "Веб Агент Twin",
"Please use ***your-twin-labs-api-key*** as value for API Key": "Пожалуйста, используйте ***your-twin-labs-api-key*** в качестве значения ключа API",
"Browse": "Обзор",
"Browse the internet with an AI web navigation agent that can find information for you": "Просмотреть интернет с агентом ИИ веб-навигации, который может найти информацию для вас",
"startUrl": "startUrl",
"Goal": "Цель",
"The URL where the browsing task should begin": "URL-адрес, в котором должна начинаться задача просмотра",
"The goal or objective of the browsing task": "Цель или цель браузерной задачи"
}

View File

@@ -0,0 +1,9 @@
{
"Please use ***your-twin-labs-api-key*** as value for API Key": "Please use ***your-twin-labs-api-key*** as value for API Key",
"Browse": "Browse",
"Browse the internet with an AI web navigation agent that can find information for you": "Browse the internet with an AI web navigation agent that can find information for you",
"startUrl": "startUrl",
"Goal": "Goal",
"The URL where the browsing task should begin": "The URL where the browsing task should begin",
"The goal or objective of the browsing task": "The goal or objective of the browsing task"
}

View File

@@ -0,0 +1,10 @@
{
"Twin Web Agent": "Twin Web Agent",
"Please use ***your-twin-labs-api-key*** as value for API Key": "Please use ***your-twin-labs-api-key*** as value for API Key",
"Browse": "Browse",
"Browse the internet with an AI web navigation agent that can find information for you": "Browse the internet with an AI web navigation agent that can find information for you",
"startUrl": "startUrl",
"Goal": "Mục tiêu",
"The URL where the browsing task should begin": "The URL where the browsing task should begin",
"The goal or objective of the browsing task": "The goal or objective of the browsing task"
}

View File

@@ -0,0 +1,9 @@
{
"Please use ***your-twin-labs-api-key*** as value for API Key": "Please use ***your-twin-labs-api-key*** as value for API Key",
"Browse": "Browse",
"Browse the internet with an AI web navigation agent that can find information for you": "Browse the internet with an AI web navigation agent that can find information for you",
"startUrl": "startUrl",
"Goal": "Goal",
"The URL where the browsing task should begin": "The URL where the browsing task should begin",
"The goal or objective of the browsing task": "The goal or objective of the browsing task"
}

View File

@@ -0,0 +1,20 @@
import { createPiece, PieceAuth } from "@activepieces/pieces-framework";
import { startBrowsingTask } from "./lib/actions/start-browsing-task";
export const twinLabsAuth = PieceAuth.SecretText({
displayName:'API Key',
required:true,
description:"Please use ***your-twin-labs-api-key*** as value for API Key"
});
export const twinLabs = createPiece({
displayName: "Twin Web Agent",
auth: twinLabsAuth,
minimumSupportedRelease: '0.20.0',
logoUrl: "https://cdn.activepieces.com/pieces/twin-labs.png",
authors: [],
actions: [startBrowsingTask],
triggers: [],
});

View File

@@ -0,0 +1,93 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
import { twinLabsAuth } from '../..';
// API BASE URL
const API_BASE_URL = 'https://paris.prod.api.twin.so';
export const startBrowsingTask = createAction({
name: 'startBrowsingTask',
auth: twinLabsAuth,
displayName: 'Browse',
description:
'Browse the internet with an AI web navigation agent that can find information for you',
props: {
startUrl: Property.ShortText({
displayName: 'startUrl',
required: true,
description: 'The URL where the browsing task should begin',
defaultValue: '',
}),
goal: Property.ShortText({
displayName: 'Goal',
required: true,
description: 'The goal or objective of the browsing task',
}),
},
async run(context) {
// Interface for the initial /browse
interface BrowseStartResponse {
url: string;
universeId: string;
worldId: number;
unitId: number;
}
// Interface for the GET polling
interface BrowseStatusResponse {
completed : boolean;
pending?: boolean;
output?: string;
}
// Start the browsing task
const startRes = await httpClient.sendRequest<BrowseStartResponse>({
method: HttpMethod.POST,
url: `${API_BASE_URL}/browse`,
headers: {
'x-api-key': context.auth.secret_text,
'Content-Type': 'application/json',
},
body: {
goal: context.propsValue['goal'],
startUrl: context.propsValue['startUrl'],
outputType: 'string',
completionCallbackUrl: 'https://',
},
});
const pollingUrl = startRes.body.url;
let statusResponse: BrowseStatusResponse = {
completed: false,
pending: true,
};
const timeoutAt = Date.now() + 15 * 60 * 1000; // 15 minutes
// Poll for task completion every 5 seconds until timeout
while (!statusResponse.completed && Date.now() < timeoutAt) {
await new Promise((resolve) => setTimeout(resolve, 5_000)); // wait 5 seconds
const pollRes = await httpClient.sendRequest<BrowseStatusResponse>({
method: HttpMethod.GET,
url: pollingUrl,
headers: {
'x-api-key': context.auth.secret_text,
'Content-Type': 'application/json',
},
});
statusResponse = pollRes.body; // update statusResponse with the latest response
}
// Return the final statusResponse in all cases
return statusResponse;
},
});

View File

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

View File

@@ -0,0 +1,11 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs",
"outDir": "../../../../dist/out-tsc",
"declaration": true,
"types": ["node"]
},
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"],
"include": ["src/**/*.ts"]
}