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-leexi
This library was generated with [Nx](https://nx.dev).
## Building
Run `nx build pieces-leexi` to build the library.

View File

@@ -0,0 +1,10 @@
{
"name": "@activepieces/piece-leexi",
"version": "0.0.1",
"type": "commonjs",
"main": "./src/index.js",
"types": "./src/index.d.ts",
"dependencies": {
"tslib": "^2.3.0"
}
}

View File

@@ -0,0 +1,65 @@
{
"name": "pieces-leexi",
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "packages/pieces/community/leexi/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/leexi",
"tsConfig": "packages/pieces/community/leexi/tsconfig.lib.json",
"packageJson": "packages/pieces/community/leexi/package.json",
"main": "packages/pieces/community/leexi/src/index.ts",
"assets": [
"packages/pieces/community/leexi/*.md",
{
"input": "packages/pieces/community/leexi/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/leexi",
"command": "bun install --no-save --silent"
}
},
"lint": {
"executor": "@nx/eslint:lint",
"outputs": [
"{options.outputFile}"
]
}
}
}

View File

@@ -0,0 +1,32 @@
import { createPiece} from "@activepieces/pieces-framework";
import { PieceCategory } from "@activepieces/shared";
import { getCallAction } from "./lib/actions/get-call";
import { newCallCreatedTrigger } from "./lib/triggers/new-call-created";
import { leexiAuth } from "./lib/common/auth";
import { createCustomApiCallAction } from "@activepieces/pieces-common";
import { BASE_URL } from "./lib/common/constants";
export const leexi = createPiece({
displayName: "Leexi",
description:'AI Notetaker',
categories: [PieceCategory.ARTIFICIAL_INTELLIGENCE, PieceCategory.PRODUCTIVITY],
auth: leexiAuth,
minimumSupportedRelease: '0.36.1',
logoUrl: "https://cdn.activepieces.com/pieces/leexi.png",
authors: ['kishanprmr'],
actions: [getCallAction,
createCustomApiCallAction({
auth:leexiAuth,
baseUrl:()=>BASE_URL,
authMapping:async (auth)=>{
return{
Authorization: `Basic ${Buffer.from(`${auth.username}:${auth.password}`).toString(
'base64'
)}`
}
}
})
],
triggers: [newCallCreatedTrigger],
});

View File

@@ -0,0 +1,32 @@
import { createAction, Property } from "@activepieces/pieces-framework";
import { leexiAuth } from "../common/auth";
import { AuthenticationType, httpClient, HttpMethod } from "@activepieces/pieces-common";
import { BASE_URL } from "../common/constants";
export const getCallAction = createAction({
name: 'get-call',
auth: leexiAuth,
displayName: 'Get Call',
description: 'Gets call details.',
props: {
callId: Property.ShortText({
displayName: 'Call ID',
required: true
})
},
async run(context) {
const { callId } = context.propsValue;
const response = await httpClient.sendRequest({
method: HttpMethod.GET,
url: BASE_URL + `/calls/${callId}`,
authentication: {
type: AuthenticationType.BASIC,
username: context.auth.username,
password: context.auth.password
}
})
return response.body;
}
})

View File

@@ -0,0 +1,38 @@
import { PieceAuth } from "@activepieces/pieces-framework";
import { AuthenticationType, httpClient, HttpMethod } from "@activepieces/pieces-common"
import { BASE_URL } from "./constants";
export const leexiAuth = PieceAuth.BasicAuth({
required: true,
description: `You can obtain Key ID and secret from [API Keys Settings](https://app.leexi.ai/en/settings/api_keys).`,
username: {
displayName: 'Key ID',
},
password: {
displayName: 'Key Secret'
},
validate: async ({ auth }) => {
try {
await httpClient.sendRequest({
method: HttpMethod.GET,
url: BASE_URL + '/users',
authentication: {
type: AuthenticationType.BASIC,
username: auth.username,
password: auth.password
}
})
return {
valid: true
}
}
catch {
return {
valid: false,
error: 'Invalid Credentials'
}
}
}
})

View File

@@ -0,0 +1 @@
export const BASE_URL ='https://public-api.leexi.ai/v1'

View File

@@ -0,0 +1,9 @@
export type ListCallResponse = {
data:Array<{
created_at:string
}>,
pagination:{
page:number,
items:number
}
}

View File

@@ -0,0 +1,86 @@
import { AppConnectionValueForAuthProperty, createTrigger, TriggerStrategy } from "@activepieces/pieces-framework";
import { leexiAuth } from "../common/auth";
import { AuthenticationType, DedupeStrategy, httpClient, HttpMethod, Polling, pollingHelper, QueryParams } from "@activepieces/pieces-common";
import dayjs from 'dayjs';
import { BASE_URL } from "../common/constants";
import { ListCallResponse } from "../common/types";
import { isEmpty } from "@activepieces/shared";
const polling: Polling<AppConnectionValueForAuthProperty<typeof leexiAuth>, Record<string, never>> = {
strategy: DedupeStrategy.TIMEBASED,
async items({ auth, lastFetchEpochMS }) {
const isTestMode = lastFetchEpochMS === 0;
let page = 1;
let hasMore = true;
const calls = [];
do {
const qs: QueryParams = {
page: page.toString(),
items: isTestMode ? '5' : '100',
}
if (!isTestMode) qs['from'] = dayjs(lastFetchEpochMS).toISOString();
const response = await httpClient.sendRequest<ListCallResponse>({
method: HttpMethod.GET,
url: BASE_URL + '/calls',
authentication: {
type: AuthenticationType.BASIC,
username: auth.username,
password: auth.password
}
});
const callList = response.body.data ?? [];
for (const call of callList) {
calls.push(call)
}
if (isTestMode) break;
page++;
hasMore = !isEmpty(callList)
} while (hasMore);
return calls.map((call) => ({
epochMilliSeconds: dayjs(call.created_at).valueOf(),
data: call
}))
}
}
export const newCallCreatedTrigger = createTrigger({
name: 'new-call-created',
auth: leexiAuth,
displayName: 'New Call Created',
description: 'Triggers when a new call is created.',
type: TriggerStrategy.POLLING,
props: {},
async onEnable(context) {
await pollingHelper.onEnable(polling, {
auth: context.auth,
store: context.store,
propsValue: context.propsValue,
});
},
async onDisable(context) {
await pollingHelper.onDisable(polling, {
auth: context.auth,
store: context.store,
propsValue: context.propsValue,
});
},
async test(context) {
return await pollingHelper.test(polling, context);
},
async run(context) {
return await pollingHelper.poll(polling, context);
},
sampleData: undefined
})

View File

@@ -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"
}
]
}

View File

@@ -0,0 +1,9 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../../dist/out-tsc",
"declaration": true,
"types": ["node"]
},
"include": ["src/**/*.ts"]
}