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,120 @@
import { Property, createAction } from '@activepieces/pieces-framework';
import { createClient, ExtendedReadableStream } from '../common';
import { elevenlabsAuth } from '../..';
export const textToSpeech = createAction({
description: 'Convert text to speech using Elevenlabs',
displayName: 'Text to Speech',
name: 'elevenlabs-text-to-speech',
auth: elevenlabsAuth,
props: {
model: Property.Dropdown({
auth: elevenlabsAuth,
displayName: 'Model',
required: false,
refreshers: [],
refreshOnSearch: false,
options: async ({ auth }) => {
const apiAuth = auth
if (!apiAuth) {
return {
disabled: true,
placeholder: 'Enter your API key first',
options: [],
};
}
try {
const elevenlabs = createClient(apiAuth);
const models = await elevenlabs.models.list()
return {
disabled: false,
placeholder: 'Default model',
options: models.map((template) => {
return {
// there are models with the same name
label: `${template.name} (${template.modelId})`,
value: template.modelId,
};
}),
};
} catch (error) {
return {
disabled: true,
options: [],
placeholder: "Couldn't load models, check your API key.",
};
}
},
}),
voice: Property.Dropdown({
auth: elevenlabsAuth,
displayName: 'Voice',
required: true,
description: 'Select the voice for the text to speech',
refreshers: [],
refreshOnSearch: false,
options: async ({ auth }) => {
const apiAuth = auth
if (!apiAuth) {
return {
disabled: true,
placeholder: 'Enter your API key first',
options: [],
};
}
try {
const elevenlabs = createClient(apiAuth);
const response = await elevenlabs.voices.getAll()
return {
disabled: false,
options: response.voices.map((template) => {
return {
label: `${template.name}`,
value: template.voiceId,
};
}),
};
} catch (error) {
return {
disabled: true,
options: [],
placeholder: "Couldn't load voices, check your API key.",
};
}
},
}),
text: Property.LongText({
displayName: 'Text',
required: true,
description: 'The text to convert to speech',
}),
},
async run({ auth, propsValue, files }) {
const elevenlabs = createClient(auth);
const audioStream = await elevenlabs.textToSpeech.stream(
propsValue.voice,
{
modelId: propsValue.model || undefined,
text: propsValue.text,
}
// node implementation of ReadableStream<Uint8Array> has asyncInterator
) as ExtendedReadableStream<Buffer>;
const chunks: Buffer[] = [];
for await (const chunk of audioStream) {
chunks.push(chunk);
}
return files.write({
fileName: 'audio.mp3',
data: Buffer.concat(chunks),
});
},
});

View File

@@ -0,0 +1,39 @@
import { ElevenLabsClient } from '@elevenlabs/elevenlabs-js';
import { ElevenLabsEnvironment } from '@elevenlabs/elevenlabs-js/environments';
import { AppConnectionValueForAuthProperty } from '@activepieces/pieces-framework';
import { elevenlabsAuth } from '..';
export type ElevenResidency = 'default' | 'us' | 'eu';
export interface ExtendedReadableStream<R> extends ReadableStream {
[Symbol.asyncIterator](): AsyncIterableIterator<R>;
}
export const ELEVEN_RESIDENCY: Record<ElevenResidency, ElevenLabsEnvironment> = {
default: ElevenLabsEnvironment.Production,
us: ElevenLabsEnvironment.ProductionUs,
eu: ElevenLabsEnvironment.ProductionEu,
};
// get API key with backward compatibility:
// new format is object { apiKey: '', region: '' }
// old format is a secret that is deserealised as an object
export const getApiKey = (auth: AppConnectionValueForAuthProperty<typeof elevenlabsAuth> | string): string => {
if (typeof auth === 'string') {
return auth;
}
return auth?.props.apiKey ?? Object.values(auth.props).join('');
}
export const getRegionApiUrl = (region?: ElevenResidency) => {
return ELEVEN_RESIDENCY[region ?? 'default'].base;
}
export const createClient = (auth: AppConnectionValueForAuthProperty<typeof elevenlabsAuth>) => {
return new ElevenLabsClient({
apiKey: `${getApiKey(auth)}`,
environment: ELEVEN_RESIDENCY[auth.props.region ?? 'default'],
});
}