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:
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"extends": [
|
||||
"../../../../.eslintrc.base.json"
|
||||
],
|
||||
"ignorePatterns": [
|
||||
"!**/*"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"*.ts",
|
||||
"*.tsx",
|
||||
"*.js",
|
||||
"*.jsx"
|
||||
],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"*.ts",
|
||||
"*.tsx"
|
||||
],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"*.js",
|
||||
"*.jsx"
|
||||
],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# pieces-phantombuster
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Building
|
||||
|
||||
Run `nx build pieces-phantombuster` to build the library.
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "@activepieces/piece-phantombuster",
|
||||
"version": "0.0.1",
|
||||
"type": "commonjs",
|
||||
"main": "./src/index.js",
|
||||
"types": "./src/index.d.ts",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"name": "pieces-phantombuster",
|
||||
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "packages/pieces/community/phantombuster/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/phantombuster",
|
||||
"tsConfig": "packages/pieces/community/phantombuster/tsconfig.lib.json",
|
||||
"packageJson": "packages/pieces/community/phantombuster/package.json",
|
||||
"main": "packages/pieces/community/phantombuster/src/index.ts",
|
||||
"assets": [
|
||||
"packages/pieces/community/phantombuster/*.md",
|
||||
{
|
||||
"input": "packages/pieces/community/phantombuster/src/i18n",
|
||||
"output": "./src/i18n",
|
||||
"glob": "**/!(i18n.json)"
|
||||
}
|
||||
],
|
||||
"buildableProjectDepsInPackageJsonType": "dependencies",
|
||||
"updateBuildableProjectDepsInPackageJson": true
|
||||
},
|
||||
"dependsOn": [
|
||||
"prebuild",
|
||||
"^build"
|
||||
]
|
||||
},
|
||||
"nx-release-publish": {
|
||||
"options": {
|
||||
"packageRoot": "dist/{projectRoot}"
|
||||
}
|
||||
},
|
||||
"prebuild": {
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
],
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"cwd": "packages/pieces/community/phantombuster",
|
||||
"command": "bun install --no-save --silent"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/eslint:lint",
|
||||
"outputs": [
|
||||
"{options.outputFile}"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Automate your web scraping and web automation tasks": "Automate your web scraping and web automation tasks",
|
||||
"\n1. Login to your Pushbullet Dashboard.\n2. Go to **https://phantombuster.com/workspace-settings**.\n3. change to the **API Keys** tab and **Add API Key**.\n4. Copy the API Key to the clipboard and paste it.\n": "\n1. Login to your Pushbullet Dashboard.\n2. Go to **https://phantombuster.com/workspace-settings**.\n3. change to the **API Keys** tab and **Add API Key**.\n4. Copy the API Key to the clipboard and paste it.\n",
|
||||
"Launch Phantom": "Launch Phantom",
|
||||
"Custom API Call": "Custom API Call",
|
||||
"Launch a Phantombuster agent and add it to the execution queue": "Launch a Phantombuster agent and add it to the execution queue",
|
||||
"Make a custom API call to a specific endpoint": "Make a custom API call to a specific endpoint",
|
||||
"Agent": "Agent",
|
||||
"Wait for Output": "Wait for Output",
|
||||
"Argument": "Argument",
|
||||
"Max Retries": "Max Retries",
|
||||
"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 the Phantombuster agent to use": "Select the Phantombuster agent to use",
|
||||
"Wait until the agent execution completes and output is available": "Wait until the agent execution completes and output is available",
|
||||
"Optional argument to pass to the agent": "Optional argument to pass to the agent",
|
||||
"Maximum number of retries if the agent fails": "Maximum number of retries if the agent fails",
|
||||
"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",
|
||||
"New Output": "New Output",
|
||||
"Trigger when a new output is generated by a Phantombuster agent ": "Trigger when a new output is generated by a Phantombuster agent ",
|
||||
"Markdown": "Markdown",
|
||||
"## Webhook Configuration\n\nTo use this trigger, you need to configure a webhook URL in your Phantombuster agent:\n\n### Steps to Configure:\n1. Go to your Phantombuster agent page\n2. Click on **Settings**\n3. Select **Advanced Notification Settings**\n4. Input the webhook URL in the webhook field:\n```\n{{webhookUrl}}\n```\n\n### Supported Exit Messages (Status Values):\n- `finished` - Agent completed successfully (exitCode: 0)\n- `killed` - Agent was killed\n- `global timeout` - Global timeout reached\n- `org timeout` - ": "## Webhook Configuration\n\nTo use this trigger, you need to configure a webhook URL in your Phantombuster agent:\n\n### Steps to Configure:\n1. Go to your Phantombuster agent page\n2. Click on **Settings**\n3. Select **Advanced Notification Settings**\n4. Input the webhook URL in the webhook field:\n```\n{{webhookUrl}}\n```\n\n### Supported Exit Messages (Status Values):\n- `finished` - Agent completed successfully (exitCode: 0)\n- `killed` - Agent was killed\n- `global timeout` - Global timeout reached\n- `org timeout` - Organization timeout reached\n- `agent timeout` - Agent timeout reached\n- `unknown` - Unknown status\n\n### Important Notes:\n- Maximum webhook timeout: 11 seconds\n- Webhooks accept POST requests only\n- Maximum 2 redirections allowed\n- Response body is ignored (you don't need to respond with data)\n\n[Learn more about webhooks](https://hub.phantombuster.com/docs/using-webhooks)"
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { createPiece, PieceAuth } from '@activepieces/pieces-framework';
|
||||
import { launchPhantom } from './lib/actions/launch-phantom';
|
||||
import { phantombusterAuth } from './lib/common/auth';
|
||||
import { newOutput } from './lib/triggers/new-output';
|
||||
import { createCustomApiCallAction } from '@activepieces/pieces-common';
|
||||
import { BASE_URL } from './lib/common/client';
|
||||
import { PieceCategory } from '@activepieces/shared';
|
||||
|
||||
export const phantombuster = createPiece({
|
||||
displayName: 'PhantomBuster',
|
||||
auth: phantombusterAuth,
|
||||
minimumSupportedRelease: '0.36.1',
|
||||
logoUrl: 'https://cdn.activepieces.com/pieces/phantombuster.png',
|
||||
description: 'Automate your web scraping and web automation tasks',
|
||||
categories: [PieceCategory.MARKETING, PieceCategory.SALES_AND_CRM],
|
||||
authors: ['sanket-a11y'],
|
||||
actions: [
|
||||
launchPhantom,
|
||||
createCustomApiCallAction({
|
||||
auth: phantombusterAuth,
|
||||
baseUrl: () => BASE_URL,
|
||||
authMapping: async (auth) => {
|
||||
const apiKey = auth;
|
||||
return {
|
||||
'X-Phantombuster-Key': `${apiKey.secret_text}`,
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
},
|
||||
}),
|
||||
],
|
||||
triggers: [newOutput],
|
||||
});
|
||||
@@ -0,0 +1,94 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { phantombusterAuth } from '../common/auth';
|
||||
import { makeRequest } from '../common/client';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { agentIdDropdown } from '../common/props';
|
||||
import { outside } from 'semver';
|
||||
|
||||
export const launchPhantom = createAction({
|
||||
auth: phantombusterAuth,
|
||||
name: 'launchPhantom',
|
||||
displayName: 'Launch Phantom',
|
||||
description: 'Launch a Phantombuster agent and add it to the execution queue',
|
||||
props: {
|
||||
agentId: agentIdDropdown,
|
||||
waitForOutput: Property.Checkbox({
|
||||
displayName: 'Wait for Output',
|
||||
description:
|
||||
'Wait until the agent execution completes and output is available',
|
||||
required: false,
|
||||
defaultValue: false,
|
||||
}),
|
||||
argument: Property.Object({
|
||||
displayName: 'Argument',
|
||||
description: 'Optional argument to pass to the agent',
|
||||
required: false,
|
||||
}),
|
||||
maxRetries: Property.Number({
|
||||
displayName: 'Max Retries',
|
||||
description: 'Maximum number of retries if the agent fails',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
const body: Record<string, unknown> = {
|
||||
id: context.propsValue.agentId,
|
||||
};
|
||||
|
||||
if (context.propsValue.argument !== undefined) {
|
||||
body['argument'] = context.propsValue.argument;
|
||||
}
|
||||
|
||||
if (context.propsValue.maxRetries !== undefined) {
|
||||
body['maxRetries'] = context.propsValue.maxRetries;
|
||||
}
|
||||
|
||||
const launchResponse = await makeRequest(
|
||||
context.auth,
|
||||
HttpMethod.POST,
|
||||
'/agents/launch',
|
||||
body
|
||||
);
|
||||
|
||||
if (context.propsValue.waitForOutput) {
|
||||
let output = null;
|
||||
const pollInterval = 5000; // 5 seconds
|
||||
|
||||
let isCompleted = false;
|
||||
|
||||
while (!isCompleted) {
|
||||
try {
|
||||
const outputResponse = await makeRequest(
|
||||
context.auth,
|
||||
HttpMethod.GET,
|
||||
`/agents/fetch-output?id=${context.propsValue.agentId}`,
|
||||
undefined
|
||||
);
|
||||
console.log(outputResponse);
|
||||
if (outputResponse) {
|
||||
output = outputResponse;
|
||||
const status = outputResponse.status;
|
||||
|
||||
if (
|
||||
status === 'finished' ||
|
||||
status === 'error' ||
|
||||
status === 'unknown'
|
||||
) {
|
||||
isCompleted = true;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// Continue polling if output is not yet available
|
||||
}
|
||||
|
||||
if (!isCompleted) {
|
||||
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
return launchResponse;
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { PieceAuth } from '@activepieces/pieces-framework';
|
||||
import { makeRequest } from './client';
|
||||
import { AppConnectionType } from '@activepieces/shared';
|
||||
|
||||
const authHelpDescription = `
|
||||
1. Login to your Pushbullet Dashboard.
|
||||
2. Go to **https://phantombuster.com/workspace-settings**.
|
||||
3. change to the **API Keys** tab and **Add API Key**.
|
||||
4. Copy the API Key to the clipboard and paste it.
|
||||
`;
|
||||
|
||||
export const phantombusterAuth = PieceAuth.SecretText({
|
||||
displayName: 'API Token',
|
||||
description: authHelpDescription,
|
||||
required: true,
|
||||
validate: async (auth) => {
|
||||
try {
|
||||
await makeRequest(
|
||||
{ secret_text: auth.auth, type: AppConnectionType.SECRET_TEXT },
|
||||
HttpMethod.GET,
|
||||
'/agents/fetch-all'
|
||||
);
|
||||
return { valid: true };
|
||||
} catch (e) {
|
||||
return {
|
||||
valid: false,
|
||||
error: 'Invalid API key or insufficient permissions',
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
import { HttpMethod, httpClient } from '@activepieces/pieces-common';
|
||||
import { AppConnectionValueForAuthProperty } from '@activepieces/pieces-framework';
|
||||
import { phantombusterAuth } from './auth';
|
||||
|
||||
export const BASE_URL = `https://api.phantombuster.com/api/v2`;
|
||||
|
||||
export async function makeRequest(
|
||||
apiKey: AppConnectionValueForAuthProperty<typeof phantombusterAuth>,
|
||||
method: HttpMethod,
|
||||
path: string,
|
||||
body?: unknown
|
||||
) {
|
||||
try {
|
||||
console.log( `${BASE_URL}${path}`)
|
||||
const response = await httpClient.sendRequest({
|
||||
method,
|
||||
url: `${BASE_URL}${path}`,
|
||||
headers: {
|
||||
'X-Phantombuster-Key': `${apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body,
|
||||
});
|
||||
return response.body;
|
||||
} catch (error: any) {
|
||||
throw new Error(`Unexpected error: ${error.message || String(error)}`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { Property } from '@activepieces/pieces-framework';
|
||||
import { makeRequest } from './client';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { phantombusterAuth } from './auth';
|
||||
|
||||
export const agentIdDropdown = Property.Dropdown({
|
||||
auth: phantombusterAuth,
|
||||
displayName: 'Agent',
|
||||
description: 'Select the Phantombuster agent to use',
|
||||
required: true,
|
||||
refreshers: [],
|
||||
options: async ({ auth }) => {
|
||||
if (!auth) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
placeholder: 'Please configure the authentication first',
|
||||
};
|
||||
}
|
||||
const response = await makeRequest(
|
||||
auth,
|
||||
HttpMethod.GET,
|
||||
'/agents/fetch-all'
|
||||
);
|
||||
|
||||
return {
|
||||
disabled: false,
|
||||
options: response.map((agent: any) => ({
|
||||
label: agent.name + ' ' + agent.createdAt,
|
||||
value: agent.id,
|
||||
})),
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,79 @@
|
||||
import {
|
||||
createTrigger,
|
||||
Property,
|
||||
TriggerStrategy,
|
||||
} from '@activepieces/pieces-framework';
|
||||
import { phantombusterAuth } from '../common/auth';
|
||||
import { agentIdDropdown } from '../common/props';
|
||||
import { MarkdownVariant } from '@activepieces/shared';
|
||||
|
||||
export const newOutput = createTrigger({
|
||||
auth: phantombusterAuth,
|
||||
name: 'newOutput',
|
||||
displayName: 'New Output',
|
||||
description:
|
||||
'Trigger when a new output is generated by a Phantombuster agent ',
|
||||
props: {
|
||||
agentId: agentIdDropdown,
|
||||
markdown: Property.MarkDown({
|
||||
variant: MarkdownVariant.INFO,
|
||||
value: `## Webhook Configuration
|
||||
|
||||
To use this trigger, you need to configure a webhook URL in your Phantombuster agent:
|
||||
|
||||
### Steps to Configure:
|
||||
1. Go to your Phantombuster agent page
|
||||
2. Click on **Settings**
|
||||
3. Select **Advanced Notification Settings**
|
||||
4. Input the webhook URL in the webhook field:
|
||||
\`\`\`
|
||||
{{webhookUrl}}
|
||||
\`\`\`
|
||||
|
||||
### Supported Exit Messages (Status Values):
|
||||
- \`finished\` - Agent completed successfully (exitCode: 0)
|
||||
- \`killed\` - Agent was killed
|
||||
- \`global timeout\` - Global timeout reached
|
||||
- \`org timeout\` - Organization timeout reached
|
||||
- \`agent timeout\` - Agent timeout reached
|
||||
- \`unknown\` - Unknown status
|
||||
|
||||
### Important Notes:
|
||||
- Maximum webhook timeout: 11 seconds
|
||||
- Webhooks accept POST requests only
|
||||
- Maximum 2 redirections allowed
|
||||
- Response body is ignored (you don't need to respond with data)
|
||||
|
||||
[Learn more about webhooks](https://hub.phantombuster.com/docs/using-webhooks)`,
|
||||
}),
|
||||
},
|
||||
sampleData: {
|
||||
agentId: '5027055349780535',
|
||||
agentName: 'Test Script',
|
||||
containerId: '3358014727012763',
|
||||
script: 'test_script_50516785467.js',
|
||||
scriptOrg: 'phantombuster',
|
||||
branch: 'test-branch-0545107204',
|
||||
launchDuration: 121,
|
||||
runDuration: 1850,
|
||||
resultObject: {},
|
||||
exitMessage: 'finished',
|
||||
exitCode: 0,
|
||||
},
|
||||
type: TriggerStrategy.WEBHOOK,
|
||||
async onEnable(context) {
|
||||
// implement webhook creation logic
|
||||
},
|
||||
async onDisable(context) {
|
||||
// implement webhook deletion logic
|
||||
},
|
||||
async run(context) {
|
||||
const output = context.payload.body as any;
|
||||
console.log(output);
|
||||
if (output.agentId != context.propsValue.agentId) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [output];
|
||||
},
|
||||
});
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../../dist/out-tsc",
|
||||
"declaration": true,
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
Reference in New Issue
Block a user