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

View File

@@ -0,0 +1,4 @@
{
"name": "@activepieces/piece-comfyicu",
"version": "0.0.5"
}

View File

@@ -0,0 +1,65 @@
{
"name": "pieces-comfyicu",
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "packages/pieces/community/comfyicu/src",
"projectType": "library",
"release": {
"version": {
"currentVersionResolver": "git-tag",
"preserveLocalDependencyProtocols": false,
"manifestRootsToUpdate": [
"dist/{projectRoot}"
]
}
},
"tags": [],
"targets": {
"build": {
"executor": "@nx/js:tsc",
"outputs": [
"{options.outputPath}"
],
"options": {
"outputPath": "dist/packages/pieces/community/comfyicu",
"tsConfig": "packages/pieces/community/comfyicu/tsconfig.lib.json",
"packageJson": "packages/pieces/community/comfyicu/package.json",
"main": "packages/pieces/community/comfyicu/src/index.ts",
"assets": [
"packages/pieces/community/comfyicu/*.md",
{
"input": "packages/pieces/community/comfyicu/src/i18n",
"output": "./src/i18n",
"glob": "**/!(i18n.json)"
}
],
"buildableProjectDepsInPackageJsonType": "dependencies",
"updateBuildableProjectDepsInPackageJson": true
},
"dependsOn": [
"^build",
"prebuild"
]
},
"nx-release-publish": {
"options": {
"packageRoot": "dist/{projectRoot}"
}
},
"lint": {
"executor": "@nx/eslint:lint",
"outputs": [
"{options.outputFile}"
]
},
"prebuild": {
"executor": "nx:run-commands",
"options": {
"cwd": "packages/pieces/community/comfyicu",
"command": "bun install --no-save --silent"
},
"dependsOn": [
"^build"
]
}
}
}

View File

@@ -0,0 +1,23 @@
{
"You can obtain API key from [Account Settings](https://comfy.icu/account).": "Sie können den API-Schlüssel unter [Kontoeinstellungen](https://comfy.icu/account).",
"Get Run Output": "Starte Ausgabe",
"Get Run Status": "Start-Status erhalten",
"List Workflows": "Workflows auflisten",
"Submit Workflow Run": "Workflow-Run absenden",
"Retrieves generated images or videos from a completed run.": "Ruft generierte Bilder oder Videos von einem abgeschlossenen Lauf ab.",
"Retrieves the status of workflow run.": "Ruft den Status des Workflows ab.",
"Retrieves available workflows for execution.": "Ruft verfügbare Workflows für die Ausführung ab.",
"Execute a workflow with specified prompt.": "Führen Sie einen Workflow mit der angegebenen Abfrage aus.",
"Workflow ID": "Workflow-ID",
"Run ID": "Ausführen ID",
"Prompt": "Prompt",
"Webhook": "Webhook",
"You can get workflow prompt by navigating to History->Select Run -> Copy API Workflow.": "Sie können einen Workflow-Prompt erhalten, indem Sie unter History->Auswahl Ausführen -> API Workflow kopieren.",
"Webhook URL to recieve run status.": "Webhook URL um den Ausführungsstatus zu erhalten.",
"New Workflow Created": "Neuer Workflow erstellt",
"Run Completed": "Ausführung abgeschlossen",
"Run Failed": "Ausführung fehlgeschlagen",
"Triggers when a new workflow is created.": "Wird ausgelöst, wenn ein neuer Workflow erstellt wird.",
"Triggers when a workflow run is successfully completed.": "Wird ausgelöst, wenn ein Workflow erfolgreich abgeschlossen wurde.",
"Triggers when a workflow run is failed.": "Wird ausgelöst, wenn ein Workflow-Lauf fehlgeschlagen ist."
}

View File

@@ -0,0 +1,23 @@
{
"You can obtain API key from [Account Settings](https://comfy.icu/account).": "Puedes obtener la clave API de [Configuración de la cuenta](https://comfy.icu/account).",
"Get Run Output": "Obtener salida",
"Get Run Status": "Obtener estado de ejecución",
"List Workflows": "Listar flujos de trabajo",
"Submit Workflow Run": "Enviar ejecución de flujo de trabajo",
"Retrieves generated images or videos from a completed run.": "Recuperar imágenes o videos generados desde una ejecución completada.",
"Retrieves the status of workflow run.": "Recuperar el estado de ejecución del flujo de trabajo.",
"Retrieves available workflows for execution.": "Recuperar flujos de trabajo disponibles para su ejecución.",
"Execute a workflow with specified prompt.": "Ejecutar un flujo de trabajo con un indicador especificado.",
"Workflow ID": "ID de flujo de trabajo",
"Run ID": "Ejecutar ID",
"Prompt": "Petición",
"Webhook": "Webhook",
"You can get workflow prompt by navigating to History->Select Run -> Copy API Workflow.": "Puede obtener el indicador de flujo de trabajo navegando a Historia->Seleccione Ejecutar-> Copiar flujo de trabajo API.",
"Webhook URL to recieve run status.": "Webhook URL para recibir el estado de ejecución.",
"New Workflow Created": "Nuevo flujo de trabajo creado",
"Run Completed": "Ejecutar completado",
"Run Failed": "Error al ejecutar",
"Triggers when a new workflow is created.": "Dispara cuando se crea un nuevo flujo de trabajo.",
"Triggers when a workflow run is successfully completed.": "Se activa cuando un flujo de trabajo se ejecuta correctamente.",
"Triggers when a workflow run is failed.": "Dispara cuando un flujo de trabajo falla."
}

View File

@@ -0,0 +1,23 @@
{
"You can obtain API key from [Account Settings](https://comfy.icu/account).": "Vous pouvez obtenir la clé API dans [Paramètres du compte](https://comfy.icu/account).",
"Get Run Output": "Obtenir la sortie d'exécution",
"Get Run Status": "Obtenir le statut d'exécution",
"List Workflows": "Lister les Workflows",
"Submit Workflow Run": "Soumettre l'exécution du flux de travail",
"Retrieves generated images or videos from a completed run.": "Récupère des images ou des vidéos générées à partir d'une exécution terminée.",
"Retrieves the status of workflow run.": "Récupère le statut de l'exécution du workflow.",
"Retrieves available workflows for execution.": "Récupère les workflows disponibles pour l'exécution.",
"Execute a workflow with specified prompt.": "Exécute un workflow avec l'invite spécifiée.",
"Workflow ID": "ID du flux de travail",
"Run ID": "Lancer l'ID",
"Prompt": "Prompt",
"Webhook": "Webhook",
"You can get workflow prompt by navigating to History->Select Run -> Copy API Workflow.": "Vous pouvez obtenir l'invite de workflow en accédant à History->Select Run -> Copy API Workflow.",
"Webhook URL to recieve run status.": "URL du Webhook pour recevoir le statut de l'exécution.",
"New Workflow Created": "Nouveau Workflow créé",
"Run Completed": "Exécution terminée",
"Run Failed": "Run Failed",
"Triggers when a new workflow is created.": "Déclenche lorsqu'un nouveau workflow est créé.",
"Triggers when a workflow run is successfully completed.": "Déclenche lorsqu'un workflow est exécuté avec succès.",
"Triggers when a workflow run is failed.": "Déclenche lorsqu'un workflow est exécuté en échec."
}

View File

@@ -0,0 +1,23 @@
{
"You can obtain API key from [Account Settings](https://comfy.icu/account).": "[アカウント設定](https://comfy.icu/account)からAPIキーを取得できます。",
"Get Run Output": "実行出力を取得",
"Get Run Status": "実行状況を取得する",
"List Workflows": "ワークフローの一覧",
"Submit Workflow Run": "ワークフロー実行を送信",
"Retrieves generated images or videos from a completed run.": "完了した実行から生成された画像や動画を取得します。",
"Retrieves the status of workflow run.": "ワークフローの実行の状態を取得します。",
"Retrieves available workflows for execution.": "実行のために利用可能なワークフローを取得します。",
"Execute a workflow with specified prompt.": "指定したプロンプトでワークフローを実行します。",
"Workflow ID": "ワークフロー ID",
"Run ID": "実行 ID",
"Prompt": "Prompt",
"Webhook": "Webhook",
"You can get workflow prompt by navigating to History->Select Run -> Copy API Workflow.": "History->format@@0->format@@1->format@@2ワークフローをコピーすることで、ワークフロープロンプトを取得できます。",
"Webhook URL to recieve run status.": "実行ステータスを受け取るWebhookのURL。",
"New Workflow Created": "新しいワークフローを作成しました",
"Run Completed": "実行完了",
"Run Failed": "実行に失敗しました",
"Triggers when a new workflow is created.": "新しいワークフローが作成されたときにトリガーします。",
"Triggers when a workflow run is successfully completed.": "ワークフローの実行が正常に完了したときにトリガーします。",
"Triggers when a workflow run is failed.": "ワークフローの実行に失敗したときにトリガーします。"
}

View File

@@ -0,0 +1,23 @@
{
"You can obtain API key from [Account Settings](https://comfy.icu/account).": "U kunt API-sleutel verkrijgen via [Accountinstellingen](https://comfy.icu/account).",
"Get Run Output": "Krijg Run Output",
"Get Run Status": "Uitvoerstatus krijgen",
"List Workflows": "Lijst Workflows",
"Submit Workflow Run": "Indienen Workflow Run",
"Retrieves generated images or videos from a completed run.": "Ophalen gegenereerde afbeeldingen of video's van een voltooide uitvoering.",
"Retrieves the status of workflow run.": "Haalt de status van de workflow op.",
"Retrieves available workflows for execution.": "Ophalen beschikbare workflows voor uitvoering.",
"Execute a workflow with specified prompt.": "Voer een workflow uit met een bepaald prompt.",
"Workflow ID": "Workflow ID",
"Run ID": "ID uitvoeren",
"Prompt": "Prompt",
"Webhook": "Webhook",
"You can get workflow prompt by navigating to History->Select Run -> Copy API Workflow.": "U kunt workflow prompt krijgen door te navigeren naar Geschiedenis>Selecteer Uitvoer-> Kopieer API Workflow.",
"Webhook URL to recieve run status.": "Webhook URL om uitvoeringsstatus te ontvangen.",
"New Workflow Created": "Nieuwe Workflow aangemaakt",
"Run Completed": "Uitvoeren voltooid",
"Run Failed": "Run Mislukt",
"Triggers when a new workflow is created.": "Triggert wanneer een nieuwe workflow wordt gemaakt.",
"Triggers when a workflow run is successfully completed.": "Triggert wanneer een workflow met succes wordt uitgevoerd.",
"Triggers when a workflow run is failed.": "Triggert wanneer een workflow wordt uitgevoerd."
}

View File

@@ -0,0 +1,23 @@
{
"You can obtain API key from [Account Settings](https://comfy.icu/account).": "Você pode obter uma chave de API em [Configurações da Conta](https://comfy.icu/account).",
"Get Run Output": "Obter saída de execução",
"Get Run Status": "Obter status de execução",
"List Workflows": "Listar Workflows",
"Submit Workflow Run": "Enviar execução de workflow",
"Retrieves generated images or videos from a completed run.": "Obtém imagens geradas ou vídeos de uma execução completa.",
"Retrieves the status of workflow run.": "Recupera o status da execução do fluxo de trabalho.",
"Retrieves available workflows for execution.": "Recupera fluxos de trabalho disponíveis para execução.",
"Execute a workflow with specified prompt.": "Executa um fluxo de trabalho com um prompt especificado.",
"Workflow ID": "ID do workflow",
"Run ID": "Executar ID",
"Prompt": "Aviso",
"Webhook": "Webhook",
"You can get workflow prompt by navigating to History->Select Run -> Copy API Workflow.": "Você pode obter a notificação de fluxo de trabalho navegando até o Histórico>Selecione Executar -> Copiar fluxo de trabalho da API.",
"Webhook URL to recieve run status.": "URL do Webhook para receber o status do executar.",
"New Workflow Created": "Novo workflow criado",
"Run Completed": "Execução concluída",
"Run Failed": "Execução Falhou",
"Triggers when a new workflow is created.": "Dispara quando um novo fluxo de trabalho é criado.",
"Triggers when a workflow run is successfully completed.": "Dispara quando uma execução do fluxo de trabalho for concluída com êxito.",
"Triggers when a workflow run is failed.": "Aciona quando a execução de um fluxo de trabalho falhou."
}

View File

@@ -0,0 +1,24 @@
{
"Comfy.ICU": "Comfy.ICU",
"You can obtain API key from [Account Settings](https://comfy.icu/account).": "Вы можете получить ключ API в [Настройках аккаунта](https://comfy.icu/account).",
"Get Run Output": "Запустить вывод",
"Get Run Status": "Получить статус запуска",
"List Workflows": "Список рабочих процессов",
"Submit Workflow Run": "Отправить запуск рабочего процесса",
"Retrieves generated images or videos from a completed run.": "Получает сгенерированные изображения или видео из завершённого запуска.",
"Retrieves the status of workflow run.": "Получает статус выполнения рабочего процесса.",
"Retrieves available workflows for execution.": "Получает доступные рабочие процессы для выполнения.",
"Execute a workflow with specified prompt.": "Выполнить рабочий процесс с указанной подсказкой.",
"Workflow ID": "ID рабочего процесса",
"Run ID": "ID запуска",
"Prompt": "Prompt",
"Webhook": "Вебхук",
"You can get workflow prompt by navigating to History->Select Run -> Copy API Workflow.": "Вы можете получить запрос рабочего процесса, перейдя по ссылке History->Select Run -> Copy API Workflow.",
"Webhook URL to recieve run status.": "Webhook URL для получения статуса запуска.",
"New Workflow Created": "Новый рабочий процесс создан",
"Run Completed": "Запуск завершен",
"Run Failed": "Неудачный запуск",
"Triggers when a new workflow is created.": "Триггеры при создании нового рабочего процесса.",
"Triggers when a workflow run is successfully completed.": "Триггеры при запуске рабочего процесса успешно завершены.",
"Triggers when a workflow run is failed.": "Триггеры, когда рабочий процесс не запущен."
}

View File

@@ -0,0 +1,23 @@
{
"You can obtain API key from [Account Settings](https://comfy.icu/account).": "You can obtain API key from [Account Settings](https://comfy.icu/account).",
"Get Run Output": "Get Run Output",
"Get Run Status": "Get Run Status",
"List Workflows": "List Workflows",
"Submit Workflow Run": "Submit Workflow Run",
"Retrieves generated images or videos from a completed run.": "Retrieves generated images or videos from a completed run.",
"Retrieves the status of workflow run.": "Retrieves the status of workflow run.",
"Retrieves available workflows for execution.": "Retrieves available workflows for execution.",
"Execute a workflow with specified prompt.": "Execute a workflow with specified prompt.",
"Workflow ID": "Workflow ID",
"Run ID": "Run ID",
"Prompt": "Prompt",
"Webhook": "Webhook",
"You can get workflow prompt by navigating to History->Select Run -> Copy API Workflow.": "You can get workflow prompt by navigating to History->Select Run -> Copy API Workflow.",
"Webhook URL to recieve run status.": "Webhook URL to recieve run status.",
"New Workflow Created": "New Workflow Created",
"Run Completed": "Run Completed",
"Run Failed": "Run Failed",
"Triggers when a new workflow is created.": "Triggers when a new workflow is created.",
"Triggers when a workflow run is successfully completed.": "Triggers when a workflow run is successfully completed.",
"Triggers when a workflow run is failed.": "Triggers when a workflow run is failed."
}

View File

@@ -0,0 +1,24 @@
{
"Comfy.ICU": "Comfy.ICU",
"You can obtain API key from [Account Settings](https://comfy.icu/account).": "You can obtain API key from [Account Settings](https://comfy.icu/account).",
"Get Run Output": "Get Run Output",
"Get Run Status": "Get Run Status",
"List Workflows": "List Workflows",
"Submit Workflow Run": "Submit Workflow Run",
"Retrieves generated images or videos from a completed run.": "Retrieves generated images or videos from a completed run.",
"Retrieves the status of workflow run.": "Retrieves the status of workflow run.",
"Retrieves available workflows for execution.": "Retrieves available workflows for execution.",
"Execute a workflow with specified prompt.": "Execute a workflow with specified prompt.",
"Workflow ID": "Workflow ID",
"Run ID": "Run ID",
"Prompt": "Prompt",
"Webhook": "Webhook",
"You can get workflow prompt by navigating to History->Select Run -> Copy API Workflow.": "You can get workflow prompt by navigating to History->Select Run -> Copy API Workflow.",
"Webhook URL to recieve run status.": "Webhook URL to recieve run status.",
"New Workflow Created": "New Workflow Created",
"Run Completed": "Run Completed",
"Run Failed": "Run Failed",
"Triggers when a new workflow is created.": "Triggers when a new workflow is created.",
"Triggers when a workflow run is successfully completed.": "Triggers when a workflow run is successfully completed.",
"Triggers when a workflow run is failed.": "Triggers when a workflow run is failed."
}

View File

@@ -0,0 +1,23 @@
{
"You can obtain API key from [Account Settings](https://comfy.icu/account).": "You can obtain API key from [Account Settings](https://comfy.icu/account).",
"Get Run Output": "Get Run Output",
"Get Run Status": "Get Run Status",
"List Workflows": "List Workflows",
"Submit Workflow Run": "Submit Workflow Run",
"Retrieves generated images or videos from a completed run.": "Retrieves generated images or videos from a completed run.",
"Retrieves the status of workflow run.": "Retrieves the status of workflow run.",
"Retrieves available workflows for execution.": "Retrieves available workflows for execution.",
"Execute a workflow with specified prompt.": "Execute a workflow with specified prompt.",
"Workflow ID": "Workflow ID",
"Run ID": "Run ID",
"Prompt": "Prompt",
"Webhook": "Webhook",
"You can get workflow prompt by navigating to History->Select Run -> Copy API Workflow.": "You can get workflow prompt by navigating to History->Select Run -> Copy API Workflow.",
"Webhook URL to recieve run status.": "Webhook URL to recieve run status.",
"New Workflow Created": "New Workflow Created",
"Run Completed": "Run Completed",
"Run Failed": "运行失败",
"Triggers when a new workflow is created.": "Triggers when a new workflow is created.",
"Triggers when a workflow run is successfully completed.": "Triggers when a workflow run is successfully completed.",
"Triggers when a workflow run is failed.": "Triggers when a workflow run is failed."
}

View File

@@ -0,0 +1,26 @@
import { createPiece, PieceAuth } from '@activepieces/pieces-framework';
import { PieceCategory } from '@activepieces/shared';
import { getRunOutputAction } from './lib/actions/get-run-outputs';
import { getRunStatusAction } from './lib/actions/get-run-status';
import { listWorkflowsAction } from './lib/actions/list-workflows';
import { submitWorkflowRunAction } from './lib/actions/submit-workflow-run';
import { newWorkflowCreatedTrigger } from './lib/triggers/new-workflow-created';
import { runCompletedTrigger } from './lib/triggers/run-completed';
import { runFailedTrigger } from './lib/triggers/run-failed';
export const comfyIcuAuth = PieceAuth.SecretText({
displayName: 'API Key',
required: true,
description: `You can obtain API key from [Account Settings](https://comfy.icu/account).`,
});
export const comfyicu = createPiece({
displayName: 'Comfy.ICU',
categories: [PieceCategory.ARTIFICIAL_INTELLIGENCE],
auth: comfyIcuAuth,
minimumSupportedRelease: '0.36.1',
logoUrl: 'https://cdn.activepieces.com/pieces/comfyicu.png',
authors: ['rimjhimyadav'],
actions: [getRunOutputAction,getRunStatusAction,listWorkflowsAction,submitWorkflowRunAction],
triggers: [newWorkflowCreatedTrigger,runCompletedTrigger,runFailedTrigger],
});

View File

@@ -0,0 +1,25 @@
import { comfyIcuAuth } from '../../index';
import { createAction } from '@activepieces/pieces-framework';
import { comfyIcuApiCall, commonProps } from '../common';
import { HttpMethod } from '@activepieces/pieces-common';
export const getRunOutputAction = createAction({
auth: comfyIcuAuth,
name: 'get-run-output',
displayName: 'Get Run Output',
description: 'Retrieves generated images or videos from a completed run.',
props: {
...commonProps,
},
async run(context) {
const { workflow_id, run_id } = context.propsValue;
const response = await comfyIcuApiCall({
apiKey: context.auth,
endpoint: `/workflows/${workflow_id}/runs/${run_id}`,
method: HttpMethod.GET,
});
const runOutput = response.body as { output: Array<Record<string,any>> };
return { result: runOutput.output };
},
});

View File

@@ -0,0 +1,25 @@
import { comfyIcuAuth } from '../../index';
import { createAction } from '@activepieces/pieces-framework';
import { comfyIcuApiCall, commonProps } from '../common';
import { HttpMethod } from '@activepieces/pieces-common';
export const getRunStatusAction = createAction({
auth: comfyIcuAuth,
name: 'get-run-status',
displayName: 'Get Run Status',
description: 'Retrieves the status of workflow run.',
props: {
...commonProps,
},
async run(context) {
const { workflow_id, run_id } = context.propsValue;
const response = await comfyIcuApiCall({
apiKey: context.auth,
endpoint: `/workflows/${workflow_id}/runs/${run_id}`,
method: HttpMethod.GET,
});
const runStatus = response.body as { status: string };
return { status: runStatus.status };
},
});

View File

@@ -0,0 +1,21 @@
import { comfyIcuAuth } from '../../index';
import { createAction } from '@activepieces/pieces-framework';
import { comfyIcuApiCall } from '../common';
import { HttpMethod } from '@activepieces/pieces-common';
export const listWorkflowsAction = createAction({
auth: comfyIcuAuth,
name: 'list-workflows',
displayName: 'List Workflows',
description: 'Retrieves available workflows for execution.',
props: {},
async run(context) {
const response = await comfyIcuApiCall({
apiKey: context.auth,
endpoint: '/workflows',
method: HttpMethod.GET,
});
return response.body;
},
});

View File

@@ -0,0 +1,38 @@
import { comfyIcuAuth } from '../../index';
import { createAction, Property } from '@activepieces/pieces-framework';
import { comfyIcuApiCall, commonProps } from '../common';
import { HttpMethod } from '@activepieces/pieces-common';
export const submitWorkflowRunAction = createAction({
auth: comfyIcuAuth,
name: 'submit-workflow-run',
displayName: 'Submit Workflow Run',
description: 'Execute a workflow with specified prompt.',
props: {
workflow_id:commonProps.workflow_id,
prompt:Property.Json({
displayName:'Prompt',
description:'You can get workflow prompt by navigating to History->Select Run -> Copy API Workflow.',
required:true
}),
webhook:Property.ShortText({
displayName:'Webhook',
required:false,
description:'Webhook URL to recieve run status.'
})
},
async run(context) {
const { workflow_id, prompt,webhook } = context.propsValue;
const response = await comfyIcuApiCall({
apiKey: context.auth,
endpoint: `/workflows/${workflow_id}/runs`,
method: HttpMethod.POST,
body:{
prompt,
webhook
}
});
return response.body;
},
});

View File

@@ -0,0 +1,78 @@
import {
HttpMethod,
QueryParams,
httpClient,
HttpRequest,
AuthenticationType,
} from '@activepieces/pieces-common';
import { AppConnectionValueForAuthProperty, Property } from '@activepieces/pieces-framework';
import { comfyIcuAuth } from '../..';
export async function comfyIcuApiCall({
apiKey: {secret_text},
endpoint,
method,
qparams,
body,
}: {
apiKey: AppConnectionValueForAuthProperty<typeof comfyIcuAuth>;
endpoint: string;
method: HttpMethod;
qparams?: QueryParams;
body?: any;
}) {
const request: HttpRequest = {
url: `https://comfy.icu/api/v1${endpoint}`,
method,
queryParams: qparams,
body,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: secret_text,
},
};
const response = await httpClient.sendRequest(request);
return response;
}
export const commonProps = {
workflow_id:Property.Dropdown({
auth: comfyIcuAuth,
displayName:'Workflow ID',
refreshers:[],
required:true,
options:async ({auth})=>{
if(!auth)
{
return{
disabled:true,
options:[],
placeholder:'Please connect your account first.'
}
}
const response = await comfyIcuApiCall({
apiKey:auth,
endpoint:'/workflows',
method:HttpMethod.GET
})
const workflows = response.body as {id:string,name:string}[];
return {
disabled:false,
options:workflows.map((workflow)=>{
return{
label:workflow.name,
value:workflow.id
}
})
}
}
}),
run_id:Property.ShortText({
displayName:'Run ID',
required:true
})
}

View File

@@ -0,0 +1,88 @@
import {
DedupeStrategy,
HttpMethod,
Polling,
pollingHelper,
} from '@activepieces/pieces-common';
import { comfyIcuAuth } from '../../index';
import {
AppConnectionValueForAuthProperty,
createTrigger,
PiecePropValueSchema,
TriggerStrategy,
} from '@activepieces/pieces-framework';
import { comfyIcuApiCall } from '../common';
import dayjs from 'dayjs';
export const newWorkflowCreatedTrigger = createTrigger({
auth: comfyIcuAuth,
name: 'new-workflow-created',
displayName: 'New Workflow Created',
description: 'Triggers when a new workflow 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: {
id: 'RKhLj7NZrWL7Yhk_lzEf7',
name: 'Test Flow',
description: null,
created_at: '2025-05-23T14:34:16.610Z',
updated_at: '2025-05-23T14:35:11.052Z',
tags: [],
is_nsfw: false,
visibility: 'PUBLIC',
user_id: 'xyz',
current_run_id: 'H8fwBSWw_SJtn4hrfbsbb',
models: null,
accelerator: null,
project_id: 12297,
deleted_at: null,
featuredImages: [],
},
});
const polling: Polling<
AppConnectionValueForAuthProperty<typeof comfyIcuAuth>,
Record<string, any>
> = {
strategy: DedupeStrategy.TIMEBASED,
async items({ auth }) {
const response = await comfyIcuApiCall({
apiKey: auth,
endpoint: '/workflows',
method: HttpMethod.GET,
});
const workflows = response.body as {
id: string;
name: string;
created_at: string;
}[];
return workflows.map((workflow) => {
return {
epochMilliSeconds: dayjs(workflow.created_at).valueOf(),
data: workflow,
};
});
},
};

View File

@@ -0,0 +1,493 @@
import { comfyIcuAuth } from '../../index';
import {
AppConnectionValueForAuthProperty,
createTrigger,
TriggerStrategy,
} from '@activepieces/pieces-framework';
import { comfyIcuApiCall, commonProps } from '../common';
import {
DedupeStrategy,
HttpMethod,
Polling,
pollingHelper,
} from '@activepieces/pieces-common';
import dayjs from 'dayjs';
const polling: Polling<
AppConnectionValueForAuthProperty<typeof comfyIcuAuth>,
{ workflow_id: string }
> = {
strategy: DedupeStrategy.TIMEBASED,
async items({ auth, propsValue, lastFetchEpochMS }) {
const { workflow_id } = propsValue;
const listRunsResponse = await comfyIcuApiCall({
apiKey: auth,
endpoint: `/workflows/${workflow_id}/runs`,
method: HttpMethod.GET,
});
const runs = listRunsResponse.body as {
id: string;
status: string;
created_at: string;
}[];
const result = [];
for (const run of runs) {
if (run.status === 'COMPLETED') {
const runDetailsResponse = await comfyIcuApiCall({
apiKey: auth,
endpoint: `/workflows/${workflow_id}/runs/${run.id}`,
method: HttpMethod.GET,
});
const runDetail = runDetailsResponse.body as { created_at: string };
result.push(runDetail);
}
if (lastFetchEpochMS === 0 && result.length === 5) break;
}
return result.map((run) => {
return {
epochMilliSeconds: dayjs(run.created_at).valueOf(),
data: run,
};
});
},
};
export const runCompletedTrigger = createTrigger({
auth: comfyIcuAuth,
name: 'run-completed',
displayName: 'Run Completed',
description: 'Triggers when a workflow run is successfully completed.',
props: {
workflow_id: commonProps.workflow_id,
},
type: TriggerStrategy.POLLING,
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: {
id: 'CNCr5a_lu6g9Nz3gqKC9P',
created_at: '2025-05-23T15:08:18.297Z',
updated_at: '2025-05-23T15:08:27.476Z',
deleted_at: null,
started_at: '2025-05-23T15:08:19.760Z',
completed_at: '2025-05-23T15:08:27.384Z',
run_time: 6357,
status: 'COMPLETED',
kind: 'comfyui',
data: {
prompt: {
'3': {
_meta: {
title: 'KSampler',
},
inputs: {
cfg: 8,
seed: 253353381004563,
model: ['4', 0],
steps: 20,
denoise: 1,
negative: ['7', 0],
positive: ['6', 0],
scheduler: 'normal',
latent_image: ['5', 0],
sampler_name: 'euler',
},
class_type: 'KSampler',
},
'4': {
_meta: {
title: 'Load Checkpoint',
},
inputs: {
ckpt_name: 'v1-5-pruned-emaonly-fp16.safetensors',
},
class_type: 'CheckpointLoaderSimple',
},
'5': {
_meta: {
title: 'Empty Latent Image',
},
inputs: {
width: 512,
height: 512,
batch_size: 1,
},
class_type: 'EmptyLatentImage',
},
'6': {
_meta: {
title: 'CLIP Text Encode (Prompt)',
},
inputs: {
clip: ['4', 1],
text: 'beautiful scenery nature glass bottle landscape, , purple galaxy bottle,',
},
class_type: 'CLIPTextEncode',
},
'7': {
_meta: {
title: 'CLIP Text Encode (Prompt)',
},
inputs: {
clip: ['4', 1],
text: 'text, watermark',
},
class_type: 'CLIPTextEncode',
},
'8': {
_meta: {
title: 'VAE Decode',
},
inputs: {
vae: ['4', 2],
samples: ['3', 0],
},
class_type: 'VAEDecode',
},
'9': {
_meta: {
title: 'Save Image',
},
inputs: {
images: ['8', 0],
filename_prefix: 'ComfyUI',
},
class_type: 'SaveImage',
},
},
extra_data: {
extra_pnginfo: {
workflow: {
extra: {
ds: {
scale: 0.45,
offset: [],
},
ue_links: [],
VHS_MetadataImage: true,
VHS_latentpreview: false,
VHS_KeepIntermediate: true,
VHS_latentpreviewrate: 0,
},
links: [
[1, 4, 0, 3, 0, 'MODEL'],
[2, 5, 0, 3, 3, 'LATENT'],
[3, 4, 1, 6, 0, 'CLIP'],
[4, 6, 0, 3, 1, 'CONDITIONING'],
[5, 4, 1, 7, 0, 'CLIP'],
[6, 7, 0, 3, 2, 'CONDITIONING'],
[7, 3, 0, 8, 0, 'LATENT'],
[8, 4, 2, 8, 1, 'VAE'],
[9, 8, 0, 9, 0, 'IMAGE'],
],
nodes: [
{
id: 7,
pos: [413, 389],
mode: 0,
size: [],
type: 'CLIPTextEncode',
flags: {},
order: 3,
inputs: [
{
link: 5,
name: 'clip',
type: 'CLIP',
},
],
outputs: [
{
name: 'CONDITIONING',
type: 'CONDITIONING',
links: [6],
slot_index: 0,
},
],
properties: {
'Node name for S&R': 'CLIPTextEncode',
},
widgets_values: ['text, watermark'],
},
{
id: 5,
pos: [473, 609],
mode: 0,
size: [315, 106],
type: 'EmptyLatentImage',
flags: {},
order: 0,
inputs: [],
outputs: [
{
name: 'LATENT',
type: 'LATENT',
links: [2],
slot_index: 0,
},
],
properties: {
'Node name for S&R': 'EmptyLatentImage',
},
widgets_values: [512, 512, 1],
},
{
id: 8,
pos: [1209, 188],
mode: 0,
size: [210, 46],
type: 'VAEDecode',
flags: {},
order: 5,
inputs: [
{
link: 7,
name: 'samples',
type: 'LATENT',
},
{
link: 8,
name: 'vae',
type: 'VAE',
},
],
outputs: [
{
name: 'IMAGE',
type: 'IMAGE',
links: [9],
slot_index: 0,
},
],
properties: {
'Node name for S&R': 'VAEDecode',
},
widgets_values: [],
},
{
id: 9,
pos: [1451, 189],
mode: 0,
size: [210, 270],
type: 'SaveImage',
flags: {},
order: 6,
inputs: [
{
link: 9,
name: 'images',
type: 'IMAGE',
},
],
outputs: [],
properties: {
'Node name for S&R': 'SaveImage',
},
widgets_values: ['ComfyUI'],
},
{
id: 3,
pos: [],
mode: 0,
size: [315, 262],
type: 'KSampler',
flags: {},
order: 4,
inputs: [
{
link: 1,
name: 'model',
type: 'MODEL',
},
{
link: 4,
name: 'positive',
type: 'CONDITIONING',
},
{
link: 6,
name: 'negative',
type: 'CONDITIONING',
},
{
link: 2,
name: 'latent_image',
type: 'LATENT',
},
],
outputs: [
{
name: 'LATENT',
type: 'LATENT',
links: [7],
slot_index: 0,
},
],
properties: {
'Node name for S&R': 'KSampler',
},
widgets_values: [
253353381004563,
'randomize',
20,
8,
'euler',
'normal',
1,
],
},
{
id: 4,
pos: [],
mode: 0,
size: [315, 310],
type: 'CheckpointLoaderSimple',
flags: {},
order: 1,
inputs: [],
outputs: [
{
name: 'MODEL',
type: 'MODEL',
links: [1],
slot_index: 0,
},
{
name: 'CLIP',
type: 'CLIP',
links: [3, 5],
slot_index: 1,
},
{
name: 'VAE',
type: 'VAE',
links: [8],
slot_index: 2,
},
],
properties: {
'Node name for S&R': 'CheckpointLoaderSimple',
},
widgets_values: ['v1-5-pruned-emaonly-fp16.safetensors'],
},
{
id: 6,
pos: [415, 186],
mode: 0,
size: [422.8450317382812, 164.3130493164062],
type: 'CLIPTextEncode',
flags: {},
order: 2,
inputs: [
{
link: 3,
name: 'clip',
type: 'CLIP',
},
],
outputs: [
{
name: 'CONDITIONING',
type: 'CONDITIONING',
links: [4],
slot_index: 0,
},
],
properties: {
'Node name for S&R': 'CLIPTextEncode',
},
widgets_values: [
'beautiful scenery nature glass bottle landscape, , purple galaxy bottle,',
],
},
],
config: {},
groups: [],
version: 0.4,
last_link_id: 9,
last_node_id: 9,
},
},
},
accelerator: 'L4',
},
workflow: null,
workflow_api: null,
workflow_id: 'xyz',
output: [
{
filename:
'/workflows/xyz/output/xyz/ComfyUI_00001_.png',
url: 'https://r2.comfy.icu/workflows/xyz/output/CNCr5a_lu6g9Nz3gqKC9P/ComfyUI_00001_.png',
thumbnail_url:
'https://img.comfy.icu/sig/width:300/quality:85/xyz',
},
],
error: null,
files: {},
name: null,
tags: [],
view_count: 0,
download_count: 0,
is_nsfw: false,
user_id: '6VbYatQkJjualbDX84D2j',
device: 'NVIDIA L4',
accelerator: 'L4',
visibility: 'PUBLIC',
parent_workflow_id: null,
parent_run_id: null,
api_key_id: null,
retry_count: 1,
client_agent: null,
webhook: null,
project_id: 12297,
user: {
id: '6VbYatQkJjualbDX84D2j',
name: 'John Doe',
image: '',
plan: 'FREE',
},
metadata: {
extensions: ['comfyanonymous__ComfyUI'],
nodes: [
'CLIPTextEncode',
'EmptyLatentImage',
'VAEDecode',
'SaveImage',
'KSampler',
'CheckpointLoaderSimple',
],
checkpoints: ['v1-5-pruned-emaonly-fp16.safetensors'],
notes: [
{
text: 'text, watermark',
},
{
text: 'beautiful scenery nature glass bottle landscape, , purple galaxy bottle,',
},
],
groups: [],
},
},
});

View File

@@ -0,0 +1,248 @@
import { comfyIcuAuth } from '../../index';
import {
AppConnectionValueForAuthProperty,
createTrigger,
PiecePropValueSchema,
TriggerStrategy,
} from '@activepieces/pieces-framework';
import { comfyIcuApiCall, commonProps } from '../common';
import {
DedupeStrategy,
HttpMethod,
Polling,
pollingHelper,
} from '@activepieces/pieces-common';
import dayjs from 'dayjs';
const polling: Polling<
AppConnectionValueForAuthProperty<typeof comfyIcuAuth>,
{ workflow_id: string }
> = {
strategy: DedupeStrategy.TIMEBASED,
async items({ auth, propsValue, lastFetchEpochMS }) {
const { workflow_id } = propsValue;
const listRunsResponse = await comfyIcuApiCall({
apiKey: auth,
endpoint: `/workflows/${workflow_id}/runs`,
method: HttpMethod.GET,
});
const runs = listRunsResponse.body as {
id: string;
status: string;
created_at: string;
}[];
const result = [];
for (const run of runs) {
if (run.status === 'ERROR') {
const runDetailsResponse = await comfyIcuApiCall({
apiKey: auth,
endpoint: `/workflows/${workflow_id}/runs/${run.id}`,
method: HttpMethod.GET,
});
const runDetail = runDetailsResponse.body as { created_at: string };
result.push(runDetail);
if (lastFetchEpochMS === 0 && result.length === 5) break;
}
}
return result.map((run) => {
return {
epochMilliSeconds: dayjs(run.created_at).valueOf(),
data: run,
};
});
},
};
export const runFailedTrigger = createTrigger({
auth: comfyIcuAuth,
name: 'run-failed',
displayName: 'Run Failed',
description: 'Triggers when a workflow run is failed.',
props: {
workflow_id: commonProps.workflow_id,
},
type: TriggerStrategy.POLLING,
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: {
id: 'sve-GBc89WPLPncm4laYG',
created_at: '2025-05-23T15:09:44.273Z',
updated_at: '2025-05-23T15:09:48.128Z',
deleted_at: null,
started_at: '2025-05-23T15:09:45.700Z',
completed_at: '2025-05-23T15:09:47.997Z',
run_time: 3,
status: 'ERROR',
kind: 'comfyui',
data: {
prompt: {
'3': {
_meta: {
title: 'KSampler',
},
inputs: {
cfg: 8,
seed: 156680208700286,
model: ['4', 0],
steps: 20,
denoise: 1,
negative: ['7', 0],
positive: ['6', 0],
scheduler: 'normal',
latent_image: ['5', 0],
sampler_name: 'euler',
},
class_type: 'KSampler',
},
'4': {
_meta: {
title: 'Load Checkpoint',
},
inputs: {
ckpt_name: 'v1-5-pruned-emaonly-fp16.safetensors',
},
class_type: 'CheckpointLoaderSimple',
},
'5': {
_meta: {
title: 'Empty Latent Image',
},
inputs: {
width: 512,
height: 512,
batch_size: 1,
},
class_type: 'EmptyLatentImage',
},
'6': {
_meta: {
title: 'CLIP Text Encode (Prompt)',
},
inputs: {
clip: ['4', 1],
text: 'beautiful scenery nature glass bottle landscape, , purple galaxy bottle,',
},
class_type: 'CLIPTextEncode',
},
'7': {
_meta: {
title: 'CLIP Text Encode (Prompt)',
},
inputs: {
clip: ['4', 1],
text: 'text, watermark',
},
class_type: 'xx',
},
'8': {
_meta: {
title: 'VAE Decode',
},
inputs: {
vae: ['4', 2],
samples: ['3', 0],
},
class_type: 'VAEDecode',
},
'9': {
_meta: {
title: 'Save Image',
},
inputs: {
images: ['8', 0],
filename_prefix: 'ComfyUI',
},
class_type: 'SaveImage',
},
},
webhook: '',
},
workflow: null,
workflow_api: null,
workflow_id: '46AnCzsekAoAXLZqVQUXH',
output: {
error: {
details: {
error: {
type: 'invalid_prompt',
details: "Node ID '#7'",
message: 'Cannot execute because node xx does not exist.',
extra_info: {},
},
node_errors: {},
},
prompt_id: 'sve-GBc89WPLPncm4laYG',
traceback: [
' File "/app/headless.py", line 771, in run_workflow\n prompt_id = self.queue_prompt(wf["prompt"])\n',
' File "/app/headless.py", line 421, in queue_prompt\n raise RuntimeError(output)\n',
],
exception_type: 'invalid_prompt',
exception_message:
"Cannot execute because node xx does not exist.: Node ID '#7'",
},
output: {
error: {
details: {
error: {
type: 'invalid_prompt',
details: "Node ID '#7'",
message: 'Cannot execute because node xx does not exist.',
extra_info: {},
},
node_errors: {},
},
prompt_id: 'sve-GBc89WPLPncm4laYG',
traceback: [
' File "/app/headless.py", line 771, in run_workflow\n prompt_id = self.queue_prompt(wf["prompt"])\n',
' File "/app/headless.py", line 421, in queue_prompt\n raise RuntimeError(output)\n',
],
exception_type: 'invalid_prompt',
exception_message:
"Cannot execute because node xx does not exist.: Node ID '#7'",
},
},
},
error: null,
files: {},
name: null,
tags: [],
view_count: 0,
download_count: 0,
is_nsfw: false,
user_id: '6VbYatQkJjualbDX84D2j',
device: 'NVIDIA L4',
accelerator: 'L4',
visibility: 'PUBLIC',
parent_workflow_id: null,
parent_run_id: null,
api_key_id: 'GM2FYwGmgiNpXTNDqT6MT',
retry_count: 1,
client_agent: null,
webhook: '',
project_id: 12297,
user: null,
},
});

View File

@@ -0,0 +1,19 @@
{
"extends": "../../../../tsconfig.base.json",
"compilerOptions": {
"module": "commonjs",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noPropertyAccessFromIndexSignature": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
}
]
}

View File

@@ -0,0 +1,11 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs",
"outDir": "../../../../dist/out-tsc",
"declaration": true,
"types": ["node"]
},
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"],
"include": ["src/**/*.ts"]
}