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

View File

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

View File

@@ -0,0 +1,34 @@
import { createPiece, PieceAuth } from '@activepieces/pieces-framework';
import { createAgent } from './lib/actions/create-agent';
import { createConversation } from './lib/actions/create-conversation';
import { updateSettings } from './lib/actions/update-settings';
import { sendMessage } from './lib/actions/send-message';
import { findConversation } from './lib/actions/find-conversation';
import { deleteAgent } from './lib/actions/delete-agent';
import { updateAgent } from './lib/actions/update-agent';
import { exportConversation } from './lib/actions/export-conversation';
import { customgptAuth } from './lib/common/auth';
import { newConversation } from './lib/triggers/new-conversation';
import { PieceCategory } from '@activepieces/shared';
export const customgpt = createPiece({
displayName: 'CustomGPT',
auth: customgptAuth,
minimumSupportedRelease: '0.36.1',
logoUrl: 'https://cdn.activepieces.com/pieces/customgpt.png',
authors: ['sanket-a11y'],
categories: [PieceCategory.ARTIFICIAL_INTELLIGENCE],
description:
'CustomGPT allows you to create and deploy custom AI chatbots tailored to your specific needs.',
actions: [
createAgent,
createConversation,
deleteAgent,
exportConversation,
findConversation,
sendMessage,
updateAgent,
updateSettings,
],
triggers: [newConversation],
});

View File

@@ -0,0 +1,55 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { customgptAuth } from '../common/auth';
import { makeRequest } from '../common/client';
import { HttpMethod } from '@activepieces/pieces-common';
export const createAgent = createAction({
auth: customgptAuth,
name: 'createAgent',
displayName: 'Create Agent',
description:
'Create a new CustomGPT agent by importing data from a sitemap or file',
props: {
project_name: Property.ShortText({
displayName: 'Project Name',
description: 'The name for your agent/project',
required: true,
}),
sitemap_path: Property.ShortText({
displayName: 'Sitemap URL',
description:
'URL to a sitemap for importing agent knowledge (leave empty if uploading a file)',
required: false,
}),
file: Property.File({
displayName: 'File',
description:
'Upload a file (text, audio, or video format) for agent knowledge (leave empty if using sitemap)',
required: false,
}),
},
async run(context) {
const { project_name, sitemap_path, file } = context.propsValue;
const body: any = {
project_name,
};
if (sitemap_path) {
body.sitemap_path = sitemap_path;
}
if (file) {
body.file = file;
}
const response = await makeRequest(
context.auth.secret_text,
HttpMethod.POST,
'/projects',
body
);
return response;
},
});

View File

@@ -0,0 +1,35 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { customgptAuth } from '../common/auth';
import { makeRequest } from '../common/client';
import { HttpMethod } from '@activepieces/pieces-common';
import { projectId } from '../common/props';
export const createConversation = createAction({
auth: customgptAuth,
name: 'createConversation',
displayName: 'Create Conversation',
description: 'Create a new conversation in a CustomGPT project',
props: {
projectId: projectId,
name: Property.ShortText({
displayName: 'Conversation Name',
description: 'Optional name for the conversation',
required: false,
}),
},
async run(context) {
const { projectId, name } = context.propsValue;
const body: any = {};
if (name) body.name = name;
const response = await makeRequest(
context.auth.secret_text,
HttpMethod.POST,
`/projects/${projectId}/conversations`,
body
);
return response;
},
});

View File

@@ -0,0 +1,26 @@
import { createAction } from '@activepieces/pieces-framework';
import { projectId } from '../common/props';
import { customgptAuth } from '../common/auth';
import { makeRequest } from '../common/client';
import { HttpMethod } from '@activepieces/pieces-common';
export const deleteAgent = createAction({
auth: customgptAuth,
name: 'deleteAgent',
displayName: 'Delete Agent',
description: 'Delete a CustomGPT agent',
props: {
projectId: projectId,
},
async run(context) {
const { projectId } = context.propsValue;
const response = await makeRequest(
context.auth.secret_text,
HttpMethod.DELETE,
`/projects/${projectId}`
);
return response;
},
});

View File

@@ -0,0 +1,28 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { customgptAuth } from '../common/auth';
import { projectId } from '../common/props';
import { makeRequest } from '../common/client';
import { HttpMethod } from '@activepieces/pieces-common';
export const exportConversation = createAction({
auth: customgptAuth,
name: 'export_conversation',
displayName: 'Export Conversation',
description:
'Export a conversation with all messages, authors, feedbacks and metadata',
props: {
project_id: projectId,
session_id: Property.ShortText({
displayName: 'Session ID',
description: 'The session ID of the conversation to export',
required: true,
}),
},
async run({ auth, propsValue }) {
return await makeRequest(
auth.secret_text,
HttpMethod.GET,
`/projects/${propsValue.project_id}/conversations/${propsValue.session_id}/export`
);
},
});

View File

@@ -0,0 +1,41 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { customgptAuth } from '../common/auth';
import { makeRequest } from '../common/client';
import { HttpMethod } from '@activepieces/pieces-common';
import { tr } from 'zod/v4/locales';
export const findAgent = createAction({
auth: customgptAuth,
name: 'findAgent',
displayName: 'Find Agent',
description: 'Find Agent by name ',
props: {
name: Property.ShortText({
displayName: 'Agent Name Filter',
description: 'Filter agents by name (optional)',
required: true,
}),
},
async run(context) {
const { name } = context.propsValue;
const queryParams: any = {};
queryParams.order = 'desc';
queryParams.orderBy = 'created_at';
if (name) queryParams.name = name;
const queryString =
Object.keys(queryParams).length > 0
? '?' + new URLSearchParams(queryParams).toString()
: '';
const response = await makeRequest(
context.auth.secret_text,
HttpMethod.GET,
`/projects${queryString}`
);
return response;
},
});

View File

@@ -0,0 +1,69 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { customgptAuth } from '../common/auth';
import { projectId } from '../common/props';
import { makeRequest } from '../common/client';
import { HttpMethod } from '@activepieces/pieces-common';
export const findConversation = createAction({
auth: customgptAuth,
name: 'findConversation',
displayName: 'Find Conversation',
description:
'List and search conversations for an agent with optional filtering',
props: {
project_id: projectId,
name: Property.ShortText({
displayName: 'Conversation Name',
description: 'Filter conversations by name (optional)',
required: false,
}),
userFilter: Property.StaticDropdown({
displayName: 'User Filter',
description: 'Filter by user who created the conversation (default: all)',
required: false,
defaultValue: 'all',
options: {
options: [
{ label: 'All Users', value: 'all' },
{ label: 'Anonymous Users', value: 'anonymous' },
{ label: 'Team Members', value: 'team_member' },
{ label: 'Me', value: 'me' },
],
},
}),
lastUpdatedAfter: Property.DateTime({
displayName: 'Last Updated After',
description:
'Only return conversations with messages after this date (optional) e.g. 2025-01-09T18:30:00Z',
required: false,
}),
},
async run(context) {
const { project_id, name, userFilter, lastUpdatedAfter } =
context.propsValue;
// Build query parameters
const queryParams: any = {};
queryParams.order = 'desc';
queryParams.orderBy = 'created_at';
if (userFilter) queryParams.userFilter = userFilter;
if (name) queryParams.name = name;
if (lastUpdatedAfter) queryParams.lastUpdatedAfter = lastUpdatedAfter;
// Build query string
const queryString =
Object.keys(queryParams).length > 0
? '?' + new URLSearchParams(queryParams).toString()
: '';
const response = await makeRequest(
context.auth.secret_text,
HttpMethod.GET,
`/projects/${project_id}/conversations${queryString}`
);
return response;
},
});

View File

@@ -0,0 +1,144 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { customgptAuth } from '../common/auth';
import { projectId } from '../common/props';
import { makeRequest } from '../common/client';
import { HttpMethod } from '@activepieces/pieces-common';
export const sendMessage = createAction({
auth: customgptAuth,
name: 'sendMessage',
displayName: 'Send Message',
description: 'Send a message to a conversation within an agent',
props: {
project_id: projectId,
session_id: Property.ShortText({
displayName: 'Session ID',
description: 'The session ID of the conversation',
required: true,
}),
prompt: Property.LongText({
displayName: 'Prompt',
description: 'The message/question to send',
required: true,
}),
stream: Property.Checkbox({
displayName: 'Enable Streaming',
description: 'Stream the response in real-time (default: false)',
required: false,
defaultValue: false,
}),
custom_persona: Property.LongText({
displayName: 'Custom Persona',
description: 'Custom persona/instructions for the agent (optional)',
required: false,
}),
chatbot_model: Property.StaticDropdown({
displayName: 'Agent Model',
description: 'AI model to use for the conversation (optional)',
required: false,
options: {
options: [
{ label: 'GPT-4o', value: 'gpt-4o' },
{ label: 'GPT-4o Mini', value: 'gpt-4o-mini' },
{ label: 'GPT-4 Turbo', value: 'gpt-4-turbo' },
{ label: 'GPT-4', value: 'gpt-4' },
{ label: 'GPT-3.5 Turbo', value: 'gpt-3.5-turbo' },
{ label: 'Claude 3.5 Sonnet', value: 'claude-3-5-sonnet-20240620' },
{ label: 'Claude 3 Opus', value: 'claude-3-opus-20240229' },
{ label: 'Claude 3 Sonnet', value: 'claude-3-sonnet-20240229' },
{ label: 'Claude 3 Haiku', value: 'claude-3-haiku-20240307' },
{ label: 'Gemini 1.5 Pro', value: 'gemini-1.5-pro' },
{ label: 'Gemini 1.5 Flash', value: 'gemini-1.5-flash' },
{ label: 'o1-preview', value: 'o1-preview' },
],
},
}),
response_source: Property.StaticDropdown({
displayName: 'Response Source',
description:
'Source for generating responses (default: use only your content)',
required: false,
defaultValue: 'default',
options: {
options: [
{ label: 'Default (Your Content Only)', value: 'default' },
{ label: 'Own Content', value: 'own_content' },
{ label: 'OpenAI Content (Can improvise)', value: 'openai_content' },
],
},
}),
agent_capability: Property.StaticDropdown({
displayName: 'Agent Capability',
description: 'Capability mode for the agent (default: fastest-responses)',
required: false,
defaultValue: 'fastest-responses',
options: {
options: [
{ label: 'Fastest Responses', value: 'fastest-responses' },
{ label: 'Optimal Choice', value: 'optimal-choice' },
{ label: 'Advanced Reasoning', value: 'advanced-reasoning' },
{ label: 'Complex Tasks', value: 'complex-tasks' },
],
},
}),
lang: Property.ShortText({
displayName: 'Language',
description: 'Language code for the prompt (default: en)',
required: false,
defaultValue: 'en',
}),
custom_context: Property.LongText({
displayName: 'Custom Context',
description: 'Additional context for the conversation (optional)',
required: false,
}),
file: Property.File({
displayName: 'File',
description:
'File to upload with the message (Max 5MB: pdf, docx, doc, odt, txt, jpg, jpeg, png, webp)',
required: false,
}),
},
async run(context) {
const {
project_id,
session_id,
prompt,
stream,
custom_persona,
chatbot_model,
response_source,
agent_capability,
lang,
custom_context,
file,
} = context.propsValue;
const queryParams: any = {
stream: stream ? 'true' : 'false',
};
if (lang) queryParams.lang = lang;
const body: any = {
prompt,
};
if (custom_persona) body.custom_persona = custom_persona;
if (chatbot_model) body.chatbot_model = chatbot_model;
if (response_source) body.response_source = response_source;
if (agent_capability) body.agent_capability = agent_capability;
if (custom_context) body.custom_context = custom_context;
if (file) body.file = file;
const queryString = '?' + new URLSearchParams(queryParams).toString();
const response = await makeRequest(
context.auth.secret_text,
HttpMethod.POST,
`/projects/${project_id}/conversations/${session_id}/messages${queryString}`,
body
);
return response;
},
});

View File

@@ -0,0 +1,96 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { projectId } from '../common/props';
import { BASE_URL } from '../common/client';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
import { customgptAuth } from '../common/auth';
export const updateAgent = createAction({
auth: customgptAuth,
name: 'update_agent',
displayName: 'Update Agent',
description: 'Update an existing agent with specific details',
props: {
project_id: projectId,
project_name: Property.ShortText({
displayName: 'Agent Name',
description: 'The name of the agent',
required: false,
}),
is_shared: Property.Checkbox({
displayName: 'Is Shared',
description: 'Whether the agent is public or not',
required: false,
}),
sitemap_path: Property.ShortText({
displayName: 'Sitemap Path',
description: 'URL of the sitemap to crawl',
required: false,
}),
file_data_retension: Property.Checkbox({
displayName: 'File Data Retention',
description: 'Enable file data retention',
required: false,
}),
is_ocr_enabled: Property.Checkbox({
displayName: 'OCR Enabled',
description: 'Enable Optical Character Recognition for images',
required: false,
}),
is_anonymized: Property.Checkbox({
displayName: 'Anonymized',
description: 'Enable anonymization of data',
required: false,
}),
file: Property.File({
displayName: 'File',
description: 'Upload a file to the agent',
required: false,
}),
are_licenses_allowed: Property.Checkbox({
displayName: 'Are Licenses Allowed',
description: 'Whether project licenses are allowed',
required: false,
defaultValue: false,
}),
},
async run({ auth, propsValue }) {
const body: any = {};
if (propsValue.project_name !== undefined) {
body.project_name = propsValue.project_name;
}
if (propsValue.is_shared !== undefined) {
body.is_shared = propsValue.is_shared;
}
if (propsValue.sitemap_path !== undefined) {
body.sitemap_path = propsValue.sitemap_path;
}
if (propsValue.file_data_retension !== undefined) {
body.file_data_retension = propsValue.file_data_retension;
}
if (propsValue.is_ocr_enabled !== undefined) {
body.is_ocr_enabled = propsValue.is_ocr_enabled;
}
if (propsValue.is_anonymized !== undefined) {
body.is_anonymized = propsValue.is_anonymized;
}
if (propsValue.file !== undefined) {
body.file = propsValue.file;
}
if (propsValue.are_licenses_allowed !== undefined) {
body.are_licenses_allowed = propsValue.are_licenses_allowed;
}
const response = await httpClient.sendRequest({
method: HttpMethod.POST,
url: `${BASE_URL}/projects/${propsValue.project_id}`,
headers: {
Authorization: `Bearer ${auth.secret_text}`,
'content-type': 'multipart/form-data',
},
body,
});
return response.body;
},
});

View File

@@ -0,0 +1,258 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { customgptAuth } from '../common/auth';
import { projectId } from '../common/props';
import { makeRequest } from '../common/client';
import { HttpMethod } from '@activepieces/pieces-common';
export const updateSettings = createAction({
auth: customgptAuth,
name: 'update_settings',
displayName: 'Update Agent Settings',
description: 'Update agent configuration and behavior settings',
props: {
project_id: projectId,
default_prompt: Property.ShortText({
displayName: 'Default Prompt',
description:
'Default prompt text shown to users (e.g., "Ask Me Anything ...")',
required: false,
}),
response_source: Property.StaticDropdown({
displayName: 'Response Source',
description: 'Source for generating responses',
required: false,
options: {
options: [
{ label: 'Default', value: 'default' },
{ label: 'Own Content', value: 'own_content' },
{ label: 'OpenAI Content', value: 'openai_content' },
],
},
}),
chat_bot_avatar: Property.File({
displayName: 'Chatbot Avatar',
description: 'Upload avatar image for the chatbot',
required: false,
}),
chat_bot_bg: Property.File({
displayName: 'Chatbot Background',
description: 'Upload background image for the chatbot',
required: false,
}),
chatbot_msg_lang: Property.ShortText({
displayName: 'Chatbot Message Language',
description:
'Language code for chatbot messages (e.g., "en", "es", "ur")',
required: false,
}),
chatbot_color: Property.ShortText({
displayName: 'Chatbot Color',
description:
'Primary color for chatbot UI (hex color code, e.g., "#0e57cc")',
required: false,
}),
chatbot_toolbar_color: Property.ShortText({
displayName: 'Chatbot Toolbar Color',
description: 'Toolbar color for chatbot UI (hex color code)',
required: false,
}),
persona_instructions: Property.LongText({
displayName: 'Persona Instructions',
description: 'Custom persona/behavior instructions for the chatbot',
required: false,
}),
citations_answer_source_label_msg: Property.ShortText({
displayName: 'Citations Answer Source Label',
description: 'Label message for citation answer sources',
required: false,
}),
citations_sources_label_msg: Property.ShortText({
displayName: 'Citations Sources Label',
description: 'Label message for citation sources',
required: false,
}),
hang_in_there_msg: Property.ShortText({
displayName: 'Hang In There Message',
description: 'Message shown during long processing times',
required: false,
}),
chatbot_siesta_msg: Property.ShortText({
displayName: 'Chatbot Siesta Message',
description: 'Message shown when chatbot is temporarily unavailable',
required: false,
}),
is_loading_indicator_enabled: Property.Checkbox({
displayName: 'Enable Loading Indicator',
description: 'Show loading indicator during responses',
required: false,
}),
enable_citations: Property.Checkbox({
displayName: 'Enable Citations',
description: 'Enable citation display in responses',
required: false,
}),
enable_feedbacks: Property.Checkbox({
displayName: 'Enable Feedbacks',
description: 'Enable user feedback collection',
required: false,
}),
citations_view_type: Property.StaticDropdown({
displayName: 'Citations View Type',
description: 'How citations should be displayed',
required: false,
options: {
options: [
{ label: 'User Controlled', value: 'user' },
{ label: 'Always Show', value: 'show' },
{ label: 'Always Hide', value: 'hide' },
],
},
}),
no_answer_message: Property.ShortText({
displayName: 'No Answer Message',
description: 'Message shown when no answer is found',
required: false,
}),
ending_message: Property.ShortText({
displayName: 'Ending Message',
description: 'Message shown at the end of conversations',
required: false,
}),
remove_branding: Property.Checkbox({
displayName: 'Remove Branding',
description: 'Remove CustomGPT branding from the chatbot',
required: false,
}),
enable_recaptcha_for_public_chatbots: Property.Checkbox({
displayName: 'Enable reCAPTCHA',
description: 'Enable reCAPTCHA for public chatbots',
required: false,
}),
chatbot_model: Property.StaticDropdown({
displayName: 'Chatbot Model',
description: 'Default AI model for the chatbot',
required: false,
options: {
options: [
{ label: 'GPT-4o', value: 'gpt-4o' },
{ label: 'GPT-4o Mini', value: 'gpt-4o-mini' },
{ label: 'GPT-4 Turbo', value: 'gpt-4-turbo' },
{ label: 'GPT-4', value: 'gpt-4' },
{ label: 'Claude 3.5 Sonnet', value: 'claude-3-5-sonnet-20240620' },
{ label: 'Claude 3 Sonnet', value: 'claude-3-sonnet-20240229' },
],
},
}),
is_selling_enabled: Property.Checkbox({
displayName: 'Enable Selling',
description: 'Enable selling features',
required: false,
}),
},
async run({ auth, propsValue }) {
const { chat_bot_avatar, chat_bot_bg } = propsValue;
const formData = new FormData();
if (chat_bot_avatar) {
const avatarBuffer = Buffer.from(chat_bot_avatar.base64, 'base64');
const avatarBlob = new Blob([avatarBuffer], {
type: 'application/octet-stream',
});
formData.append('file', avatarBlob, chat_bot_avatar.filename || 'avatar');
}
if (chat_bot_bg) {
const bgBuffer = Buffer.from(chat_bot_bg.base64, 'base64');
const bgBlob = new Blob([bgBuffer], {
type: 'application/octet-stream',
});
formData.append('chat_bot_bg', bgBlob, chat_bot_bg.filename);
}
if (propsValue.default_prompt !== undefined) {
formData.append('default_prompt', propsValue.default_prompt);
}
if (propsValue.response_source !== undefined) {
formData.append('response_source', propsValue.response_source);
}
if (propsValue.chatbot_msg_lang !== undefined) {
formData.append('chatbot_msg_lang', propsValue.chatbot_msg_lang);
}
if (propsValue.chatbot_color !== undefined) {
formData.append('chatbot_color', propsValue.chatbot_color);
}
if (propsValue.chatbot_toolbar_color !== undefined) {
formData.append(
'chatbot_toolbar_color',
propsValue.chatbot_toolbar_color
);
}
if (propsValue.persona_instructions !== undefined) {
formData.append('persona_instructions', propsValue.persona_instructions);
}
if (propsValue.citations_answer_source_label_msg !== undefined) {
formData.append(
'citations_answer_source_label_msg',
propsValue.citations_answer_source_label_msg
);
}
if (propsValue.citations_sources_label_msg !== undefined) {
formData.append(
'citations_sources_label_msg',
propsValue.citations_sources_label_msg
);
}
if (propsValue.hang_in_there_msg !== undefined) {
formData.append('hang_in_there_msg', propsValue.hang_in_there_msg);
}
if (propsValue.chatbot_siesta_msg !== undefined) {
formData.append('chatbot_siesta_msg', propsValue.chatbot_siesta_msg);
}
if (propsValue.is_loading_indicator_enabled !== undefined) {
formData.append(
'is_loading_indicator_enabled',
String(propsValue.is_loading_indicator_enabled)
);
}
if (propsValue.enable_citations !== undefined) {
formData.append('enable_citations', String(propsValue.enable_citations));
}
if (propsValue.enable_feedbacks !== undefined) {
formData.append('enable_feedbacks', String(propsValue.enable_feedbacks));
}
if (propsValue.citations_view_type !== undefined) {
formData.append('citations_view_type', propsValue.citations_view_type);
}
if (propsValue.no_answer_message !== undefined) {
formData.append('no_answer_message', propsValue.no_answer_message);
}
if (propsValue.ending_message !== undefined) {
formData.append('ending_message', propsValue.ending_message);
}
if (propsValue.remove_branding !== undefined) {
formData.append('remove_branding', String(propsValue.remove_branding));
}
if (propsValue.enable_recaptcha_for_public_chatbots !== undefined) {
formData.append(
'enable_recaptcha_for_public_chatbots',
String(propsValue.enable_recaptcha_for_public_chatbots)
);
}
if (propsValue.chatbot_model !== undefined) {
formData.append('chatbot_model', propsValue.chatbot_model);
}
if (propsValue.is_selling_enabled !== undefined) {
formData.append(
'is_selling_enabled',
String(propsValue.is_selling_enabled)
);
}
return await makeRequest(
auth.secret_text,
HttpMethod.POST,
`/projects/${propsValue.project_id}/settings`,
formData
);
},
});

View File

@@ -0,0 +1,19 @@
import { PieceAuth } from '@activepieces/pieces-framework';
const markdownDescription = `
To get your CustomGPT API Key:
1. Log in to [CustomGPT](https://app.customgpt.ai/login)
2. Click the circle in the top right corner of the page and select **My Profile**
3. Select the **API** tab
4. Click the **Create API Key** button at the bottom of the page
5. Copy your API key and paste it below
For more details, visit the [API Keys and Authentication guide](https://docs.customgpt.ai/reference/api-keys-and-authentication).
`;
export const customgptAuth = PieceAuth.SecretText({
displayName: 'API Key',
required: true,
description: markdownDescription,
});

View File

@@ -0,0 +1,25 @@
import { HttpMethod, httpClient } from '@activepieces/pieces-common';
export const BASE_URL = `https://app.customgpt.ai/api/v1`;
export async function makeRequest(
api_key: string,
method: HttpMethod,
path: string,
body?: unknown
) {
try {
const response = await httpClient.sendRequest({
method,
url: `${BASE_URL}${path}`,
headers: {
Authorization: `Bearer ${api_key}`,
'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,49 @@
import { Property } from '@activepieces/pieces-framework';
import { customgptAuth } from './auth';
import { makeRequest } from './client';
import { HttpMethod } from '@activepieces/pieces-common';
export const projectId = Property.Dropdown({
auth: customgptAuth,
displayName: 'Agent',
description: 'Select the agent to use',
refreshers: [],
required: true,
options: async ({ auth }) => {
if (!auth) {
return {
disabled: true,
placeholder: 'Connect your Cursor account first',
options: [],
};
}
try {
const response = await makeRequest(
auth.secret_text,
HttpMethod.GET,
'/projects'
);
if (!response.data || response.data.data.length === 0) {
return {
options: [],
placeholder: 'No agents found',
};
}
return {
options: response.data.data.map((agent: any) => ({
label: `${agent.project_name} (${agent.status})`,
value: agent.id,
})),
};
} catch (error) {
return {
disabled: true,
options: [],
placeholder: 'Failed to load agents. Check your API key.',
};
}
},
});

View File

@@ -0,0 +1,87 @@
import {
createTrigger,
TriggerStrategy,
AppConnectionValueForAuthProperty,
} from '@activepieces/pieces-framework';
import {
DedupeStrategy,
Polling,
pollingHelper,
HttpMethod,
} from '@activepieces/pieces-common';
import dayjs from 'dayjs';
import { customgptAuth } from '../common/auth';
import { projectId } from '../common/props';
import { makeRequest } from '../common/client';
const polling: Polling<
AppConnectionValueForAuthProperty<typeof customgptAuth>,
{ project_id: unknown }
> = {
strategy: DedupeStrategy.TIMEBASED,
items: async ({ auth, propsValue, lastFetchEpochMS }) => {
const items: any[] = [];
const page = 1;
const queryParams: any = {
page: page.toString(),
order: 'desc',
orderBy: 'created_at',
};
if (lastFetchEpochMS) {
const lastFetchDate = new Date(lastFetchEpochMS).toISOString();
queryParams.lastUpdatedAfter = lastFetchDate;
}
const queryString = new URLSearchParams(queryParams).toString();
const response = await makeRequest(
auth.secret_text,
HttpMethod.GET,
`/projects/${propsValue['project_id']}/conversations?${queryString}`
);
if (response.body.status === 'success' && response.body.data.data) {
items.push(...response.body.data.data);
}
return items.map((item) => ({
epochMilliSeconds: dayjs(item.created_at).valueOf(),
data: item,
}));
},
};
export const newConversation = createTrigger({
auth: customgptAuth,
name: 'new_conversation',
displayName: 'New Conversation',
description: 'Triggers when a new conversation is created in an agent',
props: {
project_id: projectId,
},
sampleData: {
id: 1,
session_id: 'f1b9aaf0-5e4e-11eb-ae93-0242ac130002',
name: 'Conversation 1',
project_id: 1,
created_by: 1,
created_at: '2023-04-30 16:43:53',
updated_at: '2023-04-30 16:43:53',
deleted_at: null,
},
type: TriggerStrategy.POLLING,
async test(context) {
return await pollingHelper.test(polling, context);
},
async onEnable(context) {
await pollingHelper.onEnable(polling, context);
},
async onDisable(context) {
await pollingHelper.onDisable(polling, context);
},
async run(context) {
return await pollingHelper.poll(polling, context);
},
});

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