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,29 @@
import { propsValidation } from '@activepieces/pieces-common';
import { createAction } from '@activepieces/pieces-framework';
import { vlmRunAuth, vlmRunCommon } from '../common';
import { analyzeAudioProperties } from '../common/properties';
import { analyzeAudioSchema } from '../common/schemas';
export const analyzeAudio = createAction({
auth: vlmRunAuth,
name: 'analyzeAudio',
displayName: 'Analyze Audio',
description: 'Process an audio file, extracting features or transcription',
props: analyzeAudioProperties,
async run({ auth: apiKey, propsValue }) {
await propsValidation.validateZod(propsValue, analyzeAudioSchema);
const uploadResponse = await vlmRunCommon.uploadFile({
apiKey:apiKey.secret_text,
file: propsValue.audio,
});
const response = await vlmRunCommon.analyzeAudio({
apiKey:apiKey.secret_text,
file_id: uploadResponse.id,
});
return await vlmRunCommon.getresponse(apiKey.secret_text, response.id, response.status);
},
});

View File

@@ -0,0 +1,32 @@
import { propsValidation } from '@activepieces/pieces-common';
import { createAction } from '@activepieces/pieces-framework';
import { vlmRunAuth, vlmRunCommon } from '../common';
import { analyzeDocumentProperties } from '../common/properties';
import { analyzeDocumentSchema } from '../common/schemas';
export const analyzeDocument = createAction({
auth: vlmRunAuth,
name: 'analyzeDocument',
displayName: 'Analyze Document',
description:
'Process a document (PDF, DOCX, etc.), extracting structured data or text.',
props: analyzeDocumentProperties,
async run({ auth: apiKey, propsValue }) {
await propsValidation.validateZod(propsValue, analyzeDocumentSchema);
const { document, domain } = propsValue;
const uploadResponse = await vlmRunCommon.uploadFile({
apiKey:apiKey.secret_text,
file: document,
});
const response = await vlmRunCommon.analyzeDocument({
apiKey:apiKey.secret_text,
file_id: uploadResponse.id,
domain,
});
return await vlmRunCommon.getresponse(apiKey.secret_text, response.id, response.status);
},
});

View File

@@ -0,0 +1,28 @@
import { propsValidation } from '@activepieces/pieces-common';
import { createAction } from '@activepieces/pieces-framework';
import { vlmRunAuth, vlmRunCommon } from '../common';
import { analyzeImageProperties } from '../common/properties';
import { analyzeImageSchema } from '../common/schemas';
export const analyzeImage = createAction({
auth: vlmRunAuth,
name: 'analyzeImage',
displayName: 'Analyze Image',
description:
'Process an image (file or URL), extracting descriptions, detecting objects, etc.',
props: analyzeImageProperties,
async run({ auth: apiKey, propsValue }) {
await propsValidation.validateZod(propsValue, analyzeImageSchema);
const { image, domain } = propsValue;
const response = await vlmRunCommon.analyzeImage({
apiKey:apiKey.secret_text,
images: [image],
domain,
});
return await vlmRunCommon.getresponse(apiKey.secret_text, response.id, response.status);
},
});

View File

@@ -0,0 +1,32 @@
import { propsValidation } from '@activepieces/pieces-common';
import { createAction } from '@activepieces/pieces-framework';
import { vlmRunAuth, vlmRunCommon } from '../common';
import { analyzeVideoProperties } from '../common/properties';
import { analyzeVideoSchema } from '../common/schemas';
export const analyzeVideo = createAction({
auth: vlmRunAuth,
name: 'analyzeVideo',
displayName: 'Analyze Video',
description:
'Analyze a video file or URL, e.g. extract frames, detect content, etc.',
props: analyzeVideoProperties,
async run({ auth: apiKey, propsValue }) {
await propsValidation.validateZod(propsValue, analyzeVideoSchema);
const { video, domain } = propsValue;
const uploadResponse = await vlmRunCommon.uploadFile({
apiKey:apiKey.secret_text,
file: video,
});
const response = await vlmRunCommon.analyzeVideo({
apiKey:apiKey.secret_text,
file_id: uploadResponse.id,
domain,
});
return await vlmRunCommon.getresponse(apiKey.secret_text, response.id, response.status);
},
});

View File

@@ -0,0 +1,17 @@
import { propsValidation } from '@activepieces/pieces-common';
import { createAction } from '@activepieces/pieces-framework';
import { vlmRunAuth, vlmRunCommon } from '../common';
import { getFileProperties } from '../common/properties';
import { getFileSchema } from '../common/schemas';
export const getFile = createAction({
auth: vlmRunAuth,
name: 'getFile',
displayName: 'Get File',
description: "Gets file's metadata by ID.",
props: getFileProperties,
async run({ auth: apiKey, propsValue }) {
await propsValidation.validateZod(propsValue, getFileSchema);
return await vlmRunCommon.getFile({ apiKey: apiKey.secret_text, file_id: propsValue.fileId });
},
});

View File

@@ -0,0 +1,132 @@
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
import { PieceAuth } from '@activepieces/pieces-framework';
import FormData from 'form-data';
import {
AnalyzeAudioParams,
AnalyzeDocumentParams,
AnalyzeImageParams,
AnalyzeVideoParams,
FileResponse,
GetFileParams,
PredictionResponse,
UploadFileParams,
} from './types';
export const vlmRunAuth = PieceAuth.SecretText({
displayName: 'API Key',
description: 'Your VLM Run API Key',
required: true,
});
export const vlmRunCommon = {
baseUrl: 'https://api.vlm.run/v1',
baseHeaders: (apiKey: string) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${apiKey}`,
}),
endpoints: {
uploadFile: '/files',
listFiles: '/files',
analyzeAudio: '/audio/generate',
analyzeImage: '/image/generate',
analyzeDocument: '/document/generate',
analyzeVideo: '/video/generate',
getresponse: '/predictions',
getFile: (fileId: string) => `/files/${fileId}`,
},
// Methods
getresponse: async (apiKey: string, requestId: string, status: string) => {
let statusnow = status;
const timeoutAt = Date.now() + 5 * 60 * 1000;
while (statusnow !== 'completed' && Date.now() < timeoutAt) {
await new Promise((resolve) => setTimeout(resolve, 5000));
const pollRes = await httpClient.sendRequest<any>({
method: HttpMethod.GET,
url: `${vlmRunCommon.baseUrl}${vlmRunCommon.endpoints.getresponse}/${requestId}`,
headers: vlmRunCommon.baseHeaders(apiKey),
});
statusnow = pollRes.body?.status;
console.log('first', statusnow);
if (statusnow === 'completed') {
return pollRes.body.response;
}
}
throw new Error('generation timed out or failed.');
},
uploadFile: async ({ apiKey, file }: UploadFileParams) => {
const formData = new FormData();
formData.append('file', file.data, { filename: file.filename });
const response = await httpClient.sendRequest<FileResponse>({
method: HttpMethod.POST,
url: `${vlmRunCommon.baseUrl}${vlmRunCommon.endpoints.uploadFile}`,
headers: {
...vlmRunCommon.baseHeaders(apiKey),
'Content-Type': 'multipart/form-data',
},
body: formData,
});
return response.body;
},
listFiles: async (apiKey: string) => {
const response = await httpClient.sendRequest<FileResponse[]>({
method: HttpMethod.GET,
url: `${vlmRunCommon.baseUrl}${vlmRunCommon.endpoints.listFiles}`,
headers: vlmRunCommon.baseHeaders(apiKey),
});
return response.body;
},
analyzeAudio: async ({ apiKey, ...params }: AnalyzeAudioParams) => {
const response = await httpClient.sendRequest<PredictionResponse>({
method: HttpMethod.POST,
url: `${vlmRunCommon.baseUrl}${vlmRunCommon.endpoints.analyzeAudio}`,
headers: vlmRunCommon.baseHeaders(apiKey),
body: {
domain: 'audio.transcription',
...params,
},
});
return response.body;
},
analyzeImage: async ({ apiKey, ...params }: AnalyzeImageParams) => {
const response = await httpClient.sendRequest<PredictionResponse>({
method: HttpMethod.POST,
url: `${vlmRunCommon.baseUrl}${vlmRunCommon.endpoints.analyzeImage}`,
headers: vlmRunCommon.baseHeaders(apiKey),
body: params,
});
return response.body;
},
analyzeDocument: async ({ apiKey, ...params }: AnalyzeDocumentParams) => {
const response = await httpClient.sendRequest<PredictionResponse>({
method: HttpMethod.POST,
url: `${vlmRunCommon.baseUrl}${vlmRunCommon.endpoints.analyzeDocument}`,
headers: vlmRunCommon.baseHeaders(apiKey),
body: params,
});
return response.body;
},
analyzeVideo: async ({ apiKey, ...params }: AnalyzeVideoParams) => {
const response = await httpClient.sendRequest<PredictionResponse>({
method: HttpMethod.POST,
url: `${vlmRunCommon.baseUrl}${vlmRunCommon.endpoints.analyzeVideo}`,
headers: vlmRunCommon.baseHeaders(apiKey),
body: params,
});
return response.body;
},
getFile: async ({ apiKey, file_id }: GetFileParams) => {
const response = await httpClient.sendRequest<FileResponse>({
method: HttpMethod.GET,
url: `${vlmRunCommon.baseUrl}${vlmRunCommon.endpoints.getFile(file_id)}`,
headers: vlmRunCommon.baseHeaders(apiKey),
});
return response.body;
},
};

View File

@@ -0,0 +1,118 @@
import { Property } from '@activepieces/pieces-framework';
import { vlmRunAuth, vlmRunCommon } from '.';
// Action Properties
export const analyzeAudioProperties = {
audio: Property.File({
displayName: 'Audio File',
description: 'The audio file to get the transcription from. Only MP3 supported.',
required: true,
}),
};
export const analyzeImageProperties = {
image: Property.ShortText({
displayName: 'Image File',
description: 'The image file to be analyzed.',
required: true,
}),
domain: Property.StaticDropdown({
displayName: 'Domain',
description: 'The specific analysis domain for the image.',
required: true,
options: {
options: [
{ label: 'Image Classification', value: 'image.classification' },
{ label: 'Image Caption', value: 'image.caption' },
{ label: 'Image TV News', value: 'image.tv-news' },
{ label: 'Image Q and A', value: 'image.q-and-a' },
],
},
}),
};
export const analyzeDocumentProperties = {
document: Property.File({
displayName: 'Document File',
description: 'The document file to be analyzed.',
required: true,
}),
domain: Property.StaticDropdown({
displayName: 'Domain',
description: 'The specific analysis domain for the document.',
required: true,
options: {
options: [
{ label: 'Bank Statement', value: 'document.bank-statement' },
{ label: 'Document Classification', value: 'document.classification' },
{ label: 'Invoice', value: 'document.invoice' },
{ label: 'Markdown', value: 'document.markdown' },
{ label: 'Q and A', value: 'document.q-and-a' },
{ label: 'Receipt', value: 'document.receipt' },
{ label: 'Resume', value: 'document.resume' },
{ label: "US Driver's License", value: 'document.us-drivers-license' },
{ label: 'Utility Bill', value: 'document.utility-bill' },
],
},
}),
};
export const analyzeVideoProperties = {
video: Property.File({
displayName: 'Video File',
description: 'The video file to be analyzed.',
required: true,
}),
domain: Property.StaticDropdown({
displayName: 'Domain',
description: 'The specific analysis domain for the video.',
required: true,
options: {
options: [
{ label: 'Video Transcription', value: 'video.transcription' },
{
label: 'Video Transcription Summary',
value: 'video.transcription-summary',
},
{
label: 'Video Product Demo Summary',
value: 'video.product-demo-summary',
},
{
label: 'Video Conferencing Summary',
value: 'video.conferencing-summary',
},
{ label: 'Video Podcast Summary', value: 'video.podcast-summary' },
{ label: 'Video Summary', value: 'video.summary' },
{ label: 'Video Dashcam Analytics', value: 'video.dashcam-analytics' },
],
},
}),
};
export const getFileProperties = {
fileId: Property.Dropdown({
auth: vlmRunAuth,
displayName: 'File',
description: 'Select a file to retrieve its details.',
required: true,
refreshers: ['auth'],
options: async ({ auth }) => {
if (!auth) {
return {
disabled: true,
placeholder: 'Connect your VLM Run account to see files',
options: [],
};
}
const files = await vlmRunCommon.listFiles(auth.secret_text);
return {
disabled: false,
options: files.map((file) => ({
label: file.filename,
value: file.id,
})),
};
},
}),
};

View File

@@ -0,0 +1,59 @@
import z from 'zod';
const imageDomains = [
'image.classification',
'image.caption',
'image.tv-news',
'image.q-and-a',
] as const;
const documentDomains = [
'document.bank-statement',
'document.classification',
'document.invoice',
'document.markdown',
'document.q-and-a',
'document.receipt',
'document.resume',
'document.us-drivers-license',
'document.utility-bill',
] as const;
const videoDomains = [
'video.transcription',
'video.transcription-summary',
'video.product-demo-summary',
'video.conferencing-summary',
'video.podcast-summary',
'video.summary',
'video.dashcam-analytics'
] as const;
const apFileSchema = z.object({
filename: z.string(),
data: z.instanceof(Buffer),
extension: z.string()
});
// Schemas
export const analyzeAudioSchema = {
audio: apFileSchema,
};
export const analyzeImageSchema = {
image: z.string().url(),
domain: z.enum(imageDomains)
};
export const analyzeDocumentSchema = {
document: apFileSchema,
domain: z.enum(documentDomains)
};
export const analyzeVideoSchema = {
video: apFileSchema,
domain: z.enum(videoDomains)
};
export const getFileSchema = {
fileId: z.string()
};

View File

@@ -0,0 +1,59 @@
import { ApFile } from "@activepieces/pieces-framework";
export interface AuthenticationParams {
apiKey: string;
}
// Request types
export interface UploadFileParams extends AuthenticationParams {
file: ApFile;
}
export interface AnalyzeAudioParams extends AuthenticationParams {
file_id: string;
}
export interface AnalyzeImageParams extends AuthenticationParams {
images: string[];
domain: string;
}
export interface AnalyzeDocumentParams extends AuthenticationParams {
file_id: string;
domain: string;
}
export interface AnalyzeVideoParams extends AuthenticationParams {
file_id: string;
domain: string;
}
export interface GetFileParams extends AuthenticationParams {
file_id: string;
}
// Response types
export interface FileResponse {
id: string;
filename: string;
bytes: number;
purpose: string;
created_at: string;
object: 'file';
}
type JobStatus = string;
interface CreditUsage {
elements_processed?: number;
element_type?: 'image' | 'page' | 'video' | 'audio';
credits_used?: number;
}
export interface PredictionResponse {
id: string;
created_at: string;
completed_at?: string;
response?: any;
status: JobStatus;
message?: string;
usage?: CreditUsage;
}