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,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;
},
});

View File

@@ -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',
};
}
},
});

View File

@@ -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)}`);
}
}

View File

@@ -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,
})),
};
},
});

View File

@@ -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];
},
});