Compare commits

..

3 Commits

Author SHA1 Message Date
waleed
3696658b03 changed zapier to use oauth 2025-12-09 14:28:04 -08:00
waleed
4d9ae94047 working checkbox, need to add zapier oauth2 2025-12-09 14:13:50 -08:00
waleed
ce72880935 feat(tools): added zapier 2025-12-09 13:20:48 -08:00
37 changed files with 2993 additions and 501 deletions

View File

@@ -4151,7 +4151,7 @@ export function DuckDuckGoIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='-108 -108 216 216'>
<circle r='108' fill='#d53' />
<circle r='96' fill='none' stroke='#ffffff' stroke-width='7' />
<circle r='96' fill='none' stroke='#ffffff' strokeWidth='7' />
<path
d='M-32-55C-62-48-51-6-51-6l19 93 7 3M-39-73h-8l11 4s-11 0-11 7c24-1 35 5 35 5'
fill='#ddd'
@@ -4199,3 +4199,25 @@ export function RssIcon(props: SVGProps<SVGSVGElement>) {
</svg>
)
}
export function ZapierIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
{...props}
width='800px'
height='800px'
viewBox='0 0 256 256'
version='1.1'
xmlns='http://www.w3.org/2000/svg'
xmlnsXlink='http://www.w3.org/1999/xlink'
>
<g>
<path
d='M128.080089,-0.000183105 C135.311053,0.0131003068 142.422517,0.624138494 149.335663,1.77979593 L149.335663,1.77979593 L149.335663,76.2997796 L202.166953,23.6044907 C208.002065,27.7488446 213.460883,32.3582023 218.507811,37.3926715 C223.557281,42.4271407 228.192318,47.8867213 232.346817,53.7047992 L232.346817,53.7047992 L179.512985,106.400063 L254.227854,106.400063 C255.387249,113.29414 256,120.36111 256,127.587243 L256,127.587243 L256,127.759881 C256,134.986013 255.387249,142.066204 254.227854,148.960282 L254.227854,148.960282 L179.500273,148.960282 L232.346817,201.642324 C228.192318,207.460402 223.557281,212.919983 218.523066,217.954452 L218.523066,217.954452 L218.507811,217.954452 C213.460883,222.988921 208.002065,227.6115 202.182208,231.742607 L202.182208,231.742607 L149.335663,179.04709 L149.335663,253.5672 C142.435229,254.723036 135.323765,255.333244 128.092802,255.348499 L128.092802,255.348499 L127.907197,255.348499 C120.673691,255.333244 113.590195,254.723036 106.677048,253.5672 L106.677048,253.5672 L106.677048,179.04709 L53.8457596,231.742607 C42.1780766,223.466917 31.977435,213.278734 23.6658953,201.642324 L23.6658953,201.642324 L76.4997269,148.960282 L1.78485803,148.960282 C0.612750404,142.052729 0,134.946095 0,127.719963 L0,127.719963 L0,127.349037 C0.0121454869,125.473817 0.134939797,123.182933 0.311311815,120.812834 L0.36577283,120.099764 C0.887996182,113.428547 1.78485803,106.400063 1.78485803,106.400063 L1.78485803,106.400063 L76.4997269,106.400063 L23.6658953,53.7047992 C27.8076812,47.8867213 32.4300059,42.4403618 37.4769335,37.4193681 L37.4769335,37.4193681 L37.5023588,37.3926715 C42.5391163,32.3582023 48.0106469,27.7488446 53.8457596,23.6044907 L53.8457596,23.6044907 L106.677048,76.2997796 L106.677048,1.77979593 C113.590195,0.624138494 120.688946,0.0131003068 127.932622,-0.000183105 L127.932622,-0.000183105 L128.080089,-0.000183105 Z M128.067377,95.7600714 L127.945335,95.7600714 C118.436262,95.7600714 109.32891,97.5001809 100.910584,100.661566 C97.7553011,109.043534 96.0085811,118.129275 95.9958684,127.613685 L95.9958684,127.733184 C96.0085811,137.217594 97.7553011,146.303589 100.923296,154.685303 C109.32891,157.846943 118.436262,159.587052 127.945335,159.587052 L128.067377,159.587052 C137.576449,159.587052 146.683802,157.846943 155.089415,154.685303 C158.257411,146.290368 160.004131,137.217594 160.004131,127.733184 L160.004131,127.613685 C160.004131,118.129275 158.257411,109.043534 155.089415,100.661566 C146.683802,97.5001809 137.576449,95.7600714 128.067377,95.7600714 Z'
fill='#FF4A00'
fillRule='nonzero'
/>
</g>
</svg>
)
}

View File

@@ -110,6 +110,7 @@ import {
WordpressIcon,
xIcon,
YouTubeIcon,
ZapierIcon,
ZendeskIcon,
ZepIcon,
ZoomIcon,
@@ -121,6 +122,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
zoom: ZoomIcon,
zep: ZepIcon,
zendesk: ZendeskIcon,
zapier: ZapierIcon,
youtube: YouTubeIcon,
x: xIcon,
wordpress: WordpressIcon,

View File

@@ -30,19 +30,15 @@ Verwende den Start-Block für alles, was aus dem Editor, deploy-to-API oder depl
<Card title="Schedule" href="/triggers/schedule">
Cron- oder intervallbasierte Ausführung
</Card>
<Card title="RSS Feed" href="/triggers/rss">
RSS- und Atom-Feeds auf neue Inhalte überwachen
</Card>
</Cards>
## Schneller Vergleich
| Trigger | Startbedingung |
|---------|-----------------|
| **Start** | Editor-Ausführungen, Deploy-to-API-Anfragen oder Chat-Nachrichten |
| **Start** | Editor-Ausführungen, deploy-to-API Anfragen oder Chat-Nachrichten |
| **Schedule** | Timer, der im Schedule-Block verwaltet wird |
| **Webhook** | Bei eingehender HTTP-Anfrage |
| **RSS Feed** | Neues Element im Feed veröffentlicht |
> Der Start-Block stellt immer `input`, `conversationId` und `files` Felder bereit. Füge benutzerdefinierte Felder zum Eingabeformat für zusätzliche strukturierte Daten hinzu.

View File

@@ -1,49 +0,0 @@
---
title: RSS-Feed
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Image } from '@/components/ui/image'
Der RSS-Feed-Block überwacht RSS- und Atom-Feeds wenn neue Einträge veröffentlicht werden, wird Ihr Workflow automatisch ausgelöst.
<div className="flex justify-center">
<Image
src="/static/blocks/rss.png"
alt="RSS-Feed-Block"
width={500}
height={400}
className="my-6"
/>
</div>
## Konfiguration
1. **RSS-Feed-Block hinzufügen** - Ziehen Sie den RSS-Feed-Block, um Ihren Workflow zu starten
2. **Feed-URL eingeben** - Fügen Sie die URL eines beliebigen RSS- oder Atom-Feeds ein
3. **Bereitstellen** - Stellen Sie Ihren Workflow bereit, um das Polling zu aktivieren
Nach der Bereitstellung wird der Feed jede Minute auf neue Einträge überprüft.
## Ausgabefelder
| Feld | Typ | Beschreibung |
|-------|------|-------------|
| `title` | string | Titel des Eintrags |
| `link` | string | Link des Eintrags |
| `pubDate` | string | Veröffentlichungsdatum |
| `item` | object | Rohdaten des Eintrags mit allen Feldern |
| `feed` | object | Rohdaten der Feed-Metadaten |
Greifen Sie direkt auf zugeordnete Felder zu (`<rss.title>`) oder verwenden Sie die Rohobjekte für beliebige Felder (`<rss.item.author>`, `<rss.feed.language>`).
## Anwendungsfälle
- **Inhaltsüberwachung** - Verfolgen Sie Blogs, Nachrichtenseiten oder Updates von Wettbewerbern
- **Podcast-Automatisierung** - Lösen Sie Workflows aus, wenn neue Episoden erscheinen
- **Release-Tracking** - Überwachen Sie GitHub-Releases, Changelogs oder Produkt-Updates
- **Social-Media-Aggregation** - Sammeln Sie Inhalte von Plattformen, die RSS-Feeds anbieten
<Callout>
RSS-Trigger werden nur für Einträge ausgelöst, die nach dem Speichern des Triggers veröffentlicht wurden. Bestehende Feed-Einträge werden nicht verarbeitet.
</Callout>

View File

@@ -110,6 +110,7 @@
"wordpress",
"x",
"youtube",
"zapier",
"zendesk",
"zep",
"zoom"

View File

@@ -0,0 +1,275 @@
---
title: Zapier
description: Execute actions across 7,000+ apps using Zapier AI Actions
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="zapier"
color="#FFFFFF"
/>
{/* MANUAL-CONTENT-START:intro */}
[Zapier](https://zapier.com/) connects 7,000+ apps and automates workflows without manual coding. The Zapier integration in Sim empowers you to execute, search, build, and manage powerful AI-driven actions across thousands of applications—all with plain English instructions.
With Zapier AI Actions in Sim, you can:
- **Execute Actions:** Instantly trigger any stored AI Action in your Zapier account. Launch emails, messages, project updates, document workflows, CRM updates, and much more.
- **List Actions:** Retrieve a list of your available AI Actions configured in Zapier. Discover what's possible and find the right tool for your workflow.
- **Search Apps:** Find apps in Zapiers ecosystem by name or keyword. Easily check if the app you need is supported before building automations.
- **Find Actions (Guess):** Describe what you want to accomplish in plain English (e.g., "send a Slack message", "create a Google Sheet row"), and let Zapiers AI suggest matching actions—even across unfamiliar apps or APIs.
- **Create Actions:** Programmatically define new AI Actions by specifying the target app, action type (write, read, search), and required parameters, directly from your workflow.
By combining these capabilities, you can search for apps, define new AI Actions, discover possible automations, list available actions, and execute any workflow—fully automated, with the power of both Sim and Zapier.
{/* MANUAL-CONTENT-END */}
## Usage Instructions
Connect to Zapier AI Actions to execute any of 30,000+ actions across 7,000+ apps. Send emails, create documents, update CRMs, post messages, and more - all through natural language instructions. Requires a Zapier AI Actions API key.
## Tools
### `zapier_execute_action`
Execute a stored AI Action in Zapier. Runs any of the 30,000+ actions across 7,000+ apps that Zapier supports.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Zapier AI Actions API key from actions.zapier.com/credentials |
| `actionId` | string | Yes | The ID of the AI Action to execute |
| `instructions` | string | Yes | Plain English instructions for what the action should do \(e.g., "Send a message about the weekly report to #general"\) |
| `previewOnly` | boolean | No | If true, preview the execution without actually running it |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `executionLogId` | string | Unique identifier for this execution \(can be used for feedback\) |
| `actionUsed` | string | Name of the action that was executed |
| `inputParams` | json | Parameters that were passed to the API |
| `resolvedParams` | json | Parameters that the AI resolved for execution |
| `results` | json | Results from the action execution |
| `resultFieldLabels` | json | Human-readable labels for result fields |
| `status` | string | Execution status: success, error, empty, preview, or halted |
| `error` | string | Error message if execution failed |
### `zapier_list_actions`
List all AI Actions configured in your Zapier account. Returns stored actions that can be executed.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Zapier AI Actions API key from actions.zapier.com/credentials |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `actions` | json | Array of configured AI Actions with id, description, actionType, app, appLabel, action, actionLabel, params, accountId, authenticationId, needs |
| `configurationLink` | string | Link to configure more actions in Zapier |
### `zapier_search_apps`
Search for apps available in Zapier. Returns apps with their available action counts.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Zapier AI Actions API key from actions.zapier.com/credentials |
| `query` | string | No | Optional search query to filter apps by name |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `apps` | json | Array of apps with app, name, logoUrl, authType, actions \(raw counts by type\), actionCount, writeActionCount, searchActionCount, readActionCount |
### `zapier_guess_actions`
Find relevant Zapier actions using natural language. Searches across 30,000+ actions to find the best matches for your query.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Zapier AI Actions API key from actions.zapier.com/credentials |
| `query` | string | Yes | Natural language description of what you want to do \(e.g., "send a Slack message", "create a Google Doc"\) |
| `actionTypes` | array | No | Types of actions to search for: write, search, read. If not specified, returns all types. |
| `count` | number | No | Maximum number of results to return \(default: 25\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `actions` | json | Array of matching actions with app, action, actionType, name \(combined app/action name\), description, image, and score |
### `zapier_create_action`
Create a new stored AI Action in Zapier. The action can then be executed with zapier_execute_action.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Zapier AI Actions API key from actions.zapier.com/credentials |
| `app` | string | Yes | The app identifier \(e.g., "slack", "gmail", "google-docs"\) |
| `action` | string | Yes | The action identifier \(e.g., "send_channel_message", "send_email"\) |
| `actionType` | string | No | Type of action: write, search, or read. Defaults to write. |
| `accountId` | number | No | Zapier account ID |
| `authenticationId` | number | No | Authentication ID for the app connection |
| `meta` | json | No | Metadata object with params labels, app_label, action_label, authentication_label, app_needs_auth |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | The ID of the created AI Action \(use this with execute_action\) |
| `description` | string | Description of the action |
| `actionType` | string | Type of action \(write, search, read, read_bulk, search_or_write, search_and_write\) |
| `app` | string | App identifier |
| `appLabel` | string | Human-readable app label from meta |
| `action` | string | Action identifier |
| `actionLabel` | string | Human-readable action label from meta |
### `zapier_stateless_execute`
Execute any Zapier action directly without creating a stored AI Action first. Provide the app, action, and instructions.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Zapier AI Actions API key from actions.zapier.com/credentials |
| `app` | string | Yes | The app to use \(e.g., "SlackAPI", "GoogleSheetsV2API", "GmailV2API"\) |
| `action` | string | Yes | The action to run \(e.g., "direct_message", "add_row", "send_email"\) |
| `instructions` | string | Yes | Plain English instructions about how to run the action \(e.g., "Send a message saying hello to #general"\) |
| `actionType` | string | No | Type of action: write, search, read, read_bulk, search_or_write, search_and_write |
| `previewOnly` | boolean | No | If true, preview the execution without actually running it |
| `authenticationId` | number | No | Authentication ID for the app connection |
| `accountId` | number | No | Zapier account ID |
| `providerId` | string | No | Provider ID for AI Actions |
| `tokenBudget` | number | No | Max tokens per field \(default: 1000\) |
| `skipParamGuessing` | boolean | No | Skip AI parameter guessing |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `executionLogId` | string | Unique identifier for this execution |
| `actionUsed` | string | Name of the action that was executed |
| `inputParams` | json | Parameters that were passed to the API |
| `resolvedParams` | json | Parameters that the AI resolved for execution |
| `results` | json | Results from the action execution |
| `resultFieldLabels` | json | Human-readable labels for result fields |
| `status` | string | Execution status: success, error, empty, preview, or halted |
| `error` | string | Error message if execution failed |
### `zapier_search_app_actions`
Search for available actions within a specific Zapier app. Returns all actions the app supports.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Zapier AI Actions API key from actions.zapier.com/credentials |
| `app` | string | Yes | The app identifier to search actions for \(e.g., "SlackAPI", "GmailV2API"\) |
| `query` | string | No | Optional search query to filter actions by name or description |
| `actionTypes` | array | No | Filter by action types: write, search, read, read_bulk, search_or_write, search_and_write. Defaults to write and search. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `actions` | json | Array of actions with app, action, actionType, displayName, description, relevancyScore, appNeedsAuth, appInfo |
### `zapier_get_action_details`
Get detailed information about a specific action including its required inputs (needs) and outputs (gives).
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Zapier AI Actions API key from actions.zapier.com/credentials |
| `app` | string | Yes | The app identifier \(e.g., "SlackAPI", "GmailV2API"\) |
| `action` | string | Yes | The action identifier \(e.g., "send_channel_message", "send_email"\) |
| `actionType` | string | No | Type of action: write, search, read. Defaults to write. |
| `includeNeeds` | boolean | No | Include input requirements \(needs\). Defaults to true. |
| `includeGives` | boolean | No | Include output specifications \(gives\). Defaults to false. |
| `includeSample` | boolean | No | Include sample execution result. Defaults to false. |
| `accountId` | number | No | Zapier account ID |
| `authenticationId` | number | No | Authentication ID for the app connection |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `action` | json | Action metadata including type, key, name, noun, and description |
| `needs` | json | Array of input requirements with key, type, label, required, helpText, defaultValue, choices, dependsOn |
| `gives` | json | Array of output fields with key, label, type, important, sample |
| `sample` | json | Sample execution result if requested |
| `customNeedsProbability` | number | Probability \(0-1\) that this action has custom/dynamic input fields |
### `zapier_update_action`
Update an existing stored AI Action configuration in Zapier.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Zapier AI Actions API key from actions.zapier.com/credentials |
| `actionId` | string | Yes | The ID of the AI Action to update |
| `app` | string | Yes | The app identifier \(e.g., "SlackAPI", "GmailV2API"\) |
| `action` | string | Yes | The action identifier \(e.g., "send_channel_message", "send_email"\) |
| `actionType` | string | No | Type of action: write, search, read, read_bulk, search_or_write, search_and_write |
| `accountId` | number | No | Zapier account ID |
| `authenticationId` | number | No | Authentication ID for the app connection |
| `meta` | json | No | Metadata object with params labels, app_label, action_label, authentication_label, app_needs_auth |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | The ID of the updated AI Action |
| `description` | string | Description of the action |
| `actionType` | string | Type of action |
| `app` | string | App identifier |
| `appLabel` | string | Human-readable app label |
| `action` | string | Action identifier |
| `actionLabel` | string | Human-readable action label |
### `zapier_delete_action`
Delete a stored AI Action from Zapier.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Zapier AI Actions API key from actions.zapier.com/credentials |
| `actionId` | string | Yes | The ID of the AI Action to delete |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `deleted` | boolean | Whether the action was successfully deleted |
| `message` | string | Status message |
## Notes
- Category: `tools`
- Type: `zapier`

View File

@@ -1,184 +0,0 @@
---
title: SFTP
description: Transferir archivos a través de SFTP (Protocolo de transferencia de
archivos SSH)
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="sftp"
color="#2D3748"
/>
{/* MANUAL-CONTENT-START:intro */}
[SFTP (Protocolo de transferencia de archivos SSH)](https://en.wikipedia.org/wiki/SSH_File_Transfer_Protocol) es un protocolo de red seguro que te permite subir, descargar y gestionar archivos en servidores remotos. SFTP opera sobre SSH, lo que lo hace ideal para transferencias de archivos automatizadas y cifradas, así como para la gestión remota de archivos dentro de flujos de trabajo modernos.
Con las herramientas SFTP integradas en Sim, puedes automatizar fácilmente el movimiento de archivos entre tus agentes de IA y sistemas o servidores externos. Esto permite a tus agentes gestionar intercambios críticos de datos, copias de seguridad, generación de documentos y orquestación de sistemas remotos, todo con una seguridad robusta.
**Funcionalidades clave disponibles a través de las herramientas SFTP:**
- **Subir archivos:** Transfiere sin problemas archivos de cualquier tipo desde tu flujo de trabajo a un servidor remoto, con soporte tanto para autenticación por contraseña como por clave privada SSH.
- **Descargar archivos:** Recupera archivos de servidores SFTP remotos directamente para su procesamiento, archivo o automatización adicional.
- **Listar y gestionar archivos:** Enumera directorios, elimina o crea archivos y carpetas, y gestiona permisos del sistema de archivos de forma remota.
- **Autenticación flexible:** Conéctate usando contraseñas tradicionales o claves SSH, con soporte para frases de contraseña y control de permisos.
- **Soporte para archivos grandes:** Gestiona programáticamente cargas y descargas de archivos grandes, con límites de tamaño incorporados para mayor seguridad.
Al integrar SFTP en Sim, puedes automatizar operaciones seguras de archivos como parte de cualquier flujo de trabajo, ya sea recopilación de datos, informes, mantenimiento de sistemas remotos o intercambio dinámico de contenido entre plataformas.
Las secciones a continuación describen las principales herramientas SFTP disponibles:
- **sftp_upload:** Sube uno o más archivos a un servidor remoto.
- **sftp_download:** Descarga archivos desde un servidor remoto a tu flujo de trabajo.
- **sftp_list:** Lista el contenido de directorios en un servidor SFTP remoto.
- **sftp_delete:** Elimina archivos o directorios de un servidor remoto.
- **sftp_create:** Crea nuevos archivos en un servidor SFTP remoto.
- **sftp_mkdir:** Crea nuevos directorios de forma remota.
Consulta la documentación de la herramienta a continuación para conocer los parámetros detallados de entrada y salida para cada operación.
{/* MANUAL-CONTENT-END */}
## Instrucciones de uso
Sube, descarga, lista y gestiona archivos en servidores remotos a través de SFTP. Compatible con autenticación por contraseña y clave privada para transferencias seguras de archivos.
## Herramientas
### `sftp_upload`
Subir archivos a un servidor SFTP remoto
#### Entrada
| Parámetro | Tipo | Obligatorio | Descripción |
| --------- | ---- | ----------- | ----------- |
| `host` | string | Sí | Nombre de host o dirección IP del servidor SFTP |
| `port` | number | Sí | Puerto del servidor SFTP \(predeterminado: 22\) |
| `username` | string | Sí | Nombre de usuario SFTP |
| `password` | string | No | Contraseña para autenticación \(si no se usa clave privada\) |
| `privateKey` | string | No | Clave privada para autenticación \(formato OpenSSH\) |
| `passphrase` | string | No | Frase de contraseña para clave privada cifrada |
| `remotePath` | string | Sí | Directorio de destino en el servidor remoto |
| `files` | file[] | No | Archivos para subir |
| `fileContent` | string | No | Contenido directo del archivo para subir \(para archivos de texto\) |
| `fileName` | string | No | Nombre del archivo cuando se usa contenido directo |
| `overwrite` | boolean | No | Si se deben sobrescribir archivos existentes \(predeterminado: true\) |
| `permissions` | string | No | Permisos del archivo \(p. ej., 0644\) |
#### Salida
| Parámetro | Tipo | Descripción |
| --------- | ---- | ----------- |
| `success` | boolean | Si la subida fue exitosa |
| `uploadedFiles` | json | Array de detalles de archivos subidos \(nombre, rutaRemota, tamaño\) |
| `message` | string | Mensaje de estado de la operación |
### `sftp_download`
Descargar un archivo desde un servidor SFTP remoto
#### Entrada
| Parámetro | Tipo | Obligatorio | Descripción |
| --------- | ---- | -------- | ----------- |
| `host` | string | Sí | Nombre de host o dirección IP del servidor SFTP |
| `port` | number | Sí | Puerto del servidor SFTP \(predeterminado: 22\) |
| `username` | string | Sí | Nombre de usuario SFTP |
| `password` | string | No | Contraseña para autenticación \(si no se usa clave privada\) |
| `privateKey` | string | No | Clave privada para autenticación \(formato OpenSSH\) |
| `passphrase` | string | No | Frase de contraseña para clave privada cifrada |
| `remotePath` | string | Sí | Ruta al archivo en el servidor remoto |
| `encoding` | string | No | Codificación de salida: utf-8 para texto, base64 para binario \(predeterminado: utf-8\) |
#### Salida
| Parámetro | Tipo | Descripción |
| --------- | ---- | ----------- |
| `success` | boolean | Si la descarga fue exitosa |
| `fileName` | string | Nombre del archivo descargado |
| `content` | string | Contenido del archivo \(texto o codificado en base64\) |
| `size` | number | Tamaño del archivo en bytes |
| `encoding` | string | Codificación del contenido \(utf-8 o base64\) |
| `message` | string | Mensaje de estado de la operación |
### `sftp_list`
Listar archivos y directorios en un servidor SFTP remoto
#### Entrada
| Parámetro | Tipo | Obligatorio | Descripción |
| --------- | ---- | -------- | ----------- |
| `host` | string | Sí | Nombre de host o dirección IP del servidor SFTP |
| `port` | number | Sí | Puerto del servidor SFTP \(predeterminado: 22\) |
| `username` | string | Sí | Nombre de usuario SFTP |
| `password` | string | No | Contraseña para autenticación \(si no se usa clave privada\) |
| `privateKey` | string | No | Clave privada para autenticación \(formato OpenSSH\) |
| `passphrase` | string | No | Frase de contraseña para clave privada cifrada |
| `remotePath` | string | Sí | Ruta del directorio en el servidor remoto |
| `detailed` | boolean | No | Incluir información detallada de archivos \(tamaño, permisos, fecha de modificación\) |
#### Salida
| Parámetro | Tipo | Descripción |
| --------- | ---- | ----------- |
| `success` | boolean | Si la operación fue exitosa |
| `path` | string | Ruta del directorio que fue listado |
| `entries` | json | Array de entradas del directorio con nombre, tipo, tamaño, permisos, modifiedAt |
| `count` | number | Número de entradas en el directorio |
| `message` | string | Mensaje de estado de la operación |
### `sftp_delete`
Eliminar un archivo o directorio en un servidor SFTP remoto
#### Entrada
| Parámetro | Tipo | Requerido | Descripción |
| --------- | ---- | -------- | ----------- |
| `host` | string | Sí | Nombre de host o dirección IP del servidor SFTP |
| `port` | number | Sí | Puerto del servidor SFTP \(predeterminado: 22\) |
| `username` | string | Sí | Nombre de usuario SFTP |
| `password` | string | No | Contraseña para autenticación \(si no se usa clave privada\) |
| `privateKey` | string | No | Clave privada para autenticación \(formato OpenSSH\) |
| `passphrase` | string | No | Frase de contraseña para clave privada cifrada |
| `remotePath` | string | Sí | Ruta al archivo o directorio a eliminar |
| `recursive` | boolean | No | Eliminar directorios recursivamente |
#### Salida
| Parámetro | Tipo | Descripción |
| --------- | ---- | ----------- |
| `success` | boolean | Si la eliminación fue exitosa |
| `deletedPath` | string | Ruta que fue eliminada |
| `message` | string | Mensaje de estado de la operación |
### `sftp_mkdir`
Crear un directorio en un servidor SFTP remoto
#### Entrada
| Parámetro | Tipo | Obligatorio | Descripción |
| --------- | ---- | ----------- | ----------- |
| `host` | string | Sí | Nombre de host o dirección IP del servidor SFTP |
| `port` | number | Sí | Puerto del servidor SFTP \(predeterminado: 22\) |
| `username` | string | Sí | Nombre de usuario SFTP |
| `password` | string | No | Contraseña para autenticación \(si no se usa clave privada\) |
| `privateKey` | string | No | Clave privada para autenticación \(formato OpenSSH\) |
| `passphrase` | string | No | Frase de contraseña para clave privada cifrada |
| `remotePath` | string | Sí | Ruta para el nuevo directorio |
| `recursive` | boolean | No | Crear directorios principales si no existen |
#### Salida
| Parámetro | Tipo | Descripción |
| --------- | ---- | ----------- |
| `success` | boolean | Si el directorio se creó correctamente |
| `createdPath` | string | Ruta del directorio creado |
| `message` | string | Mensaje de estado de la operación |
## Notas
- Categoría: `tools`
- Tipo: `sftp`

View File

@@ -30,9 +30,6 @@ Utiliza el bloque Start para todo lo que se origina desde el editor, despliegue
<Card title="Schedule" href="/triggers/schedule">
Ejecución basada en cron o intervalos
</Card>
<Card title="RSS Feed" href="/triggers/rss">
Monitorea feeds RSS y Atom para nuevo contenido
</Card>
</Cards>
## Comparación rápida
@@ -42,7 +39,6 @@ Utiliza el bloque Start para todo lo que se origina desde el editor, despliegue
| **Start** | Ejecuciones del editor, solicitudes de despliegue a API o mensajes de chat |
| **Schedule** | Temporizador gestionado en el bloque de programación |
| **Webhook** | Al recibir una solicitud HTTP entrante |
| **RSS Feed** | Nuevo elemento publicado en el feed |
> El bloque Start siempre expone los campos `input`, `conversationId` y `files`. Añade campos personalizados al formato de entrada para datos estructurados adicionales.

View File

@@ -1,49 +0,0 @@
---
title: Feed RSS
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Image } from '@/components/ui/image'
El bloque de Feed RSS monitorea feeds RSS y Atom cuando se publican nuevos elementos, tu flujo de trabajo se activa automáticamente.
<div className="flex justify-center">
<Image
src="/static/blocks/rss.png"
alt="Bloque de Feed RSS"
width={500}
height={400}
className="my-6"
/>
</div>
## Configuración
1. **Añadir bloque de Feed RSS** - Arrastra el bloque de Feed RSS para iniciar tu flujo de trabajo
2. **Introducir URL del feed** - Pega la URL de cualquier feed RSS o Atom
3. **Implementar** - Implementa tu flujo de trabajo para activar el sondeo
Una vez implementado, el feed se comprueba cada minuto en busca de nuevos elementos.
## Campos de salida
| Campo | Tipo | Descripción |
|-------|------|-------------|
| `title` | string | Título del elemento |
| `link` | string | Enlace del elemento |
| `pubDate` | string | Fecha de publicación |
| `item` | object | Elemento en bruto con todos los campos |
| `feed` | object | Metadatos en bruto del feed |
Accede a los campos mapeados directamente (`<rss.title>`) o utiliza los objetos en bruto para cualquier campo (`<rss.item.author>`, `<rss.feed.language>`).
## Casos de uso
- **Monitoreo de contenido** - Sigue blogs, sitios de noticias o actualizaciones de competidores
- **Automatización de podcasts** - Activa flujos de trabajo cuando se publican nuevos episodios
- **Seguimiento de lanzamientos** - Monitorea lanzamientos de GitHub, registros de cambios o actualizaciones de productos
- **Agregación social** - Recopila contenido de plataformas que exponen feeds RSS
<Callout>
Los disparadores RSS solo se activan para elementos publicados después de guardar el disparador. Los elementos existentes en el feed no se procesan.
</Callout>

View File

@@ -21,28 +21,24 @@ import { Image } from '@/components/ui/image'
Utilisez le bloc Démarrer pour tout ce qui provient de l'éditeur, du déploiement vers l'API ou des expériences de déploiement vers le chat. D'autres déclencheurs restent disponibles pour les flux de travail basés sur des événements :
<Cards>
<Card title="Start" href="/triggers/start">
<Card title="Démarrer" href="/triggers/start">
Point d'entrée unifié qui prend en charge les exécutions de l'éditeur, les déploiements d'API et les déploiements de chat
</Card>
<Card title="Webhook" href="/triggers/webhook">
Recevoir des charges utiles de webhook externes
</Card>
<Card title="Schedule" href="/triggers/schedule">
<Card title="Planification" href="/triggers/schedule">
Exécution basée sur cron ou intervalle
</Card>
<Card title="RSS Feed" href="/triggers/rss">
Surveiller les flux RSS et Atom pour du nouveau contenu
</Card>
</Cards>
## Comparaison rapide
| Déclencheur | Condition de démarrage |
|---------|-----------------|
| **Start** | Exécutions de l'éditeur, requêtes de déploiement d'API ou messages de chat |
| **Schedule** | Minuteur géré dans le bloc de planification |
| **Démarrer** | Exécutions de l'éditeur, requêtes de déploiement vers l'API ou messages de chat |
| **Planification** | Minuteur géré dans le bloc de planification |
| **Webhook** | Sur requête HTTP entrante |
| **RSS Feed** | Nouvel élément publié dans le flux |
> Le bloc Démarrer expose toujours les champs `input`, `conversationId` et `files`. Ajoutez des champs personnalisés au format d'entrée pour des données structurées supplémentaires.

View File

@@ -1,49 +0,0 @@
---
title: Flux RSS
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Image } from '@/components/ui/image'
Le bloc Flux RSS surveille les flux RSS et Atom lorsque de nouveaux éléments sont publiés, votre workflow se déclenche automatiquement.
<div className="flex justify-center">
<Image
src="/static/blocks/rss.png"
alt="Bloc Flux RSS"
width={500}
height={400}
className="my-6"
/>
</div>
## Configuration
1. **Ajouter le bloc Flux RSS** - Faites glisser le bloc Flux RSS pour démarrer votre workflow
2. **Saisir l'URL du flux** - Collez l'URL de n'importe quel flux RSS ou Atom
3. **Déployer** - Déployez votre workflow pour activer l'interrogation
Une fois déployé, le flux est vérifié chaque minute pour détecter de nouveaux éléments.
## Champs de sortie
| Champ | Type | Description |
|-------|------|-------------|
| `title` | string | Titre de l'élément |
| `link` | string | Lien de l'élément |
| `pubDate` | string | Date de publication |
| `item` | object | Élément brut avec tous les champs |
| `feed` | object | Métadonnées brutes du flux |
Accédez directement aux champs mappés (`<rss.title>`) ou utilisez les objets bruts pour n'importe quel champ (`<rss.item.author>`, `<rss.feed.language>`).
## Cas d'utilisation
- **Surveillance de contenu** - Suivez les blogs, sites d'actualités ou mises à jour des concurrents
- **Automatisation de podcast** - Déclenchez des workflows lors de la sortie de nouveaux épisodes
- **Suivi des versions** - Surveillez les versions GitHub, les journaux de modifications ou les mises à jour de produits
- **Agrégation sociale** - Collectez du contenu à partir de plateformes qui exposent des flux RSS
<Callout>
Les déclencheurs RSS ne s'activent que pour les éléments publiés après l'enregistrement du déclencheur. Les éléments existants du flux ne sont pas traités.
</Callout>

View File

@@ -21,28 +21,24 @@ import { Image } from '@/components/ui/image'
エディタ、APIへのデプロイ、またはチャットへのデプロイエクスペリエンスから始まるすべてのものにはスタートブロックを使用します。イベント駆動型ワークフローには他のトリガーも利用可能です
<Cards>
<Card title="Start" href="/triggers/start">
<Card title="スタート" href="/triggers/start">
エディタ実行、APIデプロイメント、チャットデプロイメントをサポートする統合エントリーポイント
</Card>
<Card title="Webhook" href="/triggers/webhook">
外部のwebhookペイロードを受信
<Card title="ウェブフック" href="/triggers/webhook">
外部ウェブフックペイロードを受信
</Card>
<Card title="Schedule" href="/triggers/schedule">
<Card title="スケジュール" href="/triggers/schedule">
Cronまたは間隔ベースの実行
</Card>
<Card title="RSS Feed" href="/triggers/rss">
新しいコンテンツのRSSとAtomフィードを監視
</Card>
</Cards>
## クイック比較
| トリガー | 開始条件 |
|---------|-----------------|
| **Start** | エディタ実行、APIへのデプロイリクエスト、またはチャットメッセージ |
| **Schedule** | スケジュールブロックで管理されるタイマー |
| **Webhook** | 受信HTTPリクエスト時 |
| **RSS Feed** | フィードに新しいアイテムが公開された時 |
| **スタート** | エディタ実行、APIへのデプロイリクエスト、またはチャットメッセージ |
| **スケジュール** | スケジュールブロックで管理されるタイマー |
| **ウェブフック** | 受信HTTPリクエスト時 |
> スタートブロックは常に `input`、`conversationId`、および `files` フィールドを公開します。追加の構造化データには入力フォーマットにカスタムフィールドを追加してください。

View File

@@ -1,49 +0,0 @@
---
title: RSSフィード
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Image } from '@/components/ui/image'
RSSフィードブロックはRSSとAtomフィードを監視します - 新しいアイテムが公開されると、ワークフローが自動的にトリガーされます。
<div className="flex justify-center">
<Image
src="/static/blocks/rss.png"
alt="RSSフィードブロック"
width={500}
height={400}
className="my-6"
/>
</div>
## 設定
1. **RSSフィードブロックを追加** - RSSフィードブロックをドラッグしてワークフローを開始
2. **フィードURLを入力** - 任意のRSSまたはAtomフィードのURLを貼り付け
3. **デプロイ** - ワークフローをデプロイしてポーリングを有効化
デプロイ後、フィードは1分ごとに新しいアイテムをチェックします。
## 出力フィールド
| フィールド | 型 | 説明 |
|-------|------|-------------|
| `title` | string | アイテムのタイトル |
| `link` | string | アイテムのリンク |
| `pubDate` | string | 公開日 |
| `item` | object | すべてのフィールドを含む生のアイテム |
| `feed` | object | 生のフィードメタデータ |
マッピングされたフィールドに直接アクセスするか(`<rss.title>`)、任意のフィールドに生のオブジェクトを使用します(`<rss.item.author>`、`<rss.feed.language>`)。
## ユースケース
- **コンテンツ監視** - ブログ、ニュースサイト、または競合他社の更新を追跡
- **ポッドキャスト自動化** - 新しいエピソードが公開されたときにワークフローをトリガー
- **リリース追跡** - GitHubリリース、変更ログ、または製品アップデートを監視
- **ソーシャルアグリゲーション** - RSSフィードを公開しているプラットフォームからコンテンツを収集
<Callout>
RSSトリガーは、トリガーを保存した後に公開されたアイテムに対してのみ実行されます。既存のフィードアイテムは処理されません。
</Callout>

View File

@@ -21,28 +21,24 @@ import { Image } from '@/components/ui/image'
使用 Start 块处理从编辑器、部署到 API 或部署到聊天的所有操作。其他触发器可用于事件驱动的工作流:
<Cards>
<Card title="开始" href="/triggers/start">
<Card title="Start" href="/triggers/start">
支持编辑器运行、API 部署和聊天部署的统一入口点
</Card>
<Card title="Webhook" href="/triggers/webhook">
接收外部 webhook 负载
</Card>
<Card title="计划" href="/triggers/schedule">
<Card title="Schedule" href="/triggers/schedule">
基于 Cron 或间隔的执行
</Card>
<Card title="RSS 源" href="/triggers/rss">
监控 RSS 和 Atom 源的新内容
</Card>
</Cards>
## 快速对比
| 触发器 | 启动条件 |
|---------|-----------------|
| **开始** | 编辑器运行、部署到 API 请求或聊天消息 |
| **计划** | 在计划块中管理的计时器 |
| **Webhook** | 收到入站 HTTP 请求 |
| **RSS 源** | 源中发布了新项目 |
| **Start** | 编辑器运行、部署到 API 请求或聊天消息 |
| **Schedule** | 在 Schedule 块中管理的计时器 |
| **Webhook** | 收到入站 HTTP 请求 |
> Start 块始终公开 `input`、`conversationId` 和 `files` 字段。通过向输入格式添加自定义字段来增加结构化数据。

View File

@@ -1,49 +0,0 @@
---
title: RSS 订阅源
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Image } from '@/components/ui/image'
RSS 订阅源模块监控 RSS 和 Atom 订阅源——当有新内容发布时,您的工作流会自动触发。
<div className="flex justify-center">
<Image
src="/static/blocks/rss.png"
alt="RSS 订阅源模块"
width={500}
height={400}
className="my-6"
/>
</div>
## 配置
1. **添加 RSS 订阅源模块** - 拖动 RSS 订阅源模块以开始您的工作流
2. **输入订阅源 URL** - 粘贴任意 RSS 或 Atom 订阅源的 URL
3. **部署** - 部署您的工作流以激活轮询
部署后,订阅源每分钟检查一次是否有新内容。
## 输出字段
| 字段 | 类型 | 描述 |
|-------|------|-------------|
| `title` | string | 内容标题 |
| `link` | string | 内容链接 |
| `pubDate` | string | 发布日期 |
| `item` | object | 包含所有字段的原始内容 |
| `feed` | object | 原始订阅源元数据 |
可以直接访问映射字段 (`<rss.title>`),或者使用原始对象访问任意字段 (`<rss.item.author>`, `<rss.feed.language>`)。
## 使用场景
- **内容监控** - 跟踪博客、新闻网站或竞争对手的更新
- **播客自动化** - 当新剧集发布时触发工作流
- **版本跟踪** - 监控 GitHub 发布、更新日志或产品更新
- **社交聚合** - 收集支持 RSS 订阅源的平台内容
<Callout>
RSS 触发器仅对您保存触发器后发布的内容生效。现有的订阅源内容不会被处理。
</Callout>

View File

@@ -5760,9 +5760,9 @@ checksums:
content/1: e71056df0f7b2eb3b2f271f21d0052cc
content/2: da2b445db16c149f56558a4ea876a5f0
content/3: cec18f48b2cd7974eb556880e6604f7f
content/4: b200402d6a01ab565fd56d113c530ef6
content/4: c187ae3362455acfe43282399f0d163a
content/5: 4c3a5708af82c1ee42a12d14fd34e950
content/6: 64fbd5b16f4cff18ba976492a275c05e
content/6: 12a43b499c1e8bb06b050964053ebde3
content/7: a28151eeb5ba3518b33809055b04f0f6
content/8: cffe5b901d78ebf2000d07dc7579533e
content/9: 73486253d24eeff7ac44dfd0c8868d87
@@ -49300,17 +49300,3 @@ checksums:
content/42: dc2cfed837ea55adfa23bd7c87d5299d
content/43: b3f310d5ef115bea5a8b75bf25d7ea9a
content/44: df2ef65659b8ea0a13916358943f965b
ebed3bd73520bf81399749586796f9d0:
meta/title: 1763bebd6001500cdfc1b5127b0c1cde
content/0: eb0ed7078f192304703144f4cac3442f
content/1: ba5ba29787a0eb35c46dacb3544bafe1
content/2: 5ed74bf0e91235f71eeceb25712ad2d3
content/3: 0441638444240cd20a6c69ea1d3afbb1
content/4: ef102e10f1402df7290680c1e9df8a5e
content/5: 95afa83a30cb01724b932b19dd69f20b
content/6: 8ebc5e005f61d253c006824168abaf22
content/7: df81a49b54d378523fb74aa0b0fb8be1
content/8: c5fb77d31bae86aa85f2b2b84ce0beab
content/9: 7a3be8a3771ee428ecf09008e42c0e2e
content/10: 42e4caf9b036a8d7726a8968f3ed201f
content/11: e74f8ee79105babdaa8dfec520ecdf74

View File

@@ -262,6 +262,8 @@ const SCOPE_DESCRIPTIONS: Record<string, string> = {
'sharing.write': 'Share files and folders with others',
// WordPress.com scopes
global: 'Full access to manage your WordPress.com sites, posts, pages, media, and settings',
// Zapier AI Actions scopes
'nla:exposed_actions:execute': 'Execute Zapier AI Actions on your behalf',
}
function getScopeDescription(scope: string): string {

View File

@@ -0,0 +1,808 @@
import { ZapierIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import { AuthMode } from '@/blocks/types'
import type { ZapierResponse } from '@/tools/zapier/types'
export const ZapierBlock: BlockConfig<ZapierResponse> = {
type: 'zapier',
name: 'Zapier',
description: 'Execute actions across 7,000+ apps using Zapier AI Actions',
authMode: AuthMode.OAuth,
longDescription:
'Connect to Zapier AI Actions to execute any of 30,000+ actions across 7,000+ apps. Send emails, create documents, update CRMs, post messages, and more - all through natural language instructions.',
docsLink: 'https://docs.sim.ai/tools/zapier',
category: 'tools',
bgColor: '#FFFFFF',
icon: ZapierIcon,
subBlocks: [
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
options: [
{ label: 'Execute Action', id: 'execute' },
{ label: 'Stateless Execute', id: 'stateless_execute' },
{ label: 'List Actions', id: 'list' },
{ label: 'Search Apps', id: 'search_apps' },
{ label: 'Search App Actions', id: 'search_app_actions' },
{ label: 'Find Actions', id: 'guess' },
{ label: 'Get Action Details', id: 'get_action_details' },
{ label: 'Create Action', id: 'create' },
{ label: 'Update Action', id: 'update' },
{ label: 'Delete Action', id: 'delete' },
],
value: () => 'execute',
},
{
id: 'credential',
title: 'Zapier Account',
type: 'oauth-input',
serviceId: 'zapier',
requiredScopes: ['openid', 'nla:exposed_actions:execute'],
placeholder: 'Select Zapier account',
required: true,
},
// Execute Action fields
{
id: 'actionId',
title: 'Action ID',
type: 'short-input',
placeholder: 'Enter the AI Action ID to execute',
condition: {
field: 'operation',
value: 'execute',
},
required: true,
},
{
id: 'instructions',
title: 'Instructions',
type: 'long-input',
placeholder:
'Describe what you want to do in plain English (e.g., "Send a message to #general saying hello")',
condition: {
field: 'operation',
value: 'execute',
},
required: true,
},
{
id: 'params',
title: 'Parameters',
type: 'code',
placeholder: '{\n "channel": {"mode": "locked", "value": "#general"}\n}',
condition: {
field: 'operation',
value: 'execute',
},
},
{
id: 'previewOnly',
title: 'Preview Mode',
type: 'dropdown',
options: [
{ label: 'Execute', id: 'false' },
{ label: 'Preview Only', id: 'true' },
],
value: () => 'false',
condition: {
field: 'operation',
value: 'execute',
},
},
// Search Apps fields
{
id: 'searchQuery',
title: 'Search Query',
type: 'short-input',
placeholder: 'Enter app name to search (e.g., "slack", "google")',
condition: {
field: 'operation',
value: 'search_apps',
},
},
// Guess Actions fields
{
id: 'guessQuery',
title: 'What do you want to do?',
type: 'long-input',
placeholder:
'Describe in plain English (e.g., "send a Slack message", "create a Google Doc")',
condition: {
field: 'operation',
value: 'guess',
},
required: true,
},
{
id: 'actionTypes',
title: 'Action Types',
type: 'checkbox-list',
options: [
{ label: 'Write (Create/Send)', id: 'actionTypes_write' },
{ label: 'Search (Find)', id: 'actionTypes_search' },
{ label: 'Read (Get)', id: 'actionTypes_read' },
],
condition: {
field: 'operation',
value: 'guess',
},
},
{
id: 'resultCount',
title: 'Max Results',
type: 'short-input',
placeholder: '25',
condition: {
field: 'operation',
value: 'guess',
},
},
// Create Action fields
{
id: 'app',
title: 'App',
type: 'short-input',
placeholder: 'App identifier (e.g., "slack", "gmail")',
condition: {
field: 'operation',
value: 'create',
},
required: true,
},
{
id: 'action',
title: 'Action',
type: 'short-input',
placeholder: 'Action identifier (e.g., "send_channel_message")',
condition: {
field: 'operation',
value: 'create',
},
required: true,
},
{
id: 'createActionType',
title: 'Action Type',
type: 'dropdown',
options: [
{ label: 'Write', id: 'write' },
{ label: 'Search', id: 'search' },
{ label: 'Read', id: 'read' },
],
value: () => 'write',
condition: {
field: 'operation',
value: 'create',
},
},
{
id: 'createParams',
title: 'Parameters',
type: 'code',
placeholder: '{\n "channel": "#general"\n}',
condition: {
field: 'operation',
value: 'create',
},
},
// Stateless Execute fields
{
id: 'statelessApp',
title: 'App',
type: 'short-input',
placeholder: 'App identifier (e.g., "SlackAPI", "GmailV2API")',
condition: {
field: 'operation',
value: 'stateless_execute',
},
required: true,
},
{
id: 'statelessAction',
title: 'Action',
type: 'short-input',
placeholder: 'Action identifier (e.g., "send_channel_message")',
condition: {
field: 'operation',
value: 'stateless_execute',
},
required: true,
},
{
id: 'statelessInstructions',
title: 'Instructions',
type: 'long-input',
placeholder: 'Describe what you want to do in plain English',
condition: {
field: 'operation',
value: 'stateless_execute',
},
required: true,
},
{
id: 'statelessActionType',
title: 'Action Type',
type: 'dropdown',
options: [
{ label: 'Write', id: 'write' },
{ label: 'Search', id: 'search' },
{ label: 'Read', id: 'read' },
],
value: () => 'write',
condition: {
field: 'operation',
value: 'stateless_execute',
},
},
{
id: 'statelessParams',
title: 'Parameters',
type: 'code',
placeholder: '{\n "channel": {"mode": "locked", "value": "#general"}\n}',
condition: {
field: 'operation',
value: 'stateless_execute',
},
},
{
id: 'statelessPreviewOnly',
title: 'Preview Mode',
type: 'dropdown',
options: [
{ label: 'Execute', id: 'false' },
{ label: 'Preview Only', id: 'true' },
],
value: () => 'false',
condition: {
field: 'operation',
value: 'stateless_execute',
},
},
// Search App Actions fields
{
id: 'searchAppActionsApp',
title: 'App',
type: 'short-input',
placeholder: 'App identifier (e.g., "SlackAPI", "GmailV2API")',
condition: {
field: 'operation',
value: 'search_app_actions',
},
required: true,
},
{
id: 'searchAppActionsQuery',
title: 'Search Query',
type: 'short-input',
placeholder: 'Optional: filter actions by name',
condition: {
field: 'operation',
value: 'search_app_actions',
},
},
{
id: 'searchAppActionsTypes',
title: 'Action Types',
type: 'checkbox-list',
options: [
{ label: 'Write (Create/Send)', id: 'searchAppActionsTypes_write' },
{ label: 'Search (Find)', id: 'searchAppActionsTypes_search' },
{ label: 'Read (Get)', id: 'searchAppActionsTypes_read' },
],
condition: {
field: 'operation',
value: 'search_app_actions',
},
},
// Get Action Details fields
{
id: 'detailsApp',
title: 'App',
type: 'short-input',
placeholder: 'App identifier (e.g., "SlackAPI", "GmailV2API")',
condition: {
field: 'operation',
value: 'get_action_details',
},
required: true,
},
{
id: 'detailsAction',
title: 'Action',
type: 'short-input',
placeholder: 'Action identifier (e.g., "send_channel_message")',
condition: {
field: 'operation',
value: 'get_action_details',
},
required: true,
},
{
id: 'detailsActionType',
title: 'Action Type',
type: 'dropdown',
options: [
{ label: 'Write', id: 'write' },
{ label: 'Search', id: 'search' },
{ label: 'Read', id: 'read' },
],
value: () => 'write',
condition: {
field: 'operation',
value: 'get_action_details',
},
},
{
id: 'includeNeeds',
title: 'Include Inputs (Needs)',
type: 'dropdown',
options: [
{ label: 'Yes', id: 'true' },
{ label: 'No', id: 'false' },
],
value: () => 'true',
condition: {
field: 'operation',
value: 'get_action_details',
},
},
{
id: 'includeGives',
title: 'Include Outputs (Gives)',
type: 'dropdown',
options: [
{ label: 'Yes', id: 'true' },
{ label: 'No', id: 'false' },
],
value: () => 'false',
condition: {
field: 'operation',
value: 'get_action_details',
},
},
{
id: 'includeSample',
title: 'Include Sample',
type: 'dropdown',
options: [
{ label: 'Yes', id: 'true' },
{ label: 'No', id: 'false' },
],
value: () => 'false',
condition: {
field: 'operation',
value: 'get_action_details',
},
},
// Update Action fields
{
id: 'updateActionId',
title: 'Action ID',
type: 'short-input',
placeholder: 'The ID of the AI Action to update',
condition: {
field: 'operation',
value: 'update',
},
required: true,
},
{
id: 'updateApp',
title: 'App',
type: 'short-input',
placeholder: 'App identifier (e.g., "SlackAPI")',
condition: {
field: 'operation',
value: 'update',
},
required: true,
},
{
id: 'updateAction',
title: 'Action',
type: 'short-input',
placeholder: 'Action identifier (e.g., "send_channel_message")',
condition: {
field: 'operation',
value: 'update',
},
required: true,
},
{
id: 'updateActionType',
title: 'Action Type',
type: 'dropdown',
options: [
{ label: 'Write', id: 'write' },
{ label: 'Search', id: 'search' },
{ label: 'Read', id: 'read' },
],
value: () => 'write',
condition: {
field: 'operation',
value: 'update',
},
},
{
id: 'updateParams',
title: 'Parameters',
type: 'code',
placeholder: '{\n "channel": "#general"\n}',
condition: {
field: 'operation',
value: 'update',
},
},
// Delete Action fields
{
id: 'deleteActionId',
title: 'Action ID',
type: 'short-input',
placeholder: 'The ID of the AI Action to delete',
condition: {
field: 'operation',
value: 'delete',
},
required: true,
},
],
tools: {
access: [
'zapier_execute_action',
'zapier_list_actions',
'zapier_search_apps',
'zapier_guess_actions',
'zapier_create_action',
'zapier_stateless_execute',
'zapier_search_app_actions',
'zapier_get_action_details',
'zapier_update_action',
'zapier_delete_action',
],
config: {
tool: (params) => {
switch (params.operation) {
case 'execute':
return 'zapier_execute_action'
case 'stateless_execute':
return 'zapier_stateless_execute'
case 'list':
return 'zapier_list_actions'
case 'search_apps':
return 'zapier_search_apps'
case 'search_app_actions':
return 'zapier_search_app_actions'
case 'guess':
return 'zapier_guess_actions'
case 'get_action_details':
return 'zapier_get_action_details'
case 'create':
return 'zapier_create_action'
case 'update':
return 'zapier_update_action'
case 'delete':
return 'zapier_delete_action'
default:
throw new Error(`Invalid Zapier operation: ${params.operation}`)
}
},
params: (params) => {
const {
operation,
credential,
actionId,
instructions,
params: execParams,
previewOnly,
searchQuery,
guessQuery,
resultCount,
app,
action,
createActionType,
createParams,
statelessApp,
statelessAction,
statelessInstructions,
statelessActionType,
statelessParams,
statelessPreviewOnly,
searchAppActionsApp,
searchAppActionsQuery,
detailsApp,
detailsAction,
detailsActionType,
includeNeeds,
includeGives,
includeSample,
updateActionId,
updateApp,
updateAction,
updateActionType,
updateParams,
deleteActionId,
} = params
const baseParams: Record<string, any> = { credential }
// Helper to parse JSON params
const parseJsonParams = (jsonParams: any) => {
if (!jsonParams) return undefined
try {
return typeof jsonParams === 'string' ? JSON.parse(jsonParams) : jsonParams
} catch {
throw new Error('Invalid JSON in parameters field')
}
}
// Helper to collect checkbox-list values
// Use truthy check since values may be boolean true or string "true" after serialization
const collectActionTypes = (prefix: string) => {
const types: string[] = []
const writeVal = params[`${prefix}_write`]
const searchVal = params[`${prefix}_search`]
const readVal = params[`${prefix}_read`]
if (writeVal === true || writeVal === 'true') types.push('write')
if (searchVal === true || searchVal === 'true') types.push('search')
if (readVal === true || readVal === 'true') types.push('read')
return types.length > 0 ? types : undefined
}
switch (operation) {
case 'execute':
baseParams.actionId = actionId
baseParams.instructions = instructions
baseParams.params = parseJsonParams(execParams)
baseParams.previewOnly = previewOnly === 'true'
break
case 'stateless_execute':
baseParams.app = statelessApp
baseParams.action = statelessAction
baseParams.instructions = statelessInstructions
baseParams.actionType = statelessActionType || 'write'
baseParams.params = parseJsonParams(statelessParams)
baseParams.previewOnly = statelessPreviewOnly === 'true'
break
case 'list':
break
case 'search_apps':
if (searchQuery) baseParams.query = searchQuery
break
case 'search_app_actions':
baseParams.app = searchAppActionsApp
if (searchAppActionsQuery) baseParams.query = searchAppActionsQuery
baseParams.actionTypes = collectActionTypes('searchAppActionsTypes')
break
case 'guess': {
baseParams.query = guessQuery
// Checkbox-list values are stored under prefixed option IDs (actionTypes_write, etc.)
baseParams.actionTypes = collectActionTypes('actionTypes')
if (resultCount) {
const count = Number.parseInt(resultCount, 10)
if (!Number.isNaN(count)) baseParams.count = count
}
break
}
case 'get_action_details':
baseParams.app = detailsApp
baseParams.action = detailsAction
baseParams.actionType = detailsActionType || 'write'
baseParams.includeNeeds = includeNeeds !== 'false'
baseParams.includeGives = includeGives === 'true'
baseParams.includeSample = includeSample === 'true'
break
case 'create':
baseParams.app = app
baseParams.action = action
baseParams.actionType = createActionType || 'write'
baseParams.params = parseJsonParams(createParams)
break
case 'update':
baseParams.actionId = updateActionId
baseParams.app = updateApp
baseParams.action = updateAction
baseParams.actionType = updateActionType || 'write'
baseParams.params = parseJsonParams(updateParams)
break
case 'delete':
baseParams.actionId = deleteActionId
break
}
return baseParams
},
},
},
inputs: {
operation: { type: 'string', description: 'Operation to perform' },
credential: { type: 'string', description: 'Zapier OAuth credential' },
// Execute inputs
actionId: { type: 'string', description: 'AI Action ID to execute' },
instructions: { type: 'string', description: 'Plain English instructions for the action' },
params: { type: 'json', description: 'Optional parameter constraints' },
previewOnly: { type: 'string', description: 'Whether to preview without executing' },
// Stateless execute inputs
statelessApp: { type: 'string', description: 'App identifier for stateless execute' },
statelessAction: { type: 'string', description: 'Action identifier for stateless execute' },
statelessInstructions: { type: 'string', description: 'Instructions for stateless execute' },
statelessActionType: { type: 'string', description: 'Action type for stateless execute' },
statelessParams: { type: 'json', description: 'Parameters for stateless execute' },
statelessPreviewOnly: { type: 'string', description: 'Preview mode for stateless execute' },
// Search inputs
searchQuery: { type: 'string', description: 'App search query' },
// Search app actions inputs
searchAppActionsApp: { type: 'string', description: 'App to search actions for' },
searchAppActionsQuery: { type: 'string', description: 'Query to filter actions' },
searchAppActionsTypes_write: { type: 'boolean', description: 'Include write actions' },
searchAppActionsTypes_search: { type: 'boolean', description: 'Include search actions' },
searchAppActionsTypes_read: { type: 'boolean', description: 'Include read actions' },
// Guess inputs
guessQuery: { type: 'string', description: 'Natural language query to find actions' },
actionTypes_write: { type: 'boolean', description: 'Include write actions' },
actionTypes_search: { type: 'boolean', description: 'Include search actions' },
actionTypes_read: { type: 'boolean', description: 'Include read actions' },
resultCount: { type: 'string', description: 'Maximum number of results' },
// Get action details inputs
detailsApp: { type: 'string', description: 'App identifier for action details' },
detailsAction: { type: 'string', description: 'Action identifier for action details' },
detailsActionType: { type: 'string', description: 'Action type for action details' },
includeNeeds: { type: 'string', description: 'Include input requirements' },
includeGives: { type: 'string', description: 'Include output specifications' },
includeSample: { type: 'string', description: 'Include sample data' },
// Create inputs
app: { type: 'string', description: 'App identifier' },
action: { type: 'string', description: 'Action identifier' },
createActionType: { type: 'string', description: 'Type of action to create' },
createParams: { type: 'json', description: 'Pre-configured parameters' },
// Update inputs
updateActionId: { type: 'string', description: 'AI Action ID to update' },
updateApp: { type: 'string', description: 'App identifier for update' },
updateAction: { type: 'string', description: 'Action identifier for update' },
updateActionType: { type: 'string', description: 'Action type for update' },
updateParams: { type: 'json', description: 'Parameters for update' },
// Delete inputs
deleteActionId: { type: 'string', description: 'AI Action ID to delete' },
},
outputs: {
// Execute Action outputs
executionLogId: {
type: 'string',
description: 'Unique identifier for the execution',
},
actionUsed: {
type: 'string',
description: 'Name of the action that was executed',
},
inputParams: {
type: 'json',
description: 'Parameters passed to the API',
},
resolvedParams: {
type: 'json',
description: 'Parameters resolved by AI for execution',
},
results: {
type: 'json',
description: 'Results from action execution',
},
resultFieldLabels: {
type: 'json',
description: 'Human-readable labels for result fields',
},
status: {
type: 'string',
description: 'Execution status (success, error, preview, etc.)',
},
error: {
type: 'string',
description: 'Error message if execution failed',
},
// List Actions outputs
actions: {
type: 'json',
description:
'Array of AI Actions with id, description, actionType, app, appLabel, action, actionLabel, params, accountId, authenticationId, configurationLink (list) or guessed actions (find)',
},
configurationLink: {
type: 'string',
description: 'Link to configure actions in Zapier (list operation only)',
},
// Search Apps outputs
apps: {
type: 'json',
description:
'Array of apps with app, name, logoUrl, authType, actionCount, writeActionCount, searchActionCount, readActionCount',
},
// Guess Actions outputs (in addition to 'actions' above)
name: {
type: 'string',
description: 'Combined app and action name (find operation)',
},
image: {
type: 'string',
description: 'App logo URL (find operation)',
},
score: {
type: 'number',
description: 'Relevance score for guessed actions (find operation)',
},
// Create Action outputs
id: {
type: 'string',
description: 'ID of the created AI Action',
},
description: {
type: 'string',
description: 'Description of the action',
},
actionType: {
type: 'string',
description:
'Type of action (write, search, read, read_bulk, search_or_write, search_and_write)',
},
app: {
type: 'string',
description: 'App identifier',
},
appLabel: {
type: 'string',
description: 'Human-readable app label',
},
action: {
type: 'string',
description: 'Action identifier',
},
actionLabel: {
type: 'string',
description: 'Human-readable action label',
},
params: {
type: 'json',
description: 'Configured parameter values',
},
accountId: {
type: 'number',
description: 'Zapier account ID',
},
authenticationId: {
type: 'number',
description: 'Authentication ID used for the app',
},
// Get Action Details outputs
needs: {
type: 'json',
description: 'Array of input requirements for the action',
},
gives: {
type: 'json',
description: 'Array of output fields from the action',
},
sample: {
type: 'json',
description: 'Sample execution result',
},
customNeedsProbability: {
type: 'number',
description: 'Probability that action has custom/dynamic input fields',
},
// Delete Action outputs
deleted: {
type: 'boolean',
description: 'Whether the action was successfully deleted',
},
message: {
type: 'string',
description: 'Status message for delete operation',
},
},
}

View File

@@ -133,6 +133,7 @@ import { WorkflowBlock } from '@/blocks/blocks/workflow'
import { WorkflowInputBlock } from '@/blocks/blocks/workflow_input'
import { XBlock } from '@/blocks/blocks/x'
import { YouTubeBlock } from '@/blocks/blocks/youtube'
import { ZapierBlock } from '@/blocks/blocks/zapier'
import { ZendeskBlock } from '@/blocks/blocks/zendesk'
import { ZepBlock } from '@/blocks/blocks/zep'
import { ZoomBlock } from '@/blocks/blocks/zoom'
@@ -275,8 +276,9 @@ export const registry: Record<string, BlockConfig> = {
workflow_input: WorkflowInputBlock,
x: XBlock,
youtube: YouTubeBlock,
zep: ZepBlock,
zapier: ZapierBlock,
zendesk: ZendeskBlock,
zep: ZepBlock,
zoom: ZoomBlock,
}

View File

@@ -4151,7 +4151,7 @@ export function DuckDuckGoIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='-108 -108 216 216'>
<circle r='108' fill='#d53' />
<circle r='96' fill='none' stroke='#ffffff' stroke-width='7' />
<circle r='96' fill='none' stroke='#ffffff' strokeWidth='7' />
<path
d='M-32-55C-62-48-51-6-51-6l19 93 7 3M-39-73h-8l11 4s-11 0-11 7c24-1 35 5 35 5'
fill='#ddd'
@@ -4199,3 +4199,25 @@ export function RssIcon(props: SVGProps<SVGSVGElement>) {
</svg>
)
}
export function ZapierIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
{...props}
width='800px'
height='800px'
viewBox='0 0 256 256'
version='1.1'
xmlns='http://www.w3.org/2000/svg'
xmlnsXlink='http://www.w3.org/1999/xlink'
>
<g>
<path
d='M128.080089,-0.000183105 C135.311053,0.0131003068 142.422517,0.624138494 149.335663,1.77979593 L149.335663,1.77979593 L149.335663,76.2997796 L202.166953,23.6044907 C208.002065,27.7488446 213.460883,32.3582023 218.507811,37.3926715 C223.557281,42.4271407 228.192318,47.8867213 232.346817,53.7047992 L232.346817,53.7047992 L179.512985,106.400063 L254.227854,106.400063 C255.387249,113.29414 256,120.36111 256,127.587243 L256,127.587243 L256,127.759881 C256,134.986013 255.387249,142.066204 254.227854,148.960282 L254.227854,148.960282 L179.500273,148.960282 L232.346817,201.642324 C228.192318,207.460402 223.557281,212.919983 218.523066,217.954452 L218.523066,217.954452 L218.507811,217.954452 C213.460883,222.988921 208.002065,227.6115 202.182208,231.742607 L202.182208,231.742607 L149.335663,179.04709 L149.335663,253.5672 C142.435229,254.723036 135.323765,255.333244 128.092802,255.348499 L128.092802,255.348499 L127.907197,255.348499 C120.673691,255.333244 113.590195,254.723036 106.677048,253.5672 L106.677048,253.5672 L106.677048,179.04709 L53.8457596,231.742607 C42.1780766,223.466917 31.977435,213.278734 23.6658953,201.642324 L23.6658953,201.642324 L76.4997269,148.960282 L1.78485803,148.960282 C0.612750404,142.052729 0,134.946095 0,127.719963 L0,127.719963 L0,127.349037 C0.0121454869,125.473817 0.134939797,123.182933 0.311311815,120.812834 L0.36577283,120.099764 C0.887996182,113.428547 1.78485803,106.400063 1.78485803,106.400063 L1.78485803,106.400063 L76.4997269,106.400063 L23.6658953,53.7047992 C27.8076812,47.8867213 32.4300059,42.4403618 37.4769335,37.4193681 L37.4769335,37.4193681 L37.5023588,37.3926715 C42.5391163,32.3582023 48.0106469,27.7488446 53.8457596,23.6044907 L53.8457596,23.6044907 L106.677048,76.2997796 L106.677048,1.77979593 C113.590195,0.624138494 120.688946,0.0131003068 127.932622,-0.000183105 L127.932622,-0.000183105 L128.080089,-0.000183105 Z M128.067377,95.7600714 L127.945335,95.7600714 C118.436262,95.7600714 109.32891,97.5001809 100.910584,100.661566 C97.7553011,109.043534 96.0085811,118.129275 95.9958684,127.613685 L95.9958684,127.733184 C96.0085811,137.217594 97.7553011,146.303589 100.923296,154.685303 C109.32891,157.846943 118.436262,159.587052 127.945335,159.587052 L128.067377,159.587052 C137.576449,159.587052 146.683802,157.846943 155.089415,154.685303 C158.257411,146.290368 160.004131,137.217594 160.004131,127.733184 L160.004131,127.613685 C160.004131,118.129275 158.257411,109.043534 155.089415,100.661566 C146.683802,97.5001809 137.576449,95.7600714 128.067377,95.7600714 Z'
fill='#FF4A00'
fillRule='nonzero'
/>
</g>
</svg>
)
}

View File

@@ -1847,6 +1847,59 @@ export const auth = betterAuth({
}
},
},
// Zapier AI Actions provider
{
providerId: 'zapier',
clientId: env.ZAPIER_CLIENT_ID as string,
clientSecret: env.ZAPIER_CLIENT_SECRET as string,
authorizationUrl: 'https://actions.zapier.com/oauth/authorize/',
tokenUrl: 'https://actions.zapier.com/oauth/token/',
userInfoUrl: 'https://actions.zapier.com/api/v2/check/',
scopes: ['openid', 'nla:exposed_actions:execute'],
responseType: 'code',
pkce: true,
accessType: 'offline',
prompt: 'consent',
redirectURI: `${getBaseUrl()}/api/auth/oauth2/callback/zapier`,
getUserInfo: async (tokens) => {
try {
logger.info('Fetching Zapier user profile')
// Zapier's check endpoint returns account info when using OAuth
const response = await fetch('https://actions.zapier.com/api/v2/check/', {
headers: {
Authorization: `Bearer ${tokens.accessToken}`,
},
})
if (!response.ok) {
logger.error('Failed to fetch Zapier user info', {
status: response.status,
statusText: response.statusText,
})
throw new Error('Failed to fetch user info')
}
const data = await response.json()
// Zapier check endpoint returns account_id and other info
const userId = data.account_id || data.user_id || `zapier-${Date.now()}`
return {
id: userId.toString(),
name: data.email || 'Zapier User',
email: data.email || `${userId}@zapier.user`,
emailVerified: !!data.email,
createdAt: new Date(),
updatedAt: new Date(),
}
} catch (error) {
logger.error('Error in Zapier getUserInfo:', { error })
return null
}
},
},
],
}),
// Include SSO plugin when enabled

View File

@@ -230,6 +230,8 @@ export const env = createEnv({
ZOOM_CLIENT_SECRET: z.string().optional(), // Zoom OAuth client secret
WORDPRESS_CLIENT_ID: z.string().optional(), // WordPress.com OAuth client ID
WORDPRESS_CLIENT_SECRET: z.string().optional(), // WordPress.com OAuth client secret
ZAPIER_CLIENT_ID: z.string().optional(), // Zapier AI Actions OAuth client ID
ZAPIER_CLIENT_SECRET: z.string().optional(), // Zapier AI Actions OAuth client secret
// E2B Remote Code Execution
E2B_ENABLED: z.string().optional(), // Enable E2B remote code execution

View File

@@ -37,6 +37,7 @@ import {
WebflowIcon,
WordpressIcon,
xIcon,
ZapierIcon,
ZoomIcon,
} from '@/components/icons'
import { env } from '@/lib/core/config/env'
@@ -70,6 +71,7 @@ export type OAuthProvider =
| 'shopify'
| 'zoom'
| 'wordpress'
| 'zapier'
| string
export type OAuthService =
@@ -111,6 +113,7 @@ export type OAuthService =
| 'shopify'
| 'zoom'
| 'wordpress'
| 'zapier'
export interface OAuthProviderConfig {
id: OAuthProvider
name: string
@@ -891,6 +894,23 @@ export const OAUTH_PROVIDERS: Record<string, OAuthProviderConfig> = {
},
defaultService: 'wordpress',
},
zapier: {
id: 'zapier',
name: 'Zapier',
icon: (props) => ZapierIcon(props),
services: {
zapier: {
id: 'zapier',
name: 'Zapier AI Actions',
description: 'Execute actions across 7,000+ apps using Zapier AI Actions.',
providerId: 'zapier',
icon: (props) => ZapierIcon(props),
baseProviderIcon: (props) => ZapierIcon(props),
scopes: ['openid', 'nla:exposed_actions:execute'],
},
},
defaultService: 'zapier',
},
}
/**
@@ -1470,6 +1490,20 @@ function getProviderAuthConfig(provider: string): ProviderAuthConfig {
supportsRefreshTokenRotation: false,
}
}
case 'zapier': {
// Zapier AI Actions OAuth - tokens expire after 10 hours
const { clientId, clientSecret } = getCredentials(
env.ZAPIER_CLIENT_ID,
env.ZAPIER_CLIENT_SECRET
)
return {
tokenEndpoint: 'https://actions.zapier.com/oauth/token/',
clientId,
clientSecret,
useBasicAuth: false,
supportsRefreshTokenRotation: true,
}
}
default:
throw new Error(`Unsupported provider: ${provider}`)
}

View File

@@ -115,10 +115,6 @@ export class Serializer {
safeParallels
)
if (validateRequired) {
this.validateSubflowsBeforeExecution(blocks, safeLoops, safeParallels)
}
return {
version: '1.0',
blocks: Object.values(blocks).map((block) =>
@@ -139,18 +135,6 @@ export class Serializer {
}
}
/**
* Validate loop and parallel subflows for required inputs when running in "each/collection" modes
*/
private validateSubflowsBeforeExecution(
blocks: Record<string, BlockState>,
loops: Record<string, Loop>,
parallels: Record<string, Parallel>
): void {
// Note: Empty collections in forEach loops and parallel collection mode are handled gracefully
// at runtime - the loop/parallel will simply be skipped. No build-time validation needed.
}
private serializeBlock(
block: BlockState,
options: {
@@ -367,6 +351,15 @@ export class Serializer {
) {
params[id] = subBlock.value
}
if (subBlockConfig?.type === 'checkbox-list' && Array.isArray(subBlockConfig.options)) {
subBlockConfig.options.forEach((option: { id: string; label: string }) => {
const optionSubBlock = block.subBlocks[option.id]
if (optionSubBlock !== undefined) {
params[option.id] = optionSubBlock.value
}
})
}
})
// Then check for any subBlocks with default values

View File

@@ -1260,6 +1260,18 @@ import {
youtubeSearchTool,
youtubeVideoDetailsTool,
} from '@/tools/youtube'
import {
zapierCreateAiActionTool,
zapierDeleteAiActionTool,
zapierExecuteActionTool,
zapierGetActionDetailsTool,
zapierGuessActionsTool,
zapierListActionsTool,
zapierSearchAppActionsTool,
zapierSearchAppsTool,
zapierStatelessExecuteTool,
zapierUpdateAiActionTool,
} from '@/tools/zapier'
import {
zendeskAutocompleteOrganizationsTool,
zendeskCreateOrganizationsBulkTool,
@@ -2501,4 +2513,16 @@ export const tools: Record<string, ToolConfig> = {
zoom_get_meeting_recordings: zoomGetMeetingRecordingsTool,
zoom_delete_recording: zoomDeleteRecordingTool,
zoom_list_past_participants: zoomListPastParticipantsTool,
// Zapier tools
zapier_execute_action: zapierExecuteActionTool,
zapier_list_actions: zapierListActionsTool,
zapier_search_apps: zapierSearchAppsTool,
zapier_guess_actions: zapierGuessActionsTool,
zapier_create_action: zapierCreateAiActionTool,
zapier_stateless_execute: zapierStatelessExecuteTool,
zapier_search_app_actions: zapierSearchAppActionsTool,
zapier_get_action_details: zapierGetActionDetailsTool,
zapier_update_action: zapierUpdateAiActionTool,
zapier_delete_action: zapierDeleteAiActionTool,
}

View File

@@ -0,0 +1,172 @@
import type { ToolConfig } from '@/tools/types'
import type { ZapierCreateAiActionParams, ZapierCreateAiActionResponse } from '@/tools/zapier/types'
export const zapierCreateAiActionTool: ToolConfig<
ZapierCreateAiActionParams,
ZapierCreateAiActionResponse
> = {
id: 'zapier_create_action',
name: 'Zapier Create AI Action',
description:
'Create a new stored AI Action in Zapier. The action can then be executed with zapier_execute_action.',
version: '1.0.0',
oauth: {
required: true,
provider: 'zapier',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Access token for Zapier AI Actions API',
},
app: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'The app identifier (e.g., "slack", "gmail", "google-docs")',
},
action: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'The action identifier (e.g., "send_channel_message", "send_email")',
},
actionType: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Type of action: write, search, or read. Defaults to write.',
default: 'write',
},
params: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description: 'Pre-configured parameter values for the action',
},
accountId: {
type: 'number',
required: false,
visibility: 'user-only',
description: 'Zapier account ID',
},
authenticationId: {
type: 'number',
required: false,
visibility: 'user-only',
description: 'Authentication ID for the app connection',
},
meta: {
type: 'json',
required: false,
visibility: 'user-only',
description:
'Metadata object with params labels, app_label, action_label, authentication_label, app_needs_auth',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.accountId !== undefined) {
queryParams.append('account_id', String(params.accountId))
}
if (params.authenticationId !== undefined) {
queryParams.append('authentication_id', String(params.authenticationId))
}
const query = queryParams.toString()
return `https://actions.zapier.com/api/v2/ai-actions/${query ? `?${query}` : ''}`
},
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${params.accessToken}`,
}),
body: (params) => ({
app: params.app,
action: params.action,
action_type: params.actionType || 'write',
params: params.params || {},
meta: params.meta || { params: {} },
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || data.detail || `Zapier API error: ${response.status}`)
}
// API response includes: id, description, account_id, authentication_id, app, action, action_type, params, meta, needs
// Labels come from meta object if available
const appLabel = data.meta?.app_label || ''
const actionLabel = data.meta?.action_label || ''
return {
success: true,
output: {
id: data.id || '',
description: data.description || '',
actionType: data.action_type || '',
app: data.app || '',
appLabel,
action: data.action || '',
actionLabel,
params: data.params || {},
accountId: data.account_id ?? null,
authenticationId: data.authentication_id ?? null,
},
}
},
outputs: {
id: {
type: 'string',
description: 'The ID of the created AI Action (use this with execute_action)',
},
description: {
type: 'string',
description: 'Description of the action',
},
actionType: {
type: 'string',
description:
'Type of action (write, search, read, read_bulk, search_or_write, search_and_write)',
},
app: {
type: 'string',
description: 'App identifier',
},
appLabel: {
type: 'string',
description: 'Human-readable app label from meta',
},
action: {
type: 'string',
description: 'Action identifier',
},
actionLabel: {
type: 'string',
description: 'Human-readable action label from meta',
},
params: {
type: 'json',
description: 'Configured parameter values',
},
accountId: {
type: 'number',
description: 'Zapier account ID',
optional: true,
},
authenticationId: {
type: 'number',
description: 'Authentication ID used for the app',
optional: true,
},
},
}

View File

@@ -0,0 +1,73 @@
import type { ToolConfig } from '@/tools/types'
import type { ZapierDeleteAiActionParams, ZapierDeleteAiActionResponse } from '@/tools/zapier/types'
export const zapierDeleteAiActionTool: ToolConfig<
ZapierDeleteAiActionParams,
ZapierDeleteAiActionResponse
> = {
id: 'zapier_delete_action',
name: 'Zapier Delete AI Action',
description: 'Delete a stored AI Action from Zapier.',
version: '1.0.0',
oauth: {
required: true,
provider: 'zapier',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Access token for Zapier AI Actions API',
},
actionId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'The ID of the AI Action to delete',
},
},
request: {
url: (params) =>
`https://actions.zapier.com/api/v2/ai-actions/${encodeURIComponent(params.actionId)}/`,
method: 'DELETE',
headers: (params) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (response: Response) => {
// DELETE returns a boolean directly
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || data.detail || `Zapier API error: ${response.status}`)
}
// API returns true if deleted, false if not found
const deleted = data === true
return {
success: true,
output: {
deleted,
message: deleted ? 'AI Action deleted successfully' : 'AI Action not found',
},
}
},
outputs: {
deleted: {
type: 'boolean',
description: 'Whether the action was successfully deleted',
},
message: {
type: 'string',
description: 'Status message',
},
},
}

View File

@@ -0,0 +1,136 @@
import type { ToolConfig } from '@/tools/types'
import type { ZapierExecuteActionParams, ZapierExecuteActionResponse } from '@/tools/zapier/types'
export const zapierExecuteActionTool: ToolConfig<
ZapierExecuteActionParams,
ZapierExecuteActionResponse
> = {
id: 'zapier_execute_action',
name: 'Zapier Execute Action',
description:
'Execute a stored AI Action in Zapier. Runs any of the 30,000+ actions across 7,000+ apps that Zapier supports.',
version: '1.0.0',
oauth: {
required: true,
provider: 'zapier',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Access token for Zapier AI Actions API',
},
actionId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'The ID of the AI Action to execute',
},
instructions: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'Plain English instructions for what the action should do (e.g., "Send a message about the weekly report to #general")',
},
params: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description:
'Optional parameter constraints. Each key maps to {mode: "locked"|"guess"|"choose_from"|"ignored", value: string}',
},
previewOnly: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'If true, preview the execution without actually running it',
default: false,
},
},
request: {
url: (params) =>
`https://actions.zapier.com/api/v2/ai-actions/${encodeURIComponent(params.actionId)}/execute/${params.previewOnly ? '?preview_only=true' : ''}`,
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${params.accessToken}`,
}),
body: (params) => {
const body: Record<string, any> = {
instructions: params.instructions,
}
if (params.params !== undefined) {
body.params = params.params
}
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || data.detail || `Zapier API error: ${response.status}`)
}
const isSuccess = data.status === 'success' || data.status === 'preview'
const errorMessage =
data.error || (isSuccess ? undefined : `Zapier action ${data.status || 'failed'}`)
return {
success: isSuccess,
error: errorMessage, // Top-level error for the framework to capture
output: {
executionLogId: data.execution_log_id || '',
actionUsed: data.action_used || '',
inputParams: data.input_params || {},
resolvedParams: data.resolved_params || {},
results: data.results || [],
resultFieldLabels: data.result_field_labels || {},
status: data.status || 'error',
error: data.error || undefined,
},
}
},
outputs: {
executionLogId: {
type: 'string',
description: 'Unique identifier for this execution (can be used for feedback)',
},
actionUsed: {
type: 'string',
description: 'Name of the action that was executed',
},
inputParams: {
type: 'json',
description: 'Parameters that were passed to the API',
},
resolvedParams: {
type: 'json',
description: 'Parameters that the AI resolved for execution',
},
results: {
type: 'json',
description: 'Results from the action execution',
},
resultFieldLabels: {
type: 'json',
description: 'Human-readable labels for result fields',
},
status: {
type: 'string',
description: 'Execution status: success, error, empty, preview, or halted',
},
error: {
type: 'string',
description: 'Error message if execution failed',
optional: true,
},
},
}

View File

@@ -0,0 +1,202 @@
import type { ToolConfig } from '@/tools/types'
import type {
ZapierGetActionDetailsParams,
ZapierGetActionDetailsResponse,
} from '@/tools/zapier/types'
export const zapierGetActionDetailsTool: ToolConfig<
ZapierGetActionDetailsParams,
ZapierGetActionDetailsResponse
> = {
id: 'zapier_get_action_details',
name: 'Zapier Get Action Details',
description:
'Get detailed information about a specific action including its required inputs (needs) and outputs (gives).',
version: '1.0.0',
oauth: {
required: true,
provider: 'zapier',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Access token for Zapier AI Actions API',
},
app: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The app identifier (e.g., "SlackAPI", "GmailV2API")',
},
action: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The action identifier (e.g., "send_channel_message", "send_email")',
},
actionType: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Type of action: write, search, read. Defaults to write.',
},
includeNeeds: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Include input requirements (needs). Defaults to true.',
},
includeGives: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Include output specifications (gives). Defaults to false.',
},
includeSample: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Include sample execution result. Defaults to false.',
},
params: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description: 'Optional params to pass for dynamic field resolution',
},
accountId: {
type: 'number',
required: false,
visibility: 'user-only',
description: 'Zapier account ID',
},
authenticationId: {
type: 'number',
required: false,
visibility: 'user-only',
description: 'Authentication ID for the app connection',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.actionType) {
queryParams.append('action_type', params.actionType)
}
if (params.accountId !== undefined) {
queryParams.append('account_id', String(params.accountId))
}
if (params.authenticationId !== undefined) {
queryParams.append('authentication_id', String(params.authenticationId))
}
// Build action_extra array based on flags
const actionExtra: string[] = []
if (params.includeNeeds !== false) {
actionExtra.push('action_needs')
}
if (params.includeGives) {
actionExtra.push('action_gives')
}
if (params.includeSample) {
actionExtra.push('action_sample')
}
actionExtra.forEach((extra) => {
queryParams.append('action_extra', extra)
})
const query = queryParams.toString()
return `https://actions.zapier.com/api/v2/apps/${encodeURIComponent(params.app)}/actions/${encodeURIComponent(params.action)}/${query ? `?${query}` : ''}`
},
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${params.accessToken}`,
}),
body: (params) => ({
params: params.params || {},
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || data.detail || `Zapier API error: ${response.status}`)
}
const results = data.results || []
const result = results[0] || {}
// Transform needs array
const needs = (result.action_needs || []).map((need: any) => ({
key: need.key || '',
type: need.type || '',
label: need.label || '',
required: need.required || false,
helpText: need.help_text || '',
defaultValue: need.default ?? null,
choices: need.choices || null,
dependsOn: need.depends_on || null,
customField: need.custom_field || false,
}))
// Transform gives array
const gives = (result.action_gives || []).map((give: any) => ({
key: give.key || '',
label: give.label || '',
type: give.type || '',
score: give.score ?? null,
subscore: give.subscore ?? null,
important: give.important || false,
sample: give.zap_meta_sample ?? null,
}))
return {
success: true,
output: {
action: result.action
? {
type: result.action.type || '',
key: result.action.key || '',
name: result.action.name || '',
noun: result.action.noun || '',
description: result.action.description || '',
}
: null,
needs,
gives,
sample: result.action_sample || null,
customNeedsProbability: result.action_has_custom_needs_probability ?? 0,
},
}
},
outputs: {
action: {
type: 'json',
description: 'Action metadata including type, key, name, noun, and description',
},
needs: {
type: 'json',
description:
'Array of input requirements with key, type, label, required, helpText, defaultValue, choices, dependsOn',
},
gives: {
type: 'json',
description: 'Array of output fields with key, label, type, important, sample',
},
sample: {
type: 'json',
description: 'Sample execution result if requested',
optional: true,
},
customNeedsProbability: {
type: 'number',
description: 'Probability (0-1) that this action has custom/dynamic input fields',
},
},
}

View File

@@ -0,0 +1,103 @@
import type { ToolConfig } from '@/tools/types'
import type { ZapierGuessActionsParams, ZapierGuessActionsResponse } from '@/tools/zapier/types'
export const zapierGuessActionsTool: ToolConfig<
ZapierGuessActionsParams,
ZapierGuessActionsResponse
> = {
id: 'zapier_guess_actions',
name: 'Zapier Guess Actions',
description:
'Find relevant Zapier actions using natural language. Searches across 30,000+ actions to find the best matches for your query.',
version: '1.0.0',
oauth: {
required: true,
provider: 'zapier',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Access token for Zapier AI Actions API',
},
query: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'Natural language description of what you want to do (e.g., "send a Slack message", "create a Google Doc")',
},
actionTypes: {
type: 'array',
required: false,
visibility: 'user-only',
description:
'Types of actions to search for: write, search, read. If not specified, returns all types.',
},
count: {
type: 'number',
required: false,
visibility: 'user-only',
description: 'Maximum number of results to return (default: 25)',
default: 25,
},
},
request: {
url: 'https://actions.zapier.com/api/v2/guess-actions/',
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${params.accessToken}`,
}),
body: (params) => {
const body: Record<string, any> = {
query: params.query,
count: params.count || 25,
}
// Only include action_types if explicitly provided (user selected filters)
// If not provided, API returns all action types
if (params.actionTypes && params.actionTypes.length > 0) {
body.action_types = params.actionTypes
}
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || data.detail || `Zapier API error: ${response.status}`)
}
const actions = Array.isArray(data) ? data : data.results || []
return {
success: true,
output: {
actions: actions.map((action: any) => ({
// API returns: app, action, action_type, name (combined), description, image, score
app: action.app || '',
action: action.action || '',
actionType: action.action_type || '',
name: action.name || '', // Combined app and action name from API
description: action.description || '',
image: action.image || '',
score: action.score || 0,
})),
},
}
},
outputs: {
actions: {
type: 'json',
description:
'Array of matching actions with app, action, actionType, name (combined app/action name), description, image, and score',
},
},
}

View File

@@ -0,0 +1,23 @@
import { zapierCreateAiActionTool } from '@/tools/zapier/create_action'
import { zapierDeleteAiActionTool } from '@/tools/zapier/delete_action'
import { zapierExecuteActionTool } from '@/tools/zapier/execute_action'
import { zapierGetActionDetailsTool } from '@/tools/zapier/get_action_details'
import { zapierGuessActionsTool } from '@/tools/zapier/guess_actions'
import { zapierListActionsTool } from '@/tools/zapier/list_actions'
import { zapierSearchAppActionsTool } from '@/tools/zapier/search_app_actions'
import { zapierSearchAppsTool } from '@/tools/zapier/search_apps'
import { zapierStatelessExecuteTool } from '@/tools/zapier/stateless_execute'
import { zapierUpdateAiActionTool } from '@/tools/zapier/update_action'
export {
zapierExecuteActionTool,
zapierListActionsTool,
zapierSearchAppsTool,
zapierGuessActionsTool,
zapierCreateAiActionTool,
zapierStatelessExecuteTool,
zapierSearchAppActionsTool,
zapierGetActionDetailsTool,
zapierUpdateAiActionTool,
zapierDeleteAiActionTool,
}

View File

@@ -0,0 +1,78 @@
import type { ToolConfig } from '@/tools/types'
import type { ZapierListActionsParams, ZapierListActionsResponse } from '@/tools/zapier/types'
export const zapierListActionsTool: ToolConfig<ZapierListActionsParams, ZapierListActionsResponse> =
{
id: 'zapier_list_actions',
name: 'Zapier List Actions',
description:
'List all AI Actions configured in your Zapier account. Returns stored actions that can be executed.',
version: '1.0.0',
oauth: {
required: true,
provider: 'zapier',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Access token for Zapier AI Actions API',
},
},
request: {
url: 'https://actions.zapier.com/api/v2/ai-actions/',
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || data.detail || `Zapier API error: ${response.status}`)
}
// API returns an array of actions and a configuration_link
const actions = Array.isArray(data) ? data : data.results || []
const configurationLink = data.configuration_link || 'https://actions.zapier.com/providers/'
return {
success: true,
output: {
actions: actions.map((action: any) => ({
id: action.id || '',
description: action.description || '',
actionType: action.action_type || '',
app: action.app || '',
appLabel: action.meta?.app_label || '',
action: action.action || '',
actionLabel: action.meta?.action_label || '',
params: action.params || {},
accountId: action.account_id ?? null,
authenticationId: action.authentication_id ?? null,
needs: action.needs || null,
})),
configurationLink,
},
}
},
outputs: {
actions: {
type: 'json',
description:
'Array of configured AI Actions with id, description, actionType, app, appLabel, action, actionLabel, params, accountId, authenticationId, needs',
},
configurationLink: {
type: 'string',
description: 'Link to configure more actions in Zapier',
},
},
}

View File

@@ -0,0 +1,111 @@
import type { ToolConfig } from '@/tools/types'
import type {
ZapierSearchAppActionsParams,
ZapierSearchAppActionsResponse,
} from '@/tools/zapier/types'
export const zapierSearchAppActionsTool: ToolConfig<
ZapierSearchAppActionsParams,
ZapierSearchAppActionsResponse
> = {
id: 'zapier_search_app_actions',
name: 'Zapier Search App Actions',
description:
'Search for available actions within a specific Zapier app. Returns all actions the app supports.',
version: '1.0.0',
oauth: {
required: true,
provider: 'zapier',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Access token for Zapier AI Actions API',
},
app: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The app identifier to search actions for (e.g., "SlackAPI", "GmailV2API")',
},
query: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Optional search query to filter actions by name or description',
},
actionTypes: {
type: 'array',
required: false,
visibility: 'user-only',
description:
'Filter by action types: write, search, read, read_bulk, search_or_write, search_and_write. Defaults to write and search.',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.query) {
queryParams.append('query', params.query)
}
if (params.actionTypes && params.actionTypes.length > 0) {
params.actionTypes.forEach((type: string) => {
queryParams.append('filter_action_type', type)
})
}
const query = queryParams.toString()
return `https://actions.zapier.com/api/v2/apps/${encodeURIComponent(params.app)}/actions/${query ? `?${query}` : ''}`
},
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || data.detail || `Zapier API error: ${response.status}`)
}
const actions = data.results || []
return {
success: true,
output: {
actions: actions.map((action: any) => ({
app: action.app || '',
action: action.action || '',
actionType: action.type || '',
displayName: action.display_name || '',
description: action.description || '',
relevancyScore: action.search_relevancy_score || 0,
appNeedsAuth: action.app_needs_auth || false,
appInfo: action.app_info
? {
app: action.app_info.app || '',
name: action.app_info.name || '',
logoUrl: action.app_info.logo_url || '',
authType: action.app_info.auth_type || '',
}
: null,
})),
},
}
},
outputs: {
actions: {
type: 'json',
description:
'Array of actions with app, action, actionType, displayName, description, relevancyScore, appNeedsAuth, appInfo',
},
},
}

View File

@@ -0,0 +1,106 @@
import type { ToolConfig } from '@/tools/types'
import type { ZapierSearchAppsParams, ZapierSearchAppsResponse } from '@/tools/zapier/types'
export const zapierSearchAppsTool: ToolConfig<ZapierSearchAppsParams, ZapierSearchAppsResponse> = {
id: 'zapier_search_apps',
name: 'Zapier Search Apps',
description:
'Search for apps available in Zapier. Returns apps with their available action counts.',
version: '1.0.0',
oauth: {
required: true,
provider: 'zapier',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Access token for Zapier AI Actions API',
},
query: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Optional search query to filter apps by name',
},
},
request: {
url: (params) => {
const baseUrl = 'https://actions.zapier.com/api/v2/apps/search/'
if (params.query) {
return `${baseUrl}?query=${encodeURIComponent(params.query)}`
}
return baseUrl
},
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || data.detail || `Zapier API error: ${response.status}`)
}
const apps = Array.isArray(data) ? data : data.results || []
return {
success: true,
output: {
apps: apps.map((app: any) => {
// The API returns an 'actions' dictionary with action type counts
// Keys can be: write, search, read, read_bulk, search_or_write, search_and_write
const actions = app.actions || {}
// Sum all action counts
const actionCount =
typeof actions === 'object'
? Object.values(actions).reduce(
(sum: number, count: any) => sum + (typeof count === 'number' ? count : 0),
0
)
: 0
// Sum write-type actions (write, search_or_write, search_and_write)
const writeActionCount =
(actions.write || 0) + (actions.search_or_write || 0) + (actions.search_and_write || 0)
// Sum search-type actions (search, search_or_write, search_and_write)
const searchActionCount =
(actions.search || 0) + (actions.search_or_write || 0) + (actions.search_and_write || 0)
// Sum read-type actions (read, read_bulk)
const readActionCount = (actions.read || 0) + (actions.read_bulk || 0)
return {
app: app.app || '',
name: app.name || '',
logoUrl: app.logo_url || '',
authType: app.auth_type ?? null,
actions,
actionCount,
writeActionCount,
searchActionCount,
readActionCount,
}
}),
},
}
},
outputs: {
apps: {
type: 'json',
description:
'Array of apps with app, name, logoUrl, authType, actions (raw counts by type), actionCount, writeActionCount, searchActionCount, readActionCount',
},
},
}

View File

@@ -0,0 +1,207 @@
import type { ToolConfig } from '@/tools/types'
import type {
ZapierStatelessExecuteParams,
ZapierStatelessExecuteResponse,
} from '@/tools/zapier/types'
export const zapierStatelessExecuteTool: ToolConfig<
ZapierStatelessExecuteParams,
ZapierStatelessExecuteResponse
> = {
id: 'zapier_stateless_execute',
name: 'Zapier Stateless Execute',
description:
'Execute any Zapier action directly without creating a stored AI Action first. Provide the app, action, and instructions.',
version: '1.0.0',
oauth: {
required: true,
provider: 'zapier',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Access token for Zapier AI Actions API',
},
app: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The app to use (e.g., "SlackAPI", "GoogleSheetsV2API", "GmailV2API")',
},
action: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The action to run (e.g., "direct_message", "add_row", "send_email")',
},
instructions: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'Plain English instructions about how to run the action (e.g., "Send a message saying hello to #general")',
},
actionType: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Type of action: write, search, read, read_bulk, search_or_write, search_and_write',
},
params: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description:
'Optional parameter constraints. Each key maps to {mode: "locked"|"guess"|"choose_from"|"ignored", value: any}',
},
previewOnly: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'If true, preview the execution without actually running it',
},
authenticationId: {
type: 'number',
required: false,
visibility: 'user-only',
description: 'Authentication ID for the app connection',
},
accountId: {
type: 'number',
required: false,
visibility: 'user-only',
description: 'Zapier account ID',
},
providerId: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Provider ID for AI Actions',
},
tokenBudget: {
type: 'number',
required: false,
visibility: 'user-only',
description: 'Max tokens per field (default: 1000)',
},
skipParamGuessing: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Skip AI parameter guessing',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.previewOnly) {
queryParams.append('preview_only', 'true')
}
if (params.providerId) {
queryParams.append('provider_id', params.providerId)
}
if (params.tokenBudget !== undefined) {
queryParams.append('token_budget', String(params.tokenBudget))
}
if (params.skipParamGuessing) {
queryParams.append('skip_param_guessing', 'true')
}
const query = queryParams.toString()
return `https://actions.zapier.com/api/v2/execute/${query ? `?${query}` : ''}`
},
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${params.accessToken}`,
}),
body: (params) => {
const body: Record<string, any> = {
instructions: params.instructions,
app: params.app,
action: params.action,
}
if (params.actionType) {
body.action_type = params.actionType
}
if (params.params) {
body.params = params.params
}
if (params.authenticationId !== undefined) {
body.authentication_id = params.authenticationId
}
if (params.accountId !== undefined) {
body.account_id = params.accountId
}
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || data.detail || `Zapier API error: ${response.status}`)
}
const isSuccess = data.status === 'success' || data.status === 'preview'
const errorMessage =
data.error || (isSuccess ? undefined : `Zapier action ${data.status || 'failed'}`)
return {
success: isSuccess,
error: errorMessage,
output: {
executionLogId: data.execution_log_id || '',
actionUsed: data.action_used || '',
inputParams: data.input_params || {},
resolvedParams: data.resolved_params || {},
results: data.results || [],
resultFieldLabels: data.result_field_labels || {},
status: data.status || 'error',
error: data.error || undefined,
},
}
},
outputs: {
executionLogId: {
type: 'string',
description: 'Unique identifier for this execution',
},
actionUsed: {
type: 'string',
description: 'Name of the action that was executed',
},
inputParams: {
type: 'json',
description: 'Parameters that were passed to the API',
},
resolvedParams: {
type: 'json',
description: 'Parameters that the AI resolved for execution',
},
results: {
type: 'json',
description: 'Results from the action execution',
},
resultFieldLabels: {
type: 'json',
description: 'Human-readable labels for result fields',
},
status: {
type: 'string',
description: 'Execution status: success, error, empty, preview, or halted',
},
error: {
type: 'string',
description: 'Error message if execution failed',
optional: true,
},
},
}

View File

@@ -0,0 +1,330 @@
import type { ToolResponse } from '@/tools/types'
// Base params - all Zapier tools require OAuth access token
export interface ZapierBaseParams {
accessToken: string
}
// Parameter constraint for execute action
export interface ZapierParamConstraint {
mode: 'locked' | 'guess' | 'choose_from' | 'ignored'
value?: string | number | boolean | any[] | Record<string, any>
label?: string | any[] | Record<string, any>
}
// Action types supported by Zapier API
export type ZapierActionType =
| 'write'
| 'search'
| 'read'
| 'read_bulk'
| 'search_or_write'
| 'search_and_write'
// Execute Action params
export interface ZapierExecuteActionParams extends ZapierBaseParams {
actionId: string
instructions: string
params?: Record<string, ZapierParamConstraint>
previewOnly?: boolean
}
// List Actions params
export interface ZapierListActionsParams extends ZapierBaseParams {}
// Search Apps params
export interface ZapierSearchAppsParams extends ZapierBaseParams {
query?: string
}
// Guess Actions params (find actions based on natural language)
export interface ZapierGuessActionsParams extends ZapierBaseParams {
query: string
actionTypes?: ZapierActionType[]
count?: number
}
// Create AI Action params
export interface ZapierCreateAiActionParams extends ZapierBaseParams {
app: string
action: string
actionType?: ZapierActionType
params?: Record<string, string | string[]>
accountId?: number
authenticationId?: number
meta?: {
params?: Record<string, { label?: string }>
app_label?: string
action_label?: string
authentication_label?: string
app_needs_auth?: boolean
}
}
// Stateless Execute params
export interface ZapierStatelessExecuteParams extends ZapierBaseParams {
app: string
action: string
instructions: string
actionType?: ZapierActionType
params?: Record<string, ZapierParamConstraint>
previewOnly?: boolean
authenticationId?: number
accountId?: number
providerId?: string
tokenBudget?: number
skipParamGuessing?: boolean
}
// Search App Actions params
export interface ZapierSearchAppActionsParams extends ZapierBaseParams {
app: string
query?: string
actionTypes?: ZapierActionType[]
}
// Get Action Details params
export interface ZapierGetActionDetailsParams extends ZapierBaseParams {
app: string
action: string
actionType?: ZapierActionType
includeNeeds?: boolean
includeGives?: boolean
includeSample?: boolean
params?: Record<string, any>
accountId?: number
authenticationId?: number
}
// Update AI Action params
export interface ZapierUpdateAiActionParams extends ZapierBaseParams {
actionId: string
app: string
action: string
actionType?: ZapierActionType
params?: Record<string, string | string[]>
accountId?: number
authenticationId?: number
meta?: {
params?: Record<string, { label?: string }>
app_label?: string
action_label?: string
authentication_label?: string
app_needs_auth?: boolean
}
}
// Delete AI Action params
export interface ZapierDeleteAiActionParams extends ZapierBaseParams {
actionId: string
}
// Execute Action response
export interface ZapierExecuteActionResponse extends ToolResponse {
output: {
executionLogId: string
actionUsed: string
inputParams: Record<string, any>
resolvedParams: Record<string, any>
results: any[]
resultFieldLabels: Record<string, string>
status: 'success' | 'error' | 'empty' | 'preview' | 'halted'
error?: string
}
}
// List Actions response
export interface ZapierAiAction {
id: string
description: string
actionType: string
app: string
appLabel: string
action: string
actionLabel: string
params: Record<string, any>
accountId: number | null
authenticationId: number | null
needs: any[] | null
}
export interface ZapierListActionsResponse extends ToolResponse {
output: {
actions: ZapierAiAction[]
configurationLink: string
}
}
// Search Apps response
export interface ZapierAppActions {
write?: number
search?: number
read?: number
read_bulk?: number
search_or_write?: number
search_and_write?: number
}
export interface ZapierApp {
app: string
name: string
logoUrl: string
authType: string | null
actions: ZapierAppActions
actionCount: number
writeActionCount: number
searchActionCount: number
readActionCount: number
}
export interface ZapierSearchAppsResponse extends ToolResponse {
output: {
apps: ZapierApp[]
}
}
// Guess Actions response - matches exact API response structure
export interface ZapierGuessedAction {
app: string
action: string
actionType: string
name: string // Combined app and action name from API
description: string
image: string
score: number
}
export interface ZapierGuessActionsResponse extends ToolResponse {
output: {
actions: ZapierGuessedAction[]
}
}
// Create AI Action response - matches exact API response structure
export interface ZapierCreateAiActionResponse extends ToolResponse {
output: {
id: string
description: string
actionType: string
app: string
appLabel: string
action: string
actionLabel: string
params: Record<string, any>
accountId: number | null
authenticationId: number | null
}
}
// Stateless Execute response - same as Execute Action response
export interface ZapierStatelessExecuteResponse extends ToolResponse {
output: {
executionLogId: string
actionUsed: string
inputParams: Record<string, any>
resolvedParams: Record<string, any>
results: any[]
resultFieldLabels: Record<string, string>
status: 'success' | 'error' | 'empty' | 'preview' | 'halted'
error?: string
}
}
// Search App Actions response
export interface ZapierAppAction {
app: string
action: string
actionType: string
displayName: string
description: string
relevancyScore: number
appNeedsAuth: boolean
appInfo: {
app: string
name: string
logoUrl: string
authType: string
} | null
}
export interface ZapierSearchAppActionsResponse extends ToolResponse {
output: {
actions: ZapierAppAction[]
}
}
// Get Action Details response
export interface ZapierActionNeed {
key: string
type: string
label: string
required: boolean
helpText: string
defaultValue: any
choices: any[] | null
dependsOn: string[] | null
customField: boolean
}
export interface ZapierActionGive {
key: string
label: string
type: string
score: number | null
subscore: number | null
important: boolean
sample: any
}
export interface ZapierGetActionDetailsResponse extends ToolResponse {
output: {
action: {
type: string
key: string
name: string
noun: string
description: string
} | null
needs: ZapierActionNeed[]
gives: ZapierActionGive[]
sample: any
customNeedsProbability: number
}
}
// Update AI Action response - same as Create AI Action response
export interface ZapierUpdateAiActionResponse extends ToolResponse {
output: {
id: string
description: string
actionType: string
app: string
appLabel: string
action: string
actionLabel: string
params: Record<string, any>
accountId: number | null
authenticationId: number | null
}
}
// Delete AI Action response
export interface ZapierDeleteAiActionResponse extends ToolResponse {
output: {
deleted: boolean
message: string
}
}
// Union type for all Zapier responses
export type ZapierResponse =
| ZapierExecuteActionResponse
| ZapierListActionsResponse
| ZapierSearchAppsResponse
| ZapierGuessActionsResponse
| ZapierCreateAiActionResponse
| ZapierStatelessExecuteResponse
| ZapierSearchAppActionsResponse
| ZapierGetActionDetailsResponse
| ZapierUpdateAiActionResponse
| ZapierDeleteAiActionResponse

View File

@@ -0,0 +1,174 @@
import type { ToolConfig } from '@/tools/types'
import type { ZapierUpdateAiActionParams, ZapierUpdateAiActionResponse } from '@/tools/zapier/types'
export const zapierUpdateAiActionTool: ToolConfig<
ZapierUpdateAiActionParams,
ZapierUpdateAiActionResponse
> = {
id: 'zapier_update_action',
name: 'Zapier Update AI Action',
description: 'Update an existing stored AI Action configuration in Zapier.',
version: '1.0.0',
oauth: {
required: true,
provider: 'zapier',
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Access token for Zapier AI Actions API',
},
actionId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'The ID of the AI Action to update',
},
app: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'The app identifier (e.g., "SlackAPI", "GmailV2API")',
},
action: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'The action identifier (e.g., "send_channel_message", "send_email")',
},
actionType: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Type of action: write, search, read, read_bulk, search_or_write, search_and_write',
},
params: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description: 'Pre-configured parameter values for the action',
},
accountId: {
type: 'number',
required: false,
visibility: 'user-only',
description: 'Zapier account ID',
},
authenticationId: {
type: 'number',
required: false,
visibility: 'user-only',
description: 'Authentication ID for the app connection',
},
meta: {
type: 'json',
required: false,
visibility: 'user-only',
description:
'Metadata object with params labels, app_label, action_label, authentication_label, app_needs_auth',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.accountId !== undefined) {
queryParams.append('account_id', String(params.accountId))
}
if (params.authenticationId !== undefined) {
queryParams.append('authentication_id', String(params.authenticationId))
}
const query = queryParams.toString()
return `https://actions.zapier.com/api/v2/ai-actions/${encodeURIComponent(params.actionId)}/${query ? `?${query}` : ''}`
},
method: 'PUT',
headers: (params) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${params.accessToken}`,
}),
body: (params) => ({
app: params.app,
action: params.action,
action_type: params.actionType || 'write',
params: params.params || {},
meta: params.meta || { params: {} },
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || data.detail || `Zapier API error: ${response.status}`)
}
const appLabel = data.meta?.app_label || ''
const actionLabel = data.meta?.action_label || ''
return {
success: true,
output: {
id: data.id || '',
description: data.description || '',
actionType: data.action_type || '',
app: data.app || '',
appLabel,
action: data.action || '',
actionLabel,
params: data.params || {},
accountId: data.account_id ?? null,
authenticationId: data.authentication_id ?? null,
},
}
},
outputs: {
id: {
type: 'string',
description: 'The ID of the updated AI Action',
},
description: {
type: 'string',
description: 'Description of the action',
},
actionType: {
type: 'string',
description: 'Type of action',
},
app: {
type: 'string',
description: 'App identifier',
},
appLabel: {
type: 'string',
description: 'Human-readable app label',
},
action: {
type: 'string',
description: 'Action identifier',
},
actionLabel: {
type: 'string',
description: 'Human-readable action label',
},
params: {
type: 'json',
description: 'Configured parameter values',
},
accountId: {
type: 'number',
description: 'Zapier account ID',
optional: true,
},
authenticationId: {
type: 'number',
description: 'Authentication ID used for the app',
optional: true,
},
},
}