Compare commits

..

20 Commits

Author SHA1 Message Date
Waleed
13a6e6c3fa v0.5.54: seo, model blacklist, helm chart updates, fireflies integration, autoconnect improvements, billing fixes 2026-01-07 16:09:45 -08:00
Vikhyath Mondreti
cb12ceb82c fix(preproc-errors): should not charge base execution cost in this case (#2719)
* fix(preproc-errors): should not charge base execution cost in this case

* remove comment
2026-01-07 15:32:37 -08:00
Waleed
0f32310ba6 feat(i18n): update translations (#2717)
* feat(i18n): update translations

* fixed chinese docs

---------

Co-authored-by: waleedlatif1 <waleedlatif1@users.noreply.github.com>
2026-01-07 14:28:58 -08:00
Vikhyath Mondreti
730ddf5a66 ui improvements for deploy mcp (#2718) 2026-01-07 14:25:03 -08:00
Waleed
ef4bec2c37 improvement(context-menu): added awareness for chat and variables being open, fixed select calculation to match height calculation for selecting multiple blocks (#2715) 2026-01-07 13:40:53 -08:00
Waleed
2bd27f9a4d feat(fireflies): added fireflies tools and trigger (#2713)
* feat(fireflies): added fireflies tools and trigger

* finished fireflies

* added wandConfig to all timestamp subblocks on the platform

* added current time to timestamp wand generation

* fix file upload subblock styling, tested all fireflies ops

* removed dropdown for trigger for fireflies

* updated docs

* added fireflies to formatWebhookInput

* added more wandConfigs
2026-01-07 13:40:36 -08:00
Vikhyath Mondreti
3b4f7d6adb improvement(add-block): intuitive autoconnect + positioning (#2714)
* improvement(add-block): intuitive autoconnect + positioning

* cleanup code
2026-01-07 12:52:12 -08:00
Waleed
142c9a0428 fix(grain): add grain key to idempotency service (#2712)
* fix(grain): add grain key to idempotency service

* fixed dropdown issue for grain, webhook registration
2026-01-07 12:00:32 -08:00
Waleed
9dc02f3728 improvement(helm): added missing optional envvars to helm for whitelabeling (#2711) 2026-01-07 10:56:13 -08:00
Vikhyath Mondreti
833825f04a fix(deploy-check): race condition fixes (#2710) 2026-01-07 10:48:54 -08:00
Waleed
261becd129 feat(blacklist): added ability to blacklist models & providers (#2709)
* feat(blacklist): added ability to blacklist models & providers

* ack PR comments
2026-01-07 10:41:57 -08:00
Waleed
3ecf7a15eb feat(seo): updated out-of-date site metadata, removed unused static assets, updated emails (#2708)
* feat(seo): updated out-of-date site metadata, removed unused static assets, updated emails

* more

* more

* remove unused social photos
2026-01-07 09:38:40 -08:00
Waleed
1420bfb73c fix(resolver): add both new and old workflow blocks for backwards compatibility 2026-01-07 08:03:36 -08:00
Waleed
f5ab7f21ae v0.5.53: hotkey improvements, added redis fallback, fixes for workflow tool 2026-01-06 23:34:52 -08:00
Waleed
02229f0cb2 fix(agent-tool): fix workflow tool in agent to respect user-provided params, added badge for deployment status (#2705)
* fix(agent-tool): fix workflow tool in agent to respect user-provided params, added badge for deployment status

* ack PR comment

* updated gh stars
2026-01-06 23:22:59 -08:00
Waleed
a2451ef3d3 feat(locks): add no-op for locking without redis to allow deployments without redis (#2703)
* feat(locks): add no-op for locking without redis to allow deployments without redis

* ack PR comments, fixed worklfow block color
2026-01-06 23:14:34 -08:00
Waleed
6a262f3988 feat(i18n): update translations (#2702)
Co-authored-by: waleedlatif1 <waleedlatif1@users.noreply.github.com>
2026-01-06 19:57:29 -08:00
Waleed
5145ce1684 improvement(response): removed nested response block output, add docs for webhook block, styling improvements for subblocks (#2700)
* improvement(response): removed nested response block output, add docs for webhook block, styling improvements for subblocks

* remove outdated block docs

* updated docs

* remove outdated tests
2026-01-06 19:43:25 -08:00
Waleed
e5bd5e4474 fix(canvas): add handler for focus loss for hotkey operations (#2701) 2026-01-06 19:36:52 -08:00
Waleed
e9aede087d improvement(triggers): moved save configuration above instructions for better visibility, fixed styling inconsistencies (#2699) 2026-01-06 17:29:42 -08:00
266 changed files with 12030 additions and 2955 deletions

View File

@@ -4414,3 +4414,164 @@ export function JiraServiceManagementIcon(props: SVGProps<SVGSVGElement>) {
</svg>
)
}
export function FirefliesIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='-6 -6 68 68'>
<defs>
<linearGradient
id='fireflies_g1'
gradientUnits='userSpaceOnUse'
x1='144.6644'
y1='-133.7781'
x2='54.3811'
y2='-38.9195'
gradientTransform='matrix(0.8571 0 0 -0.8571 -79.2389 -68.1736)'
>
<stop offset='0' stopColor='#E82A73' />
<stop offset='0.113' stopColor='#DE2D7A' />
<stop offset='0.3' stopColor='#C5388F' />
<stop offset='0.54' stopColor='#9B4AB0' />
<stop offset='0.818' stopColor='#6262DE' />
<stop offset='0.994' stopColor='#3B73FF' />
</linearGradient>
<linearGradient
id='fireflies_g2'
gradientUnits='userSpaceOnUse'
x1='145.1664'
y1='-133.3084'
x2='54.8831'
y2='-38.4499'
gradientTransform='matrix(0.8571 0 0 -0.8571 -79.2389 -68.1736)'
>
<stop offset='0' stopColor='#FF3C82' />
<stop offset='0.103' stopColor='#F53E88' />
<stop offset='0.274' stopColor='#DC4598' />
<stop offset='0.492' stopColor='#B251B2' />
<stop offset='0.745' stopColor='#7961D7' />
<stop offset='0.994' stopColor='#3B73FF' />
</linearGradient>
<linearGradient
id='fireflies_g3'
gradientUnits='userSpaceOnUse'
x1='144.7625'
y1='-123.2011'
x2='114.171'
y2='-12.3403'
gradientTransform='matrix(0.8571 0 0 -0.8571 -79.2389 -68.1736)'
>
<stop offset='0' stopColor='#E82A73' />
<stop offset='0.113' stopColor='#DE2D7A' />
<stop offset='0.3' stopColor='#C5388F' />
<stop offset='0.54' stopColor='#9B4AB0' />
<stop offset='0.818' stopColor='#6262DE' />
<stop offset='0.994' stopColor='#3B73FF' />
</linearGradient>
<linearGradient
id='fireflies_g4'
gradientUnits='userSpaceOnUse'
x1='134.8237'
y1='-132.3271'
x2='25.3098'
y2='-98.9636'
gradientTransform='matrix(0.8571 0 0 -0.8571 -79.2389 -68.1736)'
>
<stop offset='0' stopColor='#E82A73' />
<stop offset='0.113' stopColor='#DE2D7A' />
<stop offset='0.3' stopColor='#C5388F' />
<stop offset='0.54' stopColor='#9B4AB0' />
<stop offset='0.818' stopColor='#6262DE' />
<stop offset='0.994' stopColor='#3B73FF' />
</linearGradient>
<linearGradient
id='fireflies_g5'
gradientUnits='userSpaceOnUse'
x1='82.2078'
y1='-52.7908'
x2='112.8836'
y2='-123.0805'
gradientTransform='matrix(0.8571 0 0 -0.8571 -79.2389 -68.1736)'
>
<stop offset='0' stopColor='#E82A73' />
<stop offset='0.114' stopColor='#DE286E' />
<stop offset='0.303' stopColor='#C52361' />
<stop offset='0.544' stopColor='#9B1A4D' />
<stop offset='0.825' stopColor='#620F30' />
<stop offset='0.994' stopColor='#3D081E' />
</linearGradient>
<linearGradient
id='fireflies_g6'
gradientUnits='userSpaceOnUse'
x1='107.6542'
y1='-78.5296'
x2='138.33'
y2='-148.8194'
gradientTransform='matrix(0.8571 0 0 -0.8571 -79.2389 -68.1736)'
>
<stop offset='0' stopColor='#E82A73' />
<stop offset='0.114' stopColor='#DE286E' />
<stop offset='0.303' stopColor='#C52361' />
<stop offset='0.544' stopColor='#9B1A4D' />
<stop offset='0.825' stopColor='#620F30' />
<stop offset='0.994' stopColor='#3D081E' />
</linearGradient>
<linearGradient
id='fireflies_g7'
gradientUnits='userSpaceOnUse'
x1='70.8311'
y1='-99.3209'
x2='140.3046'
y2='-145.474'
gradientTransform='matrix(0.8571 0 0 -0.8571 -79.2389 -68.1736)'
>
<stop offset='0' stopColor='#E82A73' />
<stop offset='0.114' stopColor='#DE286E' />
<stop offset='0.303' stopColor='#C52361' />
<stop offset='0.544' stopColor='#9B1A4D' />
<stop offset='0.825' stopColor='#620F30' />
<stop offset='0.994' stopColor='#3D081E' />
</linearGradient>
<linearGradient
id='fireflies_g8'
gradientUnits='userSpaceOnUse'
x1='297.6904'
y1='-1360.8851'
x2='309.5946'
y2='-1454.8754'
gradientTransform='matrix(0.8571 0 0 -0.8571 -79.2389 -68.1736)'
>
<stop offset='0' stopColor='#E82A73' />
<stop offset='0.114' stopColor='#DE286E' />
<stop offset='0.303' stopColor='#C52361' />
<stop offset='0.544' stopColor='#9B1A4D' />
<stop offset='0.825' stopColor='#620F30' />
<stop offset='0.994' stopColor='#3D081E' />
</linearGradient>
</defs>
<g>
<path fill='url(#fireflies_g1)' d='M18.4,0H0v18.3h18.4V0z' />
<path fill='url(#fireflies_g2)' d='M40.2,22.1H21.8v18.3h18.4V22.1z' />
<path
fill='url(#fireflies_g3)'
d='M40.2,0H21.8v18.3H56v-2.6c0-4.2-1.7-8.1-4.6-11.1C48.4,1.7,44.4,0,40.2,0L40.2,0z'
/>
<path
fill='url(#fireflies_g4)'
d='M0,22.1v18.3c0,4.2,1.7,8.1,4.6,11.1c3,2.9,7,4.6,11.2,4.6h2.6V22.1H0z'
/>
<path fill='url(#fireflies_g5)' opacity='0.18' d='M0,0l18.4,18.3H0V0z' />
<path fill='url(#fireflies_g6)' opacity='0.18' d='M21.8,22.1l18.4,18.3H21.8V22.1z' />
<path
fill='url(#fireflies_g7)'
opacity='0.18'
d='M0,40.3c0,4.2,1.7,8.1,4.6,11.1c3,2.9,7,4.6,11.2,4.6h2.6V22.1L0,40.3z'
/>
<path
fill='url(#fireflies_g8)'
opacity='0.18'
d='M40.2,0c4.2,0,8.2,1.7,11.2,4.6c3,2.9,4.6,6.9,4.6,11.1v2.6H21.8L40.2,0z'
/>
</g>
</svg>
)
}

View File

@@ -28,6 +28,7 @@ import {
ExaAIIcon,
EyeIcon,
FirecrawlIcon,
FirefliesIcon,
GithubIcon,
GitLabIcon,
GmailIcon,
@@ -147,6 +148,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
exa: ExaAIIcon,
file: DocumentIcon,
firecrawl: FirecrawlIcon,
fireflies: FirefliesIcon,
github: GithubIcon,
gitlab: GitLabIcon,
gmail: GmailIcon,

View File

@@ -0,0 +1,89 @@
---
title: Webhook
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Image } from '@/components/ui/image'
Der Webhook-Block sendet HTTP-POST-Anfragen an externe Webhook-Endpunkte mit automatischen Webhook-Headern und optionaler HMAC-Signierung.
<div className="flex justify-center">
<Image
src="/static/blocks/webhook.png"
alt="Webhook-Block"
width={500}
height={400}
className="my-6"
/>
</div>
## Konfiguration
### Webhook-URL
Der Ziel-Endpunkt für Ihre Webhook-Anfrage. Unterstützt sowohl statische URLs als auch dynamische Werte aus anderen Blöcken.
### Payload
JSON-Daten, die im Anfrage-Body gesendet werden. Verwenden Sie den KI-Zauberstab, um Payloads zu generieren oder auf Workflow-Variablen zu verweisen:
```json
{
"event": "workflow.completed",
"data": {
"result": "<agent.content>",
"timestamp": "<function.result>"
}
}
```
### Signierungsgeheimnis
Optionales Geheimnis für die HMAC-SHA256-Payload-Signierung. Wenn angegeben, wird ein `X-Webhook-Signature`Header hinzugefügt:
```
X-Webhook-Signature: t=1704067200000,v1=5d41402abc4b2a76b9719d911017c592...
```
Um Signaturen zu verifizieren, berechnen Sie `HMAC-SHA256(secret, "${timestamp}.${body}")` und vergleichen Sie mit dem `v1`Wert.
### Zusätzliche Header
Benutzerdefinierte Schlüssel-Wert-Header, die in die Anfrage aufgenommen werden. Diese überschreiben alle automatischen Header mit demselben Namen.
## Automatische Header
Jede Anfrage enthält automatisch diese Header:
| Header | Beschreibung |
|--------|-------------|
| `Content-Type` | `application/json` |
| `X-Webhook-Timestamp` | Unix-Zeitstempel in Millisekunden |
| `X-Delivery-ID` | Eindeutige UUID für diese Zustellung |
| `Idempotency-Key` | Identisch mit `X-Delivery-ID` zur Deduplizierung |
## Ausgaben
| Ausgabe | Typ | Beschreibung |
|--------|------|-------------|
| `data` | json | Antwort-Body vom Endpunkt |
| `status` | number | HTTP-Statuscode |
| `headers` | object | Antwort-Header |
## Beispiel-Anwendungsfälle
**Externe Dienste benachrichtigen** - Workflow-Ergebnisse an Slack, Discord oder benutzerdefinierte Endpunkte senden
```
Agent → Function (format) → Webhook (notify)
```
**Externe Workflows auslösen** - Prozesse in anderen Systemen starten, wenn Bedingungen erfüllt sind
```
Condition (check) → Webhook (trigger) → Response
```
<Callout>
Der Webhook-Block verwendet immer POST. Für andere HTTP-Methoden oder mehr Kontrolle verwenden Sie den [API-Block](/blocks/api).
</Callout>

View File

@@ -0,0 +1,233 @@
---
title: Fireflies
description: Interagieren Sie mit Fireflies.ai-Besprechungstranskripten und -aufzeichnungen
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="fireflies"
color="#100730"
/>
{/* MANUAL-CONTENT-START:intro */}
[Fireflies.ai](https://fireflies.ai/) ist eine Plattform für Besprechungstranskription und -intelligenz, die sich in Sim integriert und es Ihren Agenten ermöglicht, direkt mit Besprechungsaufzeichnungen, Transkripten und Erkenntnissen über No-Code-Automatisierungen zu arbeiten.
Die Fireflies-Integration in Sim bietet Tools für:
- **Besprechungstranskripte auflisten:** Rufen Sie mehrere Besprechungen und deren Zusammenfassungsinformationen für Ihr Team oder Konto ab.
- **Vollständige Transkriptdetails abrufen:** Greifen Sie auf detaillierte Transkripte zu, einschließlich Zusammenfassungen, Aktionspunkten, Themen und Teilnehmeranalysen für jede Besprechung.
- **Audio oder Video hochladen:** Laden Sie Audio-/Videodateien hoch oder geben Sie URLs zur Transkription an optional können Sie Sprache, Titel, Teilnehmer festlegen und automatisierte Besprechungsnotizen erhalten.
- **Transkripte durchsuchen:** Finden Sie Besprechungen nach Stichwort, Teilnehmer, Moderator oder Zeitraum, um relevante Diskussionen schnell zu lokalisieren.
- **Transkripte löschen:** Entfernen Sie bestimmte Besprechungstranskripte aus Ihrem Fireflies-Workspace.
- **Soundbites (Bites) erstellen:** Extrahieren und markieren Sie wichtige Momente aus Transkripten als Audio- oder Videoclips.
- **Workflows bei Transkriptionsabschluss auslösen:** Aktivieren Sie Sim-Workflows automatisch, wenn eine Fireflies-Besprechungstranskription abgeschlossen ist, mithilfe des bereitgestellten Webhook-Triggers dies ermöglicht Echtzeit-Automatisierungen und Benachrichtigungen basierend auf neuen Besprechungsdaten.
Durch die Kombination dieser Funktionen können Sie Aktionen nach Besprechungen optimieren, strukturierte Erkenntnisse extrahieren, Benachrichtigungen automatisieren, Aufzeichnungen verwalten und benutzerdefinierte Workflows rund um die Anrufe Ihrer Organisation orchestrieren alles sicher unter Verwendung Ihres API-Schlüssels und Ihrer Fireflies-Anmeldedaten.
{/* MANUAL-CONTENT-END */}
## Nutzungsanweisungen
Integrieren Sie Fireflies.ai in den Workflow. Verwalten Sie Besprechungstranskripte, fügen Sie Bots zu Live-Besprechungen hinzu, erstellen Sie Soundbites und mehr. Kann auch Workflows auslösen, wenn Transkriptionen abgeschlossen sind.
## Tools
### `fireflies_list_transcripts`
Meeting-Transkripte von Fireflies.ai mit optionaler Filterung auflisten
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Ja | Fireflies API-Schlüssel |
| `keyword` | string | Nein | Suchbegriff im Meeting-Titel oder Transkript |
| `fromDate` | string | Nein | Transkripte ab diesem Datum filtern \(ISO 8601-Format\) |
| `toDate` | string | Nein | Transkripte bis zu diesem Datum filtern \(ISO 8601-Format\) |
| `hostEmail` | string | Nein | Nach E-Mail-Adresse des Meeting-Hosts filtern |
| `participants` | string | Nein | Nach E-Mail-Adressen der Teilnehmer filtern \(durch Komma getrennt\) |
| `limit` | number | Nein | Maximale Anzahl der zurückzugebenden Transkripte \(max. 50\) |
| `skip` | number | Nein | Anzahl der zu überspringenden Transkripte für Paginierung |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `transcripts` | array | Liste der Transkripte |
| `count` | number | Anzahl der zurückgegebenen Transkripte |
### `fireflies_get_transcript`
Ein einzelnes Transkript mit vollständigen Details einschließlich Zusammenfassung, Aktionspunkten und Analysen abrufen
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Ja | Fireflies API-Schlüssel |
| `transcriptId` | string | Ja | Die abzurufende Transkript-ID |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `transcript` | object | Das Transkript mit vollständigen Details |
### `fireflies_get_user`
Ruft Benutzerinformationen von Fireflies.ai ab. Gibt den aktuellen Benutzer zurück, wenn keine ID angegeben ist.
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Ja | Fireflies-API-Schlüssel |
| `userId` | string | Nein | Abzurufende Benutzer-ID \(optional, Standardwert ist der Inhaber des API-Schlüssels\) |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `user` | object | Benutzerinformationen |
### `fireflies_list_users`
Listet alle Benutzer in Ihrem Fireflies.ai-Team auf
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Ja | Fireflies-API-Schlüssel |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `users` | array | Liste der Teammitglieder |
### `fireflies_upload_audio`
Lädt eine Audiodatei-URL zur Transkription zu Fireflies.ai hoch
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Ja | Fireflies-API-Schlüssel |
| `audioFile` | file | Nein | Audio-/Videodatei zur Transkription hochladen |
| `audioUrl` | string | Nein | Öffentliche HTTPS-URL der Audio-/Videodatei \(MP3, MP4, WAV, M4A, OGG\) |
| `title` | string | Nein | Titel für das Meeting/Transkript |
| `webhook` | string | Nein | Webhook-URL zur Benachrichtigung, wenn die Transkription abgeschlossen ist |
| `language` | string | Nein | Sprachcode für die Transkription \(z. B. „es" für Spanisch, „de" für Deutsch\) |
| `attendees` | string | Nein | Teilnehmer im JSON-Format: \[\{"displayName": "Name", "email": "email@example.com"\}\] |
| `clientReferenceId` | string | Nein | Benutzerdefinierte Referenz-ID zur Nachverfolgung |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `success` | boolean | Ob der Upload erfolgreich war |
| `title` | string | Titel des hochgeladenen Meetings |
| `message` | string | Statusmeldung von Fireflies |
### `fireflies_delete_transcript`
Ein Transkript von Fireflies.ai löschen
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Ja | Fireflies API-Schlüssel |
| `transcriptId` | string | Ja | Die zu löschende Transkript-ID |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `success` | boolean | Ob das Transkript erfolgreich gelöscht wurde |
### `fireflies_add_to_live_meeting`
Fügen Sie den Fireflies.ai-Bot zu einem laufenden Meeting hinzu, um aufzuzeichnen und zu transkribieren
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Ja | Fireflies API-Schlüssel |
| `meetingLink` | string | Ja | Gültige Meeting-URL \(Zoom, Google Meet, Microsoft Teams, etc.\) |
| `title` | string | Nein | Titel für das Meeting \(max. 256 Zeichen\) |
| `meetingPassword` | string | Nein | Passwort für das Meeting, falls erforderlich \(max. 32 Zeichen\) |
| `duration` | number | Nein | Meetingdauer in Minuten \(15120, Standard: 60\) |
| `language` | string | Nein | Sprachcode für die Transkription \(z. B. "en", "es", "de"\) |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `success` | boolean | Ob der Bot erfolgreich zum Meeting hinzugefügt wurde |
### `fireflies_create_bite`
Erstellen Sie einen Soundbite/Highlight aus einem bestimmten Zeitbereich in einem Transkript
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Ja | Fireflies API-Schlüssel |
| `transcriptId` | string | Ja | ID des Transkripts, aus dem der Bite erstellt werden soll |
| `startTime` | number | Ja | Startzeit des Bites in Sekunden |
| `endTime` | number | Ja | Endzeit des Bites in Sekunden |
| `name` | string | Nein | Name für den Bite \(max. 256 Zeichen\) |
| `mediaType` | string | Nein | Medientyp: "video" oder "audio" |
| `summary` | string | Nein | Zusammenfassung für den Bite \(max. 500 Zeichen\) |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `bite` | object | Details des erstellten Bites |
### `fireflies_list_bites`
Soundbites/Highlights von Fireflies.ai auflisten
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Ja | Fireflies API-Schlüssel |
| `transcriptId` | string | Nein | Bites für ein bestimmtes Transkript filtern |
| `mine` | boolean | Nein | Nur Bites zurückgeben, die dem Besitzer des API-Schlüssels gehören \(Standard: true\) |
| `limit` | number | Nein | Maximale Anzahl der zurückzugebenden Bites \(max. 50\) |
| `skip` | number | Nein | Anzahl der zu überspringenden Bites für die Paginierung |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `bites` | array | Liste der Bites/Soundbites |
### `fireflies_list_contacts`
Alle Kontakte aus Ihren Fireflies.ai-Meetings auflisten
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Ja | Fireflies-API-Schlüssel |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `contacts` | array | Liste der Kontakte aus Meetings |
## Hinweise
- Kategorie: `tools`
- Typ: `fireflies`

View File

@@ -1,231 +0,0 @@
---
title: Webhook
description: Empfangen Sie Webhooks von jedem Dienst durch Konfiguration eines
benutzerdefinierten Webhooks.
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
import { Image } from '@/components/ui/image'
<BlockInfoCard
type="generic_webhook"
color="#10B981"
/>
<div className="flex justify-center">
<Image
src="/static/blocks/webhook.png"
alt="Webhook-Block-Konfiguration"
width={500}
height={400}
className="my-6"
/>
</div>
## Übersicht
Der generische Webhook-Block ermöglicht den Empfang von Webhooks von jedem externen Dienst. Dies ist ein flexibler Trigger, der jede JSON-Nutzlast verarbeiten kann und sich daher ideal für die Integration mit Diensten eignet, die keinen dedizierten Sim-Block haben.
## Grundlegende Verwendung
### Einfacher Durchleitungsmodus
Ohne ein definiertes Eingabeformat leitet der Webhook den gesamten Anforderungstext unverändert weiter:
```bash
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
-H "Content-Type: application/json" \
-H "X-Sim-Secret: your-secret" \
-d '{
"message": "Test webhook trigger",
"data": {
"key": "value"
}
}'
```
Greifen Sie in nachgelagerten Blöcken auf die Daten zu mit:
- `<webhook1.message>` → "Test webhook trigger"
- `<webhook1.data.key>` → "value"
### Strukturiertes Eingabeformat (optional)
Definieren Sie ein Eingabeschema, um typisierte Felder zu erhalten und erweiterte Funktionen wie Datei-Uploads zu aktivieren:
**Konfiguration des Eingabeformats:**
```json
[
{ "name": "message", "type": "string" },
{ "name": "priority", "type": "number" },
{ "name": "documents", "type": "files" }
]
```
**Webhook-Anfrage:**
```bash
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
-H "Content-Type: application/json" \
-H "X-Sim-Secret: your-secret" \
-d '{
"message": "Invoice submission",
"priority": 1,
"documents": [
{
"type": "file",
"data": "data:application/pdf;base64,JVBERi0xLjQK...",
"name": "invoice.pdf",
"mime": "application/pdf"
}
]
}'
```
## Datei-Uploads
### Unterstützte Dateiformate
Der Webhook unterstützt zwei Dateieingabeformate:
#### 1. Base64-kodierte Dateien
Zum direkten Hochladen von Dateiinhalten:
```json
{
"documents": [
{
"type": "file",
"data": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgA...",
"name": "screenshot.png",
"mime": "image/png"
}
]
}
```
- **Maximale Größe**: 20MB pro Datei
- **Format**: Standard-Daten-URL mit Base64-Kodierung
- **Speicherung**: Dateien werden in sicheren Ausführungsspeicher hochgeladen
#### 2. URL-Referenzen
Zum Übergeben vorhandener Datei-URLs:
```json
{
"documents": [
{
"type": "url",
"data": "https://example.com/files/document.pdf",
"name": "document.pdf",
"mime": "application/pdf"
}
]
}
```
### Zugriff auf Dateien in nachgelagerten Blöcken
Dateien werden in `UserFile`Objekte mit den folgenden Eigenschaften verarbeitet:
```typescript
{
id: string, // Unique file identifier
name: string, // Original filename
url: string, // Presigned URL (valid for 5 minutes)
size: number, // File size in bytes
type: string, // MIME type
key: string, // Storage key
uploadedAt: string, // ISO timestamp
expiresAt: string // ISO timestamp (5 minutes)
}
```
**Zugriff in Blöcken:**
- `<webhook1.documents[0].url>` → Download-URL
- `<webhook1.documents[0].name>` → "invoice.pdf"
- `<webhook1.documents[0].size>` → 524288
- `<webhook1.documents[0].type>` → "application/pdf"
### Vollständiges Datei-Upload-Beispiel
```bash
# Create a base64-encoded file
echo "Hello World" | base64
# SGVsbG8gV29ybGQK
# Send webhook with file
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
-H "Content-Type: application/json" \
-H "X-Sim-Secret: your-secret" \
-d '{
"subject": "Document for review",
"attachments": [
{
"type": "file",
"data": "data:text/plain;base64,SGVsbG8gV29ybGQK",
"name": "sample.txt",
"mime": "text/plain"
}
]
}'
```
## Authentifizierung
### Authentifizierung konfigurieren (Optional)
In der Webhook-Konfiguration:
1. Aktiviere "Authentifizierung erforderlich"
2. Setze einen geheimen Token
3. Wähle den Header-Typ:
- **Benutzerdefinierter Header**: `X-Sim-Secret: your-token`
- **Authorization Bearer**: `Authorization: Bearer your-token`
### Verwendung der Authentifizierung
```bash
# With custom header
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
-H "Content-Type: application/json" \
-H "X-Sim-Secret: your-secret-token" \
-d '{"message": "Authenticated request"}'
# With bearer token
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-secret-token" \
-d '{"message": "Authenticated request"}'
```
## Best Practices
1. **Eingabeformat für Struktur verwenden**: Definiere ein Eingabeformat, wenn du das erwartete Schema kennst. Dies bietet:
- Typvalidierung
- Bessere Autovervollständigung im Editor
- Datei-Upload-Funktionen
2. **Authentifizierung**: Aktiviere immer die Authentifizierung für Produktions-Webhooks, um unbefugten Zugriff zu verhindern.
3. **Dateigrößenbeschränkungen**: Halte Dateien unter 20 MB. Verwende für größere Dateien URL-Referenzen.
4. **Dateiablauf**: Heruntergeladene Dateien haben URLs mit einer Gültigkeit von 5 Minuten. Verarbeite sie umgehend oder speichere sie an anderer Stelle, wenn sie länger benötigt werden.
5. **Fehlerbehandlung**: Die Webhook-Verarbeitung erfolgt asynchron. Überprüfe die Ausführungsprotokolle auf Fehler.
6. **Testen**: Verwende die Schaltfläche "Webhook testen" im Editor, um deine Konfiguration vor der Bereitstellung zu validieren.
## Anwendungsfälle
- **Formularübermittlungen**: Empfange Daten von benutzerdefinierten Formularen mit Datei-Uploads
- **Drittanbieter-Integrationen**: Verbinde mit Diensten, die Webhooks senden (Stripe, GitHub usw.)
- **Dokumentenverarbeitung**: Akzeptiere Dokumente von externen Systemen zur Verarbeitung
- **Ereignisbenachrichtigungen**: Empfange Ereignisdaten aus verschiedenen Quellen
- **Benutzerdefinierte APIs**: Erstelle benutzerdefinierte API-Endpunkte für deine Anwendungen
## Hinweise
- Kategorie: `triggers`
- Typ: `generic_webhook`
- **Dateiunterstützung**: Verfügbar über Eingabeformat-Konfiguration
- **Maximale Dateigröße**: 20 MB pro Datei

View File

@@ -15,7 +15,7 @@ Der generische Webhook-Block erstellt einen flexiblen Endpunkt, der beliebige Pa
<div className="flex justify-center">
<Image
src="/static/blocks/webhook.png"
src="/static/blocks/webhook-trigger.png"
alt="Generische Webhook-Konfiguration"
width={500}
height={400}

View File

@@ -14,6 +14,7 @@
"router",
"variables",
"wait",
"webhook",
"workflow"
]
}

View File

@@ -0,0 +1,87 @@
---
title: Webhook
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Image } from '@/components/ui/image'
The Webhook block sends HTTP POST requests to external webhook endpoints with automatic webhook headers and optional HMAC signing.
<div className="flex justify-center">
<Image
src="/static/blocks/webhook.png"
alt="Webhook Block"
width={500}
height={400}
className="my-6"
/>
</div>
## Configuration
### Webhook URL
The destination endpoint for your webhook request. Supports both static URLs and dynamic values from other blocks.
### Payload
JSON data to send in the request body. Use the AI wand to generate payloads or reference workflow variables:
```json
{
"event": "workflow.completed",
"data": {
"result": "<agent.content>",
"timestamp": "<function.result>"
}
}
```
### Signing Secret
Optional secret for HMAC-SHA256 payload signing. When provided, adds an `X-Webhook-Signature` header:
```
X-Webhook-Signature: t=1704067200000,v1=5d41402abc4b2a76b9719d911017c592...
```
To verify signatures, compute `HMAC-SHA256(secret, "${timestamp}.${body}")` and compare with the `v1` value.
### Additional Headers
Custom key-value headers to include with the request. These override any automatic headers with the same name.
## Automatic Headers
Every request includes these headers automatically:
| Header | Description |
|--------|-------------|
| `Content-Type` | `application/json` |
| `X-Webhook-Timestamp` | Unix timestamp in milliseconds |
| `X-Delivery-ID` | Unique UUID for this delivery |
| `Idempotency-Key` | Same as `X-Delivery-ID` for deduplication |
## Outputs
| Output | Type | Description |
|--------|------|-------------|
| `data` | json | Response body from the endpoint |
| `status` | number | HTTP status code |
| `headers` | object | Response headers |
## Example Use Cases
**Notify external services** - Send workflow results to Slack, Discord, or custom endpoints
```
Agent → Function (format) → Webhook (notify)
```
**Trigger external workflows** - Start processes in other systems when conditions are met
```
Condition (check) → Webhook (trigger) → Response
```
<Callout>
The Webhook block always uses POST. For other HTTP methods or more control, use the [API block](/blocks/api).
</Callout>

View File

@@ -16,7 +16,7 @@ MCP servers group your workflow tools together. Create and manage them in worksp
<Video src="mcp/mcp-server.mp4" width={700} height={450} />
</div>
1. Navigate to **Settings → MCP Servers**
1. Navigate to **Settings → Deployed MCPs**
2. Click **Create Server**
3. Enter a name and optional description
4. Copy the server URL for use in your MCP clients
@@ -78,7 +78,7 @@ Include your API key header (`X-API-Key`) for authenticated access when using mc
## Server Management
From the server detail view in **Settings → MCP Servers**, you can:
From the server detail view in **Settings → Deployed MCPs**, you can:
- **View tools**: See all workflows added to a server
- **Copy URL**: Get the server URL for MCP clients

View File

@@ -27,7 +27,7 @@ MCP servers provide collections of tools that your agents can use. Configure the
</div>
1. Navigate to your workspace settings
2. Go to the **MCP Servers** section
2. Go to the **Deployed MCPs** section
3. Click **Add MCP Server**
4. Enter the server configuration details
5. Save the configuration

View File

@@ -0,0 +1,238 @@
---
title: Fireflies
description: Interact with Fireflies.ai meeting transcripts and recordings
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="fireflies"
color="#100730"
/>
{/* MANUAL-CONTENT-START:intro */}
[Fireflies.ai](https://fireflies.ai/) is a meeting transcription and intelligence platform that integrates with Sim, allowing your agents to work directly with meeting recordings, transcripts, and insights through no-code automations.
The Fireflies integration in Sim provides tools to:
- **List meeting transcripts:** Fetch multiple meetings and their summary information for your team or account.
- **Retrieve full transcript details:** Access detailed transcripts, including summaries, action items, topics, and participant analytics for any meeting.
- **Upload audio or video:** Upload audio/video files or provide URLs for transcription—optionally set language, title, attendees, and receive automated meeting notes.
- **Search transcripts:** Find meetings by keyword, participant, host, or timeframe to quickly locate relevant discussions.
- **Delete transcripts:** Remove specific meeting transcripts from your Fireflies workspace.
- **Create soundbites (Bites):** Extract and highlight key moments from transcripts as audio or video clips.
- **Trigger workflows on transcription completion:** Activate Sim workflows automatically when a Fireflies meeting transcription finishes using the provided webhook trigger—enabling real-time automations and notifications based on new meeting data.
By combining these capabilities, you can streamline post-meeting actions, extract structured insights, automate notifications, manage recordings, and orchestrate custom workflows around your organizations calls—all securely using your API key and Fireflies credentials.
{/* MANUAL-CONTENT-END */}
## Usage Instructions
Integrate Fireflies.ai into the workflow. Manage meeting transcripts, add bot to live meetings, create soundbites, and more. Can also trigger workflows when transcriptions complete.
## Tools
### `fireflies_list_transcripts`
List meeting transcripts from Fireflies.ai with optional filtering
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Fireflies API key |
| `keyword` | string | No | Search keyword in meeting title or transcript |
| `fromDate` | string | No | Filter transcripts from this date \(ISO 8601 format\) |
| `toDate` | string | No | Filter transcripts until this date \(ISO 8601 format\) |
| `hostEmail` | string | No | Filter by meeting host email |
| `participants` | string | No | Filter by participant emails \(comma-separated\) |
| `limit` | number | No | Maximum number of transcripts to return \(max 50\) |
| `skip` | number | No | Number of transcripts to skip for pagination |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `transcripts` | array | List of transcripts |
| `count` | number | Number of transcripts returned |
### `fireflies_get_transcript`
Get a single transcript with full details including summary, action items, and analytics
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Fireflies API key |
| `transcriptId` | string | Yes | The transcript ID to retrieve |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `transcript` | object | The transcript with full details |
### `fireflies_get_user`
Get user information from Fireflies.ai. Returns current user if no ID specified.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Fireflies API key |
| `userId` | string | No | User ID to retrieve \(optional, defaults to API key owner\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `user` | object | User information |
### `fireflies_list_users`
List all users within your Fireflies.ai team
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Fireflies API key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `users` | array | List of team users |
### `fireflies_upload_audio`
Upload an audio file URL to Fireflies.ai for transcription
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Fireflies API key |
| `audioFile` | file | No | Audio/video file to upload for transcription |
| `audioUrl` | string | No | Public HTTPS URL of the audio/video file \(MP3, MP4, WAV, M4A, OGG\) |
| `title` | string | No | Title for the meeting/transcript |
| `webhook` | string | No | Webhook URL to notify when transcription is complete |
| `language` | string | No | Language code for transcription \(e.g., "es" for Spanish, "de" for German\) |
| `attendees` | string | No | Attendees in JSON format: \[\{"displayName": "Name", "email": "email@example.com"\}\] |
| `clientReferenceId` | string | No | Custom reference ID for tracking |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `success` | boolean | Whether the upload was successful |
| `title` | string | Title of the uploaded meeting |
| `message` | string | Status message from Fireflies |
### `fireflies_delete_transcript`
Delete a transcript from Fireflies.ai
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Fireflies API key |
| `transcriptId` | string | Yes | The transcript ID to delete |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `success` | boolean | Whether the transcript was successfully deleted |
### `fireflies_add_to_live_meeting`
Add the Fireflies.ai bot to an ongoing meeting to record and transcribe
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Fireflies API key |
| `meetingLink` | string | Yes | Valid meeting URL \(Zoom, Google Meet, Microsoft Teams, etc.\) |
| `title` | string | No | Title for the meeting \(max 256 characters\) |
| `meetingPassword` | string | No | Password for the meeting if required \(max 32 characters\) |
| `duration` | number | No | Meeting duration in minutes \(15-120, default: 60\) |
| `language` | string | No | Language code for transcription \(e.g., "en", "es", "de"\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `success` | boolean | Whether the bot was successfully added to the meeting |
### `fireflies_create_bite`
Create a soundbite/highlight from a specific time range in a transcript
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Fireflies API key |
| `transcriptId` | string | Yes | ID of the transcript to create the bite from |
| `startTime` | number | Yes | Start time of the bite in seconds |
| `endTime` | number | Yes | End time of the bite in seconds |
| `name` | string | No | Name for the bite \(max 256 characters\) |
| `mediaType` | string | No | Media type: "video" or "audio" |
| `summary` | string | No | Summary for the bite \(max 500 characters\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `bite` | object | Created bite details |
### `fireflies_list_bites`
List soundbites/highlights from Fireflies.ai
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Fireflies API key |
| `transcriptId` | string | No | Filter bites for a specific transcript |
| `mine` | boolean | No | Only return bites owned by the API key owner \(default: true\) |
| `limit` | number | No | Maximum number of bites to return \(max 50\) |
| `skip` | number | No | Number of bites to skip for pagination |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `bites` | array | List of bites/soundbites |
### `fireflies_list_contacts`
List all contacts from your Fireflies.ai meetings
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Fireflies API key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `contacts` | array | List of contacts from meetings |
## Notes
- Category: `tools`
- Type: `fireflies`

View File

@@ -23,6 +23,7 @@
"exa",
"file",
"firecrawl",
"fireflies",
"github",
"gitlab",
"gmail",

View File

@@ -15,7 +15,7 @@ The Generic Webhook block creates a flexible endpoint that can receive any paylo
<div className="flex justify-center">
<Image
src="/static/blocks/webhook.png"
src="/static/blocks/webhook-trigger.png"
alt="Generic Webhook Configuration"
width={500}
height={400}

View File

@@ -0,0 +1,89 @@
---
title: Webhook
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Image } from '@/components/ui/image'
El bloque Webhook envía solicitudes HTTP POST a endpoints de webhook externos con encabezados de webhook automáticos y firma HMAC opcional.
<div className="flex justify-center">
<Image
src="/static/blocks/webhook.png"
alt="Bloque Webhook"
width={500}
height={400}
className="my-6"
/>
</div>
## Configuración
### URL del webhook
El endpoint de destino para tu solicitud de webhook. Admite tanto URL estáticas como valores dinámicos de otros bloques.
### Carga útil
Datos JSON para enviar en el cuerpo de la solicitud. Usa la varita de IA para generar cargas útiles o referenciar variables del flujo de trabajo:
```json
{
"event": "workflow.completed",
"data": {
"result": "<agent.content>",
"timestamp": "<function.result>"
}
}
```
### Secreto de firma
Secreto opcional para la firma HMAC-SHA256 de la carga útil. Cuando se proporciona, añade un encabezado `X-Webhook-Signature`:
```
X-Webhook-Signature: t=1704067200000,v1=5d41402abc4b2a76b9719d911017c592...
```
Para verificar las firmas, calcula `HMAC-SHA256(secret, "${timestamp}.${body}")` y compara con el valor `v1`.
### Encabezados adicionales
Encabezados personalizados de clave-valor para incluir con la solicitud. Estos sobrescriben cualquier encabezado automático con el mismo nombre.
## Encabezados automáticos
Cada solicitud incluye estos encabezados automáticamente:
| Encabezado | Descripción |
|--------|-------------|
| `Content-Type` | `application/json` |
| `X-Webhook-Timestamp` | Marca de tiempo Unix en milisegundos |
| `X-Delivery-ID` | UUID único para esta entrega |
| `Idempotency-Key` | Igual que `X-Delivery-ID` para deduplicación |
## Salidas
| Salida | Tipo | Descripción |
|--------|------|-------------|
| `data` | json | Cuerpo de respuesta del endpoint |
| `status` | number | Código de estado HTTP |
| `headers` | object | Encabezados de respuesta |
## Ejemplos de casos de uso
**Notificar servicios externos** - Envía resultados del flujo de trabajo a Slack, Discord o endpoints personalizados
```
Agent → Function (format) → Webhook (notify)
```
**Activar flujos de trabajo externos** - Inicia procesos en otros sistemas cuando se cumplan las condiciones
```
Condition (check) → Webhook (trigger) → Response
```
<Callout>
El bloque Webhook siempre usa POST. Para otros métodos HTTP o más control, usa el [bloque API](/blocks/api).
</Callout>

View File

@@ -0,0 +1,233 @@
---
title: Fireflies
description: Interactúa con transcripciones y grabaciones de reuniones de Fireflies.ai
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="fireflies"
color="#100730"
/>
{/* MANUAL-CONTENT-START:intro */}
[Fireflies.ai](https://fireflies.ai/) es una plataforma de transcripción e inteligencia de reuniones que se integra con Sim, permitiendo que tus agentes trabajen directamente con grabaciones de reuniones, transcripciones e información mediante automatizaciones sin código.
La integración de Fireflies en Sim proporciona herramientas para:
- **Listar transcripciones de reuniones:** Obtén múltiples reuniones y su información resumida para tu equipo o cuenta.
- **Recuperar detalles completos de transcripciones:** Accede a transcripciones detalladas, incluyendo resúmenes, elementos de acción, temas y análisis de participantes para cualquier reunión.
- **Subir audio o vídeo:** Sube archivos de audio/vídeo o proporciona URLs para transcripción—opcionalmente establece idioma, título, asistentes y recibe notas de reunión automatizadas.
- **Buscar transcripciones:** Encuentra reuniones por palabra clave, participante, anfitrión o periodo de tiempo para localizar rápidamente discusiones relevantes.
- **Eliminar transcripciones:** Elimina transcripciones de reuniones específicas de tu espacio de trabajo de Fireflies.
- **Crear fragmentos destacados (Bites):** Extrae y resalta momentos clave de las transcripciones como clips de audio o vídeo.
- **Activar flujos de trabajo al completar la transcripción:** Activa flujos de trabajo de Sim automáticamente cuando finaliza una transcripción de reunión de Fireflies usando el webhook trigger proporcionado—habilitando automatizaciones en tiempo real y notificaciones basadas en nuevos datos de reuniones.
Al combinar estas capacidades, puedes optimizar acciones posteriores a las reuniones, extraer información estructurada, automatizar notificaciones, gestionar grabaciones y orquestar flujos de trabajo personalizados en torno a las llamadas de tu organización—todo de forma segura usando tu clave API y credenciales de Fireflies.
{/* MANUAL-CONTENT-END */}
## Instrucciones de uso
Integra Fireflies.ai en el flujo de trabajo. Gestiona transcripciones de reuniones, añade bot a reuniones en vivo, crea fragmentos destacados y más. También puede activar flujos de trabajo cuando se completan las transcripciones.
## Herramientas
### `fireflies_list_transcripts`
Lista las transcripciones de reuniones de Fireflies.ai con filtrado opcional
#### Entrada
| Parámetro | Tipo | Requerido | Descripción |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Sí | Clave API de Fireflies |
| `keyword` | string | No | Palabra clave de búsqueda en el título de la reunión o transcripción |
| `fromDate` | string | No | Filtra transcripciones desde esta fecha \(formato ISO 8601\) |
| `toDate` | string | No | Filtra transcripciones hasta esta fecha \(formato ISO 8601\) |
| `hostEmail` | string | No | Filtra por correo electrónico del anfitrión de la reunión |
| `participants` | string | No | Filtra por correos electrónicos de participantes \(separados por comas\) |
| `limit` | number | No | Número máximo de transcripciones a devolver \(máx. 50\) |
| `skip` | number | No | Número de transcripciones a omitir para paginación |
#### Salida
| Parámetro | Tipo | Descripción |
| --------- | ---- | ----------- |
| `transcripts` | array | Lista de transcripciones |
| `count` | number | Número de transcripciones devueltas |
### `fireflies_get_transcript`
Obtiene una única transcripción con detalles completos, incluyendo resumen, elementos de acción y análisis
#### Entrada
| Parámetro | Tipo | Requerido | Descripción |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Sí | Clave API de Fireflies |
| `transcriptId` | string | Sí | El ID de transcripción a recuperar |
#### Salida
| Parámetro | Tipo | Descripción |
| --------- | ---- | ----------- |
| `transcript` | object | La transcripción con detalles completos |
### `fireflies_get_user`
Obtener información del usuario de Fireflies.ai. Devuelve el usuario actual si no se especifica ningún ID.
#### Entrada
| Parámetro | Tipo | Requerido | Descripción |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Sí | Clave API de Fireflies |
| `userId` | string | No | ID de usuario a recuperar \(opcional, por defecto el propietario de la clave API\) |
#### Salida
| Parámetro | Tipo | Descripción |
| --------- | ---- | ----------- |
| `user` | object | Información del usuario |
### `fireflies_list_users`
Listar todos los usuarios dentro de tu equipo de Fireflies.ai
#### Entrada
| Parámetro | Tipo | Requerido | Descripción |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Sí | Clave API de Fireflies |
#### Salida
| Parámetro | Tipo | Descripción |
| --------- | ---- | ----------- |
| `users` | array | Lista de usuarios del equipo |
### `fireflies_upload_audio`
Subir una URL de archivo de audio a Fireflies.ai para transcripción
#### Entrada
| Parámetro | Tipo | Requerido | Descripción |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Sí | Clave API de Fireflies |
| `audioFile` | file | No | Archivo de audio/vídeo a subir para transcripción |
| `audioUrl` | string | No | URL HTTPS pública del archivo de audio/vídeo \(MP3, MP4, WAV, M4A, OGG\) |
| `title` | string | No | Título para la reunión/transcripción |
| `webhook` | string | No | URL de webhook para notificar cuando la transcripción esté completa |
| `language` | string | No | Código de idioma para la transcripción \(por ejemplo, "es" para español, "de" para alemán\) |
| `attendees` | string | No | Asistentes en formato JSON: \[\{"displayName": "Nombre", "email": "email@example.com"\}\] |
| `clientReferenceId` | string | No | ID de referencia personalizado para seguimiento |
#### Salida
| Parámetro | Tipo | Descripción |
| --------- | ---- | ----------- |
| `success` | boolean | Si la carga fue exitosa |
| `title` | string | Título de la reunión cargada |
| `message` | string | Mensaje de estado de Fireflies |
### `fireflies_delete_transcript`
Eliminar una transcripción de Fireflies.ai
#### Entrada
| Parámetro | Tipo | Requerido | Descripción |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Sí | Clave API de Fireflies |
| `transcriptId` | string | Sí | El ID de la transcripción a eliminar |
#### Salida
| Parámetro | Tipo | Descripción |
| --------- | ---- | ----------- |
| `success` | boolean | Si la transcripción fue eliminada exitosamente |
### `fireflies_add_to_live_meeting`
Agregar el bot de Fireflies.ai a una reunión en curso para grabar y transcribir
#### Entrada
| Parámetro | Tipo | Requerido | Descripción |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Sí | Clave API de Fireflies |
| `meetingLink` | string | Sí | URL de reunión válida \(Zoom, Google Meet, Microsoft Teams, etc.\) |
| `title` | string | No | Título para la reunión \(máximo 256 caracteres\) |
| `meetingPassword` | string | No | Contraseña para la reunión si es necesaria \(máximo 32 caracteres\) |
| `duration` | number | No | Duración de la reunión en minutos \(15-120, predeterminado: 60\) |
| `language` | string | No | Código de idioma para la transcripción \(p. ej., "en", "es", "de"\) |
#### Salida
| Parámetro | Tipo | Descripción |
| --------- | ---- | ----------- |
| `success` | boolean | Si el bot se agregó exitosamente a la reunión |
### `fireflies_create_bite`
Crear un fragmento destacado desde un rango de tiempo específico en una transcripción
#### Entrada
| Parámetro | Tipo | Requerido | Descripción |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Sí | Clave API de Fireflies |
| `transcriptId` | string | Sí | ID de la transcripción desde la cual crear el fragmento |
| `startTime` | number | Sí | Tiempo de inicio del fragmento en segundos |
| `endTime` | number | Sí | Tiempo de finalización del fragmento en segundos |
| `name` | string | No | Nombre para el fragmento \(máximo 256 caracteres\) |
| `mediaType` | string | No | Tipo de medio: "video" o "audio" |
| `summary` | string | No | Resumen para el fragmento \(máximo 500 caracteres\) |
#### Salida
| Parámetro | Tipo | Descripción |
| --------- | ---- | ----------- |
| `bite` | object | Detalles del fragmento creado |
### `fireflies_list_bites`
Listar fragmentos destacados de Fireflies.ai
#### Entrada
| Parámetro | Tipo | Requerido | Descripción |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Sí | Clave API de Fireflies |
| `transcriptId` | string | No | Filtrar fragmentos para una transcripción específica |
| `mine` | boolean | No | Devolver solo fragmentos propiedad del titular de la clave API \(predeterminado: true\) |
| `limit` | number | No | Número máximo de fragmentos a devolver \(máximo 50\) |
| `skip` | number | No | Número de fragmentos a omitir para paginación |
#### Salida
| Parámetro | Tipo | Descripción |
| --------- | ---- | ----------- |
| `bites` | array | Lista de fragmentos/clips de audio |
### `fireflies_list_contacts`
Lista todos los contactos de tus reuniones de Fireflies.ai
#### Entrada
| Parámetro | Tipo | Requerido | Descripción |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Sí | Clave API de Fireflies |
#### Salida
| Parámetro | Tipo | Descripción |
| --------- | ---- | ----------- |
| `contacts` | array | Lista de contactos de las reuniones |
## Notas
- Categoría: `tools`
- Tipo: `fireflies`

View File

@@ -1,230 +0,0 @@
---
title: Webhook
description: Recibe webhooks de cualquier servicio configurando un webhook personalizado.
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
import { Image } from '@/components/ui/image'
<BlockInfoCard
type="generic_webhook"
color="#10B981"
/>
<div className="flex justify-center">
<Image
src="/static/blocks/webhook.png"
alt="Configuración del bloque Webhook"
width={500}
height={400}
className="my-6"
/>
</div>
## Descripción general
El bloque Webhook genérico te permite recibir webhooks desde cualquier servicio externo. Este es un disparador flexible que puede manejar cualquier carga útil JSON, lo que lo hace ideal para integrarse con servicios que no tienen un bloque Sim dedicado.
## Uso básico
### Modo de paso simple
Sin definir un formato de entrada, el webhook transmite todo el cuerpo de la solicitud tal como está:
```bash
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
-H "Content-Type: application/json" \
-H "X-Sim-Secret: your-secret" \
-d '{
"message": "Test webhook trigger",
"data": {
"key": "value"
}
}'
```
Accede a los datos en bloques posteriores usando:
- `<webhook1.message>` → "Test webhook trigger"
- `<webhook1.data.key>` → "value"
### Formato de entrada estructurado (opcional)
Define un esquema de entrada para obtener campos tipados y habilitar funciones avanzadas como cargas de archivos:
**Configuración del formato de entrada:**
```json
[
{ "name": "message", "type": "string" },
{ "name": "priority", "type": "number" },
{ "name": "documents", "type": "files" }
]
```
**Solicitud de webhook:**
```bash
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
-H "Content-Type: application/json" \
-H "X-Sim-Secret: your-secret" \
-d '{
"message": "Invoice submission",
"priority": 1,
"documents": [
{
"type": "file",
"data": "data:application/pdf;base64,JVBERi0xLjQK...",
"name": "invoice.pdf",
"mime": "application/pdf"
}
]
}'
```
## Cargas de archivos
### Formatos de archivo compatibles
El webhook admite dos formatos de entrada de archivos:
#### 1. Archivos codificados en Base64
Para cargar contenido de archivos directamente:
```json
{
"documents": [
{
"type": "file",
"data": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgA...",
"name": "screenshot.png",
"mime": "image/png"
}
]
}
```
- **Tamaño máximo**: 20MB por archivo
- **Formato**: URL de datos estándar con codificación base64
- **Almacenamiento**: Los archivos se cargan en almacenamiento seguro de ejecución
#### 2. Referencias URL
Para pasar URLs de archivos existentes:
```json
{
"documents": [
{
"type": "url",
"data": "https://example.com/files/document.pdf",
"name": "document.pdf",
"mime": "application/pdf"
}
]
}
```
### Acceso a archivos en bloques posteriores
Los archivos se procesan en objetos `UserFile` con las siguientes propiedades:
```typescript
{
id: string, // Unique file identifier
name: string, // Original filename
url: string, // Presigned URL (valid for 5 minutes)
size: number, // File size in bytes
type: string, // MIME type
key: string, // Storage key
uploadedAt: string, // ISO timestamp
expiresAt: string // ISO timestamp (5 minutes)
}
```
**Acceso en bloques:**
- `<webhook1.documents[0].url>` → URL de descarga
- `<webhook1.documents[0].name>` → "invoice.pdf"
- `<webhook1.documents[0].size>` → 524288
- `<webhook1.documents[0].type>` → "application/pdf"
### Ejemplo completo de carga de archivos
```bash
# Create a base64-encoded file
echo "Hello World" | base64
# SGVsbG8gV29ybGQK
# Send webhook with file
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
-H "Content-Type: application/json" \
-H "X-Sim-Secret: your-secret" \
-d '{
"subject": "Document for review",
"attachments": [
{
"type": "file",
"data": "data:text/plain;base64,SGVsbG8gV29ybGQK",
"name": "sample.txt",
"mime": "text/plain"
}
]
}'
```
## Autenticación
### Configurar autenticación (opcional)
En la configuración del webhook:
1. Habilitar "Requerir autenticación"
2. Establecer un token secreto
3. Elegir tipo de encabezado:
- **Encabezado personalizado**: `X-Sim-Secret: your-token`
- **Autorización Bearer**: `Authorization: Bearer your-token`
### Uso de la autenticación
```bash
# With custom header
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
-H "Content-Type: application/json" \
-H "X-Sim-Secret: your-secret-token" \
-d '{"message": "Authenticated request"}'
# With bearer token
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-secret-token" \
-d '{"message": "Authenticated request"}'
```
## Mejores prácticas
1. **Usar formato de entrada para estructura**: Define un formato de entrada cuando conozcas el esquema esperado. Esto proporciona:
- Validación de tipo
- Mejor autocompletado en el editor
- Capacidades de carga de archivos
2. **Autenticación**: Habilita siempre la autenticación para webhooks en producción para prevenir accesos no autorizados.
3. **Límites de tamaño de archivo**: Mantén los archivos por debajo de 20MB. Para archivos más grandes, usa referencias URL en su lugar.
4. **Caducidad de archivos**: Los archivos descargados tienen URLs con caducidad de 5 minutos. Procésalos rápidamente o almacénalos en otro lugar si los necesitas por más tiempo.
5. **Manejo de errores**: El procesamiento de webhooks es asíncrono. Revisa los registros de ejecución para detectar errores.
6. **Pruebas**: Usa el botón "Probar webhook" en el editor para validar tu configuración antes de implementarla.
## Casos de uso
- **Envíos de formularios**: Recibe datos de formularios personalizados con cargas de archivos
- **Integraciones con terceros**: Conéctate con servicios que envían webhooks (Stripe, GitHub, etc.)
- **Procesamiento de documentos**: Acepta documentos de sistemas externos para procesarlos
- **Notificaciones de eventos**: Recibe datos de eventos de varias fuentes
- **APIs personalizadas**: Construye endpoints de API personalizados para tus aplicaciones
## Notas
- Categoría: `triggers`
- Tipo: `generic_webhook`
- **Soporte de archivos**: disponible a través de la configuración del formato de entrada
- **Tamaño máximo de archivo**: 20MB por archivo

View File

@@ -15,8 +15,8 @@ El bloque de webhook genérico crea un punto de conexión flexible que puede rec
<div className="flex justify-center">
<Image
src="/static/blocks/webhook.png"
alt="Configuración genérica de webhook"
src="/static/blocks/webhook-trigger.png"
alt="Configuración de webhook genérico"
width={500}
height={400}
className="my-6"

View File

@@ -0,0 +1,89 @@
---
title: Webhook
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Image } from '@/components/ui/image'
Le bloc Webhook envoie des requêtes HTTP POST vers des points de terminaison webhook externes avec des en-têtes webhook automatiques et une signature HMAC optionnelle.
<div className="flex justify-center">
<Image
src="/static/blocks/webhook.png"
alt="Bloc Webhook"
width={500}
height={400}
className="my-6"
/>
</div>
## Configuration
### URL du webhook
Le point de terminaison de destination pour votre requête webhook. Prend en charge les URL statiques et les valeurs dynamiques provenant d'autres blocs.
### Charge utile
Données JSON à envoyer dans le corps de la requête. Utilisez la baguette IA pour générer des charges utiles ou référencer des variables de workflow :
```json
{
"event": "workflow.completed",
"data": {
"result": "<agent.content>",
"timestamp": "<function.result>"
}
}
```
### Secret de signature
Secret optionnel pour la signature HMAC-SHA256 de la charge utile. Lorsqu'il est fourni, ajoute un en-tête `X-Webhook-Signature` :
```
X-Webhook-Signature: t=1704067200000,v1=5d41402abc4b2a76b9719d911017c592...
```
Pour vérifier les signatures, calculez `HMAC-SHA256(secret, "${timestamp}.${body}")` et comparez avec la valeur `v1`.
### En-têtes supplémentaires
En-têtes personnalisés clé-valeur à inclure avec la requête. Ceux-ci remplacent tous les en-têtes automatiques portant le même nom.
## En-têtes automatiques
Chaque requête inclut automatiquement ces en-têtes :
| En-tête | Description |
|--------|-------------|
| `Content-Type` | `application/json` |
| `X-Webhook-Timestamp` | Horodatage Unix en millisecondes |
| `X-Delivery-ID` | UUID unique pour cette livraison |
| `Idempotency-Key` | Identique à `X-Delivery-ID` pour la déduplication |
## Sorties
| Sortie | Type | Description |
|--------|------|-------------|
| `data` | json | Corps de la réponse du point de terminaison |
| `status` | number | Code de statut HTTP |
| `headers` | object | En-têtes de réponse |
## Exemples de cas d'usage
**Notifier des services externes** - Envoyer les résultats du workflow vers Slack, Discord ou des points de terminaison personnalisés
```
Agent → Function (format) → Webhook (notify)
```
**Déclencher des workflows externes** - Démarrer des processus dans d'autres systèmes lorsque des conditions sont remplies
```
Condition (check) → Webhook (trigger) → Response
```
<Callout>
Le bloc Webhook utilise toujours POST. Pour d'autres méthodes HTTP ou plus de contrôle, utilisez le [bloc API](/blocks/api).
</Callout>

View File

@@ -0,0 +1,234 @@
---
title: Fireflies
description: Interagissez avec les transcriptions et enregistrements de réunions
Fireflies.ai
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="fireflies"
color="#100730"
/>
{/* MANUAL-CONTENT-START:intro */}
[Fireflies.ai](https://fireflies.ai/) est une plateforme de transcription et d'intelligence de réunions qui s'intègre avec Sim, permettant à vos agents de travailler directement avec les enregistrements de réunions, les transcriptions et les informations via des automatisations sans code.
L'intégration Fireflies dans Sim fournit des outils pour :
- **Lister les transcriptions de réunions :** récupérez plusieurs réunions et leurs informations récapitulatives pour votre équipe ou compte.
- **Récupérer les détails complets de transcription :** accédez aux transcriptions détaillées, y compris les résumés, les éléments d'action, les sujets et les analyses des participants pour toute réunion.
- **Télécharger de l'audio ou de la vidéo :** téléchargez des fichiers audio/vidéo ou fournissez des URL pour la transcription—définissez éventuellement la langue, le titre, les participants et recevez des notes de réunion automatisées.
- **Rechercher des transcriptions :** trouvez des réunions par mot-clé, participant, hôte ou période pour localiser rapidement les discussions pertinentes.
- **Supprimer des transcriptions :** supprimez des transcriptions de réunions spécifiques de votre espace de travail Fireflies.
- **Créer des extraits sonores (Bites) :** extrayez et mettez en évidence les moments clés des transcriptions sous forme de clips audio ou vidéo.
- **Déclencher des workflows à la fin de la transcription :** activez automatiquement les workflows Sim lorsqu'une transcription de réunion Fireflies se termine en utilisant le déclencheur webhook fourni—permettant des automatisations en temps réel et des notifications basées sur les nouvelles données de réunion.
En combinant ces capacités, vous pouvez rationaliser les actions post-réunion, extraire des informations structurées, automatiser les notifications, gérer les enregistrements et orchestrer des workflows personnalisés autour des appels de votre organisation—le tout de manière sécurisée en utilisant votre clé API et vos identifiants Fireflies.
{/* MANUAL-CONTENT-END */}
## Instructions d'utilisation
Intégrez Fireflies.ai dans le workflow. Gérez les transcriptions de réunions, ajoutez un bot aux réunions en direct, créez des extraits sonores et plus encore. Peut également déclencher des workflows lorsque les transcriptions sont terminées.
## Outils
### `fireflies_list_transcripts`
Lister les transcriptions de réunions depuis Fireflies.ai avec filtrage optionnel
#### Entrée
| Paramètre | Type | Requis | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Oui | Clé API Fireflies |
| `keyword` | string | Non | Mot-clé de recherche dans le titre de la réunion ou la transcription |
| `fromDate` | string | Non | Filtrer les transcriptions à partir de cette date \(format ISO 8601\) |
| `toDate` | string | Non | Filtrer les transcriptions jusqu'à cette date \(format ISO 8601\) |
| `hostEmail` | string | Non | Filtrer par e-mail de l'organisateur de la réunion |
| `participants` | string | Non | Filtrer par e-mails des participants \(séparés par des virgules\) |
| `limit` | number | Non | Nombre maximum de transcriptions à retourner \(max 50\) |
| `skip` | number | Non | Nombre de transcriptions à ignorer pour la pagination |
#### Sortie
| Paramètre | Type | Description |
| --------- | ---- | ----------- |
| `transcripts` | array | Liste des transcriptions |
| `count` | number | Nombre de transcriptions retournées |
### `fireflies_get_transcript`
Obtenir une transcription unique avec tous les détails, y compris le résumé, les actions à effectuer et les analyses
#### Entrée
| Paramètre | Type | Requis | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Oui | Clé API Fireflies |
| `transcriptId` | string | Oui | L'identifiant de la transcription à récupérer |
#### Sortie
| Paramètre | Type | Description |
| --------- | ---- | ----------- |
| `transcript` | object | La transcription avec tous les détails |
### `fireflies_get_user`
Obtenir les informations utilisateur depuis Fireflies.ai. Renvoie l'utilisateur actuel si aucun ID n'est spécifié.
#### Entrée
| Paramètre | Type | Requis | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Oui | Clé API Fireflies |
| `userId` | string | Non | ID utilisateur à récupérer \(optionnel, par défaut le propriétaire de la clé API\) |
#### Sortie
| Paramètre | Type | Description |
| --------- | ---- | ----------- |
| `user` | object | Informations utilisateur |
### `fireflies_list_users`
Lister tous les utilisateurs de votre équipe Fireflies.ai
#### Entrée
| Paramètre | Type | Requis | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Oui | Clé API Fireflies |
#### Sortie
| Paramètre | Type | Description |
| --------- | ---- | ----------- |
| `users` | array | Liste des utilisateurs de l'équipe |
### `fireflies_upload_audio`
Télécharger une URL de fichier audio vers Fireflies.ai pour transcription
#### Entrée
| Paramètre | Type | Requis | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Oui | Clé API Fireflies |
| `audioFile` | file | Non | Fichier audio/vidéo à télécharger pour transcription |
| `audioUrl` | string | Non | URL HTTPS publique du fichier audio/vidéo \(MP3, MP4, WAV, M4A, OGG\) |
| `title` | string | Non | Titre de la réunion/transcription |
| `webhook` | string | Non | URL webhook pour notification lorsque la transcription est terminée |
| `language` | string | Non | Code de langue pour la transcription \(par ex., « es » pour l'espagnol, « de » pour l'allemand\) |
| `attendees` | string | Non | Participants au format JSON : \[\{"displayName": "Nom", "email": "email@exemple.com"\}\] |
| `clientReferenceId` | string | Non | ID de référence personnalisé pour le suivi |
#### Sortie
| Paramètre | Type | Description |
| --------- | ---- | ----------- |
| `success` | boolean | Indique si le téléversement a réussi |
| `title` | string | Titre de la réunion téléversée |
| `message` | string | Message de statut de Fireflies |
### `fireflies_delete_transcript`
Supprimer une transcription de Fireflies.ai
#### Entrée
| Paramètre | Type | Requis | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Oui | Clé API Fireflies |
| `transcriptId` | string | Oui | L'identifiant de la transcription à supprimer |
#### Sortie
| Paramètre | Type | Description |
| --------- | ---- | ----------- |
| `success` | boolean | Indique si la transcription a été supprimée avec succès |
### `fireflies_add_to_live_meeting`
Ajouter le bot Fireflies.ai à une réunion en cours pour enregistrer et transcrire
#### Entrée
| Paramètre | Type | Requis | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Oui | Clé API Fireflies |
| `meetingLink` | string | Oui | URL de réunion valide \(Zoom, Google Meet, Microsoft Teams, etc.\) |
| `title` | string | Non | Titre de la réunion \(256 caractères maximum\) |
| `meetingPassword` | string | Non | Mot de passe de la réunion si nécessaire \(32 caractères maximum\) |
| `duration` | number | Non | Durée de la réunion en minutes \(15-120, par défaut : 60\) |
| `language` | string | Non | Code de langue pour la transcription \(par exemple, "en", "es", "de"\) |
#### Sortie
| Paramètre | Type | Description |
| --------- | ---- | ----------- |
| `success` | boolean | Indique si le bot a été ajouté avec succès à la réunion |
### `fireflies_create_bite`
Créer un extrait sonore/moment fort à partir d'une plage horaire spécifique dans une transcription
#### Entrée
| Paramètre | Type | Requis | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Oui | Clé API Fireflies |
| `transcriptId` | string | Oui | ID de la transcription à partir de laquelle créer l'extrait |
| `startTime` | number | Oui | Heure de début de l'extrait en secondes |
| `endTime` | number | Oui | Heure de fin de l'extrait en secondes |
| `name` | string | Non | Nom de l'extrait \(256 caractères maximum\) |
| `mediaType` | string | Non | Type de média : « video » ou « audio » |
| `summary` | string | Non | Résumé de l'extrait \(500 caractères maximum\) |
#### Sortie
| Paramètre | Type | Description |
| --------- | ---- | ----------- |
| `bite` | object | Détails de l'extrait créé |
### `fireflies_list_bites`
Lister les extraits sonores/moments forts de Fireflies.ai
#### Entrée
| Paramètre | Type | Requis | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Oui | Clé API Fireflies |
| `transcriptId` | string | Non | Filtrer les extraits pour une transcription spécifique |
| `mine` | boolean | Non | Retourner uniquement les extraits appartenant au propriétaire de la clé API \(par défaut : true\) |
| `limit` | number | Non | Nombre maximum d'extraits à retourner \(50 maximum\) |
| `skip` | number | Non | Nombre d'extraits à ignorer pour la pagination |
#### Sortie
| Paramètre | Type | Description |
| --------- | ---- | ----------- |
| `bites` | array | Liste des extraits/extraits sonores |
### `fireflies_list_contacts`
Lister tous les contacts de vos réunions Fireflies.ai
#### Entrée
| Paramètre | Type | Requis | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Oui | Clé API Fireflies |
#### Sortie
| Paramètre | Type | Description |
| --------- | ---- | ----------- |
| `contacts` | array | Liste des contacts des réunions |
## Remarques
- Catégorie : `tools`
- Type : `fireflies`

View File

@@ -1,231 +0,0 @@
---
title: Webhook
description: Recevez des webhooks de n'importe quel service en configurant un
webhook personnalisé.
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
import { Image } from '@/components/ui/image'
<BlockInfoCard
type="generic_webhook"
color="#10B981"
/>
<div className="flex justify-center">
<Image
src="/static/blocks/webhook.png"
alt="Configuration du bloc Webhook"
width={500}
height={400}
className="my-6"
/>
</div>
## Aperçu
Le bloc Webhook générique vous permet de recevoir des webhooks depuis n'importe quel service externe. C'est un déclencheur flexible qui peut traiter n'importe quelle charge utile JSON, ce qui le rend idéal pour l'intégration avec des services qui n'ont pas de bloc Sim dédié.
## Utilisation de base
### Mode de transmission simple
Sans définir un format d'entrée, le webhook transmet l'intégralité du corps de la requête tel quel :
```bash
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
-H "Content-Type: application/json" \
-H "X-Sim-Secret: your-secret" \
-d '{
"message": "Test webhook trigger",
"data": {
"key": "value"
}
}'
```
Accédez aux données dans les blocs en aval en utilisant :
- `<webhook1.message>` → "Test webhook trigger"
- `<webhook1.data.key>` → "value"
### Format d'entrée structuré (optionnel)
Définissez un schéma d'entrée pour obtenir des champs typés et activer des fonctionnalités avancées comme les téléchargements de fichiers :
**Configuration du format d'entrée :**
```json
[
{ "name": "message", "type": "string" },
{ "name": "priority", "type": "number" },
{ "name": "documents", "type": "files" }
]
```
**Requête Webhook :**
```bash
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
-H "Content-Type: application/json" \
-H "X-Sim-Secret: your-secret" \
-d '{
"message": "Invoice submission",
"priority": 1,
"documents": [
{
"type": "file",
"data": "data:application/pdf;base64,JVBERi0xLjQK...",
"name": "invoice.pdf",
"mime": "application/pdf"
}
]
}'
```
## Téléchargements de fichiers
### Formats de fichiers pris en charge
Le webhook prend en charge deux formats d'entrée de fichiers :
#### 1. Fichiers encodés en Base64
Pour télécharger directement le contenu du fichier :
```json
{
"documents": [
{
"type": "file",
"data": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgA...",
"name": "screenshot.png",
"mime": "image/png"
}
]
}
```
- **Taille maximale** : 20 Mo par fichier
- **Format** : URL de données standard avec encodage base64
- **Stockage** : Les fichiers sont téléchargés dans un stockage d'exécution sécurisé
#### 2. Références URL
Pour transmettre des URL de fichiers existants :
```json
{
"documents": [
{
"type": "url",
"data": "https://example.com/files/document.pdf",
"name": "document.pdf",
"mime": "application/pdf"
}
]
}
```
### Accès aux fichiers dans les blocs en aval
Les fichiers sont traités en objets `UserFile` avec les propriétés suivantes :
```typescript
{
id: string, // Unique file identifier
name: string, // Original filename
url: string, // Presigned URL (valid for 5 minutes)
size: number, // File size in bytes
type: string, // MIME type
key: string, // Storage key
uploadedAt: string, // ISO timestamp
expiresAt: string // ISO timestamp (5 minutes)
}
```
**Accès dans les blocs :**
- `<webhook1.documents[0].url>` → URL de téléchargement
- `<webhook1.documents[0].name>` → "invoice.pdf"
- `<webhook1.documents[0].size>` → 524288
- `<webhook1.documents[0].type>` → "application/pdf"
### Exemple complet de téléchargement de fichier
```bash
# Create a base64-encoded file
echo "Hello World" | base64
# SGVsbG8gV29ybGQK
# Send webhook with file
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
-H "Content-Type: application/json" \
-H "X-Sim-Secret: your-secret" \
-d '{
"subject": "Document for review",
"attachments": [
{
"type": "file",
"data": "data:text/plain;base64,SGVsbG8gV29ybGQK",
"name": "sample.txt",
"mime": "text/plain"
}
]
}'
```
## Authentification
### Configurer l'authentification (optionnel)
Dans la configuration du webhook :
1. Activez "Exiger l'authentification"
2. Définissez un jeton secret
3. Choisissez le type d'en-tête :
- **En-tête personnalisé** : `X-Sim-Secret: your-token`
- **Autorisation Bearer** : `Authorization: Bearer your-token`
### Utilisation de l'authentification
```bash
# With custom header
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
-H "Content-Type: application/json" \
-H "X-Sim-Secret: your-secret-token" \
-d '{"message": "Authenticated request"}'
# With bearer token
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-secret-token" \
-d '{"message": "Authenticated request"}'
```
## Bonnes pratiques
1. **Utiliser le format d'entrée pour la structure** : définissez un format d'entrée lorsque vous connaissez le schéma attendu. Cela fournit :
- Validation de type
- Meilleure autocomplétion dans l'éditeur
- Capacités de téléchargement de fichiers
2. **Authentification** : activez toujours l'authentification pour les webhooks en production afin d'empêcher les accès non autorisés.
3. **Limites de taille de fichier** : gardez les fichiers en dessous de 20 Mo. Pour les fichiers plus volumineux, utilisez plutôt des références URL.
4. **Expiration des fichiers** : les fichiers téléchargés ont des URL d'expiration de 5 minutes. Traitez-les rapidement ou stockez-les ailleurs si vous en avez besoin plus longtemps.
5. **Gestion des erreurs** : le traitement des webhooks est asynchrone. Vérifiez les journaux d'exécution pour les erreurs.
6. **Tests** : utilisez le bouton "Tester le webhook" dans l'éditeur pour valider votre configuration avant le déploiement.
## Cas d'utilisation
- **Soumissions de formulaires** : recevez des données de formulaires personnalisés avec téléchargement de fichiers
- **Intégrations tierces** : connectez-vous avec des services qui envoient des webhooks (Stripe, GitHub, etc.)
- **Traitement de documents** : acceptez des documents de systèmes externes pour traitement
- **Notifications d'événements** : recevez des données d'événements de diverses sources
- **API personnalisées** : créez des points de terminaison API personnalisés pour vos applications
## Remarques
- Catégorie : `triggers`
- Type : `generic_webhook`
- **Support de fichiers** : disponible via la configuration du format d'entrée
- **Taille maximale de fichier** : 20 Mo par fichier

View File

@@ -15,8 +15,8 @@ Le bloc Webhook générique crée un point de terminaison flexible qui peut rece
<div className="flex justify-center">
<Image
src="/static/blocks/webhook.png"
alt="Configuration de webhook générique"
src="/static/blocks/webhook-trigger.png"
alt="Configuration du webhook générique"
width={500}
height={400}
className="my-6"

View File

@@ -0,0 +1,89 @@
---
title: Webhook
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Image } from '@/components/ui/image'
Webhookブロックは、自動的なWebhookヘッダーとオプションのHMAC署名を使用して、外部のWebhookエンドポイントにHTTP POSTリクエストを送信します。
<div className="flex justify-center">
<Image
src="/static/blocks/webhook.png"
alt="Webhookブロック"
width={500}
height={400}
className="my-6"
/>
</div>
## 設定
### Webhook URL
Webhookリクエストの送信先エンドポイントです。静的URLと他のブロックからの動的な値の両方に対応しています。
### ペイロード
リクエストボディで送信するJSONデータです。AIワンドを使用してペイロードを生成したり、ワークフロー変数を参照したりできます。
```json
{
"event": "workflow.completed",
"data": {
"result": "<agent.content>",
"timestamp": "<function.result>"
}
}
```
### 署名シークレット
HMAC-SHA256ペイロード署名用のオプションのシークレットです。指定すると、`X-Webhook-Signature`ヘッダーが追加されます。
```
X-Webhook-Signature: t=1704067200000,v1=5d41402abc4b2a76b9719d911017c592...
```
署名を検証するには、`HMAC-SHA256(secret, "${timestamp}.${body}")`を計算し、`v1`の値と比較します。
### 追加ヘッダー
リクエストに含めるカスタムのキーと値のヘッダーです。同じ名前の自動ヘッダーがある場合は上書きされます。
## 自動ヘッダー
すべてのリクエストには、以下のヘッダーが自動的に含まれます。
| ヘッダー | 説明 |
|--------|-------------|
| `Content-Type` | `application/json` |
| `X-Webhook-Timestamp` | ミリ秒単位のUnixタイムスタンプ |
| `X-Delivery-ID` | この配信の一意のUUID |
| `Idempotency-Key` | 重複排除用の`X-Delivery-ID`と同じ |
## 出力
| 出力 | 型 | 説明 |
|--------|------|-------------|
| `data` | json | エンドポイントからのレスポンスボディ |
| `status` | number | HTTPステータスコード |
| `headers` | object | レスポンスヘッダー |
## 使用例
**外部サービスへの通知** - ワークフローの結果をSlack、Discord、またはカスタムエンドポイントに送信します。
```
Agent → Function (format) → Webhook (notify)
```
**外部ワークフローのトリガー** - 条件が満たされたときに他のシステムでプロセスを開始します。
```
Condition (check) → Webhook (trigger) → Response
```
<Callout>
Webhookブロックは常にPOSTを使用します。他のHTTPメソッドやより詳細な制御が必要な場合は、[APIブロック](/blocks/api)を使用してください。
</Callout>

View File

@@ -0,0 +1,233 @@
---
title: Fireflies
description: Fireflies.aiの会議文字起こしと録画を操作
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="fireflies"
color="#100730"
/>
{/* MANUAL-CONTENT-START:intro */}
[Fireflies.ai](https://fireflies.ai/)は、会議の文字起こしとインテリジェンスプラットフォームで、Simと統合されており、エージェントがーコード自動化を通じて会議の録画、文字起こし、インサイトを直接操作できます。
SimのFireflies統合は以下のツールを提供します。
- **会議文字起こしの一覧表示:** チームまたはアカウントの複数の会議とその要約情報を取得します。
- **完全な文字起こし詳細の取得:** 任意の会議について、要約、アクションアイテム、トピック、参加者分析を含む詳細な文字起こしにアクセスします。
- **音声または動画のアップロード:** 音声/動画ファイルをアップロードするか、文字起こし用のURLを提供します。オプションで言語、タイトル、参加者を設定し、自動化された会議メモを受け取ることができます。
- **文字起こしの検索:** キーワード、参加者、ホスト、または期間で会議を検索し、関連する議論を素早く見つけます。
- **文字起こしの削除:** Firefliesワークスペースから特定の会議文字起こしを削除します。
- **サウンドバイト(Bites)の作成:** 文字起こしから重要な瞬間を音声または動画クリップとして抽出してハイライトします。
- **文字起こし完了時のワークフロートリガー:** Firefliesの会議文字起こしが完了したときに、提供されたWebhookトリガーを使用してSimワークフローを自動的に起動します。これにより、新しい会議データに基づくリアルタイムの自動化と通知が可能になります。
これらの機能を組み合わせることで、会議後のアクションを効率化し、構造化されたインサイトを抽出し、通知を自動化し、録画を管理し、組織の通話に関するカスタムワークフローを調整できます。すべてAPIキーとFirefliesの認証情報を使用して安全に実行されます。
{/* MANUAL-CONTENT-END */}
## 使用方法
Fireflies.aiをワークフローに統合します。会議の文字起こしを管理し、ライブ会議にボットを追加し、サウンドバイトを作成するなどの操作が可能です。文字起こしが完了したときにワークフローをトリガーすることもできます。
## ツール
### `fireflies_list_transcripts`
Fireflies.aiからミーティングの文字起こしをオプションのフィルタリング付きで一覧表示
#### 入力
| パラメータ | 型 | 必須 | 説明 |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | はい | Fireflies APIキー |
| `keyword` | string | いいえ | ミーティングタイトルまたは文字起こし内の検索キーワード |
| `fromDate` | string | いいえ | この日付以降の文字起こしをフィルタリングISO 8601形式 |
| `toDate` | string | いいえ | この日付までの文字起こしをフィルタリングISO 8601形式 |
| `hostEmail` | string | いいえ | ミーティングホストのメールアドレスでフィルタリング |
| `participants` | string | いいえ | 参加者のメールアドレスでフィルタリング(カンマ区切り) |
| `limit` | number | いいえ | 返す文字起こしの最大数最大50 |
| `skip` | number | いいえ | ページネーションのためにスキップする文字起こしの数 |
#### 出力
| パラメータ | 型 | 説明 |
| --------- | ---- | ----------- |
| `transcripts` | array | 文字起こしのリスト |
| `count` | number | 返された文字起こしの数 |
### `fireflies_get_transcript`
要約、アクションアイテム、分析を含む完全な詳細情報を持つ単一の文字起こしを取得
#### 入力
| パラメータ | 型 | 必須 | 説明 |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | はい | Fireflies APIキー |
| `transcriptId` | string | はい | 取得する文字起こしID |
#### 出力
| パラメータ | 型 | 説明 |
| --------- | ---- | ----------- |
| `transcript` | object | 完全な詳細情報を持つ文字起こし |
### `fireflies_get_user`
Fireflies.aiからユーザー情報を取得します。IDが指定されていない場合は現在のユーザーを返します。
#### 入力
| パラメータ | 型 | 必須 | 説明 |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | はい | Fireflies APIキー |
| `userId` | string | いいえ | 取得するユーザーIDオプション、デフォルトはAPIキー所有者 |
#### 出力
| パラメータ | 型 | 説明 |
| --------- | ---- | ----------- |
| `user` | object | ユーザー情報 |
### `fireflies_list_users`
Fireflies.aiチーム内のすべてのユーザーを一覧表示します
#### 入力
| パラメータ | 型 | 必須 | 説明 |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | はい | Fireflies APIキー |
#### 出力
| パラメータ | 型 | 説明 |
| --------- | ---- | ----------- |
| `users` | array | チームユーザーのリスト |
### `fireflies_upload_audio`
音声ファイルのURLをFireflies.aiにアップロードして文字起こしを行います
#### 入力
| パラメータ | 型 | 必須 | 説明 |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | はい | Fireflies APIキー |
| `audioFile` | file | いいえ | 文字起こし用にアップロードする音声/動画ファイル |
| `audioUrl` | string | いいえ | 音声/動画ファイルの公開HTTPS URLMP3、MP4、WAV、M4A、OGG |
| `title` | string | いいえ | ミーティング/文字起こしのタイトル |
| `webhook` | string | いいえ | 文字起こし完了時に通知するWebhook URL |
| `language` | string | いいえ | 文字起こしの言語コードスペイン語は「es」、ドイツ語は「de」 |
| `attendees` | string | いいえ | JSON形式の参加者\[\{"displayName": "名前", "email": "email@example.com"\}\] |
| `clientReferenceId` | string | いいえ | 追跡用のカスタム参照ID |
#### 出力
| パラメータ | 型 | 説明 |
| --------- | ---- | ----------- |
| `success` | boolean | アップロードが成功したかどうか |
| `title` | string | アップロードされたミーティングのタイトル |
| `message` | string | Firefliesからのステータスメッセージ |
### `fireflies_delete_transcript`
Fireflies.aiからトランスクリプトを削除する
#### 入力
| パラメータ | 型 | 必須 | 説明 |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | はい | Fireflies APIキー |
| `transcriptId` | string | はい | 削除するトランスクリプトID |
#### 出力
| パラメータ | 型 | 説明 |
| --------- | ---- | ----------- |
| `success` | boolean | トランスクリプトが正常に削除されたかどうか |
### `fireflies_add_to_live_meeting`
進行中のミーティングにFireflies.aiボットを追加して録音および文字起こしを行う
#### 入力
| パラメータ | 型 | 必須 | 説明 |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | はい | Fireflies APIキー |
| `meetingLink` | string | はい | 有効なミーティングURL(Zoom、Google Meet、Microsoft Teamsなど) |
| `title` | string | いいえ | ミーティングのタイトル(最大256文字) |
| `meetingPassword` | string | いいえ | 必要な場合のミーティングパスワード(最大32文字) |
| `duration` | number | いいえ | ミーティングの長さ(分単位、15-120、デフォルト:60) |
| `language` | string | いいえ | 文字起こしの言語コード(例:"en"、"es"、"de") |
#### 出力
| パラメータ | 型 | 説明 |
| --------- | ---- | ----------- |
| `success` | boolean | ボットがミーティングに正常に追加されたかどうか |
### `fireflies_create_bite`
トランスクリプトの特定の時間範囲からサウンドバイト/ハイライトを作成します
#### 入力
| パラメータ | 型 | 必須 | 説明 |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | はい | Fireflies APIキー |
| `transcriptId` | string | はい | バイトを作成するトランスクリプトのID |
| `startTime` | number | はい | バイトの開始時間(秒) |
| `endTime` | number | はい | バイトの終了時間(秒) |
| `name` | string | いいえ | バイトの名前(最大256文字) |
| `mediaType` | string | いいえ | メディアタイプ:「video」または「audio」 |
| `summary` | string | いいえ | バイトの概要(最大500文字) |
#### 出力
| パラメータ | 型 | 説明 |
| --------- | ---- | ----------- |
| `bite` | object | 作成されたバイトの詳細 |
### `fireflies_list_bites`
Fireflies.aiからサウンドバイト/ハイライトを一覧表示します
#### 入力
| パラメータ | 型 | 必須 | 説明 |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | はい | Fireflies APIキー |
| `transcriptId` | string | いいえ | 特定のトランスクリプトのバイトをフィルタリング |
| `mine` | boolean | いいえ | APIキー所有者が所有するバイトのみを返す(デフォルト:true) |
| `limit` | number | いいえ | 返すバイトの最大数(最大50) |
| `skip` | number | いいえ | ページネーションのためにスキップするバイトの数 |
#### 出力
| パラメータ | 型 | 説明 |
| --------- | ---- | ----------- |
| `bites` | array | バイト/サウンドバイトのリスト |
### `fireflies_list_contacts`
Fireflies.aiミーティングからすべての連絡先をリスト表示
#### 入力
| パラメータ | 型 | 必須 | 説明 |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | はい | Fireflies APIキー |
#### 出力
| パラメータ | 型 | 説明 |
| --------- | ---- | ----------- |
| `contacts` | array | ミーティングからの連絡先のリスト |
## 注記
- カテゴリ: `tools`
- タイプ: `fireflies`

View File

@@ -1,230 +0,0 @@
---
title: Webhook
description: カスタムウェブフックを設定して、任意のサービスからウェブフックを受信します。
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
import { Image } from '@/components/ui/image'
<BlockInfoCard
type="generic_webhook"
color="#10B981"
/>
<div className="flex justify-center">
<Image
src="/static/blocks/webhook.png"
alt="Webhookブロックの設定"
width={500}
height={400}
className="my-6"
/>
</div>
## 概要
汎用Webhookブロックを使用すると、任意の外部サービスからWebhookを受信できます。これは柔軟なトリガーであり、あらゆるJSONペイロードを処理できるため、専用のSimブロックがないサービスとの統合に最適です。
## 基本的な使用方法
### シンプルなパススルーモード
入力フォーマットを定義しない場合、Webhookはリクエスト本文全体をそのまま渡します
```bash
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
-H "Content-Type: application/json" \
-H "X-Sim-Secret: your-secret" \
-d '{
"message": "Test webhook trigger",
"data": {
"key": "value"
}
}'
```
下流のブロックでデータにアクセスする方法:
- `<webhook1.message>` → "Test webhook trigger"
- `<webhook1.data.key>` → "value"
### 構造化入力フォーマット(オプション)
入力スキーマを定義して、型付きフィールドを取得し、ファイルアップロードなどの高度な機能を有効にします:
**入力フォーマット設定:**
```json
[
{ "name": "message", "type": "string" },
{ "name": "priority", "type": "number" },
{ "name": "documents", "type": "files" }
]
```
**Webhookリクエスト**
```bash
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
-H "Content-Type: application/json" \
-H "X-Sim-Secret: your-secret" \
-d '{
"message": "Invoice submission",
"priority": 1,
"documents": [
{
"type": "file",
"data": "data:application/pdf;base64,JVBERi0xLjQK...",
"name": "invoice.pdf",
"mime": "application/pdf"
}
]
}'
```
## ファイルアップロード
### サポートされているファイル形式
Webhookは2つのファイル入力形式をサポートしています
#### 1. Base64エンコードファイル
ファイルコンテンツを直接アップロードする場合:
```json
{
"documents": [
{
"type": "file",
"data": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgA...",
"name": "screenshot.png",
"mime": "image/png"
}
]
}
```
- **最大サイズ**: ファイルあたり20MB
- **フォーマット**: Base64エンコーディングを使用した標準データURL
- **ストレージ**: ファイルは安全な実行ストレージにアップロードされます
#### 2. URL参照
既存のファイルURLを渡す場合
```json
{
"documents": [
{
"type": "url",
"data": "https://example.com/files/document.pdf",
"name": "document.pdf",
"mime": "application/pdf"
}
]
}
```
### 下流のブロックでファイルにアクセスする
ファイルは以下のプロパティを持つ `UserFile` オブジェクトに処理されます:
```typescript
{
id: string, // Unique file identifier
name: string, // Original filename
url: string, // Presigned URL (valid for 5 minutes)
size: number, // File size in bytes
type: string, // MIME type
key: string, // Storage key
uploadedAt: string, // ISO timestamp
expiresAt: string // ISO timestamp (5 minutes)
}
```
**ブロック内でのアクセス:**
- `<webhook1.documents[0].url>` → ダウンロードURL
- `<webhook1.documents[0].name>` → "invoice.pdf"
- `<webhook1.documents[0].size>` → 524288
- `<webhook1.documents[0].type>` → "application/pdf"
### ファイルアップロードの完全な例
```bash
# Create a base64-encoded file
echo "Hello World" | base64
# SGVsbG8gV29ybGQK
# Send webhook with file
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
-H "Content-Type: application/json" \
-H "X-Sim-Secret: your-secret" \
-d '{
"subject": "Document for review",
"attachments": [
{
"type": "file",
"data": "data:text/plain;base64,SGVsbG8gV29ybGQK",
"name": "sample.txt",
"mime": "text/plain"
}
]
}'
```
## 認証
### 認証の設定(オプション)
ウェブフック設定で:
1. 「認証を要求する」を有効にする
2. シークレットトークンを設定する
3. ヘッダータイプを選択する:
- **カスタムヘッダー**: `X-Sim-Secret: your-token`
- **認証ベアラー**: `Authorization: Bearer your-token`
### 認証の使用
```bash
# With custom header
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
-H "Content-Type: application/json" \
-H "X-Sim-Secret: your-secret-token" \
-d '{"message": "Authenticated request"}'
# With bearer token
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-secret-token" \
-d '{"message": "Authenticated request"}'
```
## ベストプラクティス
1. **構造化のための入力フォーマットの使用**: 予想されるスキーマがわかっている場合は入力フォーマットを定義してください。これにより以下が提供されます:
- 型の検証
- エディタでのより良いオートコンプリート
- ファイルアップロード機能
2. **認証**: 不正アクセスを防ぐため、本番環境のウェブフックには常に認証を有効にしてください。
3. **ファイルサイズの制限**: ファイルは20MB未満に保ってください。より大きなファイルの場合は、代わりにURL参照を使用してください。
4. **ファイルの有効期限**: ダウンロードされたファイルのURLは5分間有効です。すぐに処理するか、長期間必要な場合は別の場所に保存してください。
5. **エラー処理**: ウェブフック処理は非同期です。エラーについては実行ログを確認してください。
6. **テスト**: 設定をデプロイする前に、エディタの「ウェブフックをテスト」ボタンを使用して設定を検証してください。
## ユースケース
- **フォーム送信**: ファイルアップロード機能を持つカスタムフォームからデータを受け取る
- **サードパーティ連携**: ウェブフックを送信するサービスStripe、GitHubなどと接続する
- **ドキュメント処理**: 外部システムからドキュメントを受け取って処理する
- **イベント通知**: さまざまなソースからイベントデータを受け取る
- **カスタムAPI**: アプリケーション用のカスタムAPIエンドポイントを構築する
## 注意事項
- カテゴリ:`triggers`
- タイプ:`generic_webhook`
- **ファイルサポート**:入力フォーマット設定で利用可能
- **最大ファイルサイズ**ファイルあたり20MB

View File

@@ -15,7 +15,7 @@ Webhookを使用すると、外部サービスがHTTPリクエストを送信し
<div className="flex justify-center">
<Image
src="/static/blocks/webhook.png"
src="/static/blocks/webhook-trigger.png"
alt="汎用Webhook設定"
width={500}
height={400}

View File

@@ -0,0 +1,89 @@
---
title: Webhook
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Image } from '@/components/ui/image'
Webhook 模块会向外部 webhook 端点发送 HTTP POST 请求,自动附加 webhook 头部,并可选用 HMAC 签名。
<div className="flex justify-center">
<Image
src="/static/blocks/webhook.png"
alt="Webhook 模块"
width={500}
height={400}
className="my-6"
/>
</div>
## 配置
### Webhook URL
Webhook 请求的目标端点。支持静态 URL 和来自其他模块的动态值。
### 负载
要在请求体中发送的 JSON 数据。可使用 AI 魔杖生成负载,或引用工作流变量:
```json
{
"event": "workflow.completed",
"data": {
"result": "<agent.content>",
"timestamp": "<function.result>"
}
}
```
### 签名密钥
可选的 HMAC-SHA256 负载签名密钥。填写后会添加 `X-Webhook-Signature` 头部:
```
X-Webhook-Signature: t=1704067200000,v1=5d41402abc4b2a76b9719d911017c592...
```
要验证签名,请计算 `HMAC-SHA256(secret, "${timestamp}.${body}")` 并与 `v1` 的值进行比对。
### 额外头部
自定义的键值头部,将随请求一同发送。若与自动头部同名,则会覆盖自动头部。
## 自动头部
每个请求都会自动包含以下头部:
| Header | 说明 |
|--------|------|
| `Content-Type` | `application/json` |
| `X-Webhook-Timestamp` | Unix 时间戳(毫秒) |
| `X-Delivery-ID` | 本次投递的唯一 UUID |
| `Idempotency-Key` | 与 `X-Delivery-ID` 相同,用于去重 |
## 输出
| 输出 | 类型 | 说明 |
|------|------|------|
| `data` | json | 端点返回的响应体 |
| `status` | number | HTTP 状态码 |
| `headers` | object | 响应头部 |
## 示例用例
**通知外部服务** - 将工作流结果发送到 Slack、Discord 或自定义端点
```
Agent → Function (format) → Webhook (notify)
```
**触发外部工作流** - 当满足条件时,在其他系统中启动流程
```
Condition (check) → Webhook (trigger) → Response
```
<Callout>
Webhook 模块始终使用 POST。如需使用其他 HTTP 方法或获得更多控制,请使用 [API 模块](/blocks/api)。
</Callout>

View File

@@ -0,0 +1,233 @@
---
title: Fireflies
description: 与 Fireflies.ai 会议转录和录音进行交互
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="fireflies"
color="#100730"
/>
{/* MANUAL-CONTENT-START:intro */}
[Fireflies.ai](https://fireflies.ai/) 是一个会议转录与智能平台,可与 Sim 集成,让你的代理可以通过零代码自动化,直接处理会议录音、转录和洞察。
Fireflies 在 Sim 中的集成提供了以下工具:
- **列出会议转录:** 为你的团队或账户获取多个会议及其摘要信息。
- **获取完整转录详情:** 访问详细转录内容,包括摘要、行动项、主题和与会者分析。
- **上传音频或视频:** 上传音频/视频文件或提供 URL 进行转录——可选设置语言、标题、与会者,并自动获取会议笔记。
- **搜索转录:** 通过关键词、参与者、主持人或时间范围查找会议,快速定位相关讨论。
- **删除转录:** 从你的 Fireflies 工作区中移除特定会议转录。
- **创建音频片段Bites** 从转录中提取并高亮关键时刻,生成音频或视频片段。
- **转录完成时触发工作流:** 使用提供的 webhook 触发器,在 Fireflies 会议转录完成后自动激活 Sim 工作流,实现基于新会议数据的实时自动化和通知。
结合这些功能,你可以简化会后操作,提取结构化洞察,自动发送通知,管理录音,并围绕组织的通话编排自定义工作流——所有操作都可通过你的 API key 和 Fireflies 凭证安全完成。
{/* MANUAL-CONTENT-END */}
## 使用说明
将 Fireflies.ai 集成到工作流中。管理会议转录、为实时会议添加机器人、创建音频片段等。还可在转录完成时触发工作流。
## 工具
### `fireflies_list_transcripts`
列出来自 Fireflies.ai 的会议记录,并可选进行筛选
#### 输入
| 参数 | 类型 | 必填 | 说明 |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | 是 | Fireflies API key |
| `keyword` | string | 否 | 按会议标题或记录内容搜索关键词 |
| `fromDate` | string | 否 | 从此日期筛选记录ISO 8601 格式) |
| `toDate` | string | 否 | 筛选至此日期的记录ISO 8601 格式) |
| `hostEmail` | string | 否 | 按会议主持人邮箱筛选 |
| `participants` | string | 否 | 按参与者邮箱筛选(逗号分隔) |
| `limit` | number | 否 | 返回的最大记录数(最多 50 条) |
| `skip` | number | 否 | 分页时跳过的记录数 |
#### 输出
| 参数 | 类型 | 说明 |
| --------- | ---- | ----------- |
| `transcripts` | array | 记录列表 |
| `count` | number | 返回的记录数 |
### `fireflies_get_transcript`
获取单条会议记录,包含摘要、行动项和分析等完整信息
#### 输入
| 参数 | 类型 | 必填 | 说明 |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | 是 | Fireflies API key |
| `transcriptId` | string | 是 | 要获取的记录 ID |
#### 输出
| 参数 | 类型 | 说明 |
| --------- | ---- | ----------- |
| `transcript` | object | 包含完整信息的会议记录 |
### `fireflies_get_user`
从 Fireflies.ai 获取用户信息。如果未指定 ID则返回当前用户信息。
#### 输入
| 参数 | 类型 | 必填 | 说明 |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | 是 | Fireflies API key |
| `userId` | string | 否 | 要检索的用户 ID可选默认为 API key 所有者) |
#### 输出
| 参数 | 类型 | 说明 |
| --------- | ---- | ----------- |
| `user` | object | 用户信息 |
### `fireflies_list_users`
列出你在 Fireflies.ai 团队中的所有用户
#### 输入
| 参数 | 类型 | 必填 | 说明 |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | 是 | Fireflies API key |
#### 输出
| 参数 | 类型 | 说明 |
| --------- | ---- | ----------- |
| `users` | array | 团队用户列表 |
### `fireflies_upload_audio`
上传音频文件 URL 到 Fireflies.ai 进行转录
#### 输入
| 参数 | 类型 | 必填 | 说明 |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | 是 | Fireflies API key |
| `audioFile` | file | 否 | 要上传用于转录的音频/视频文件 |
| `audioUrl` | string | 否 | 音频/视频文件的公开 HTTPS URLMP3、MP4、WAV、M4A、OGG |
| `title` | string | 否 | 会议/转录标题 |
| `webhook` | string | 否 | 转录完成后通知的 Webhook URL |
| `language` | string | 否 | 转录语言代码(如 "es" 表示西班牙语,"de" 表示德语) |
| `attendees` | string | 否 | 以 JSON 格式填写的与会者信息:\[\{"displayName": "Name", "email": "email@example.com"\}\] |
| `clientReferenceId` | string | 否 | 用于追踪的自定义参考 ID |
#### 输出
| 参数 | 类型 | 描述 |
| --------- | ---- | ----------- |
| `success` | boolean | 上传是否成功 |
| `title` | string | 上传会议的标题 |
| `message` | string | 来自 Fireflies 的状态信息 |
### `fireflies_delete_transcript`
从 Fireflies.ai 删除一份转录记录
#### 输入
| 参数 | 类型 | 必填 | 描述 |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | 是 | Fireflies API 密钥 |
| `transcriptId` | string | 是 | 要删除的转录 ID |
#### 输出
| 参数 | 类型 | 描述 |
| --------- | ---- | ----------- |
| `success` | boolean | 转录是否已成功删除 |
### `fireflies_add_to_live_meeting`
将 Fireflies.ai 机器人添加到正在进行的会议中进行录音和转录
#### 输入
| 参数 | 类型 | 必填 | 描述 |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | 是 | Fireflies API 密钥 |
| `meetingLink` | string | 是 | 有效的会议 URL如 Zoom、Google Meet、Microsoft Teams 等) |
| `title` | string | 否 | 会议标题(最多 256 个字符) |
| `meetingPassword` | string | 否 | 会议密码(如需要,最多 32 个字符) |
| `duration` | number | 否 | 会议时长分钟15-120默认60 |
| `language` | string | 否 | 转录语言代码(如 "en"、"es"、"de" |
#### 输出
| 参数 | 类型 | 描述 |
| --------- | ---- | ----------- |
| `success` | boolean | 机器人是否已成功添加到会议中 |
### `fireflies_create_bite`
从转录文本的指定时间范围创建一个音频片段/高光
#### 输入
| 参数 | 类型 | 必填 | 描述 |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | 是 | Fireflies API key |
| `transcriptId` | string | 是 | 要创建片段的转录文本 ID |
| `startTime` | number | 是 | 片段起始时间(秒) |
| `endTime` | number | 是 | 片段结束时间(秒) |
| `name` | string | 否 | 片段名称(最多 256 个字符) |
| `mediaType` | string | 否 | 媒体类型:"video" 或 "audio" |
| `summary` | string | 否 | 片段摘要(最多 500 个字符) |
#### 输出
| 参数 | 类型 | 描述 |
| --------- | ---- | ----------- |
| `bite` | object | 创建的片段详情 |
### `fireflies_list_bites`
列出 Fireflies.ai 的音频片段/高光
#### 输入
| 参数 | 类型 | 必填 | 描述 |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | 是 | Fireflies API key |
| `transcriptId` | string | 否 | 按指定转录文本筛选片段 |
| `mine` | boolean | 否 | 仅返回 API key 拥有者拥有的片段默认true |
| `limit` | number | 否 | 返回的片段最大数量(最多 50 个) |
| `skip` | number | 否 | 分页时跳过的片段数量 |
#### 输出
| 参数 | 类型 | 描述 |
| --------- | ---- | ----------- |
| `bites` | array | bite/soundbite 列表 |
### `fireflies_list_contacts`
列出你在 Fireflies.ai 会议中的所有联系人
#### 输入
| 参数 | 类型 | 必填 | 描述 |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | 是 | Fireflies API key |
#### 输出
| 参数 | 类型 | 描述 |
| --------- | ---- | ----------- |
| `contacts` | array | 会议联系人列表 |
## 备注
- 分类:`tools`
- 类型:`fireflies`

View File

@@ -1,230 +0,0 @@
---
title: Webhook
description: 通过配置自定义 webhook从任何服务接收 webhook。
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
import { Image } from '@/components/ui/image'
<BlockInfoCard
type="generic_webhook"
color="#10B981"
/>
<div className="flex justify-center">
<Image
src="/static/blocks/webhook.png"
alt="Webhook Block Configuration"
width={500}
height={400}
className="my-6"
/>
</div>
## 概述
通用 Webhook 模块允许您接收来自任何外部服务的 webhook。这是一个灵活的触发器可以处理任何 JSON 负载,非常适合与没有专用 Sim 模块的服务集成。
## 基本用法
### 简单直通模式
在未定义输入格式的情况下webhook 会按原样传递整个请求正文:
```bash
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
-H "Content-Type: application/json" \
-H "X-Sim-Secret: your-secret" \
-d '{
"message": "Test webhook trigger",
"data": {
"key": "value"
}
}'
```
在下游模块中使用以下方式访问数据:
- `<webhook1.message>` → "测试 webhook 触发器"
- `<webhook1.data.key>` → "值"
### 结构化输入格式(可选)
定义输入模式以获取类型化字段,并启用高级功能,例如文件上传:
**输入格式配置:**
```json
[
{ "name": "message", "type": "string" },
{ "name": "priority", "type": "number" },
{ "name": "documents", "type": "files" }
]
```
**Webhook 请求:**
```bash
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
-H "Content-Type: application/json" \
-H "X-Sim-Secret: your-secret" \
-d '{
"message": "Invoice submission",
"priority": 1,
"documents": [
{
"type": "file",
"data": "data:application/pdf;base64,JVBERi0xLjQK...",
"name": "invoice.pdf",
"mime": "application/pdf"
}
]
}'
```
## 文件上传
### 支持的文件格式
webhook 支持两种文件输入格式:
#### 1. Base64 编码文件
用于直接上传文件内容:
```json
{
"documents": [
{
"type": "file",
"data": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgA...",
"name": "screenshot.png",
"mime": "image/png"
}
]
}
```
- **最大大小**:每个文件 20MB
- **格式**:带有 base64 编码的标准数据 URL
- **存储**:文件上传到安全的执行存储
#### 2. URL 引用
用于传递现有文件 URL
```json
{
"documents": [
{
"type": "url",
"data": "https://example.com/files/document.pdf",
"name": "document.pdf",
"mime": "application/pdf"
}
]
}
```
### 在下游模块中访问文件
文件被处理为具有以下属性的 `UserFile` 对象:
```typescript
{
id: string, // Unique file identifier
name: string, // Original filename
url: string, // Presigned URL (valid for 5 minutes)
size: number, // File size in bytes
type: string, // MIME type
key: string, // Storage key
uploadedAt: string, // ISO timestamp
expiresAt: string // ISO timestamp (5 minutes)
}
```
**分块访问:**
- `<webhook1.documents[0].url>` → 下载 URL
- `<webhook1.documents[0].name>` → "invoice.pdf"
- `<webhook1.documents[0].size>` → 524288
- `<webhook1.documents[0].type>` → "application/pdf"
### 完整文件上传示例
```bash
# Create a base64-encoded file
echo "Hello World" | base64
# SGVsbG8gV29ybGQK
# Send webhook with file
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
-H "Content-Type: application/json" \
-H "X-Sim-Secret: your-secret" \
-d '{
"subject": "Document for review",
"attachments": [
{
"type": "file",
"data": "data:text/plain;base64,SGVsbG8gV29ybGQK",
"name": "sample.txt",
"mime": "text/plain"
}
]
}'
```
## 身份验证
### 配置身份验证(可选)
在 webhook 配置中:
1. 启用“需要身份验证”
2. 设置一个密钥令牌
3. 选择头类型:
- **自定义头**: `X-Sim-Secret: your-token`
- **授权 Bearer**: `Authorization: Bearer your-token`
### 使用身份验证
```bash
# With custom header
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
-H "Content-Type: application/json" \
-H "X-Sim-Secret: your-secret-token" \
-d '{"message": "Authenticated request"}'
# With bearer token
curl -X POST https://sim.ai/api/webhooks/trigger/{webhook-path} \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-secret-token" \
-d '{"message": "Authenticated request"}'
```
## 最佳实践
1. **使用输入格式定义结构**:当您知道预期的模式时,定义输入格式。这提供:
- 类型验证
- 编辑器中的更好自动完成
- 文件上传功能
2. **身份验证**:在生产环境的 webhook 中始终启用身份验证,以防止未经授权的访问。
3. **文件大小限制**:将文件保持在 20MB 以下。对于更大的文件,请使用 URL 引用。
4. **文件过期**:下载的文件具有 5 分钟的 URL 过期时间。请及时处理,或如果需要更长时间,请将其存储在其他地方。
5. **错误处理**Webhook 处理是异步的。请检查执行日志以获取错误信息。
6. **测试**:在部署前,使用编辑器中的“测试 Webhook”按钮验证您的配置。
## 使用场景
- **表单提交**:接收带有文件上传的自定义表单数据
- **第三方集成**:与发送 webhook 的服务(如 Stripe、GitHub 等)连接
- **文档处理**:接受来自外部系统的文档进行处理
- **事件通知**:接收来自各种来源的事件数据
- **自定义 API**:为您的应用程序构建自定义 API 端点
## 注意事项
- 类别:`triggers`
- 类型:`generic_webhook`
- **文件支持**:通过输入格式配置可用
- **最大文件大小**:每个文件 20MB

View File

@@ -15,7 +15,7 @@ Webhook 允许外部服务通过向您的工作流发送 HTTP 请求来触发工
<div className="flex justify-center">
<Image
src="/static/blocks/webhook.png"
src="/static/blocks/webhook-trigger.png"
alt="通用 Webhook 配置"
width={500}
height={400}

View File

@@ -169,7 +169,7 @@ checksums:
content/1: 9d1b6de2021f809cc43502d19a19bd15
content/2: f4c40c45a45329eca670aca4fcece6f3
content/3: b03a97486cc185beb7b51644b548875a
content/4: a77222cf7a57362fc7eb5ebf7cc652c6
content/4: 01c24bef59948dbecc1ae19794019d5f
content/5: ba18ac99184b17d7e49bd1abdc814437
content/6: 171c4e97e509427ca63acccf136779b3
content/7: 98e1babdd0136267807b7e94ae7da6c7
@@ -50197,3 +50197,105 @@ checksums:
content/7: 7b29d23aec8fda839f3934c5fc71c6d3
content/8: b3f310d5ef115bea5a8b75bf25d7ea9a
content/9: 79ecd09a7bedc128285814d8b439ed40
2bf1f583bd3a431e459e5a0142a82efd:
meta/title: 70f95b2c27f2c3840b500fcaf79ee83c
content/0: eb0ed7078f192304703144f4cac3442f
content/1: 1bc1f971556fb854666c22551215d3c2
content/2: 5127a30fba20289720806082df2eae87
content/3: 0441638444240cd20a6c69ea1d3afbb1
content/4: 0b5805c0201ed427ba1b56b9814ee0cb
content/5: cf5305db38e782a1001f5208cdf6b5f1
content/6: 575a2fc0f65f0d24a9d75fac8e8bf5f8
content/7: 1acea0b3685c12e5c3d73c7afa9c5582
content/8: 4464a6c6f5ccc67b95309ba6399552e9
content/9: 336794d9cf3e900c1b5aba0071944f1c
content/10: bf46b631598a496c37560e074454f5ec
content/11: 3d6a55b18007832eb2ed751638e968ca
content/12: 3f97586d23efe56c4ab94c03a0b91706
content/13: f2caee00e0e386a5e5257862209aaaef
content/14: 15c9ed641ef776a33a945b6e0ddb908c
content/15: db087c66ef8c0ab22775072b10655d05
content/16: e148c1c6e1345e9ee95657c5ba40ebf4
content/17: 9feca6cbb058fb8070b23d139d2d96e6
content/18: 987932038f4e9442bd89f0f8ed3c5319
content/19: 8e0258b3891544d355fa4a92f2ae96e4
content/20: 9c2f91f89a914bf4661512275e461104
content/21: a5cc8d50937a37d5ae7e921fc85a71f1
content/22: 51b2fdf484e8d6b07cdf8434034dc872
content/23: 59da7694b8be001fec8b9f9d7b604faf
content/24: 8fb6954068c6687d44121e21a95cf1b6
content/25: 9e7b1a1a453340d20adf4cacbd532018
fa1c42261042a9cde3e5c1f691169876:
meta/title: 34a88e7137f1af4a641d20c686673cf4
meta/description: 8371b5fceeb140f5ac5a6facbb778a5f
content/0: 1b031fb0c62c46b177aeed5c3d3f8f80
content/1: 2ea4b5bc50001e7c494837ceb1370539
content/2: 6306e3afffcd2563b1792c558ca2655e
content/3: b6ba91252e179f4fb17da86e51b3df12
content/4: b02e7d685008724ca7b34d8f4b43007c
content/5: 02d371955e9386f261757123f4240365
content/6: 821e6394b0a953e2b0842b04ae8f3105
content/7: cdbaa3964c4e6a7ccf7a326161d056cf
content/8: 9c8aa3f09c9b2bd50ea4cdff3598ea4e
content/9: 6383f4ae36fb08c2899399ba021b19b1
content/10: 9697169c028783b065b30044f4c0fe26
content/11: 371d0e46b4bd2c23f559b8bc112f6955
content/12: a7186564ee9cb3e4e96cb03dbc84d710
content/13: bcadfc362b69078beee0088e5936c98b
content/14: 82e7c6eb98b5b33f22431aecdca80703
content/15: d18932457fd95c545b2c870a3daea47f
content/16: 588b8dfb6af5511044c19eb468ab865d
content/17: 371d0e46b4bd2c23f559b8bc112f6955
content/18: b0a7eeeb3feae67dd21196780ae0d5eb
content/19: bcadfc362b69078beee0088e5936c98b
content/20: c87b7e083a1ade7bfe4e5c7639bbc2b4
content/21: 9b64a33ba85db593c28ac57d74be12e0
content/22: 1cd336acd20989efc7a172f67cd3633c
content/23: 371d0e46b4bd2c23f559b8bc112f6955
content/24: a4f5290e9ed361bf4fdd835f5c6efd6c
content/25: bcadfc362b69078beee0088e5936c98b
content/26: ae0a66fda10f781f6eaf952d27025c2d
content/27: 73ee8f45410139ce0426d03776ce9a0b
content/28: b204098918149dc3d623810d2e0f10df
content/29: 371d0e46b4bd2c23f559b8bc112f6955
content/30: ad69ead28418cfe4b757c6b65ce6c985
content/31: bcadfc362b69078beee0088e5936c98b
content/32: 6d232c1820e52c643ca5074fa1ea0e0b
content/33: 383c61dc19eeb83f3b1b132087581c04
content/34: 0fb444a929514e2b5654af1f62087809
content/35: 371d0e46b4bd2c23f559b8bc112f6955
content/36: dfcfaf2aa85cde2e3282b507bc5c4b59
content/37: bcadfc362b69078beee0088e5936c98b
content/38: 4a8a54bbcff9102d58847d3dd8cf6f9d
content/39: fd73283c59dad77ce75095aece6f934b
content/40: 06668b83f4b8b37c426b0384d211a27f
content/41: 371d0e46b4bd2c23f559b8bc112f6955
content/42: 53db172d2fa498f7dca7cacdd3fdc67c
content/43: bcadfc362b69078beee0088e5936c98b
content/44: 56a281731309b62b67662e6a46a2a55b
content/45: 6accb29bf4f712a88304d74becba1aeb
content/46: a6cdfbfad60e27a6dd080833fc5c0cda
content/47: 371d0e46b4bd2c23f559b8bc112f6955
content/48: ce13160ba405b0b8d396b7aa98810b23
content/49: bcadfc362b69078beee0088e5936c98b
content/50: 65ff00bcedb0c69c3e4eec317cdfcb44
content/51: bfd77718128856a7549229a9dbe3c2d5
content/52: e322fc91f9a1546a0fdfbb137bdbdfc8
content/53: 371d0e46b4bd2c23f559b8bc112f6955
content/54: 631159b3a40d1eaf269229d34ae33eb8
content/55: bcadfc362b69078beee0088e5936c98b
content/56: b52f83ac6d343783d1a0c06d14a99368
content/57: cb2ca71e1732e20d0e100229914f3191
content/58: 825d7e4652afde24f405b6cc347f51d3
content/59: 371d0e46b4bd2c23f559b8bc112f6955
content/60: e715106d45f9a7021c4d1b76ae2277ad
content/61: bcadfc362b69078beee0088e5936c98b
content/62: 7e2dc302f6805a80dc63c8ab1dfb0955
content/63: 3373816242f7df96dcaf462f8913bdaf
content/64: 7f8c9d671cfc8a7ac34c2101de4e86cc
content/65: 371d0e46b4bd2c23f559b8bc112f6955
content/66: ad69ead28418cfe4b757c6b65ce6c985
content/67: bcadfc362b69078beee0088e5936c98b
content/68: ba6b5020ed971cd7ffc7f0423650dfbf
content/69: b3f310d5ef115bea5a8b75bf25d7ea9a
content/70: 0362be478aa7ba4b6d1ebde0bd83e83a

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

@@ -20,7 +20,7 @@ interface NavProps {
}
export default function Nav({ hideAuthButtons = false, variant = 'landing' }: NavProps = {}) {
const [githubStars, setGithubStars] = useState('24.4k')
const [githubStars, setGithubStars] = useState('25.1k')
const [isHovered, setIsHovered] = useState(false)
const [isLoginHovered, setIsLoginHovered] = useState(false)
const router = useRouter()

View File

@@ -42,17 +42,6 @@ export default function StructuredData() {
publisher: {
'@id': 'https://sim.ai/#organization',
},
potentialAction: [
{
'@type': 'SearchAction',
'@id': 'https://sim.ai/#searchaction',
target: {
'@type': 'EntryPoint',
urlTemplate: 'https://sim.ai/search?q={search_term_string}',
},
'query-input': 'required name=search_term_string',
},
],
inLanguage: 'en-US',
},
{
@@ -110,7 +99,7 @@ export default function StructuredData() {
name: 'Community Plan',
price: '0',
priceCurrency: 'USD',
priceValidUntil: '2025-12-31',
priceValidUntil: '2026-12-31',
itemCondition: 'https://schema.org/NewCondition',
availability: 'https://schema.org/InStock',
seller: {
@@ -134,7 +123,7 @@ export default function StructuredData() {
unitText: 'MONTH',
billingIncrement: 1,
},
priceValidUntil: '2025-12-31',
priceValidUntil: '2026-12-31',
itemCondition: 'https://schema.org/NewCondition',
availability: 'https://schema.org/InStock',
seller: {
@@ -154,7 +143,7 @@ export default function StructuredData() {
unitText: 'MONTH',
billingIncrement: 1,
},
priceValidUntil: '2025-12-31',
priceValidUntil: '2026-12-31',
itemCondition: 'https://schema.org/NewCondition',
availability: 'https://schema.org/InStock',
seller: {
@@ -184,8 +173,8 @@ export default function StructuredData() {
screenshot: [
{
'@type': 'ImageObject',
url: 'https://sim.ai/screenshots/workflow-builder.png',
caption: 'Sim workflow builder interface',
url: 'https://sim.ai/logo/426-240/primary/small.png',
caption: 'Sim AI agent workflow builder interface',
},
],
},
@@ -223,16 +212,9 @@ export default function StructuredData() {
}
return (
<>
<script
type='application/ld+json'
dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}
/>
{/* LLM-friendly semantic HTML comments */}
{/* About: Sim is a visual workflow builder for AI agents and large language models (LLMs) */}
{/* Purpose: Enable users to create AI-powered automations without coding */}
{/* Features: Drag-and-drop interface, 100+ integrations, multi-model support */}
{/* Use cases: Email automation, chatbots, data analysis, content generation */}
</>
<script
type='application/ld+json'
dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}
/>
)
}

View File

@@ -0,0 +1,42 @@
import { getBaseUrl } from '@/lib/core/utils/urls'
export async function GET() {
const baseUrl = getBaseUrl()
const expiresDate = new Date()
expiresDate.setFullYear(expiresDate.getFullYear() + 1)
const expires = expiresDate.toISOString()
const securityTxt = `# Security Policy for Sim
# https://securitytxt.org/
# RFC 9116: https://www.rfc-editor.org/rfc/rfc9116.html
# Required: Contact information for security reports
Contact: mailto:security@sim.ai
# Required: When this file expires (ISO 8601 format, within 1 year)
Expires: ${expires}
# Preferred languages for security reports
Preferred-Languages: en
# Canonical URL for this security.txt file
Canonical: ${baseUrl}/.well-known/security.txt
# Link to security policy page
Policy: ${baseUrl}/security
# Acknowledgments page for security researchers
# Acknowledgments: ${baseUrl}/security/thanks
# If you discover a security vulnerability, please report it responsibly.
# We appreciate your help in keeping Sim and our users secure.
`
return new Response(securityTxt, {
headers: {
'Content-Type': 'text/plain; charset=utf-8',
'Cache-Control': 'public, max-age=86400',
},
})
}

View File

@@ -2,6 +2,7 @@ import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { env } from '@/lib/core/config/env'
import type { ModelsObject } from '@/providers/ollama/types'
import { filterBlacklistedModels, isProviderBlacklisted } from '@/providers/utils'
const logger = createLogger('OllamaModelsAPI')
const OLLAMA_HOST = env.OLLAMA_URL || 'http://localhost:11434'
@@ -9,7 +10,12 @@ const OLLAMA_HOST = env.OLLAMA_URL || 'http://localhost:11434'
/**
* Get available Ollama models
*/
export async function GET(request: NextRequest) {
export async function GET(_request: NextRequest) {
if (isProviderBlacklisted('ollama')) {
logger.info('Ollama provider is blacklisted, returning empty models')
return NextResponse.json({ models: [] })
}
try {
logger.info('Fetching Ollama models', {
host: OLLAMA_HOST,
@@ -31,10 +37,12 @@ export async function GET(request: NextRequest) {
}
const data = (await response.json()) as ModelsObject
const models = data.models.map((model) => model.name)
const allModels = data.models.map((model) => model.name)
const models = filterBlacklistedModels(allModels)
logger.info('Successfully fetched Ollama models', {
count: models.length,
filtered: allModels.length - models.length,
models,
})

View File

@@ -1,6 +1,6 @@
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { filterBlacklistedModels } from '@/providers/utils'
import { filterBlacklistedModels, isProviderBlacklisted } from '@/providers/utils'
const logger = createLogger('OpenRouterModelsAPI')
@@ -30,6 +30,11 @@ export interface OpenRouterModelInfo {
}
export async function GET(_request: NextRequest) {
if (isProviderBlacklisted('openrouter')) {
logger.info('OpenRouter provider is blacklisted, returning empty models')
return NextResponse.json({ models: [], modelInfo: {} })
}
try {
const response = await fetch('https://openrouter.ai/api/v1/models', {
headers: { 'Content-Type': 'application/json' },

View File

@@ -1,13 +1,19 @@
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { env } from '@/lib/core/config/env'
import { filterBlacklistedModels, isProviderBlacklisted } from '@/providers/utils'
const logger = createLogger('VLLMModelsAPI')
/**
* Get available vLLM models
*/
export async function GET(request: NextRequest) {
export async function GET(_request: NextRequest) {
if (isProviderBlacklisted('vllm')) {
logger.info('vLLM provider is blacklisted, returning empty models')
return NextResponse.json({ models: [] })
}
const baseUrl = (env.VLLM_BASE_URL || '').replace(/\/$/, '')
if (!baseUrl) {
@@ -42,10 +48,12 @@ export async function GET(request: NextRequest) {
}
const data = (await response.json()) as { data: Array<{ id: string }> }
const models = data.data.map((model) => `vllm/${model.id}`)
const allModels = data.data.map((model) => `vllm/${model.id}`)
const models = filterBlacklistedModels(allModels)
logger.info('Successfully fetched vLLM models', {
count: models.length,
filtered: allModels.length - models.length,
models,
})

View File

@@ -59,6 +59,7 @@ interface RequestBody {
stream?: boolean
history?: ChatMessage[]
workflowId?: string
generationType?: string
}
function safeStringify(value: unknown): string {
@@ -158,7 +159,7 @@ export async function POST(req: NextRequest) {
try {
const body = (await req.json()) as RequestBody
const { prompt, systemPrompt, stream = false, history = [], workflowId } = body
const { prompt, systemPrompt, stream = false, history = [], workflowId, generationType } = body
if (!prompt) {
logger.warn(`[${requestId}] Invalid request: Missing prompt.`)
@@ -222,10 +223,26 @@ export async function POST(req: NextRequest) {
)
}
const finalSystemPrompt =
let finalSystemPrompt =
systemPrompt ||
'You are a helpful AI assistant. Generate content exactly as requested by the user.'
if (generationType === 'timestamp') {
const now = new Date()
const currentTimeContext = `\n\nCurrent date and time context for reference:
- Current UTC timestamp: ${now.toISOString()}
- Current Unix timestamp (seconds): ${Math.floor(now.getTime() / 1000)}
- Current Unix timestamp (milliseconds): ${now.getTime()}
- Current date (UTC): ${now.toISOString().split('T')[0]}
- Current year: ${now.getUTCFullYear()}
- Current month: ${now.getUTCMonth() + 1}
- Current day of month: ${now.getUTCDate()}
- Current day of week: ${['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][now.getUTCDay()]}
Use this context to calculate relative dates like "yesterday", "last week", "beginning of this month", etc.`
finalSystemPrompt += currentTimeContext
}
const messages: ChatMessage[] = [{ role: 'system', content: finalSystemPrompt }]
messages.push(...history.filter((msg) => msg.role !== 'system'))

View File

@@ -14,10 +14,6 @@ import {
} from '@/app/api/__test-utils__/utils'
const {
hasProcessedMessageMock,
markMessageAsProcessedMock,
closeRedisConnectionMock,
acquireLockMock,
generateRequestHashMock,
validateSlackSignatureMock,
handleWhatsAppVerificationMock,
@@ -28,10 +24,6 @@ const {
processWebhookMock,
executeMock,
} = vi.hoisted(() => ({
hasProcessedMessageMock: vi.fn().mockResolvedValue(false),
markMessageAsProcessedMock: vi.fn().mockResolvedValue(true),
closeRedisConnectionMock: vi.fn().mockResolvedValue(undefined),
acquireLockMock: vi.fn().mockResolvedValue(true),
generateRequestHashMock: vi.fn().mockResolvedValue('test-hash-123'),
validateSlackSignatureMock: vi.fn().mockResolvedValue(true),
handleWhatsAppVerificationMock: vi.fn().mockResolvedValue(null),
@@ -73,13 +65,6 @@ vi.mock('@/background/logs-webhook-delivery', () => ({
logsWebhookDelivery: {},
}))
vi.mock('@/lib/redis', () => ({
hasProcessedMessage: hasProcessedMessageMock,
markMessageAsProcessed: markMessageAsProcessedMock,
closeRedisConnection: closeRedisConnectionMock,
acquireLock: acquireLockMock,
}))
vi.mock('@/lib/webhooks/utils', () => ({
handleWhatsAppVerification: handleWhatsAppVerificationMock,
handleSlackChallenge: handleSlackChallengeMock,
@@ -201,9 +186,6 @@ describe('Webhook Trigger API Route', () => {
workspaceId: 'test-workspace-id',
})
hasProcessedMessageMock.mockResolvedValue(false)
markMessageAsProcessedMock.mockResolvedValue(true)
acquireLockMock.mockResolvedValue(true)
handleWhatsAppVerificationMock.mockResolvedValue(null)
processGenericDeduplicationMock.mockResolvedValue(null)
processWebhookMock.mockResolvedValue(new Response('Webhook processed', { status: 200 }))

View File

@@ -162,6 +162,15 @@ export async function POST(
if (foundWebhook.blockId) {
const blockExists = await blockExistsInDeployment(foundWorkflow.id, foundWebhook.blockId)
if (!blockExists) {
// For Grain, if block doesn't exist in deployment, treat as verification request
// Grain validates webhook URLs during creation, and the block may not be deployed yet
if (foundWebhook.provider === 'grain') {
logger.info(
`[${requestId}] Grain webhook verification - block not in deployment, returning 200 OK`
)
return NextResponse.json({ status: 'ok', message: 'Webhook endpoint verified' })
}
logger.info(
`[${requestId}] Trigger block ${foundWebhook.blockId} not found in deployment for workflow ${foundWorkflow.id}`
)

View File

@@ -117,7 +117,7 @@ export default function ChatClient({ identifier }: { identifier: string }) {
const [error, setError] = useState<string | null>(null)
const messagesEndRef = useRef<HTMLDivElement>(null)
const messagesContainerRef = useRef<HTMLDivElement>(null)
const [starCount, setStarCount] = useState('24.4k')
const [starCount, setStarCount] = useState('25.1k')
const [conversationId, setConversationId] = useState('')
const [showScrollButton, setShowScrollButton] = useState(false)

View File

@@ -0,0 +1,175 @@
import { getBaseUrl } from '@/lib/core/utils/urls'
export async function GET() {
const baseUrl = getBaseUrl()
const llmsFullContent = `# Sim - AI Agent Workflow Builder
> Sim is an open-source AI agent workflow builder used by 60,000+ developers at startups to Fortune 500 companies. Build and deploy agentic workflows with a visual drag-and-drop canvas. SOC2 and HIPAA compliant.
## Overview
Sim provides a visual interface for building AI agent workflows. Instead of writing code, users drag and drop blocks onto a canvas and connect them to create complex AI automations. Each block represents a step in the workflow - an LLM call, a tool invocation, an API request, or a code execution.
## Product Details
- **Product Name**: Sim
- **Category**: AI Development Tools / Workflow Automation
- **Deployment**: Cloud (SaaS) and Self-hosted options
- **Pricing**: Free tier, Pro ($20/month), Team ($40/month), Enterprise (custom)
- **Compliance**: SOC2 Type II, HIPAA compliant
## Core Concepts
### Workspace
A workspace is the top-level container in Sim. It holds workflows, data sources, credentials, and execution history. Users can create multiple workspaces for different projects or teams.
### Workflow
A workflow is a directed graph of blocks that defines an agentic process. Workflows can be triggered manually, on a schedule, or via webhooks. Each workflow has a unique ID and can be versioned.
### Block
A block is an individual step in a workflow. Types include:
- **Agent Block**: Executes an LLM call with system prompts and tools
- **Function Block**: Runs custom JavaScript/TypeScript code
- **API Block**: Makes HTTP requests to external services
- **Condition Block**: Branches workflow based on conditions
- **Loop Block**: Iterates over arrays or until conditions are met
- **Router Block**: Routes to different paths based on LLM classification
### Trigger
A trigger initiates workflow execution. Types include:
- **Manual**: User clicks "Run" button
- **Schedule**: Cron-based scheduling (e.g., every hour, daily at 9am)
- **Webhook**: HTTP endpoint that triggers on incoming requests
- **Event**: Triggered by external events (email received, Slack message, etc.)
### Execution
An execution is a single run of a workflow. It includes:
- Input parameters
- Block-by-block execution logs
- Output data
- Token usage and cost tracking
- Duration and performance metrics
## Capabilities
### LLM Orchestration
Sim supports all major LLM providers:
- OpenAI (GPT-5.2, GPT-5.1, GPT-5, GPT-4o, GPT-4.1)
- Anthropic (Claude Opus 4.5, Claude Opus 4.1, Claude Sonnet 4.5, Claude Haiku 4.5)
- Google (Gemini Pro 3, Gemini Pro 3 Preview, Gemini 2.5 Pro, Gemini 2.5 Flash)
- Mistral (Mistral Large, Mistral Medium)
- xAI (Grok)
- Perplexity
- Ollama or VLLM (self-hosted open-source models)
- Azure OpenAI
- Amazon Bedrock
### Integrations
100+ pre-built integrations including:
- **Communication**: Slack, Discord, Email (Gmail, Outlook), SMS (Twilio)
- **Productivity**: Notion, Airtable, Google Sheets, Google Docs
- **Development**: GitHub, GitLab, Jira, Linear
- **Data**: PostgreSQL, MySQL, MongoDB, Supabase, Pinecone
- **Storage**: AWS S3, Google Cloud Storage, Dropbox
- **CRM**: Salesforce, HubSpot, Pipedrive
### RAG (Retrieval-Augmented Generation)
Built-in support for:
- Document ingestion (PDF, DOCX, TXT, Markdown)
- Vector database integration (Pinecone, Weaviate, Qdrant)
- Semantic search and retrieval
- Chunking strategies (fixed size, semantic, recursive)
### Code Execution
- Sandboxed JavaScript/TypeScript execution
- Access to npm packages
- Persistent state across executions
- Error handling and retry logic
## Use Cases
### Customer Support Automation
- Classify incoming tickets by urgency and topic
- Generate draft responses using RAG over knowledge base
- Route to appropriate team members
- Auto-close resolved tickets
### Content Generation Pipeline
- Research topics using web search tools
- Generate outlines and drafts with LLMs
- Review and edit with human-in-the-loop
- Publish to CMS platforms
### Data Processing Workflows
- Extract data from documents (invoices, receipts, forms)
- Transform and validate data
- Load into databases or spreadsheets
- Generate reports and summaries
### Sales and Marketing Automation
- Enrich leads with company data
- Score leads based on fit criteria
- Generate personalized outreach emails
- Sync with CRM systems
## Technical Architecture
### Frontend
- Next.js 15 with App Router
- React Flow for canvas visualization
- Tailwind CSS for styling
- Zustand for state management
### Backend
- Node.js with TypeScript
- PostgreSQL for persistent storage
- Redis for caching and queues
- S3-compatible storage for files
### Execution Engine
- Isolated execution per workflow run
- Parallel block execution where possible
- Retry logic with exponential backoff
- Real-time streaming of outputs
## Getting Started
1. **Sign Up**: Create a free account at ${baseUrl}
2. **Create Workspace**: Set up your first workspace
3. **Build Workflow**: Drag blocks onto canvas and connect them
4. **Configure Blocks**: Set up LLM providers, tools, and integrations
5. **Test**: Run the workflow manually to verify
6. **Deploy**: Set up triggers for automated execution
## Links
- **Website**: ${baseUrl}
- **Documentation**: https://docs.sim.ai
- **API Reference**: https://docs.sim.ai/api
- **GitHub**: https://github.com/simstudioai/sim
- **Discord**: https://discord.gg/Hr4UWYEcTT
- **X/Twitter**: https://x.com/simdotai
- **LinkedIn**: https://linkedin.com/company/simstudioai
## Support
- **Email**: help@sim.ai
- **Security Issues**: security@sim.ai
- **Documentation**: https://docs.sim.ai
- **Community Discord**: https://discord.gg/Hr4UWYEcTT
## Legal
- **Terms of Service**: ${baseUrl}/terms
- **Privacy Policy**: ${baseUrl}/privacy
- **Security**: ${baseUrl}/.well-known/security.txt
`
return new Response(llmsFullContent, {
headers: {
'Content-Type': 'text/markdown; charset=utf-8',
'Cache-Control': 'public, max-age=86400, s-maxage=86400',
},
})
}

View File

@@ -1,51 +1,69 @@
export async function GET() {
const llmsContent = `# Sim - AI Agent Workflow Builder
Sim is an open-source AI agent workflow builder for production workflows. Developers at trail-blazing startups to Fortune 500 companies deploy agentic workflows on the Sim platform. 60,000+ developers already use Sim to build and ship AI automations with 100+ integrations. Sim is SOC2 and HIPAA compliant and is designed for secure, enterprise-grade AI automation.
import { getBaseUrl } from '@/lib/core/utils/urls'
Website: https://sim.ai
App: https://sim.ai/workspace
Docs: https://docs.sim.ai
GitHub: https://github.com/simstudioai/sim
Region: global
Primary language: en
export async function GET() {
const baseUrl = getBaseUrl()
const llmsContent = `# Sim
> Sim is an open-source AI agent workflow builder. 60,000+ developers at startups to Fortune 500 companies deploy agentic workflows on the Sim platform. SOC2 and HIPAA compliant.
Sim provides a visual drag-and-drop interface for building and deploying AI agent workflows. Connect to 100+ integrations and ship production-ready AI automations.
## Core Pages
- [Homepage](${baseUrl}): Main landing page with product overview and features
- [Templates](${baseUrl}/templates): Pre-built workflow templates to get started quickly
- [Changelog](${baseUrl}/changelog): Product updates and release notes
- [Sim Studio Blog](${baseUrl}/studio): Announcements, insights, and guides for AI workflows
## Documentation
- [Documentation](https://docs.sim.ai): Complete guides and API reference
- [Quickstart](https://docs.sim.ai/quickstart): Get started in 5 minutes
- [API Reference](https://docs.sim.ai/api): REST API documentation
## Key Concepts
- **Workspace**: Container for workflows, data sources, and executions
- **Workflow**: Directed graph of blocks defining an agentic process
- **Block**: Individual step (LLM call, tool call, HTTP request, code execution)
- **Trigger**: Event or schedule that initiates workflow execution
- **Execution**: A single run of a workflow with logs and outputs
## Capabilities
- Visual workflow builder for multi-step AI agents and tools
- Orchestration of LLM calls, tools, webhooks, and external APIs
- Scheduled and event-driven agent executions
- First-class support for retrieval-augmented generation (RAG)
- Multi-tenant, workspace-based access model
## Ideal Use Cases
- Visual workflow builder with drag-and-drop canvas
- Multi-model LLM orchestration (OpenAI, Anthropic, Google, Mistral, xAI)
- Retrieval-augmented generation (RAG) with vector databases
- 100+ integrations (Slack, Gmail, Notion, Airtable, databases)
- Scheduled and webhook-triggered executions
- Real-time collaboration and version control
## Use Cases
- AI agent workflow automation
- RAG agents and retrieval pipelines
- Chatbot and copilot workflows for SaaS products
- Document and email processing workflows
- Customer support, marketing, and growth automations
- Internal operations automations (ops, finance, legal, sales)
- RAG pipelines and document processing
- Chatbot and copilot workflows for SaaS
- Email and customer support automation
- Internal operations (sales, marketing, legal, finance)
## Key Entities
- Workspace: container for workflows, data sources, and executions
- Workflow: directed graph of blocks defining an agentic process
- Block: individual step (LLM call, tool call, HTTP request, code, etc.)
- Schedule: time-based trigger for running workflows
- Execution: a single run of a workflow
## Links
## Getting Started
- Quickstart: https://docs.sim.ai/quickstart
- Product overview: https://docs.sim.ai
- Source code: https://github.com/simstudioai/sim
- [GitHub Repository](https://github.com/simstudioai/sim): Open-source codebase
- [Discord Community](https://discord.gg/Hr4UWYEcTT): Get help and connect with users
- [X/Twitter](https://x.com/simdotai): Product updates and announcements
## Safety & Reliability
- SOC2 and HIPAA aligned security controls
- Audit-friendly execution logs and cost tracking
- Fine-grained control over external tools, APIs, and data sources
## Optional
- [Careers](${baseUrl}/careers): Join the Sim team
- [Terms of Service](${baseUrl}/terms): Legal terms
- [Privacy Policy](${baseUrl}/privacy): Data handling practices
`
return new Response(llmsContent, {
headers: {
'Content-Type': 'text/plain; charset=utf-8',
'Cache-Control': 'public, max-age=86400',
'Content-Type': 'text/markdown; charset=utf-8',
'Cache-Control': 'public, max-age=86400, s-maxage=86400',
},
})
}

View File

@@ -8,7 +8,7 @@ export default function manifest(): MetadataRoute.Manifest {
name: brand.name === 'Sim' ? 'Sim - AI Agent Workflow Builder' : brand.name,
short_name: brand.name,
description:
'Open-source AI agent workflow builder. 30,000+ developers build and deploy agentic workflows on Sim. Visual drag-and-drop interface for creating AI automations. SOC2 and HIPAA compliant.',
'Open-source AI agent workflow builder. 60,000+ developers build and deploy agentic workflows on Sim. Visual drag-and-drop interface for creating AI automations. SOC2 and HIPAA compliant.',
start_url: '/',
scope: '/',
display: 'standalone',

View File

@@ -29,23 +29,23 @@ export const metadata: Metadata = {
locale: 'en_US',
images: [
{
url: '/logo/primary/rounded.png',
width: 512,
height: 512,
url: '/logo/426-240/primary/small.png',
width: 2130,
height: 1200,
alt: 'Sim - AI Agent Workflow Builder',
type: 'image/png',
},
],
},
twitter: {
card: 'summary',
card: 'summary_large_image',
site: '@simdotai',
creator: '@simdotai',
title: 'Sim - AI Agent Workflow Builder | Open Source',
description:
'Open-source platform for agentic workflows. 60,000+ developers. Visual builder. 100+ integrations. SOC2 & HIPAA compliant.',
images: {
url: '/logo/primary/rounded.png',
url: '/logo/426-240/primary/small.png',
alt: 'Sim - AI Agent Workflow Builder',
},
},

126
apps/sim/app/robots.ts Normal file
View File

@@ -0,0 +1,126 @@
import type { MetadataRoute } from 'next'
import { getBaseUrl } from '@/lib/core/utils/urls'
export default function robots(): MetadataRoute.Robots {
const baseUrl = getBaseUrl()
const disallowedPaths = [
'/api/',
'/workspace/',
'/chat/',
'/playground/',
'/resume/',
'/invite/',
'/unsubscribe/',
'/w/',
'/_next/',
'/private/',
]
return {
rules: [
{
userAgent: '*',
allow: '/',
disallow: disallowedPaths,
},
{
userAgent: 'Googlebot',
allow: '/',
disallow: disallowedPaths,
},
{
userAgent: 'Bingbot',
allow: '/',
disallow: disallowedPaths,
},
{
userAgent: 'YandexBot',
allow: '/',
disallow: disallowedPaths,
},
{
userAgent: 'Baiduspider',
allow: '/',
disallow: disallowedPaths,
},
{
userAgent: 'GPTBot',
allow: '/',
disallow: disallowedPaths,
},
{
userAgent: 'ChatGPT-User',
allow: '/',
disallow: disallowedPaths,
},
{
userAgent: 'OAI-SearchBot',
allow: '/',
disallow: disallowedPaths,
},
{
userAgent: 'ClaudeBot',
allow: '/',
disallow: disallowedPaths,
},
{
userAgent: 'Claude-SearchBot',
allow: '/',
disallow: disallowedPaths,
},
{
userAgent: 'Google-Extended',
allow: '/',
disallow: disallowedPaths,
},
{
userAgent: 'PerplexityBot',
allow: '/',
disallow: disallowedPaths,
},
{
userAgent: 'Meta-ExternalAgent',
allow: '/',
disallow: disallowedPaths,
},
{
userAgent: 'FacebookBot',
allow: '/',
disallow: disallowedPaths,
},
{
userAgent: 'Applebot',
allow: '/',
disallow: disallowedPaths,
},
{
userAgent: 'Applebot-Extended',
allow: '/',
disallow: disallowedPaths,
},
{
userAgent: 'Amazonbot',
allow: '/',
disallow: disallowedPaths,
},
{
userAgent: 'Bytespider',
allow: '/',
disallow: disallowedPaths,
},
{
userAgent: 'CCBot',
allow: '/',
disallow: disallowedPaths,
},
{
userAgent: 'cohere-ai',
allow: '/',
disallow: disallowedPaths,
},
],
sitemap: `${baseUrl}/sitemap.xml`,
host: baseUrl,
}
}

View File

@@ -0,0 +1,12 @@
import { getBaseUrl } from '@/lib/core/utils/urls'
export async function GET() {
const baseUrl = getBaseUrl()
return new Response(null, {
status: 301,
headers: {
Location: `${baseUrl}/.well-known/security.txt`,
},
})
}

View File

@@ -11,42 +11,34 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
{
url: baseUrl,
lastModified: now,
priority: 1.0, // Homepage - highest priority
},
{
url: `${baseUrl}/studio`,
lastModified: now,
priority: 0.9, // Blog index - high value content
},
{
url: `${baseUrl}/studio/tags`,
lastModified: now,
priority: 0.7, // Tags page - discovery/navigation
},
{
url: `${baseUrl}/templates`,
lastModified: now,
priority: 0.8, // Templates - important discovery page
},
{
url: `${baseUrl}/changelog`,
lastModified: now,
priority: 0.8, // Changelog - important for users
},
{
url: `${baseUrl}/careers`,
lastModified: new Date('2024-10-06'),
priority: 0.6, // Careers - important but not core content
},
{
url: `${baseUrl}/terms`,
lastModified: new Date('2024-10-14'),
priority: 0.5, // Terms - utility page
},
{
url: `${baseUrl}/privacy`,
lastModified: new Date('2024-10-14'),
priority: 0.5, // Privacy - utility page
},
]
@@ -54,7 +46,6 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const blogPages: MetadataRoute.Sitemap = posts.map((p) => ({
url: p.canonical,
lastModified: new Date(p.updated ?? p.date),
priority: 0.9, // Blog posts - high value content
}))
return [...staticPages, ...blogPages]

View File

@@ -164,7 +164,7 @@ function getBlockIconAndColor(
return { icon: ParallelTool.icon, bgColor: ParallelTool.bgColor }
}
if (lowerType === 'workflow') {
return { icon: WorkflowIcon, bgColor: '#705335' }
return { icon: WorkflowIcon, bgColor: '#6366F1' }
}
// Look up from block registry (model maps to agent)

View File

@@ -24,9 +24,11 @@ export function PaneContextMenu({
onAddBlock,
onAutoLayout,
onOpenLogs,
onOpenVariables,
onOpenChat,
onToggleVariables,
onToggleChat,
onInvite,
isVariablesOpen = false,
isChatOpen = false,
hasClipboard = false,
disableEdit = false,
disableAdmin = false,
@@ -125,19 +127,19 @@ export function PaneContextMenu({
</PopoverItem>
<PopoverItem
onClick={() => {
onOpenVariables()
onToggleVariables()
onClose()
}}
>
Variables
{isVariablesOpen ? 'Close Variables' : 'Open Variables'}
</PopoverItem>
<PopoverItem
onClick={() => {
onOpenChat()
onToggleChat()
onClose()
}}
>
Open Chat
{isChatOpen ? 'Close Chat' : 'Open Chat'}
</PopoverItem>
{/* Admin action */}

View File

@@ -77,9 +77,13 @@ export interface PaneContextMenuProps {
onAddBlock: () => void
onAutoLayout: () => void
onOpenLogs: () => void
onOpenVariables: () => void
onOpenChat: () => void
onToggleVariables: () => void
onToggleChat: () => void
onInvite: () => void
/** Whether the variables panel is currently open */
isVariablesOpen?: boolean
/** Whether the chat panel is currently open */
isChatOpen?: boolean
/** Whether clipboard has content for pasting */
hasClipboard?: boolean
/** Whether edit actions are disabled (no permission) */

View File

@@ -3,7 +3,15 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { createLogger } from '@sim/logger'
import { useParams } from 'next/navigation'
import { Badge, Combobox, type ComboboxOption, Input, Label, Textarea } from '@/components/emcn'
import {
Badge,
Button,
Combobox,
type ComboboxOption,
Input,
Label,
Textarea,
} from '@/components/emcn'
import { Skeleton } from '@/components/ui'
import { generateToolInputSchema, sanitizeToolName } from '@/lib/mcp/workflow-tool-schema'
import { normalizeInputFormatValue } from '@/lib/workflows/input-format-utils'
@@ -35,6 +43,7 @@ interface McpDeployProps {
onAddedToServer?: () => void
onSubmittingChange?: (submitting: boolean) => void
onCanSaveChange?: (canSave: boolean) => void
onHasServersChange?: (hasServers: boolean) => void
}
/**
@@ -83,6 +92,7 @@ export function McpDeploy({
onAddedToServer,
onSubmittingChange,
onCanSaveChange,
onHasServersChange,
}: McpDeployProps) {
const params = useParams()
const workspaceId = params.workspaceId as string
@@ -247,6 +257,10 @@ export function McpDeploy({
onCanSaveChange?.(hasChanges && hasDeployedTools && !!toolName.trim())
}, [hasChanges, hasDeployedTools, toolName, onCanSaveChange])
useEffect(() => {
onHasServersChange?.(servers.length > 0)
}, [servers.length, onHasServersChange])
/**
* Save tool configuration to all deployed servers
*/
@@ -428,14 +442,16 @@ export function McpDeploy({
if (servers.length === 0) {
return (
<div className='flex h-full items-center justify-center text-[13px] text-[var(--text-muted)]'>
<button
type='button'
<div className='flex h-full flex-col items-center justify-center gap-3'>
<p className='text-[13px] text-[var(--text-muted)]'>
Create an MCP Server in Settings Deployed MCPs first.
</p>
<Button
variant='tertiary'
onClick={() => openSettingsModal({ section: 'workflow-mcp-servers' })}
className='transition-colors hover:text-[var(--text-secondary)]'
>
Create an MCP Server in Settings MCP Servers first.
</button>
Create MCP Server
</Button>
</div>
)
}

View File

@@ -19,6 +19,7 @@ import { getEnv } from '@/lib/core/config/env'
import { getInputFormatExample as getInputFormatExampleUtil } from '@/lib/workflows/operations/deployment-utils'
import type { WorkflowDeploymentVersionResponse } from '@/lib/workflows/persistence/utils'
import { startsWithUuid } from '@/executor/constants'
import { useSettingsModalStore } from '@/stores/settings-modal/store'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
import type { WorkflowState } from '@/stores/workflows/workflow/types'
@@ -62,6 +63,7 @@ export function DeployModal({
isLoadingDeployedState,
refetchDeployedState,
}: DeployModalProps) {
const openSettingsModal = useSettingsModalStore((state) => state.openModal)
const deploymentStatus = useWorkflowRegistry((state) =>
state.getWorkflowDeploymentStatus(workflowId)
)
@@ -89,6 +91,7 @@ export function DeployModal({
const [templateSubmitting, setTemplateSubmitting] = useState(false)
const [mcpToolSubmitting, setMcpToolSubmitting] = useState(false)
const [mcpToolCanSave, setMcpToolCanSave] = useState(false)
const [hasMcpServers, setHasMcpServers] = useState(false)
const [hasExistingTemplate, setHasExistingTemplate] = useState(false)
const [templateStatus, setTemplateStatus] = useState<{
status: 'pending' | 'approved' | 'rejected' | null
@@ -627,6 +630,7 @@ export function DeployModal({
isDeployed={isDeployed}
onSubmittingChange={setMcpToolSubmitting}
onCanSaveChange={setMcpToolCanSave}
onHasServersChange={setHasMcpServers}
/>
)}
</ModalTabsContent>
@@ -674,16 +678,25 @@ export function DeployModal({
</div>
</ModalFooter>
)}
{activeTab === 'mcp' && isDeployed && (
{activeTab === 'mcp' && isDeployed && hasMcpServers && (
<ModalFooter className='items-center'>
<Button
type='button'
variant='tertiary'
onClick={handleMcpToolFormSubmit}
disabled={mcpToolSubmitting || !mcpToolCanSave}
>
{mcpToolSubmitting ? 'Saving...' : 'Save Tool Schema'}
</Button>
<div className='flex gap-2'>
<Button
type='button'
variant='default'
onClick={() => openSettingsModal({ section: 'workflow-mcp-servers' })}
>
Manage
</Button>
<Button
type='button'
variant='tertiary'
onClick={handleMcpToolFormSubmit}
disabled={mcpToolSubmitting || !mcpToolCanSave}
>
{mcpToolSubmitting ? 'Saving...' : 'Save Tool Schema'}
</Button>
</div>
</ModalFooter>
)}
{activeTab === 'template' && (

View File

@@ -1,6 +1,6 @@
'use client'
import { useCallback, useState } from 'react'
import { useState } from 'react'
import { Loader2 } from 'lucide-react'
import { Button, Tooltip } from '@/components/emcn'
import { DeployModal } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/deploy-modal'
@@ -62,26 +62,13 @@ export function Deploy({ activeWorkflowId, userPermissions, className }: DeployP
const canDeploy = userPermissions.canAdmin
const isDisabled = isDeploying || !canDeploy || isEmpty
/**
* Handle deploy button click
*/
const onDeployClick = useCallback(async () => {
const onDeployClick = async () => {
if (!canDeploy || !activeWorkflowId) return
const result = await handleDeployClick()
if (result.shouldOpenModal) {
setIsModalOpen(true)
}
}, [canDeploy, activeWorkflowId, handleDeployClick])
const refetchWithErrorHandling = async () => {
if (!activeWorkflowId) return
try {
await refetchDeployedState()
} catch (error) {
// Error already logged in hook
}
}
/**
@@ -135,7 +122,7 @@ export function Deploy({ activeWorkflowId, userPermissions, className }: DeployP
needsRedeployment={changeDetected}
deployedState={deployedState!}
isLoadingDeployedState={isLoadingDeployedState}
refetchDeployedState={refetchWithErrorHandling}
refetchDeployedState={refetchDeployedState}
/>
</>
)

View File

@@ -1,6 +1,5 @@
import { useMemo } from 'react'
import { hasWorkflowChanged } from '@/lib/workflows/comparison'
import { useDebounce } from '@/hooks/use-debounce'
import { useVariablesStore } from '@/stores/panel/variables/store'
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
@@ -48,16 +47,12 @@ export function useChangeDetection({
const blockSubValues = subBlockValues?.[blockId] || {}
const subBlocks: Record<string, any> = {}
for (const [subId, value] of Object.entries(blockSubValues)) {
subBlocks[subId] = { value }
}
if (block.subBlocks) {
for (const [subId, subBlock] of Object.entries(block.subBlocks)) {
if (!subBlocks[subId]) {
subBlocks[subId] = subBlock
} else {
subBlocks[subId] = { ...subBlock, value: subBlocks[subId].value }
const storedValue = blockSubValues[subId]
subBlocks[subId] = {
...subBlock,
value: storedValue !== undefined ? storedValue : subBlock.value,
}
}
}
@@ -77,14 +72,12 @@ export function useChangeDetection({
} as WorkflowState & { variables: Record<string, any> }
}, [workflowId, blocks, edges, loops, parallels, subBlockValues, workflowVariables])
const rawChangeDetected = useMemo(() => {
const changeDetected = useMemo(() => {
if (!currentState || !deployedState || isLoadingDeployedState) {
return false
}
return hasWorkflowChanged(currentState, deployedState)
}, [currentState, deployedState, isLoadingDeployedState])
const changeDetected = useDebounce(rawChangeDetected, 300)
return { changeDetected }
}

View File

@@ -1,4 +1,4 @@
import { useEffect, useState } from 'react'
import { useCallback, useEffect, useState } from 'react'
import { createLogger } from '@sim/logger'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import type { WorkflowState } from '@/stores/workflows/workflow/types'
@@ -27,29 +27,27 @@ export function useDeployedState({
(state) => state.setWorkflowNeedsRedeployment
)
/**
* Fetches the deployed state of the workflow from the server
* This is the single source of truth for deployed workflow state
*/
const fetchDeployedState = async () => {
if (!workflowId || !isDeployed) {
const fetchDeployedState = useCallback(async () => {
const registry = useWorkflowRegistry.getState()
const currentWorkflowId = registry.activeWorkflowId
const deploymentStatus = currentWorkflowId
? registry.getWorkflowDeploymentStatus(currentWorkflowId)
: null
const currentIsDeployed = deploymentStatus?.isDeployed ?? false
if (!currentWorkflowId || !currentIsDeployed) {
setDeployedState(null)
return
}
// Store the workflow ID at the start of the request to prevent race conditions
const requestWorkflowId = workflowId
// Helper to get current active workflow ID for race condition checks
const getCurrentActiveWorkflowId = () => useWorkflowRegistry.getState().activeWorkflowId
const requestWorkflowId = currentWorkflowId
try {
setIsLoadingDeployedState(true)
const response = await fetch(`/api/workflows/${requestWorkflowId}/deployed`)
// Check if the workflow ID changed during the request (user navigated away)
if (requestWorkflowId !== getCurrentActiveWorkflowId()) {
if (requestWorkflowId !== useWorkflowRegistry.getState().activeWorkflowId) {
logger.debug('Workflow changed during deployed state fetch, ignoring response')
return
}
@@ -64,22 +62,22 @@ export function useDeployedState({
const data = await response.json()
if (requestWorkflowId === getCurrentActiveWorkflowId()) {
if (requestWorkflowId === useWorkflowRegistry.getState().activeWorkflowId) {
setDeployedState(data.deployedState || null)
} else {
logger.debug('Workflow changed after deployed state response, ignoring result')
}
} catch (error) {
logger.error('Error fetching deployed state:', { error })
if (requestWorkflowId === getCurrentActiveWorkflowId()) {
if (requestWorkflowId === useWorkflowRegistry.getState().activeWorkflowId) {
setDeployedState(null)
}
} finally {
if (requestWorkflowId === getCurrentActiveWorkflowId()) {
if (requestWorkflowId === useWorkflowRegistry.getState().activeWorkflowId) {
setIsLoadingDeployedState(false)
}
}
}
}, [])
useEffect(() => {
if (!workflowId) {

View File

@@ -6,6 +6,7 @@ import { X } from 'lucide-react'
import { useParams } from 'next/navigation'
import { Button, Combobox } from '@/components/emcn/components'
import { Progress } from '@/components/ui/progress'
import { cn } from '@/lib/core/utils/cn'
import type { WorkspaceFileRecord } from '@/lib/uploads/contexts/workspace'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
@@ -429,14 +430,14 @@ export function FileUpload({
<Button
type='button'
variant='ghost'
className='h-6 w-6 shrink-0 p-0'
className='h-5 w-5 shrink-0 p-0'
onClick={(e) => handleRemoveFile(file, e)}
disabled={isDeleting}
>
{isDeleting ? (
<div className='h-4 w-4 animate-spin rounded-full border-[1.5px] border-current border-t-transparent' />
<div className='h-3.5 w-3.5 animate-spin rounded-full border-[1.5px] border-current border-t-transparent' />
) : (
<X className='h-4 w-4' />
<X className='h-3.5 w-3.5' />
)}
</Button>
</div>
@@ -453,8 +454,8 @@ export function FileUpload({
<span className='text-[var(--text-primary)]'>{file.name}</span>
<span className='ml-2 text-[var(--text-muted)]'>({formatFileSize(file.size)})</span>
</div>
<div className='flex h-8 w-8 shrink-0 items-center justify-center'>
<div className='h-4 w-4 animate-spin rounded-full border-[1.5px] border-current border-t-transparent' />
<div className='flex h-5 w-5 shrink-0 items-center justify-center'>
<div className='h-3.5 w-3.5 animate-spin rounded-full border-[1.5px] border-current border-t-transparent' />
</div>
</div>
)
@@ -512,72 +513,66 @@ export function FileUpload({
{/* Error message */}
{uploadError && <div className='mb-2 text-red-600 text-sm'>{uploadError}</div>}
<div>
{/* File list with consistent spacing */}
{(hasFiles || isUploading) && (
<div className='mb-2 space-y-2'>
{/* Only show files that aren't currently uploading */}
{filesArray.map((file) => {
const isCurrentlyUploading = uploadingFiles.some(
(uploadingFile) => uploadingFile.name === file.name
)
return !isCurrentlyUploading && renderFileItem(file)
})}
{isUploading && (
<>
{uploadingFiles.map(renderUploadingItem)}
<div className='mt-1'>
<Progress
value={uploadProgress}
className='h-2 w-full'
indicatorClassName='bg-foreground'
/>
<div className='mt-1 text-center text-muted-foreground text-xs'>
{uploadProgress < 100 ? 'Uploading...' : 'Upload complete!'}
</div>
{/* File list with consistent spacing */}
{(hasFiles || isUploading) && (
<div className={cn('space-y-2', multiple && 'mb-2')}>
{/* Only show files that aren't currently uploading */}
{filesArray.map((file) => {
const isCurrentlyUploading = uploadingFiles.some(
(uploadingFile) => uploadingFile.name === file.name
)
return !isCurrentlyUploading && renderFileItem(file)
})}
{isUploading && (
<>
{uploadingFiles.map(renderUploadingItem)}
<div className='mt-1'>
<Progress
value={uploadProgress}
className='h-2 w-full'
indicatorClassName='bg-foreground'
/>
<div className='mt-1 text-center text-muted-foreground text-xs'>
{uploadProgress < 100 ? 'Uploading...' : 'Upload complete!'}
</div>
</>
)}
</div>
)}
</div>
</>
)}
</div>
)}
{/* Add More dropdown for multiple files */}
{hasFiles && multiple && !isUploading && (
<div>
<Combobox
options={comboboxOptions}
value={inputValue}
onChange={handleComboboxChange}
onOpenChange={(open) => {
if (open) void loadWorkspaceFiles()
}}
placeholder={loadingWorkspaceFiles ? 'Loading files...' : '+ Add More'}
disabled={disabled || loadingWorkspaceFiles}
editable={true}
filterOptions={true}
isLoading={loadingWorkspaceFiles}
/>
</div>
)}
</div>
{/* Add More dropdown for multiple files */}
{hasFiles && multiple && !isUploading && (
<Combobox
options={comboboxOptions}
value={inputValue}
onChange={handleComboboxChange}
onOpenChange={(open) => {
if (open) void loadWorkspaceFiles()
}}
placeholder={loadingWorkspaceFiles ? 'Loading files...' : '+ Add More'}
disabled={disabled || loadingWorkspaceFiles}
editable={true}
filterOptions={true}
isLoading={loadingWorkspaceFiles}
/>
)}
{/* Show dropdown selector if no files and not uploading */}
{!hasFiles && !isUploading && (
<div className='flex items-center'>
<Combobox
options={comboboxOptions}
value={inputValue}
onChange={handleComboboxChange}
onOpenChange={(open) => {
if (open) void loadWorkspaceFiles()
}}
placeholder={loadingWorkspaceFiles ? 'Loading files...' : 'Select or upload file'}
disabled={disabled || loadingWorkspaceFiles}
editable={true}
filterOptions={true}
isLoading={loadingWorkspaceFiles}
/>
</div>
<Combobox
options={comboboxOptions}
value={inputValue}
onChange={handleComboboxChange}
onOpenChange={(open) => {
if (open) void loadWorkspaceFiles()
}}
placeholder={loadingWorkspaceFiles ? 'Loading files...' : 'Select or upload file'}
disabled={disabled || loadingWorkspaceFiles}
editable={true}
filterOptions={true}
isLoading={loadingWorkspaceFiles}
/>
)}
</div>
)

View File

@@ -256,24 +256,13 @@ export function InputMapping({
if (!selectedWorkflowId) {
return (
<div className='flex flex-col items-center justify-center rounded-[4px] border border-[var(--border-1)] bg-[var(--surface-3)] p-8 text-center dark:bg-[#1F1F1F]'>
<svg
className='mb-3 h-10 w-10 text-[var(--text-tertiary)]'
fill='none'
viewBox='0 0 24 24'
stroke='currentColor'
>
<path
strokeLinecap='round'
strokeLinejoin='round'
strokeWidth={1.5}
d='M13 10V3L4 14h7v7l9-11h-7z'
/>
</svg>
<p className='font-medium text-[var(--text-tertiary)] text-sm'>No workflow selected</p>
<p className='mt-1 text-[var(--text-tertiary)]/80 text-xs'>
Select a workflow above to configure inputs
</p>
<div className='flex h-32 items-center justify-center rounded-[4px] border border-[var(--border-1)] border-dashed bg-[var(--surface-3)] dark:bg-[#1F1F1F]'>
<div className='text-center'>
<p className='font-medium text-[var(--text-secondary)] text-sm'>No workflow selected</p>
<p className='mt-1 text-[var(--text-muted)] text-xs'>
Select a workflow above to configure inputs
</p>
</div>
</div>
)
}

View File

@@ -95,7 +95,9 @@ export function FieldFormat({
}: FieldFormatProps) {
const [storeValue, setStoreValue] = useSubBlockValue<Field[]>(blockId, subBlockId)
const valueInputRefs = useRef<Record<string, HTMLInputElement | HTMLTextAreaElement>>({})
const nameInputRefs = useRef<Record<string, HTMLInputElement>>({})
const overlayRefs = useRef<Record<string, HTMLDivElement>>({})
const nameOverlayRefs = useRef<Record<string, HTMLDivElement>>({})
const accessiblePrefixes = useAccessibleReferencePrefixes(blockId)
const inputController = useSubBlockInput({
@@ -158,6 +160,97 @@ export function FieldFormat({
if (overlay) overlay.scrollLeft = scrollLeft
}
/**
* Syncs scroll position between name input and overlay for text highlighting
*/
const syncNameOverlayScroll = (fieldId: string, scrollLeft: number) => {
const overlay = nameOverlayRefs.current[fieldId]
if (overlay) overlay.scrollLeft = scrollLeft
}
/**
* Generates a unique field key for name inputs to avoid collision with value inputs
*/
const getNameFieldKey = (fieldId: string) => `name-${fieldId}`
/**
* Renders the name input field with tag dropdown support
*/
const renderNameInput = (field: Field) => {
const nameFieldKey = getNameFieldKey(field.id)
const fieldValue = field.name ?? ''
const fieldState = inputController.fieldHelpers.getFieldState(nameFieldKey)
const handlers = inputController.fieldHelpers.createFieldHandlers(
nameFieldKey,
fieldValue,
(newValue) => updateField(field.id, 'name', newValue)
)
const tagSelectHandler = inputController.fieldHelpers.createTagSelectHandler(
nameFieldKey,
fieldValue,
(newValue) => updateField(field.id, 'name', newValue)
)
const inputClassName = cn('text-transparent caret-foreground')
return (
<>
<Input
ref={(el) => {
if (el) nameInputRefs.current[field.id] = el
}}
name='name'
value={fieldValue}
onChange={handlers.onChange}
onKeyDown={handlers.onKeyDown}
onDrop={handlers.onDrop}
onDragOver={handlers.onDragOver}
onScroll={(e) => syncNameOverlayScroll(field.id, e.currentTarget.scrollLeft)}
onPaste={() =>
setTimeout(() => {
const input = nameInputRefs.current[field.id]
input && syncNameOverlayScroll(field.id, input.scrollLeft)
}, 0)
}
placeholder={placeholder}
disabled={isReadOnly}
autoComplete='off'
className={cn('allow-scroll w-full overflow-auto', inputClassName)}
style={{ overflowX: 'auto' }}
/>
<div
ref={(el) => {
if (el) nameOverlayRefs.current[field.id] = el
}}
className='pointer-events-none absolute inset-0 flex items-center overflow-x-auto bg-transparent px-[8px] py-[6px] font-medium font-sans text-sm'
style={{ overflowX: 'auto' }}
>
<div
className='w-full whitespace-pre'
style={{ scrollbarWidth: 'none', minWidth: 'fit-content' }}
>
{formatDisplayText(
fieldValue,
accessiblePrefixes ? { accessiblePrefixes } : { highlightAll: true }
)}
</div>
</div>
{fieldState.showTags && (
<TagDropdown
visible={fieldState.showTags}
onSelect={tagSelectHandler}
blockId={blockId}
activeSourceBlockId={fieldState.activeSourceBlockId}
inputValue={fieldValue}
cursorPosition={fieldState.cursorPosition}
onClose={() => inputController.fieldHelpers.hideFieldDropdowns(nameFieldKey)}
inputRef={{ current: nameInputRefs.current[field.id] || null }}
/>
)}
</>
)
}
/**
* Renders the field header with name, type badge, and action buttons
*/
@@ -417,14 +510,7 @@ export function FieldFormat({
<div className='flex flex-col gap-[8px] border-[var(--border-1)] border-t px-[10px] pt-[6px] pb-[10px]'>
<div className='flex flex-col gap-[6px]'>
<Label className='text-[13px]'>Name</Label>
<Input
name='name'
value={field.name}
onChange={(e) => updateField(field.id, 'name', e.target.value)}
placeholder={placeholder}
disabled={isReadOnly}
autoComplete='off'
/>
<div className='relative'>{renderNameInput(field)}</div>
</div>
{showType && (

View File

@@ -50,6 +50,7 @@ import {
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/custom-tool-modal/custom-tool-modal'
import { ToolCredentialSelector } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/tool-credential-selector'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value'
import { useChildDeployment } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/hooks/use-child-deployment'
import { getAllBlocks } from '@/blocks'
import {
type CustomTool as CustomToolDefinition,
@@ -582,6 +583,8 @@ function WorkflowSelectorSyncWrapper({
onChange={onChange}
placeholder={uiComponent.placeholder || 'Select workflow'}
disabled={disabled || isLoading}
searchable
searchPlaceholder='Search workflows...'
/>
)
}
@@ -752,6 +755,81 @@ function CodeEditorSyncWrapper({
)
}
/**
* Badge component showing deployment status for workflow tools
*/
function WorkflowToolDeployBadge({
workflowId,
onDeploySuccess,
}: {
workflowId: string
onDeploySuccess?: () => void
}) {
const { isDeployed, needsRedeploy, isLoading, refetch } = useChildDeployment(workflowId)
const [isDeploying, setIsDeploying] = useState(false)
const deployWorkflow = useCallback(async () => {
if (isDeploying || !workflowId) return
try {
setIsDeploying(true)
const response = await fetch(`/api/workflows/${workflowId}/deploy`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
deployChatEnabled: false,
}),
})
if (response.ok) {
refetch()
onDeploySuccess?.()
} else {
logger.error('Failed to deploy workflow')
}
} catch (error) {
logger.error('Error deploying workflow:', error)
} finally {
setIsDeploying(false)
}
}, [isDeploying, workflowId, refetch, onDeploySuccess])
if (isLoading || (isDeployed && !needsRedeploy)) {
return null
}
if (typeof isDeployed !== 'boolean') {
return null
}
return (
<Tooltip.Root>
<Tooltip.Trigger asChild>
<Badge
variant={!isDeployed ? 'red' : 'amber'}
className='cursor-pointer'
size='sm'
dot
onClick={(e: React.MouseEvent) => {
e.stopPropagation()
e.preventDefault()
if (!isDeploying) {
deployWorkflow()
}
}}
>
{isDeploying ? 'Deploying...' : !isDeployed ? 'undeployed' : 'redeploy'}
</Badge>
</Tooltip.Trigger>
<Tooltip.Content>
<span className='text-sm'>{!isDeployed ? 'Click to deploy' : 'Click to redeploy'}</span>
</Tooltip.Content>
</Tooltip.Root>
)
}
/**
* Set of built-in tool types that are core platform tools.
*
@@ -2219,10 +2297,15 @@ export function ToolInput({
{getIssueBadgeLabel(issue)}
</Badge>
</Tooltip.Trigger>
<Tooltip.Content>{issue.message}: click to open settings</Tooltip.Content>
<Tooltip.Content>
<span className='text-sm'>{issue.message}: click to open settings</span>
</Tooltip.Content>
</Tooltip.Root>
)
})()}
{tool.type === 'workflow' && tool.params?.workflowId && (
<WorkflowToolDeployBadge workflowId={tool.params.workflowId} />
)}
</div>
<div className='flex flex-shrink-0 items-center gap-[8px]'>
{supportsToolControl && !(isMcpTool && isMcpToolUnavailable(tool)) && (

View File

@@ -361,9 +361,9 @@ export function TriggerSave({
onClick={handleSave}
disabled={disabled || isProcessing}
className={cn(
'h-[32px] flex-1 rounded-[8px] px-[12px] transition-all duration-200',
saveStatus === 'saved' && 'bg-green-600 hover:bg-green-700',
saveStatus === 'error' && 'bg-red-600 hover:bg-red-700'
'flex-1',
saveStatus === 'saved' && '!bg-green-600 !text-white hover:!bg-green-700',
saveStatus === 'error' && '!bg-red-600 !text-white hover:!bg-red-700'
)}
>
{saveStatus === 'saving' && 'Saving...'}
@@ -373,12 +373,7 @@ export function TriggerSave({
</Button>
{webhookId && (
<Button
variant='default'
onClick={handleDeleteClick}
disabled={disabled || isProcessing}
className='h-[32px] rounded-[8px] px-[12px]'
>
<Button variant='default' onClick={handleDeleteClick} disabled={disabled || isProcessing}>
<Trash className='h-[14px] w-[14px]' />
</Button>
)}

View File

@@ -911,7 +911,7 @@ export const WorkflowBlock = memo(function WorkflowBlock({
type,
config.category,
displayTriggerMode,
subBlockRows.length,
subBlockRows.reduce((acc, row) => acc + row.length, 0),
conditionRows.length,
routerRows.length,
horizontalHandles,

View File

@@ -169,6 +169,7 @@ export function useWand({
systemPrompt: systemPrompt,
stream: true,
history: wandConfig?.maintainHistory ? conversationHistory : [],
generationType: wandConfig?.generationType,
}),
signal: abortControllerRef.current.signal,
cache: 'no-store',

View File

@@ -19,7 +19,6 @@ import { useShallow } from 'zustand/react/shallow'
import { useSession } from '@/lib/auth/auth-client'
import type { OAuthConnectEventDetail } from '@/lib/copilot/tools/client/other/oauth-request-access'
import type { OAuthProvider } from '@/lib/oauth'
import { DEFAULT_HORIZONTAL_SPACING } from '@/lib/workflows/autolayout/constants'
import { BLOCK_DIMENSIONS, CONTAINER_DIMENSIONS } from '@/lib/workflows/blocks/block-dimensions'
import { TriggerUtils } from '@/lib/workflows/triggers/triggers'
import { useWorkspacePermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
@@ -89,6 +88,26 @@ const logger = createLogger('Workflow')
const DEFAULT_PASTE_OFFSET = { x: 50, y: 50 }
/**
* Gets the center of the current viewport in flow coordinates
*/
function getViewportCenter(
screenToFlowPosition: (pos: { x: number; y: number }) => { x: number; y: number }
): { x: number; y: number } {
const flowContainer = document.querySelector('.react-flow')
if (!flowContainer) {
return screenToFlowPosition({
x: window.innerWidth / 2,
y: window.innerHeight / 2,
})
}
const rect = flowContainer.getBoundingClientRect()
return screenToFlowPosition({
x: rect.width / 2,
y: rect.height / 2,
})
}
/**
* Calculates the offset to paste blocks at viewport center
*/
@@ -125,14 +144,7 @@ function calculatePasteOffset(
)
const clipboardCenter = { x: (minX + maxX) / 2, y: (minY + maxY) / 2 }
const flowContainer = document.querySelector('.react-flow')
if (!flowContainer) return DEFAULT_PASTE_OFFSET
const rect = flowContainer.getBoundingClientRect()
const viewportCenter = screenToFlowPosition({
x: rect.width / 2,
y: rect.height / 2,
})
const viewportCenter = getViewportCenter(screenToFlowPosition)
return {
x: viewportCenter.x - clipboardCenter.x,
@@ -257,6 +269,10 @@ const WorkflowContent = React.memo(() => {
const snapToGridSize = useGeneralStore((state) => state.snapToGridSize)
const snapToGrid = snapToGridSize > 0
// Panel open states for context menu
const isVariablesOpen = useVariablesStore((state) => state.isOpen)
const isChatOpen = useChatStore((state) => state.isChatOpen)
const snapGrid: [number, number] = useMemo(
() => [snapToGridSize, snapToGridSize],
[snapToGridSize]
@@ -754,12 +770,14 @@ const WorkflowContent = React.memo(() => {
router.push(`/workspace/${workspaceId}/logs?workflowIds=${workflowIdParam}`)
}, [router, workspaceId, workflowIdParam])
const handleContextOpenVariables = useCallback(() => {
useVariablesStore.getState().setIsOpen(true)
const handleContextToggleVariables = useCallback(() => {
const { isOpen, setIsOpen } = useVariablesStore.getState()
setIsOpen(!isOpen)
}, [])
const handleContextOpenChat = useCallback(() => {
useChatStore.getState().setIsChatOpen(true)
const handleContextToggleChat = useCallback(() => {
const { isChatOpen, setIsChatOpen } = useChatStore.getState()
setIsChatOpen(!isChatOpen)
}, [])
const handleContextInvite = useCallback(() => {
@@ -1312,65 +1330,13 @@ const WorkflowContent = React.memo(() => {
if (!type) return
if (type === 'connectionBlock') return
// Calculate smart position - to the right of existing root-level blocks
const calculateSmartPosition = (): { x: number; y: number } => {
// Get all root-level blocks (no parentId)
const rootBlocks = Object.values(blocks).filter((b) => !b.data?.parentId)
const basePosition = getViewportCenter(screenToFlowPosition)
if (rootBlocks.length === 0) {
// No blocks yet, use viewport center
return screenToFlowPosition({
x: window.innerWidth / 2,
y: window.innerHeight / 2,
})
}
// Find the rightmost block
let maxRight = Number.NEGATIVE_INFINITY
let rightmostBlockY = 0
for (const block of rootBlocks) {
const blockWidth =
block.type === 'loop' || block.type === 'parallel'
? block.data?.width || CONTAINER_DIMENSIONS.DEFAULT_WIDTH
: BLOCK_DIMENSIONS.FIXED_WIDTH
const blockRight = block.position.x + blockWidth
if (blockRight > maxRight) {
maxRight = blockRight
rightmostBlockY = block.position.y
}
}
// Position to the right with autolayout spacing
const position = {
x: maxRight + DEFAULT_HORIZONTAL_SPACING,
y: rightmostBlockY,
}
// Ensure position doesn't overlap any container
let container = isPointInLoopNode(position)
while (container) {
position.x =
container.loopPosition.x + container.dimensions.width + DEFAULT_HORIZONTAL_SPACING
container = isPointInLoopNode(position)
}
return position
}
const basePosition = calculateSmartPosition()
// Special handling for container nodes (loop or parallel)
if (type === 'loop' || type === 'parallel') {
const id = crypto.randomUUID()
const baseName = type === 'loop' ? 'Loop' : 'Parallel'
const name = getUniqueBlockName(baseName, blocks)
const autoConnectEdge = tryCreateAutoConnectEdge(basePosition, id, {
blockType: type,
targetParentId: null,
})
// Add the container node with default dimensions and auto-connect edge
addBlock(
id,
type,
@@ -1383,7 +1349,7 @@ const WorkflowContent = React.memo(() => {
},
undefined,
undefined,
autoConnectEdge
undefined
)
return
@@ -1395,24 +1361,13 @@ const WorkflowContent = React.memo(() => {
return
}
// Check trigger constraints first
if (checkTriggerConstraints(type)) return
// Create a new block with a unique ID
const id = crypto.randomUUID()
// Prefer semantic default names for triggers; then ensure unique numbering centrally
const defaultTriggerName = TriggerUtils.getDefaultTriggerName(type)
const baseName = defaultTriggerName || blockConfig.name
const name = getUniqueBlockName(baseName, blocks)
const autoConnectEdge = tryCreateAutoConnectEdge(basePosition, id, {
blockType: type,
enableTriggerMode,
targetParentId: null,
})
// Add the block to the workflow with auto-connect edge
// Enable trigger mode if this is a trigger-capable block from the triggers tab
addBlock(
id,
type,
@@ -1421,7 +1376,7 @@ const WorkflowContent = React.memo(() => {
undefined,
undefined,
undefined,
autoConnectEdge,
undefined,
enableTriggerMode
)
}
@@ -1438,11 +1393,7 @@ const WorkflowContent = React.memo(() => {
screenToFlowPosition,
blocks,
addBlock,
tryCreateAutoConnectEdge,
isPointInLoopNode,
effectivePermissions.canEdit,
addNotification,
activeWorkflowId,
checkTriggerConstraints,
])
@@ -1947,11 +1898,26 @@ const WorkflowContent = React.memo(() => {
const handleKeyUp = (e: KeyboardEvent) => {
if (e.key === 'Shift') setIsShiftPressed(false)
}
const handleFocusLoss = () => {
setIsShiftPressed(false)
setIsSelectionDragActive(false)
}
const handleVisibilityChange = () => {
if (document.hidden) {
handleFocusLoss()
}
}
window.addEventListener('keydown', handleKeyDown)
window.addEventListener('keyup', handleKeyUp)
window.addEventListener('blur', handleFocusLoss)
document.addEventListener('visibilitychange', handleVisibilityChange)
return () => {
window.removeEventListener('keydown', handleKeyDown)
window.removeEventListener('keyup', handleKeyUp)
window.removeEventListener('blur', handleFocusLoss)
document.removeEventListener('visibilitychange', handleVisibilityChange)
}
}, [])
@@ -2904,9 +2870,11 @@ const WorkflowContent = React.memo(() => {
onAddBlock={handleContextAddBlock}
onAutoLayout={handleAutoLayout}
onOpenLogs={handleContextOpenLogs}
onOpenVariables={handleContextOpenVariables}
onOpenChat={handleContextOpenChat}
onToggleVariables={handleContextToggleVariables}
onToggleChat={handleContextToggleChat}
onInvite={handleContextInvite}
isVariablesOpen={isVariablesOpen}
isChatOpen={isChatOpen}
hasClipboard={hasClipboard()}
disableEdit={!effectivePermissions.canEdit}
disableAdmin={!effectivePermissions.canAdmin}

View File

@@ -480,7 +480,7 @@ export function General({ onOpenChange }: GeneralProps) {
</div>
<div className='flex items-center justify-between'>
<Label htmlFor='auto-connect'>Auto-connect on drop</Label>
<Label htmlFor='auto-connect'>Auto-connect on drag</Label>
<Switch
id='auto-connect'
checked={settings?.autoConnect ?? true}

View File

@@ -118,7 +118,7 @@ const allNavigationItems: NavigationItem[] = [
{ id: 'mcp', label: 'MCP Tools', icon: McpIcon, section: 'tools' },
{ id: 'environment', label: 'Environment', icon: FolderCode, section: 'system' },
{ id: 'apikeys', label: 'API Keys', icon: Key, section: 'system' },
{ id: 'workflow-mcp-servers', label: 'MCP Servers', icon: Server, section: 'system' },
{ id: 'workflow-mcp-servers', label: 'Deployed MCPs', icon: Server, section: 'system' },
{
id: 'byok',
label: 'BYOK',

View File

@@ -352,53 +352,6 @@ describe('Blocks Module', () => {
expect(typeof block?.tools.config?.tool).toBe('function')
})
})
describe('WebhookBlock', () => {
const block = getBlock('webhook')
it('should have correct metadata', () => {
expect(block?.type).toBe('webhook')
expect(block?.name).toBe('Webhook')
expect(block?.category).toBe('triggers')
expect(block?.authMode).toBe(AuthMode.OAuth)
expect(block?.triggerAllowed).toBe(true)
expect(block?.hideFromToolbar).toBe(true)
})
it('should have webhookProvider dropdown with multiple providers', () => {
const providerSubBlock = block?.subBlocks.find((sb) => sb.id === 'webhookProvider')
expect(providerSubBlock).toBeDefined()
expect(providerSubBlock?.type).toBe('dropdown')
const options = providerSubBlock?.options as Array<{ label: string; id: string }>
expect(options?.map((o) => o.id)).toContain('slack')
expect(options?.map((o) => o.id)).toContain('generic')
expect(options?.map((o) => o.id)).toContain('github')
})
it('should have conditional OAuth inputs', () => {
const gmailCredentialSubBlock = block?.subBlocks.find((sb) => sb.id === 'gmailCredential')
expect(gmailCredentialSubBlock).toBeDefined()
expect(gmailCredentialSubBlock?.type).toBe('oauth-input')
expect(gmailCredentialSubBlock?.condition).toEqual({
field: 'webhookProvider',
value: 'gmail',
})
const outlookCredentialSubBlock = block?.subBlocks.find(
(sb) => sb.id === 'outlookCredential'
)
expect(outlookCredentialSubBlock).toBeDefined()
expect(outlookCredentialSubBlock?.type).toBe('oauth-input')
expect(outlookCredentialSubBlock?.condition).toEqual({
field: 'webhookProvider',
value: 'outlook',
})
})
it('should have empty tools access', () => {
expect(block?.tools.access).toEqual([])
})
})
})
describe('SubBlock Validation', () => {
@@ -545,8 +498,8 @@ describe('Blocks Module', () => {
})
it('should handle blocks with triggerAllowed flag', () => {
const webhookBlock = getBlock('webhook')
expect(webhookBlock?.triggerAllowed).toBe(true)
const gmailBlock = getBlock('gmail')
expect(gmailBlock?.triggerAllowed).toBe(true)
const functionBlock = getBlock('function')
expect(functionBlock?.triggerAllowed).toBeUndefined()
@@ -663,16 +616,6 @@ describe('Blocks Module', () => {
expect(temperatureSubBlock?.min).toBe(0)
expect(temperatureSubBlock?.max).toBe(2)
})
it('should have required scopes on OAuth inputs', () => {
const webhookBlock = getBlock('webhook')
const gmailCredentialSubBlock = webhookBlock?.subBlocks.find(
(sb) => sb.id === 'gmailCredential'
)
expect(gmailCredentialSubBlock?.requiredScopes).toBeDefined()
expect(Array.isArray(gmailCredentialSubBlock?.requiredScopes)).toBe(true)
expect((gmailCredentialSubBlock?.requiredScopes?.length ?? 0) > 0).toBe(true)
})
})
describe('Block Consistency', () => {

View File

@@ -4,7 +4,7 @@ import { isHosted } from '@/lib/core/config/feature-flags'
import type { BlockConfig } from '@/blocks/types'
import { AuthMode } from '@/blocks/types'
import {
getAllModelProviders,
getBaseModelProviders,
getHostedModels,
getMaxTemperature,
getProviderIcon,
@@ -417,7 +417,7 @@ export const AgentBlock: BlockConfig<AgentResponse> = {
condition: () => ({
field: 'model',
value: (() => {
const allModels = Object.keys(getAllModelProviders())
const allModels = Object.keys(getBaseModelProviders())
return allModels.filter(
(model) => supportsTemperature(model) && getMaxTemperature(model) === 1
)
@@ -434,7 +434,7 @@ export const AgentBlock: BlockConfig<AgentResponse> = {
condition: () => ({
field: 'model',
value: (() => {
const allModels = Object.keys(getAllModelProviders())
const allModels = Object.keys(getBaseModelProviders())
return allModels.filter(
(model) => supportsTemperature(model) && getMaxTemperature(model) === 2
)
@@ -555,7 +555,7 @@ Example 3 (Array Input):
if (!model) {
throw new Error('No model selected')
}
const tool = getAllModelProviders()[model]
const tool = getBaseModelProviders()[model]
if (!tool) {
throw new Error(`Invalid model selected: ${model}`)
}

View File

@@ -46,6 +46,19 @@ export const AhrefsBlock: BlockConfig<AhrefsResponse> = {
type: 'short-input',
placeholder: 'YYYY-MM-DD (defaults to today)',
condition: { field: 'operation', value: 'ahrefs_domain_rating' },
wandConfig: {
enabled: true,
prompt: `Generate a date in YYYY-MM-DD format based on the user's description.
Examples:
- "today" -> Current date in YYYY-MM-DD format
- "yesterday" -> Yesterday's date in YYYY-MM-DD format
- "last week" -> Date 7 days ago in YYYY-MM-DD format
- "beginning of this month" -> First day of current month in YYYY-MM-DD format
Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the date (e.g., "yesterday", "last week", "start of month")...',
generationType: 'timestamp',
},
},
// Backlinks operation inputs
{
@@ -89,6 +102,19 @@ export const AhrefsBlock: BlockConfig<AhrefsResponse> = {
type: 'short-input',
placeholder: 'YYYY-MM-DD (defaults to today)',
condition: { field: 'operation', value: 'ahrefs_backlinks' },
wandConfig: {
enabled: true,
prompt: `Generate a date in YYYY-MM-DD format based on the user's description.
Examples:
- "today" -> Current date in YYYY-MM-DD format
- "yesterday" -> Yesterday's date in YYYY-MM-DD format
- "last week" -> Date 7 days ago in YYYY-MM-DD format
- "beginning of this month" -> First day of current month in YYYY-MM-DD format
Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the date (e.g., "yesterday", "last week", "start of month")...',
generationType: 'timestamp',
},
},
// Backlinks Stats operation inputs
{
@@ -118,6 +144,19 @@ export const AhrefsBlock: BlockConfig<AhrefsResponse> = {
type: 'short-input',
placeholder: 'YYYY-MM-DD (defaults to today)',
condition: { field: 'operation', value: 'ahrefs_backlinks_stats' },
wandConfig: {
enabled: true,
prompt: `Generate a date in YYYY-MM-DD format based on the user's description.
Examples:
- "today" -> Current date in YYYY-MM-DD format
- "yesterday" -> Yesterday's date in YYYY-MM-DD format
- "last week" -> Date 7 days ago in YYYY-MM-DD format
- "beginning of this month" -> First day of current month in YYYY-MM-DD format
Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the date (e.g., "yesterday", "last week", "start of month")...',
generationType: 'timestamp',
},
},
// Referring Domains operation inputs
{
@@ -161,6 +200,19 @@ export const AhrefsBlock: BlockConfig<AhrefsResponse> = {
type: 'short-input',
placeholder: 'YYYY-MM-DD (defaults to today)',
condition: { field: 'operation', value: 'ahrefs_referring_domains' },
wandConfig: {
enabled: true,
prompt: `Generate a date in YYYY-MM-DD format based on the user's description.
Examples:
- "today" -> Current date in YYYY-MM-DD format
- "yesterday" -> Yesterday's date in YYYY-MM-DD format
- "last week" -> Date 7 days ago in YYYY-MM-DD format
- "beginning of this month" -> First day of current month in YYYY-MM-DD format
Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the date (e.g., "yesterday", "last week", "start of month")...',
generationType: 'timestamp',
},
},
// Organic Keywords operation inputs
{
@@ -228,6 +280,19 @@ export const AhrefsBlock: BlockConfig<AhrefsResponse> = {
type: 'short-input',
placeholder: 'YYYY-MM-DD (defaults to today)',
condition: { field: 'operation', value: 'ahrefs_organic_keywords' },
wandConfig: {
enabled: true,
prompt: `Generate a date in YYYY-MM-DD format based on the user's description.
Examples:
- "today" -> Current date in YYYY-MM-DD format
- "yesterday" -> Yesterday's date in YYYY-MM-DD format
- "last week" -> Date 7 days ago in YYYY-MM-DD format
- "beginning of this month" -> First day of current month in YYYY-MM-DD format
Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the date (e.g., "yesterday", "last week", "start of month")...',
generationType: 'timestamp',
},
},
// Top Pages operation inputs
{
@@ -294,6 +359,19 @@ export const AhrefsBlock: BlockConfig<AhrefsResponse> = {
type: 'short-input',
placeholder: 'YYYY-MM-DD (defaults to today)',
condition: { field: 'operation', value: 'ahrefs_top_pages' },
wandConfig: {
enabled: true,
prompt: `Generate a date in YYYY-MM-DD format based on the user's description.
Examples:
- "today" -> Current date in YYYY-MM-DD format
- "yesterday" -> Yesterday's date in YYYY-MM-DD format
- "last week" -> Date 7 days ago in YYYY-MM-DD format
- "beginning of this month" -> First day of current month in YYYY-MM-DD format
Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the date (e.g., "yesterday", "last week", "start of month")...',
generationType: 'timestamp',
},
},
// Keyword Overview operation inputs
{
@@ -370,6 +448,19 @@ export const AhrefsBlock: BlockConfig<AhrefsResponse> = {
type: 'short-input',
placeholder: 'YYYY-MM-DD (defaults to today)',
condition: { field: 'operation', value: 'ahrefs_broken_backlinks' },
wandConfig: {
enabled: true,
prompt: `Generate a date in YYYY-MM-DD format based on the user's description.
Examples:
- "today" -> Current date in YYYY-MM-DD format
- "yesterday" -> Yesterday's date in YYYY-MM-DD format
- "last week" -> Date 7 days ago in YYYY-MM-DD format
- "beginning of this month" -> First day of current month in YYYY-MM-DD format
Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the date (e.g., "yesterday", "last week", "start of month")...',
generationType: 'timestamp',
},
},
// API Key (common to all operations)
{

View File

@@ -79,6 +79,37 @@ export const AirtableBlock: BlockConfig<AirtableResponse> = {
type: 'long-input',
placeholder: 'Airtable formula to filter records (optional)',
condition: { field: 'operation', value: 'list' },
wandConfig: {
enabled: true,
prompt: `Generate an Airtable filter formula based on the user's description.
Airtable formulas use a syntax similar to Excel/spreadsheet formulas.
Common functions:
- {Field Name} - Reference a field by name (with curly braces)
- AND(condition1, condition2) - Both conditions must be true
- OR(condition1, condition2) - Either condition can be true
- NOT(condition) - Negates the condition
- IF(condition, value_if_true, value_if_false)
- FIND("text", {Field}) - Find text in a field (returns position or 0)
- SEARCH("text", {Field}) - Case-insensitive search
- LEN({Field}) - Length of text
- DATETIME_DIFF(date1, date2, 'days') - Difference between dates
- TODAY() - Current date
- NOW() - Current date and time
- BLANK() - Empty value
- {Field} = "" - Check if field is empty
- {Field} != "" - Check if field is not empty
Examples:
- "find all completed tasks" -> {Status} = "Completed"
- "records from last 7 days" -> DATETIME_DIFF(NOW(), {Created}, 'days') <= 7
- "name contains John" -> FIND("John", {Name}) > 0
- "status is active or pending" -> OR({Status} = "Active", {Status} = "Pending")
- "priority is high and not assigned" -> AND({Priority} = "High", {Assignee} = "")
Return ONLY the formula - no explanations, no quotes around the entire formula.`,
placeholder: 'Describe the filter criteria (e.g., "completed tasks from last week")...',
},
},
{
id: 'records',
@@ -87,6 +118,44 @@ export const AirtableBlock: BlockConfig<AirtableResponse> = {
placeholder: 'For Create: `[{ "fields": { ... } }]`\n',
condition: { field: 'operation', value: ['create', 'updateMultiple'] },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate an Airtable records JSON array based on the user's description.
The array should contain objects with a "fields" property containing the record data.
Current records: {context}
Format:
[
{
"fields": {
"Field Name": "value",
"Another Field": "another value"
}
}
]
For updates, include the record ID:
[
{
"id": "recXXXXXXXXXXXXXX",
"fields": {
"Field Name": "updated value"
}
}
]
Examples:
- "add a task called 'Review PR' with status 'Pending'" ->
[{"fields": {"Name": "Review PR", "Status": "Pending"}}]
- "create 3 contacts: John, Jane, Bob" ->
[{"fields": {"Name": "John"}}, {"fields": {"Name": "Jane"}}, {"fields": {"Name": "Bob"}}]
Return ONLY the valid JSON array - no explanations, no markdown.`,
placeholder: 'Describe the records to create or update...',
generationType: 'json-object',
},
},
{
id: 'fields',
@@ -95,6 +164,32 @@ export const AirtableBlock: BlockConfig<AirtableResponse> = {
placeholder: 'Fields to update: `{ "Field Name": "New Value" }`',
condition: { field: 'operation', value: 'update' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate an Airtable fields JSON object based on the user's description.
The object should contain field names as keys and their values.
Current fields: {context}
Format:
{
"Field Name": "value",
"Another Field": "another value",
"Number Field": 123,
"Checkbox Field": true
}
Examples:
- "set status to completed and priority to low" ->
{"Status": "Completed", "Priority": "Low"}
- "update the name to 'New Project' and set the due date" ->
{"Name": "New Project", "Due Date": "2024-12-31"}
Return ONLY the valid JSON object - no explanations, no markdown.`,
placeholder: 'Describe the fields to update...',
generationType: 'json-object',
},
},
...getTrigger('airtable_webhook').subBlocks,
],

View File

@@ -46,6 +46,32 @@ export const ApifyBlock: BlockConfig<RunActorResult> = {
language: 'json',
placeholder: '{\n "startUrl": "https://example.com",\n "maxPages": 10\n}',
required: false,
wandConfig: {
enabled: true,
prompt: `Generate a JSON configuration object for an Apify actor based on the user's description.
Apify actors typically accept configuration for web scraping, automation, or data processing tasks.
Current input: {context}
Common Apify actor input patterns:
- Web scrapers: startUrls, maxPages, proxyConfiguration
- Crawlers: startUrls, maxRequestsPerCrawl, maxConcurrency
- Data processors: inputData, outputFormat, filters
Examples:
- "scrape 5 pages starting from example.com" ->
{"startUrls": [{"url": "https://example.com"}], "maxPages": 5}
- "crawl the site with proxy and limit to 100 requests" ->
{"startUrls": [{"url": "https://example.com"}], "maxRequestsPerCrawl": 100, "proxyConfiguration": {"useApifyProxy": true}}
- "extract product data with custom selectors" ->
{"startUrls": [{"url": "https://shop.example.com"}], "selectors": {"title": "h1.product-title", "price": ".price"}}
Return ONLY the valid JSON object - no explanations, no markdown.`,
placeholder: 'Describe the actor configuration you need...',
generationType: 'json-object',
},
},
{
id: 'timeout',

View File

@@ -421,6 +421,19 @@ export const ApolloBlock: BlockConfig<ApolloResponse> = {
field: 'operation',
value: ['opportunity_create', 'opportunity_update'],
},
wandConfig: {
enabled: true,
prompt: `Generate a date in YYYY-MM-DD format based on the user's description.
Examples:
- "end of this quarter" -> Calculate the last day of the current quarter in YYYY-MM-DD format
- "next month" -> Calculate 30 days from now in YYYY-MM-DD format
- "in 2 weeks" -> Calculate 14 days from now in YYYY-MM-DD format
- "end of year" -> December 31st of the current year in YYYY-MM-DD format
Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the date (e.g., "end of quarter", "in 2 weeks")...',
generationType: 'timestamp',
},
},
{
id: 'description',
@@ -517,6 +530,20 @@ export const ApolloBlock: BlockConfig<ApolloResponse> = {
type: 'short-input',
placeholder: 'ISO date (e.g., 2024-12-31T23:59:59Z)',
condition: { field: 'operation', value: 'task_create' },
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp based on the user's description.
The timestamp should be in the format: YYYY-MM-DDTHH:MM:SSZ (UTC timezone).
Examples:
- "tomorrow at 5pm" -> Calculate tomorrow's date at 17:00:00Z
- "end of day" -> Today's date at 23:59:59Z
- "next week" -> 7 days from now at 17:00:00Z
- "in 3 days" -> 3 days from now at 17:00:00Z
Return ONLY the timestamp string in ISO 8601 format - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the due date (e.g., "tomorrow at 5pm", "end of week")...',
generationType: 'timestamp',
},
},
{
id: 'completed',

View File

@@ -147,6 +147,19 @@ export const AsanaBlock: BlockConfig<AsanaResponse> = {
field: 'operation',
value: ['create_task', 'update_task'],
},
wandConfig: {
enabled: true,
prompt: `Generate a date in YYYY-MM-DD format based on the user's description.
Examples:
- "tomorrow" -> Calculate tomorrow's date in YYYY-MM-DD format
- "next Friday" -> Calculate the next Friday's date in YYYY-MM-DD format
- "in 3 days" -> Calculate 3 days from now in YYYY-MM-DD format
- "end of week" -> Calculate the upcoming Friday or Sunday in YYYY-MM-DD format
Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the due date (e.g., "tomorrow", "next Friday", "in 3 days")...',
generationType: 'timestamp',
},
},
{

View File

@@ -92,6 +92,20 @@ export const CalendlyBlock: BlockConfig<ToolResponse> = {
type: 'short-input',
placeholder: 'ISO 8601 format (e.g., 2024-01-01T00:00:00Z)',
condition: { field: 'operation', value: 'calendly_list_scheduled_events' },
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp based on the user's description.
The timestamp should be in the format: YYYY-MM-DDTHH:MM:SSZ (UTC timezone).
Examples:
- "today" -> Today's date at 00:00:00Z
- "beginning of this week" -> Monday of the current week at 00:00:00Z
- "start of month" -> First day of current month at 00:00:00Z
- "last week" -> 7 days ago at 00:00:00Z
Return ONLY the timestamp string in ISO 8601 format - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the start time (e.g., "today", "start of month")...',
generationType: 'timestamp',
},
},
{
id: 'max_start_time',
@@ -99,6 +113,20 @@ export const CalendlyBlock: BlockConfig<ToolResponse> = {
type: 'short-input',
placeholder: 'ISO 8601 format (e.g., 2024-12-31T23:59:59Z)',
condition: { field: 'operation', value: 'calendly_list_scheduled_events' },
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp based on the user's description.
The timestamp should be in the format: YYYY-MM-DDTHH:MM:SSZ (UTC timezone).
Examples:
- "end of today" -> Today's date at 23:59:59Z
- "end of this week" -> Sunday of the current week at 23:59:59Z
- "end of month" -> Last day of current month at 23:59:59Z
- "next week" -> 7 days from now at 23:59:59Z
Return ONLY the timestamp string in ISO 8601 format - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the end time (e.g., "end of week", "end of month")...',
generationType: 'timestamp',
},
},
{
id: 'status',

View File

@@ -30,6 +30,14 @@ export const ClayBlock: BlockConfig<ClayPopulateResponse> = {
JSON: Best for populating multiple columns.
Plain Text: Best for populating a table in free-form style.
`,
wandConfig: {
enabled: true,
prompt:
'Generate JSON data structure or plain text content based on the user description. For JSON, create a well-structured object or array with appropriate keys and sample values. Return ONLY the data content - no explanations, no extra formatting.',
placeholder:
'Describe the data structure you need (e.g., "array of contacts with name, email, and company")...',
generationType: 'json-object',
},
},
{
id: 'authToken',

View File

@@ -54,6 +54,19 @@ export const DatadogBlock: BlockConfig<DatadogResponse> = {
]`,
condition: { field: 'operation', value: 'datadog_submit_metrics' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a JSON array of Datadog metrics based on the user's description.
Each metric object should have:
- "metric": The metric name (e.g., "custom.app.response_time")
- "type": The metric type ("gauge", "count", or "rate")
- "points": Array of {timestamp, value} objects
- "tags": Array of tag strings (e.g., "env:production")
Return ONLY valid JSON - no explanations, no markdown code blocks.`,
placeholder: 'Describe the metrics you want to submit...',
generationType: 'json-object',
},
},
// ========================
@@ -66,6 +79,18 @@ export const DatadogBlock: BlockConfig<DatadogResponse> = {
placeholder: 'avg:system.cpu.user{*}',
condition: { field: 'operation', value: 'datadog_query_timeseries' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a Datadog metrics query based on the user's description.
The query format is: <aggregation>:<metric_name>{<tag_filters>}
Examples:
- "avg:system.cpu.user{*}" - Average CPU usage across all hosts
- "sum:app.requests{env:production} by {service}" - Sum of requests grouped by service
- "max:system.mem.used{host:webserver-1}" - Max memory on specific host
Return ONLY the query string - no explanations, no quotes around the entire query.`,
placeholder: 'Describe what metrics you want to query...',
},
},
{
id: 'from',
@@ -74,6 +99,19 @@ export const DatadogBlock: BlockConfig<DatadogResponse> = {
placeholder: 'e.g., 1701360000',
condition: { field: 'operation', value: 'datadog_query_timeseries' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a Unix timestamp (seconds since epoch) based on the user's description.
The timestamp should be a number representing seconds since January 1, 1970 UTC.
Examples:
- "yesterday" -> Calculate yesterday's date at 00:00:00 UTC as Unix timestamp
- "last week" -> Calculate 7 days ago at 00:00:00 UTC as Unix timestamp
- "1 hour ago" -> Calculate current time minus 3600 seconds
Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the start time (e.g., "1 hour ago", "yesterday")...',
generationType: 'timestamp',
},
},
{
id: 'to',
@@ -82,6 +120,19 @@ export const DatadogBlock: BlockConfig<DatadogResponse> = {
placeholder: 'e.g., 1701446400',
condition: { field: 'operation', value: 'datadog_query_timeseries' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a Unix timestamp (seconds since epoch) based on the user's description.
The timestamp should be a number representing seconds since January 1, 1970 UTC.
Examples:
- "now" -> Calculate current time as Unix timestamp
- "end of today" -> Calculate today at 23:59:59 UTC as Unix timestamp
- "tomorrow" -> Calculate tomorrow's date at 00:00:00 UTC as Unix timestamp
Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the end time (e.g., "now", "end of today")...',
generationType: 'timestamp',
},
},
// ========================
@@ -94,6 +145,15 @@ export const DatadogBlock: BlockConfig<DatadogResponse> = {
placeholder: 'Deployment completed',
condition: { field: 'operation', value: 'datadog_create_event' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a concise, descriptive event title for Datadog based on the user's description.
The title should be short (under 100 characters), clear, and action-oriented.
Examples: "Deployment completed", "High CPU usage detected", "Service restart initiated"
Return ONLY the title text - no quotes, no extra formatting.`,
placeholder: 'Describe the event you want to create...',
},
},
{
id: 'text',
@@ -102,6 +162,15 @@ export const DatadogBlock: BlockConfig<DatadogResponse> = {
placeholder: 'Describe the event...',
condition: { field: 'operation', value: 'datadog_create_event' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate descriptive event text for a Datadog event based on the user's description.
Include relevant details like what happened, when, and any important context.
Can use Markdown formatting for readability.
Return the event description text directly - no extra formatting needed.`,
placeholder: 'Describe the event details...',
},
},
{
id: 'alertType',
@@ -145,6 +214,15 @@ export const DatadogBlock: BlockConfig<DatadogResponse> = {
placeholder: 'High CPU Usage Alert',
condition: { field: 'operation', value: 'datadog_create_monitor' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a clear, descriptive monitor name for Datadog based on the user's description.
The name should be concise but descriptive, indicating what is being monitored.
Examples: "High CPU Usage Alert", "Database Connection Pool Low", "API Error Rate Spike"
Return ONLY the monitor name - no quotes, no extra formatting.`,
placeholder: 'Describe what the monitor should track...',
},
},
{
id: 'type',
@@ -170,6 +248,18 @@ export const DatadogBlock: BlockConfig<DatadogResponse> = {
placeholder: 'avg(last_5m):avg:system.cpu.idle{*} < 20',
condition: { field: 'operation', value: 'datadog_create_monitor' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a Datadog monitor query based on the user's description.
Monitor query format: <aggregation>(<time_window>):<metric_query> <comparator> <threshold>
Examples:
- "avg(last_5m):avg:system.cpu.idle{*} < 20" - Alert when average CPU idle is below 20%
- "sum(last_1h):sum:app.errors{env:production} > 100" - Alert when errors exceed 100 in an hour
- "max(last_15m):max:system.disk.used{*} by {host} > 90" - Alert when disk usage exceeds 90%
Return ONLY the monitor query string - no explanations.`,
placeholder: 'Describe what condition should trigger the alert...',
},
},
{
id: 'message',
@@ -177,6 +267,17 @@ export const DatadogBlock: BlockConfig<DatadogResponse> = {
type: 'long-input',
placeholder: 'Alert! CPU usage is high. @slack-alerts',
condition: { field: 'operation', value: 'datadog_create_monitor' },
wandConfig: {
enabled: true,
prompt: `Generate a Datadog monitor notification message based on the user's description.
The message should include:
- A clear description of what triggered the alert
- Relevant template variables like {{host.name}}, {{value}}
- Optional: notification handles like @slack-channel or @pagerduty
Return the notification message text directly.`,
placeholder: 'Describe what the notification should say...',
},
},
{
id: 'monitorTags',
@@ -198,6 +299,20 @@ export const DatadogBlock: BlockConfig<DatadogResponse> = {
type: 'code',
placeholder: '{"notify_no_data": true, "thresholds": {"critical": 90}}',
condition: { field: 'operation', value: 'datadog_create_monitor' },
wandConfig: {
enabled: true,
prompt: `Generate Datadog monitor options JSON based on the user's description.
Common options include:
- "notify_no_data": boolean - Notify when data stops arriving
- "thresholds": {"critical": number, "warning": number} - Alert thresholds
- "renotify_interval": number - Minutes between re-notifications
- "timeout_h": number - Hours before auto-resolving
- "include_tags": boolean - Include trigger tags in notifications
Return ONLY valid JSON - no explanations, no markdown code blocks.`,
placeholder: 'Describe the monitor options you need...',
generationType: 'json-object',
},
},
// ========================
@@ -254,6 +369,19 @@ export const DatadogBlock: BlockConfig<DatadogResponse> = {
type: 'short-input',
placeholder: 'Leave empty for indefinite',
condition: { field: 'operation', value: 'datadog_mute_monitor' },
wandConfig: {
enabled: true,
prompt: `Generate a Unix timestamp (seconds since epoch) based on the user's description.
The timestamp should be a number representing seconds since January 1, 1970 UTC.
Examples:
- "in 1 hour" -> Calculate current time plus 3600 seconds
- "tomorrow morning" -> Calculate tomorrow at 09:00:00 UTC as Unix timestamp
- "end of day" -> Calculate today at 23:59:59 UTC as Unix timestamp
Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
placeholder: 'Describe when mute should end (e.g., "in 1 hour", "tomorrow")...',
generationType: 'timestamp',
},
},
// ========================
@@ -266,6 +394,18 @@ export const DatadogBlock: BlockConfig<DatadogResponse> = {
placeholder: 'service:web-app status:error',
condition: { field: 'operation', value: 'datadog_query_logs' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a Datadog log search query based on the user's description.
The query uses facet syntax: facet:value
Examples:
- "service:web-app status:error" - Errors from web-app service
- "source:nginx @http.status_code:>=500" - Nginx 5xx errors
- "host:prod-* @duration:>1000" - Slow requests on prod hosts
Return ONLY the search query string - no explanations.`,
placeholder: 'Describe what logs you want to find...',
},
},
{
id: 'logFrom',
@@ -274,6 +414,20 @@ export const DatadogBlock: BlockConfig<DatadogResponse> = {
placeholder: 'now-1h',
condition: { field: 'operation', value: 'datadog_query_logs' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a Datadog relative time string based on the user's description.
The format uses relative time syntax like: now-1h, now-15m, now-1d, now-1w
Examples:
- "1 hour ago" -> now-1h
- "15 minutes ago" -> now-15m
- "yesterday" -> now-1d
- "last week" -> now-7d
Return ONLY the relative time string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the start time (e.g., "1 hour ago", "yesterday")...',
generationType: 'timestamp',
},
},
{
id: 'logTo',
@@ -282,6 +436,19 @@ export const DatadogBlock: BlockConfig<DatadogResponse> = {
placeholder: 'now',
condition: { field: 'operation', value: 'datadog_query_logs' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a Datadog relative time string based on the user's description.
The format uses relative time syntax like: now, now-1h, now-15m
Examples:
- "now" or "current time" -> now
- "5 minutes ago" -> now-5m
- "1 hour ago" -> now-1h
Return ONLY the relative time string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the end time (e.g., "now", "5 minutes ago")...',
generationType: 'timestamp',
},
},
{
id: 'logLimit',
@@ -308,6 +475,20 @@ export const DatadogBlock: BlockConfig<DatadogResponse> = {
]`,
condition: { field: 'operation', value: 'datadog_send_logs' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a JSON array of Datadog log entries based on the user's description.
Each log object should have:
- "message": The log message text
- "service": The service name
- "ddsource": The log source (e.g., "custom", "nodejs", "python")
- "ddtags": Comma-separated tags (e.g., "env:production,version:1.0")
- Optional: "hostname", "status" (info/warn/error)
Return ONLY valid JSON - no explanations, no markdown code blocks.`,
placeholder: 'Describe the logs you want to send...',
generationType: 'json-object',
},
},
// ========================
@@ -327,6 +508,15 @@ export const DatadogBlock: BlockConfig<DatadogResponse> = {
type: 'long-input',
placeholder: 'Scheduled maintenance',
condition: { field: 'operation', value: 'datadog_create_downtime' },
wandConfig: {
enabled: true,
prompt: `Generate a downtime message for Datadog based on the user's description.
The message should explain why monitoring is being muted.
Examples: "Scheduled maintenance window", "Deploying new version", "Infrastructure upgrade in progress"
Return the message text directly - no extra formatting.`,
placeholder: 'Describe the reason for the downtime...',
},
},
{
id: 'downtimeStart',
@@ -334,6 +524,19 @@ export const DatadogBlock: BlockConfig<DatadogResponse> = {
type: 'short-input',
placeholder: 'Leave empty for now',
condition: { field: 'operation', value: 'datadog_create_downtime' },
wandConfig: {
enabled: true,
prompt: `Generate a Unix timestamp (seconds since epoch) based on the user's description.
The timestamp should be a number representing seconds since January 1, 1970 UTC.
Examples:
- "now" -> Calculate current time as Unix timestamp
- "in 30 minutes" -> Calculate current time plus 1800 seconds
- "tonight at 10pm" -> Calculate today at 22:00:00 UTC as Unix timestamp
Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
placeholder: 'Describe when downtime should start (e.g., "now", "in 30 minutes")...',
generationType: 'timestamp',
},
},
{
id: 'downtimeEnd',
@@ -341,6 +544,19 @@ export const DatadogBlock: BlockConfig<DatadogResponse> = {
type: 'short-input',
placeholder: 'e.g., 1701450000',
condition: { field: 'operation', value: 'datadog_create_downtime' },
wandConfig: {
enabled: true,
prompt: `Generate a Unix timestamp (seconds since epoch) based on the user's description.
The timestamp should be a number representing seconds since January 1, 1970 UTC.
Examples:
- "in 2 hours" -> Calculate current time plus 7200 seconds
- "tomorrow morning" -> Calculate tomorrow at 09:00:00 UTC as Unix timestamp
- "end of maintenance window" -> Interpret based on context
Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
placeholder: 'Describe when downtime should end (e.g., "in 2 hours", "tomorrow")...',
generationType: 'timestamp',
},
},
{
id: 'downtimeMonitorId',

View File

@@ -235,6 +235,19 @@ export const DropboxBlock: BlockConfig<DropboxResponse> = {
type: 'short-input',
placeholder: '2025-12-31T23:59:59Z',
condition: { field: 'operation', value: 'dropbox_create_shared_link' },
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp based on the user's description.
The timestamp should be in the format: YYYY-MM-DDTHH:MM:SSZ (UTC timezone).
Examples:
- "in 1 week" -> Calculate 7 days from now at 23:59:59Z
- "end of month" -> Calculate last day of current month at 23:59:59Z
- "next year" -> Calculate January 1st of next year at 00:00:00Z
Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe when link should expire (e.g., "in 1 week", "end of month")...',
generationType: 'timestamp',
},
},
// Search operation inputs
{

View File

@@ -65,6 +65,18 @@ export const DynamoDBBlock: BlockConfig<DynamoDBResponse> = {
placeholder: '{\n "pk": "user#123"\n}',
condition: { field: 'operation', value: 'get' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a DynamoDB primary key JSON object based on the user's description.
The key should include partition key and optionally sort key.
Examples:
- {"pk": "user#123"} - Simple partition key
- {"pk": "order#456", "sk": "2024-01-15"} - Partition key with sort key
Return ONLY valid JSON - no explanations, no markdown code blocks.`,
placeholder: 'Describe the item key...',
generationType: 'json-object',
},
},
{
id: 'key',
@@ -73,6 +85,18 @@ export const DynamoDBBlock: BlockConfig<DynamoDBResponse> = {
placeholder: '{\n "pk": "user#123"\n}',
condition: { field: 'operation', value: 'update' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a DynamoDB primary key JSON object based on the user's description.
The key should include partition key and optionally sort key.
Examples:
- {"pk": "user#123"} - Simple partition key
- {"pk": "order#456", "sk": "2024-01-15"} - Partition key with sort key
Return ONLY valid JSON - no explanations, no markdown code blocks.`,
placeholder: 'Describe the item key...',
generationType: 'json-object',
},
},
{
id: 'key',
@@ -81,6 +105,18 @@ export const DynamoDBBlock: BlockConfig<DynamoDBResponse> = {
placeholder: '{\n "pk": "user#123"\n}',
condition: { field: 'operation', value: 'delete' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a DynamoDB primary key JSON object based on the user's description.
The key should include partition key and optionally sort key.
Examples:
- {"pk": "user#123"} - Simple partition key
- {"pk": "order#456", "sk": "2024-01-15"} - Partition key with sort key
Return ONLY valid JSON - no explanations, no markdown code blocks.`,
placeholder: 'Describe the item key...',
generationType: 'json-object',
},
},
// Consistent read for get
{
@@ -103,6 +139,16 @@ export const DynamoDBBlock: BlockConfig<DynamoDBResponse> = {
'{\n "pk": "user#123",\n "name": "John Doe",\n "email": "john@example.com"\n}',
condition: { field: 'operation', value: 'put' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a DynamoDB item JSON object based on the user's description.
The item must include the primary key and any additional attributes.
Use appropriate data types for values (strings, numbers, booleans, lists, maps).
Return ONLY valid JSON - no explanations, no markdown code blocks.`,
placeholder: 'Describe the item you want to store...',
generationType: 'json-object',
},
},
// Key condition expression for query
{
@@ -112,6 +158,19 @@ export const DynamoDBBlock: BlockConfig<DynamoDBResponse> = {
placeholder: 'pk = :pk',
condition: { field: 'operation', value: 'query' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a DynamoDB key condition expression based on the user's description.
The expression must reference the partition key and optionally the sort key.
Use :placeholders for values and #names for reserved words.
Examples:
- "pk = :pk" - Match partition key
- "pk = :pk AND sk BETWEEN :start AND :end" - Range query on sort key
- "pk = :pk AND begins_with(sk, :prefix)" - Prefix match on sort key
Return ONLY the expression - no explanations.`,
placeholder: 'Describe the key condition...',
},
},
// Update expression for update operation
{
@@ -121,6 +180,19 @@ export const DynamoDBBlock: BlockConfig<DynamoDBResponse> = {
placeholder: 'SET #name = :name',
condition: { field: 'operation', value: 'update' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a DynamoDB update expression based on the user's description.
Use SET, REMOVE, ADD, or DELETE clauses.
Use :placeholders for values and #names for attribute names.
Examples:
- "SET #name = :name, #age = :age" - Update multiple attributes
- "SET #count = #count + :increment" - Increment a counter
- "REMOVE #oldAttribute" - Remove an attribute
Return ONLY the expression - no explanations.`,
placeholder: 'Describe what updates to make...',
},
},
// Filter expression for query and scan
{
@@ -129,6 +201,19 @@ export const DynamoDBBlock: BlockConfig<DynamoDBResponse> = {
type: 'short-input',
placeholder: 'attribute_exists(email)',
condition: { field: 'operation', value: 'query' },
wandConfig: {
enabled: true,
prompt: `Generate a DynamoDB filter expression based on the user's description.
Filter expressions are applied after the query but before results are returned.
Use comparison operators, functions like attribute_exists(), contains(), begins_with().
Examples:
- "attribute_exists(email)" - Items with email attribute
- "#status = :active AND #age > :minAge" - Multiple conditions
- "contains(#tags, :tag)" - Contains a value in a list
Return ONLY the expression - no explanations.`,
placeholder: 'Describe how to filter results...',
},
},
{
id: 'filterExpression',
@@ -136,6 +221,19 @@ export const DynamoDBBlock: BlockConfig<DynamoDBResponse> = {
type: 'short-input',
placeholder: 'attribute_exists(email)',
condition: { field: 'operation', value: 'scan' },
wandConfig: {
enabled: true,
prompt: `Generate a DynamoDB filter expression based on the user's description.
Filter expressions are applied after the scan but before results are returned.
Use comparison operators, functions like attribute_exists(), contains(), begins_with().
Examples:
- "attribute_exists(email)" - Items with email attribute
- "#status = :active AND #age > :minAge" - Multiple conditions
- "contains(#tags, :tag)" - Contains a value in a list
Return ONLY the expression - no explanations.`,
placeholder: 'Describe how to filter results...',
},
},
// Projection expression for scan
{
@@ -152,6 +250,17 @@ export const DynamoDBBlock: BlockConfig<DynamoDBResponse> = {
type: 'code',
placeholder: '{\n "#name": "name"\n}',
condition: { field: 'operation', value: 'query' },
wandConfig: {
enabled: true,
prompt: `Generate DynamoDB expression attribute names JSON based on the user's description.
Map placeholder names (starting with #) to actual attribute names.
Required when using reserved words or for clarity.
Example: {"#name": "name", "#status": "status"}
Return ONLY valid JSON - no explanations, no markdown code blocks.`,
placeholder: 'Describe the attribute name mappings...',
generationType: 'json-object',
},
},
{
id: 'expressionAttributeNames',
@@ -159,6 +268,17 @@ export const DynamoDBBlock: BlockConfig<DynamoDBResponse> = {
type: 'code',
placeholder: '{\n "#name": "name"\n}',
condition: { field: 'operation', value: 'scan' },
wandConfig: {
enabled: true,
prompt: `Generate DynamoDB expression attribute names JSON based on the user's description.
Map placeholder names (starting with #) to actual attribute names.
Required when using reserved words or for clarity.
Example: {"#name": "name", "#status": "status"}
Return ONLY valid JSON - no explanations, no markdown code blocks.`,
placeholder: 'Describe the attribute name mappings...',
generationType: 'json-object',
},
},
{
id: 'expressionAttributeNames',
@@ -166,6 +286,17 @@ export const DynamoDBBlock: BlockConfig<DynamoDBResponse> = {
type: 'code',
placeholder: '{\n "#name": "name"\n}',
condition: { field: 'operation', value: 'update' },
wandConfig: {
enabled: true,
prompt: `Generate DynamoDB expression attribute names JSON based on the user's description.
Map placeholder names (starting with #) to actual attribute names.
Required when using reserved words or for clarity.
Example: {"#name": "name", "#status": "status"}
Return ONLY valid JSON - no explanations, no markdown code blocks.`,
placeholder: 'Describe the attribute name mappings...',
generationType: 'json-object',
},
},
// Expression attribute values for query, scan, update
{
@@ -174,6 +305,16 @@ export const DynamoDBBlock: BlockConfig<DynamoDBResponse> = {
type: 'code',
placeholder: '{\n ":pk": "user#123",\n ":name": "Jane"\n}',
condition: { field: 'operation', value: 'query' },
wandConfig: {
enabled: true,
prompt: `Generate DynamoDB expression attribute values JSON based on the user's description.
Map placeholder values (starting with :) to actual values.
Example: {":pk": "user#123", ":status": "active", ":minAge": 18}
Return ONLY valid JSON - no explanations, no markdown code blocks.`,
placeholder: 'Describe the attribute values...',
generationType: 'json-object',
},
},
{
id: 'expressionAttributeValues',
@@ -181,6 +322,16 @@ export const DynamoDBBlock: BlockConfig<DynamoDBResponse> = {
type: 'code',
placeholder: '{\n ":status": "active"\n}',
condition: { field: 'operation', value: 'scan' },
wandConfig: {
enabled: true,
prompt: `Generate DynamoDB expression attribute values JSON based on the user's description.
Map placeholder values (starting with :) to actual values.
Example: {":status": "active", ":minAge": 18}
Return ONLY valid JSON - no explanations, no markdown code blocks.`,
placeholder: 'Describe the attribute values...',
generationType: 'json-object',
},
},
{
id: 'expressionAttributeValues',
@@ -188,6 +339,16 @@ export const DynamoDBBlock: BlockConfig<DynamoDBResponse> = {
type: 'code',
placeholder: '{\n ":name": "Jane Doe"\n}',
condition: { field: 'operation', value: 'update' },
wandConfig: {
enabled: true,
prompt: `Generate DynamoDB expression attribute values JSON based on the user's description.
Map placeholder values (starting with :) to actual values.
Example: {":name": "Jane Doe", ":count": 1}
Return ONLY valid JSON - no explanations, no markdown code blocks.`,
placeholder: 'Describe the attribute values...',
generationType: 'json-object',
},
},
// Index name for query
{
@@ -219,6 +380,18 @@ export const DynamoDBBlock: BlockConfig<DynamoDBResponse> = {
type: 'short-input',
placeholder: 'attribute_exists(pk)',
condition: { field: 'operation', value: 'update' },
wandConfig: {
enabled: true,
prompt: `Generate a DynamoDB condition expression based on the user's description.
Condition expressions prevent the operation if the condition is not met.
Examples:
- "attribute_exists(pk)" - Item must exist
- "attribute_not_exists(pk)" - Item must not exist (for inserts)
- "#version = :expectedVersion" - Optimistic locking
Return ONLY the expression - no explanations.`,
placeholder: 'Describe the condition that must be true...',
},
},
{
id: 'conditionExpression',
@@ -226,6 +399,17 @@ export const DynamoDBBlock: BlockConfig<DynamoDBResponse> = {
type: 'short-input',
placeholder: 'attribute_exists(pk)',
condition: { field: 'operation', value: 'delete' },
wandConfig: {
enabled: true,
prompt: `Generate a DynamoDB condition expression based on the user's description.
Condition expressions prevent the operation if the condition is not met.
Examples:
- "attribute_exists(pk)" - Item must exist
- "#status = :deletable" - Only delete if status matches
Return ONLY the expression - no explanations.`,
placeholder: 'Describe the condition that must be true...',
},
},
],
tools: {

View File

@@ -174,6 +174,16 @@ export const ElasticsearchBlock: BlockConfig<ElasticsearchResponse> = {
placeholder: '{ "field": "value", "another_field": 123 }',
required: true,
condition: { field: 'operation', value: 'elasticsearch_index_document' },
wandConfig: {
enabled: true,
prompt: `Generate an Elasticsearch document JSON object based on the user's description.
The document should contain the fields and values to be indexed.
Use appropriate data types (strings, numbers, booleans, arrays, nested objects).
Return ONLY valid JSON - no explanations, no markdown code blocks.`,
placeholder: 'Describe the document you want to index...',
generationType: 'json-object',
},
},
// Document body - for update (partial)
@@ -184,6 +194,15 @@ export const ElasticsearchBlock: BlockConfig<ElasticsearchResponse> = {
placeholder: '{ "field_to_update": "new_value" }',
required: true,
condition: { field: 'operation', value: 'elasticsearch_update_document' },
wandConfig: {
enabled: true,
prompt: `Generate an Elasticsearch partial document JSON for updating based on the user's description.
Only include the fields that need to be updated - other fields will remain unchanged.
Return ONLY valid JSON - no explanations, no markdown code blocks.`,
placeholder: 'Describe the fields you want to update...',
generationType: 'json-object',
},
},
// Search query
@@ -193,6 +212,19 @@ export const ElasticsearchBlock: BlockConfig<ElasticsearchResponse> = {
type: 'code',
placeholder: '{ "match": { "field": "search term" } }',
condition: { field: 'operation', value: 'elasticsearch_search' },
wandConfig: {
enabled: true,
prompt: `Generate an Elasticsearch query DSL JSON based on the user's description.
Common query types:
- {"match": {"field": "text"}} - Full-text search
- {"term": {"field": "exact_value"}} - Exact match
- {"range": {"field": {"gte": 10, "lte": 100}}} - Range query
- {"bool": {"must": [...], "filter": [...]}} - Boolean combinations
Return ONLY valid JSON - no explanations, no markdown code blocks.`,
placeholder: 'Describe what you want to search for...',
generationType: 'json-object',
},
},
// Count query
@@ -202,6 +234,18 @@ export const ElasticsearchBlock: BlockConfig<ElasticsearchResponse> = {
type: 'code',
placeholder: '{ "match": { "field": "value" } }',
condition: { field: 'operation', value: 'elasticsearch_count' },
wandConfig: {
enabled: true,
prompt: `Generate an Elasticsearch query DSL JSON for counting documents based on the user's description.
Common query types:
- {"match": {"field": "text"}} - Full-text search
- {"term": {"field": "exact_value"}} - Exact match
- {"range": {"field": {"gte": 10}}} - Range query
Return ONLY valid JSON - no explanations, no markdown code blocks.`,
placeholder: 'Describe which documents to count...',
generationType: 'json-object',
},
},
// Search size
@@ -229,6 +273,18 @@ export const ElasticsearchBlock: BlockConfig<ElasticsearchResponse> = {
type: 'code',
placeholder: '[{ "field": { "order": "asc" } }]',
condition: { field: 'operation', value: 'elasticsearch_search' },
wandConfig: {
enabled: true,
prompt: `Generate an Elasticsearch sort specification JSON array based on the user's description.
Format: [{"field_name": {"order": "asc"|"desc"}}]
Examples:
- [{"timestamp": {"order": "desc"}}] - Sort by timestamp descending
- [{"_score": {"order": "desc"}}, {"date": {"order": "asc"}}] - Multi-field sort
Return ONLY valid JSON array - no explanations, no markdown code blocks.`,
placeholder: 'Describe how to sort the results...',
generationType: 'json-object',
},
},
// Source includes
@@ -264,6 +320,20 @@ export const ElasticsearchBlock: BlockConfig<ElasticsearchResponse> = {
'{ "index": { "_index": "my-index", "_id": "1" } }\n{ "field": "value" }\n{ "delete": { "_index": "my-index", "_id": "2" } }',
required: true,
condition: { field: 'operation', value: 'elasticsearch_bulk' },
wandConfig: {
enabled: true,
prompt: `Generate Elasticsearch bulk operations in NDJSON format based on the user's description.
Each operation consists of an action line followed by an optional document line.
Actions: index, create, update, delete
Format:
{"index": {"_index": "my-index", "_id": "1"}}
{"field": "value"}
{"delete": {"_index": "my-index", "_id": "2"}}
Return ONLY the NDJSON content - no explanations, no markdown code blocks.`,
placeholder: 'Describe the bulk operations you want to perform...',
generationType: 'json-object',
},
},
// Index settings
@@ -273,6 +343,19 @@ export const ElasticsearchBlock: BlockConfig<ElasticsearchResponse> = {
type: 'code',
placeholder: '{ "number_of_shards": 1, "number_of_replicas": 1 }',
condition: { field: 'operation', value: 'elasticsearch_create_index' },
wandConfig: {
enabled: true,
prompt: `Generate Elasticsearch index settings JSON based on the user's description.
Common settings:
- "number_of_shards": Number of primary shards
- "number_of_replicas": Number of replica shards
- "refresh_interval": How often to refresh the index
- "analysis": Custom analyzers, tokenizers, filters
Return ONLY valid JSON - no explanations, no markdown code blocks.`,
placeholder: 'Describe the index settings you need...',
generationType: 'json-object',
},
},
// Index mappings
@@ -282,6 +365,21 @@ export const ElasticsearchBlock: BlockConfig<ElasticsearchResponse> = {
type: 'code',
placeholder: '{ "properties": { "field": { "type": "text" } } }',
condition: { field: 'operation', value: 'elasticsearch_create_index' },
wandConfig: {
enabled: true,
prompt: `Generate Elasticsearch index mappings JSON based on the user's description.
Define field types and properties:
- "text": Full-text searchable
- "keyword": Exact match, sorting, aggregations
- "integer", "long", "float", "double": Numeric types
- "date": Date/time values
- "boolean": True/false values
- "object", "nested": Complex types
Return ONLY valid JSON - no explanations, no markdown code blocks.`,
placeholder: 'Describe the fields and their types...',
generationType: 'json-object',
},
},
// Refresh option

View File

@@ -4,7 +4,7 @@ import { isHosted } from '@/lib/core/config/feature-flags'
import type { BlockConfig, ParamType } from '@/blocks/types'
import type { ProviderId } from '@/providers/types'
import {
getAllModelProviders,
getBaseModelProviders,
getHostedModels,
getProviderIcon,
providers,
@@ -357,7 +357,7 @@ export const EvaluatorBlock: BlockConfig<EvaluatorResponse> = {
if (!model) {
throw new Error('No model selected')
}
const tool = getAllModelProviders()[model as ProviderId]
const tool = getBaseModelProviders()[model as ProviderId]
if (!tool) {
throw new Error(`Invalid model selected: ${model}`)
}

View File

@@ -0,0 +1,589 @@
import { FirefliesIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import { AuthMode } from '@/blocks/types'
import type { FirefliesResponse } from '@/tools/fireflies/types'
import { getTrigger } from '@/triggers'
export const FirefliesBlock: BlockConfig<FirefliesResponse> = {
type: 'fireflies',
name: 'Fireflies',
description: 'Interact with Fireflies.ai meeting transcripts and recordings',
authMode: AuthMode.ApiKey,
triggerAllowed: true,
longDescription:
'Integrate Fireflies.ai into the workflow. Manage meeting transcripts, add bot to live meetings, create soundbites, and more. Can also trigger workflows when transcriptions complete.',
docsLink: 'https://docs.fireflies.ai',
category: 'tools',
icon: FirefliesIcon,
bgColor: '#100730',
subBlocks: [
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
options: [
{ label: 'List Transcripts', id: 'fireflies_list_transcripts' },
{ label: 'Get Transcript', id: 'fireflies_get_transcript' },
{ label: 'Get User', id: 'fireflies_get_user' },
{ label: 'List Users', id: 'fireflies_list_users' },
{ label: 'Upload Audio', id: 'fireflies_upload_audio' },
{ label: 'Delete Transcript', id: 'fireflies_delete_transcript' },
{ label: 'Add Bot to Live Meeting', id: 'fireflies_add_to_live_meeting' },
{ label: 'Create Bite', id: 'fireflies_create_bite' },
{ label: 'List Bites', id: 'fireflies_list_bites' },
{ label: 'List Contacts', id: 'fireflies_list_contacts' },
],
value: () => 'fireflies_list_transcripts',
},
{
id: 'apiKey',
title: 'API Key',
type: 'short-input',
placeholder: 'Enter your Fireflies API key',
password: true,
required: true,
},
// Transcript ID (for get/delete/create_bite/list_bites)
{
id: 'transcriptId',
title: 'Transcript ID',
type: 'short-input',
placeholder: 'Enter transcript ID',
required: {
field: 'operation',
value: ['fireflies_get_transcript', 'fireflies_delete_transcript', 'fireflies_create_bite'],
},
condition: {
field: 'operation',
value: [
'fireflies_get_transcript',
'fireflies_delete_transcript',
'fireflies_create_bite',
'fireflies_list_bites',
],
},
},
// User ID (optional for get user)
{
id: 'userId',
title: 'User ID',
type: 'short-input',
placeholder: 'Leave empty for current user',
required: false,
condition: {
field: 'operation',
value: 'fireflies_get_user',
},
},
// List Transcripts filters
{
id: 'keyword',
title: 'Keyword',
type: 'short-input',
placeholder: 'Search in title or transcript',
required: false,
condition: {
field: 'operation',
value: 'fireflies_list_transcripts',
},
},
{
id: 'fromDate',
title: 'From Date',
type: 'short-input',
placeholder: 'e.g., 2024-01-01T00:00:00Z',
required: false,
condition: {
field: 'operation',
value: 'fireflies_list_transcripts',
},
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp based on the user's description.
The timestamp should be in the format: YYYY-MM-DDTHH:MM:SSZ (UTC timezone).
Examples:
- "yesterday" -> Calculate yesterday's date at 00:00:00Z
- "last week" -> Calculate 7 days ago at 00:00:00Z
- "beginning of this month" -> First day of current month at 00:00:00Z
- "January 1st 2024" -> 2024-01-01T00:00:00Z
Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the date (e.g., "last week", "beginning of this month")...',
generationType: 'timestamp',
},
},
{
id: 'toDate',
title: 'To Date',
type: 'short-input',
placeholder: 'e.g., 2024-12-31T23:59:59Z',
required: false,
condition: {
field: 'operation',
value: 'fireflies_list_transcripts',
},
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp based on the user's description.
The timestamp should be in the format: YYYY-MM-DDTHH:MM:SSZ (UTC timezone).
Examples:
- "today" -> Calculate today's date at 23:59:59Z
- "end of this week" -> Calculate end of week at 23:59:59Z
- "end of this month" -> Last day of current month at 23:59:59Z
- "December 31st 2024" -> 2024-12-31T23:59:59Z
Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the date (e.g., "today", "end of this month")...',
generationType: 'timestamp',
},
},
{
id: 'hostEmail',
title: 'Host Email',
type: 'short-input',
placeholder: 'Filter by host email',
required: false,
condition: {
field: 'operation',
value: 'fireflies_list_transcripts',
},
},
{
id: 'participants',
title: 'Participants',
type: 'short-input',
placeholder: 'Comma-separated participant emails',
required: false,
condition: {
field: 'operation',
value: 'fireflies_list_transcripts',
},
},
{
id: 'limit',
title: 'Limit',
type: 'short-input',
placeholder: 'Max 50 (default: 50)',
required: false,
condition: {
field: 'operation',
value: ['fireflies_list_transcripts', 'fireflies_list_bites'],
},
},
// Upload Audio fields - File upload (basic mode)
{
id: 'audioFile',
title: 'Audio/Video File',
type: 'file-upload',
canonicalParamId: 'audioFile',
placeholder: 'Upload an audio or video file',
mode: 'basic',
multiple: false,
required: false,
acceptedTypes: '.mp3,.m4a,.wav,.webm,.ogg,.flac,.aac,.opus,.mp4,.mov,.avi,.mkv',
condition: {
field: 'operation',
value: 'fireflies_upload_audio',
},
},
// Upload Audio fields - File reference (advanced mode)
{
id: 'audioFileReference',
title: 'Audio/Video File Reference',
type: 'short-input',
canonicalParamId: 'audioFile',
placeholder: 'Reference audio/video from previous blocks',
mode: 'advanced',
required: false,
condition: {
field: 'operation',
value: 'fireflies_upload_audio',
},
},
// Upload Audio fields - URL input
{
id: 'audioUrl',
title: 'Audio/Video URL',
type: 'short-input',
placeholder: 'Or enter publicly accessible audio/video URL',
description: 'Public HTTPS URL to audio file (MP3, MP4, WAV, M4A, OGG)',
required: false,
condition: {
field: 'operation',
value: 'fireflies_upload_audio',
},
},
{
id: 'title',
title: 'Title',
type: 'short-input',
placeholder: 'Meeting title',
required: false,
condition: {
field: 'operation',
value: ['fireflies_upload_audio', 'fireflies_add_to_live_meeting'],
},
wandConfig: {
enabled: true,
prompt: `Generate a clear, professional meeting title based on the user's description.
The title should be concise (3-8 words) and descriptive.
Examples:
- "weekly sync with engineering" -> "Weekly Engineering Team Sync"
- "discussing q1 roadmap" -> "Q1 Roadmap Planning Discussion"
- "interview with john for backend role" -> "Backend Engineer Interview - John"
- "customer demo for acme corp" -> "Product Demo - Acme Corp"
Return ONLY the title - no quotes, no explanations.`,
placeholder:
'Describe the meeting (e.g., "weekly team sync", "customer call with Acme")...',
},
},
{
id: 'language',
title: 'Language',
type: 'short-input',
placeholder: 'e.g., es, de, fr (default: English)',
required: false,
condition: {
field: 'operation',
value: ['fireflies_upload_audio', 'fireflies_add_to_live_meeting'],
},
wandConfig: {
enabled: true,
prompt: `Convert the language name to its ISO 639-1 two-letter code.
Examples:
- "Spanish" -> es
- "French" -> fr
- "German" -> de
- "Portuguese" -> pt
- "Japanese" -> ja
- "Chinese" -> zh
- "Korean" -> ko
- "Italian" -> it
- "Dutch" -> nl
- "Russian" -> ru
Return ONLY the two-letter language code - no explanations, no quotes.`,
placeholder: 'Enter language name (e.g., "Spanish", "French")...',
},
},
{
id: 'attendees',
title: 'Attendees',
type: 'long-input',
placeholder: '[{"displayName": "John", "email": "john@example.com"}]',
description: 'JSON array of attendees',
required: false,
condition: {
field: 'operation',
value: 'fireflies_upload_audio',
},
wandConfig: {
enabled: true,
prompt: `Generate a JSON array of attendees based on the user's description.
Each attendee should have "displayName" and "email" fields.
Examples:
- "John Smith at john@example.com" -> [{"displayName": "John Smith", "email": "john@example.com"}]
- "Alice (alice@test.com) and Bob (bob@test.com)" -> [{"displayName": "Alice", "email": "alice@test.com"}, {"displayName": "Bob", "email": "bob@test.com"}]
- "Sarah Johnson, sarah.j@company.org" -> [{"displayName": "Sarah Johnson", "email": "sarah.j@company.org"}]
Return ONLY the valid JSON array - no explanations, no markdown code blocks.`,
placeholder:
'Describe attendees (e.g., "John Smith at john@example.com and Jane Doe at jane@test.com")...',
generationType: 'json-object',
},
},
{
id: 'clientReferenceId',
title: 'Reference ID',
type: 'short-input',
placeholder: 'Custom tracking ID',
required: false,
condition: {
field: 'operation',
value: 'fireflies_upload_audio',
},
},
// Add to Live Meeting fields
{
id: 'meetingLink',
title: 'Meeting Link',
type: 'short-input',
placeholder: 'https://zoom.us/j/... or https://meet.google.com/...',
description: 'URL for Zoom, Google Meet, or Microsoft Teams meeting',
required: true,
condition: {
field: 'operation',
value: 'fireflies_add_to_live_meeting',
},
},
{
id: 'meetingPassword',
title: 'Meeting Password',
type: 'short-input',
placeholder: 'Optional meeting password',
password: true,
required: false,
condition: {
field: 'operation',
value: 'fireflies_add_to_live_meeting',
},
},
{
id: 'duration',
title: 'Duration (minutes)',
type: 'short-input',
placeholder: '60 (15-120 minutes)',
required: false,
condition: {
field: 'operation',
value: 'fireflies_add_to_live_meeting',
},
},
// Create Bite fields
{
id: 'startTime',
title: 'Start Time (seconds)',
type: 'short-input',
placeholder: 'e.g., 30',
required: true,
condition: {
field: 'operation',
value: 'fireflies_create_bite',
},
},
{
id: 'endTime',
title: 'End Time (seconds)',
type: 'short-input',
placeholder: 'e.g., 90',
required: true,
condition: {
field: 'operation',
value: 'fireflies_create_bite',
},
},
{
id: 'biteName',
title: 'Bite Name',
type: 'short-input',
placeholder: 'Name for this highlight',
required: false,
condition: {
field: 'operation',
value: 'fireflies_create_bite',
},
},
{
id: 'biteSummary',
title: 'Summary',
type: 'long-input',
placeholder: 'Brief description of the highlight',
required: false,
condition: {
field: 'operation',
value: 'fireflies_create_bite',
},
wandConfig: {
enabled: true,
prompt: `Write a concise, professional summary for a meeting highlight/soundbite based on the user's description.
The summary should be 1-2 sentences that capture the key point of the highlighted segment.
Guidelines:
- Be clear and concise
- Focus on the main topic or decision discussed
- Use professional language
- Avoid filler words
Return ONLY the summary text - no quotes, no labels.`,
placeholder: 'Describe what this highlight is about...',
},
},
// Trigger SubBlocks
...getTrigger('fireflies_transcription_complete').subBlocks,
],
tools: {
access: [
'fireflies_list_transcripts',
'fireflies_get_transcript',
'fireflies_get_user',
'fireflies_list_users',
'fireflies_upload_audio',
'fireflies_delete_transcript',
'fireflies_add_to_live_meeting',
'fireflies_create_bite',
'fireflies_list_bites',
'fireflies_list_contacts',
],
config: {
tool: (params) => {
return params.operation || 'fireflies_list_transcripts'
},
params: (params) => {
const baseParams: Record<string, unknown> = {
apiKey: params.apiKey,
}
switch (params.operation) {
case 'fireflies_list_transcripts':
return {
...baseParams,
keyword: params.keyword || undefined,
fromDate: params.fromDate || undefined,
toDate: params.toDate || undefined,
hostEmail: params.hostEmail || undefined,
participants: params.participants || undefined,
limit: params.limit ? Number(params.limit) : undefined,
}
case 'fireflies_get_transcript':
if (!params.transcriptId?.trim()) {
throw new Error('Transcript ID is required.')
}
return {
...baseParams,
transcriptId: params.transcriptId.trim(),
}
case 'fireflies_get_user':
return {
...baseParams,
userId: params.userId?.trim() || undefined,
}
case 'fireflies_list_users':
return baseParams
case 'fireflies_upload_audio': {
// Support both file upload and URL
const audioUrl = params.audioUrl?.trim()
const audioFile = params.audioFile
const audioFileReference = params.audioFileReference
if (!audioUrl && !audioFile && !audioFileReference) {
throw new Error('Either audio file or audio URL is required.')
}
return {
...baseParams,
audioUrl: audioUrl || undefined,
audioFile: audioFile || undefined,
audioFileReference: audioFileReference || undefined,
title: params.title?.trim() || undefined,
language: params.language?.trim() || undefined,
attendees: params.attendees?.trim() || undefined,
clientReferenceId: params.clientReferenceId?.trim() || undefined,
}
}
case 'fireflies_delete_transcript':
if (!params.transcriptId?.trim()) {
throw new Error('Transcript ID is required.')
}
return {
...baseParams,
transcriptId: params.transcriptId.trim(),
}
case 'fireflies_add_to_live_meeting':
if (!params.meetingLink?.trim()) {
throw new Error('Meeting link is required.')
}
return {
...baseParams,
meetingLink: params.meetingLink.trim(),
title: params.title?.trim() || undefined,
meetingPassword: params.meetingPassword?.trim() || undefined,
duration: params.duration ? Number(params.duration) : undefined,
language: params.language?.trim() || undefined,
}
case 'fireflies_create_bite':
if (!params.transcriptId?.trim()) {
throw new Error('Transcript ID is required.')
}
if (!params.startTime || !params.endTime) {
throw new Error('Start time and end time are required.')
}
return {
...baseParams,
transcriptId: params.transcriptId.trim(),
startTime: Number(params.startTime),
endTime: Number(params.endTime),
name: params.biteName?.trim() || undefined,
summary: params.biteSummary?.trim() || undefined,
}
case 'fireflies_list_bites':
return {
...baseParams,
transcriptId: params.transcriptId?.trim() || undefined,
mine: true,
limit: params.limit ? Number(params.limit) : undefined,
}
case 'fireflies_list_contacts':
return baseParams
default:
return baseParams
}
},
},
},
inputs: {
operation: { type: 'string', description: 'Operation to perform' },
apiKey: { type: 'string', description: 'Fireflies API key' },
transcriptId: { type: 'string', description: 'Transcript identifier' },
userId: { type: 'string', description: 'User identifier' },
keyword: { type: 'string', description: 'Search keyword' },
fromDate: { type: 'string', description: 'Filter from date (ISO 8601)' },
toDate: { type: 'string', description: 'Filter to date (ISO 8601)' },
hostEmail: { type: 'string', description: 'Filter by host email' },
participants: { type: 'string', description: 'Filter by participants (comma-separated)' },
limit: { type: 'number', description: 'Maximum results to return' },
audioFile: { type: 'json', description: 'Audio/video file (UserFile)' },
audioFileReference: { type: 'json', description: 'Audio/video file reference' },
audioUrl: { type: 'string', description: 'Public URL to audio file' },
title: { type: 'string', description: 'Meeting title' },
language: { type: 'string', description: 'Language code for transcription' },
attendees: { type: 'string', description: 'JSON array of attendees' },
clientReferenceId: { type: 'string', description: 'Custom reference ID for tracking' },
meetingLink: { type: 'string', description: 'Meeting URL (Zoom, Meet, Teams)' },
meetingPassword: { type: 'string', description: 'Meeting password if required' },
duration: { type: 'number', description: 'Meeting duration in minutes (15-120)' },
startTime: { type: 'number', description: 'Bite start time in seconds' },
endTime: { type: 'number', description: 'Bite end time in seconds' },
biteName: { type: 'string', description: 'Name for the bite/highlight' },
biteSummary: { type: 'string', description: 'Summary for the bite' },
},
outputs: {
// List transcripts outputs
transcripts: { type: 'json', description: 'List of transcripts' },
count: { type: 'number', description: 'Number of transcripts returned' },
// Get transcript outputs
transcript: { type: 'json', description: 'Full transcript data with summary and analytics' },
// User outputs
user: { type: 'json', description: 'User information' },
users: { type: 'json', description: 'List of team users' },
// Bite outputs
bite: { type: 'json', description: 'Created bite details' },
bites: { type: 'json', description: 'List of bites/soundbites' },
// Contact outputs
contacts: { type: 'json', description: 'List of contacts from meetings' },
// Common outputs
success: { type: 'boolean', description: 'Operation success status' },
message: { type: 'string', description: 'Status message' },
// Trigger outputs
meetingId: { type: 'string', description: 'Meeting/transcript ID from webhook' },
eventType: { type: 'string', description: 'Webhook event type' },
clientReferenceId: { type: 'string', description: 'Custom reference ID if set during upload' },
},
triggers: {
enabled: true,
available: ['fireflies_transcription_complete'],
},
}

View File

@@ -143,6 +143,14 @@ export const GitLabBlock: BlockConfig<GitLabResponse> = {
field: 'operation',
value: ['gitlab_create_issue', 'gitlab_create_merge_request'],
},
wandConfig: {
enabled: true,
prompt: `Generate a clear, descriptive title for a GitLab issue or merge request based on the user's request.
The title should be concise but informative.
Return ONLY the title - no explanations, no extra text.`,
placeholder: 'Describe the issue or merge request...',
},
},
// Description
{
@@ -159,6 +167,20 @@ export const GitLabBlock: BlockConfig<GitLabResponse> = {
'gitlab_update_merge_request',
],
},
wandConfig: {
enabled: true,
prompt: `Generate a comprehensive description for a GitLab issue or merge request based on the user's request.
Include relevant sections as appropriate:
- Summary of changes or problem
- Context and motivation
- Testing done (for MRs)
- Steps to reproduce (for bugs)
Use Markdown formatting for readability.
Return ONLY the description - no explanations outside the content.`,
placeholder: 'Describe the content in detail...',
},
},
// Comment body
{
@@ -171,6 +193,15 @@ export const GitLabBlock: BlockConfig<GitLabResponse> = {
field: 'operation',
value: ['gitlab_create_issue_note', 'gitlab_create_merge_request_note'],
},
wandConfig: {
enabled: true,
prompt: `Generate a helpful GitLab comment based on the user's request.
The comment should be clear, constructive, and professional.
Use Markdown formatting for readability.
Return ONLY the comment text - no explanations, no extra formatting.`,
placeholder: 'Describe the comment you want to write...',
},
},
// Source branch (for MR creation)
{
@@ -352,6 +383,14 @@ export const GitLabBlock: BlockConfig<GitLabResponse> = {
field: 'operation',
value: ['gitlab_merge_merge_request'],
},
wandConfig: {
enabled: true,
prompt: `Generate a clear merge commit message based on the user's request.
The message should summarize what is being merged and why.
Return ONLY the commit message - no explanations, no extra text.`,
placeholder: 'Describe the merge...',
},
},
// Per page (pagination)
{

View File

@@ -68,6 +68,14 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
placeholder: 'Email subject',
condition: { field: 'operation', value: ['send_gmail', 'draft_gmail'] },
required: false,
wandConfig: {
enabled: true,
prompt: `Generate a clear, professional email subject line based on the user's request.
The subject should be concise yet informative about the email's purpose.
Return ONLY the subject line - no explanations, no extra text.`,
placeholder: 'Describe the email topic...',
},
},
{
id: 'body',
@@ -76,6 +84,18 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
placeholder: 'Email content',
condition: { field: 'operation', value: ['send_gmail', 'draft_gmail'] },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate professional email content based on the user's request.
The email should:
- Have an appropriate greeting
- Be clear and well-structured
- Have a professional tone
- Include a proper closing
Return ONLY the email body - no explanations, no extra text.`,
placeholder: 'Describe the email you want to write...',
},
},
{
id: 'contentType',
@@ -207,6 +227,18 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
placeholder: 'Enter search terms',
condition: { field: 'operation', value: 'search_gmail' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a Gmail search query based on the user's request.
Gmail search supports operators like:
- from: to: subject: has:attachment
- is:unread is:starred is:important
- before: after: older: newer:
- filename: label: category:
Return ONLY the search query - no explanations, no extra text.`,
placeholder: 'Describe what emails you want to find...',
},
},
{
id: 'maxResults',

View File

@@ -21,6 +21,25 @@ export const GoogleSearchBlock: BlockConfig<GoogleSearchResponse> = {
type: 'long-input',
placeholder: 'Enter your search query',
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a Google search query based on the user's description.
Create an effective search query that will find relevant results.
Use search operators when appropriate:
- "exact phrase" for exact matches
- site:domain.com to search within a site
- -word to exclude terms
- OR for alternatives
- filetype:pdf for specific file types
Examples:
- "latest AI news" -> latest artificial intelligence news 2024
- "python tutorials on youtube" -> site:youtube.com python tutorial
- "PDF reports about climate change" -> climate change report filetype:pdf
Return ONLY the search query - no explanations, no quotes around the whole thing, no extra text.`,
placeholder: 'Describe what you want to search for...',
},
},
{
id: 'searchEngineId',

View File

@@ -67,6 +67,14 @@ export const GoogleCalendarBlock: BlockConfig<GoogleCalendarResponse> = {
placeholder: 'Meeting with team',
condition: { field: 'operation', value: 'create' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a clear, descriptive calendar event title based on the user's request.
The title should be concise but informative about the event's purpose.
Return ONLY the event title - no explanations, no extra text.`,
placeholder: 'Describe the event...',
},
},
{
id: 'description',
@@ -74,6 +82,18 @@ export const GoogleCalendarBlock: BlockConfig<GoogleCalendarResponse> = {
type: 'long-input',
placeholder: 'Event description',
condition: { field: 'operation', value: 'create' },
wandConfig: {
enabled: true,
prompt: `Generate a helpful calendar event description based on the user's request.
Include relevant details like:
- Purpose of the event
- Agenda items
- Preparation notes
- Links or resources
Return ONLY the description - no explanations, no extra text.`,
placeholder: 'Describe the event details...',
},
},
{
id: 'location',
@@ -89,6 +109,19 @@ export const GoogleCalendarBlock: BlockConfig<GoogleCalendarResponse> = {
placeholder: '2025-06-03T10:00:00-08:00',
condition: { field: 'operation', value: 'create' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp with timezone offset based on the user's description.
The timestamp should be in the format: YYYY-MM-DDTHH:MM:SS+HH:MM or YYYY-MM-DDTHH:MM:SS-HH:MM
Examples:
- "tomorrow at 2pm" -> Calculate tomorrow's date at 14:00:00 with local timezone offset
- "next Monday at 9am" -> Calculate next Monday at 09:00:00 with local timezone offset
- "in 2 hours" -> Calculate current time + 2 hours with local timezone offset
Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the start time (e.g., "tomorrow at 2pm", "next Monday at 9am")...',
generationType: 'timestamp',
},
},
{
id: 'endDateTime',
@@ -97,6 +130,19 @@ export const GoogleCalendarBlock: BlockConfig<GoogleCalendarResponse> = {
placeholder: '2025-06-03T11:00:00-08:00',
condition: { field: 'operation', value: 'create' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp with timezone offset based on the user's description.
The timestamp should be in the format: YYYY-MM-DDTHH:MM:SS+HH:MM or YYYY-MM-DDTHH:MM:SS-HH:MM
Examples:
- "tomorrow at 3pm" -> Calculate tomorrow's date at 15:00:00 with local timezone offset
- "1 hour after start" -> Calculate start time + 1 hour with local timezone offset
- "next Monday at 5pm" -> Calculate next Monday at 17:00:00 with local timezone offset
Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the end time (e.g., "tomorrow at 3pm", "1 hour after start")...',
generationType: 'timestamp',
},
},
{
id: 'attendees',
@@ -113,6 +159,20 @@ export const GoogleCalendarBlock: BlockConfig<GoogleCalendarResponse> = {
type: 'short-input',
placeholder: '2025-06-03T00:00:00Z',
condition: { field: 'operation', value: 'list' },
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp in UTC based on the user's description.
The timestamp should be in the format: YYYY-MM-DDTHH:MM:SSZ (UTC timezone).
Examples:
- "today" -> Calculate today's date at 00:00:00Z
- "yesterday" -> Calculate yesterday's date at 00:00:00Z
- "last week" -> Calculate 7 days ago at 00:00:00Z
- "beginning of this month" -> Calculate the first day of current month at 00:00:00Z
Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the start of time range (e.g., "today", "last week")...',
generationType: 'timestamp',
},
},
{
id: 'timeMax',
@@ -120,6 +180,20 @@ export const GoogleCalendarBlock: BlockConfig<GoogleCalendarResponse> = {
type: 'short-input',
placeholder: '2025-06-04T00:00:00Z',
condition: { field: 'operation', value: 'list' },
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp in UTC based on the user's description.
The timestamp should be in the format: YYYY-MM-DDTHH:MM:SSZ (UTC timezone).
Examples:
- "tomorrow" -> Calculate tomorrow's date at 00:00:00Z
- "end of today" -> Calculate today's date at 23:59:59Z
- "next week" -> Calculate 7 days from now at 00:00:00Z
- "end of this month" -> Calculate the last day of current month at 23:59:59Z
Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the end of time range (e.g., "tomorrow", "end of this week")...',
generationType: 'timestamp',
},
},
// Get Event Fields
@@ -159,6 +233,23 @@ export const GoogleCalendarBlock: BlockConfig<GoogleCalendarResponse> = {
placeholder: 'Meeting with John tomorrow at 3pm for 1 hour',
condition: { field: 'operation', value: 'quick_add' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a natural language event description that Google Calendar can parse.
Include:
- Event title/purpose
- Date and time
- Duration (optional)
- Location (optional)
Examples:
- "Meeting with John tomorrow at 3pm for 1 hour"
- "Lunch at Cafe Milano on Friday at noon"
- "Team standup every Monday at 9am"
Return ONLY the natural language event text - no explanations.`,
placeholder: 'Describe the event in natural language...',
},
},
{
id: 'attendees',

View File

@@ -73,6 +73,14 @@ export const GoogleDocsBlock: BlockConfig<GoogleDocsResponse> = {
placeholder: 'Enter title for the new document',
condition: { field: 'operation', value: 'create' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a clear, descriptive document title based on the user's request.
The title should be concise but informative about the document's purpose.
Return ONLY the document title - no explanations, no extra text.`,
placeholder: 'Describe the document...',
},
},
// Folder selector (basic mode)
{
@@ -107,6 +115,14 @@ export const GoogleDocsBlock: BlockConfig<GoogleDocsResponse> = {
placeholder: 'Enter document content',
condition: { field: 'operation', value: 'write' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate document content based on the user's request.
The content should be well-structured and appropriate for a Google Doc.
Return ONLY the document content - no explanations, no extra text.`,
placeholder: 'Describe the document content you want to write...',
},
},
// Content Field for create operation
{
@@ -115,6 +131,14 @@ export const GoogleDocsBlock: BlockConfig<GoogleDocsResponse> = {
type: 'long-input',
placeholder: 'Enter document content',
condition: { field: 'operation', value: 'create' },
wandConfig: {
enabled: true,
prompt: `Generate initial document content based on the user's request.
The content should be well-structured and appropriate for a new Google Doc.
Return ONLY the document content - no explanations, no extra text.`,
placeholder: 'Describe the document content you want to create...',
},
},
],
tools: {

View File

@@ -80,6 +80,17 @@ export const GoogleDriveBlock: BlockConfig<GoogleDriveResponse> = {
placeholder: 'Text content for the file',
condition: { field: 'operation', value: 'create_file' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate file content based on the user's description.
Create well-structured content appropriate for the file type.
For text files, use clear formatting and organization.
For HTML, include proper structure with appropriate tags.
For CSV, use proper comma-separated formatting.
Return ONLY the file content - no explanations, no markdown code blocks, no extra text.`,
placeholder: 'Describe the content you want to create...',
},
},
{
id: 'mimeType',
@@ -229,6 +240,24 @@ export const GoogleDriveBlock: BlockConfig<GoogleDriveResponse> = {
type: 'short-input',
placeholder: 'Search for specific files (e.g., name contains "report")',
condition: { field: 'operation', value: 'list' },
wandConfig: {
enabled: true,
prompt: `Generate a Google Drive search query based on the user's description.
Use Google Drive query syntax:
- name contains 'term' - search by filename
- mimeType = 'type' - filter by file type
- modifiedTime > 'date' - filter by date
- 'email' in owners - filter by owner
- fullText contains 'term' - search file contents
Examples:
- "PDF files" -> mimeType = 'application/pdf'
- "files named report" -> name contains 'report'
- "spreadsheets modified today" -> mimeType = 'application/vnd.google-apps.spreadsheet' and modifiedTime > '2024-01-01'
Return ONLY the query string - no explanations, no quotes around the whole thing, no extra text.`,
placeholder: 'Describe the files you want to find...',
},
},
{
id: 'pageSize',

View File

@@ -66,6 +66,22 @@ export const GoogleGroupsBlock: BlockConfig = {
type: 'short-input',
placeholder: 'Filter query (e.g., email:admin*)',
condition: { field: 'operation', value: 'list_groups' },
wandConfig: {
enabled: true,
prompt: `Generate a Google Groups search query based on the user's description.
Use Google Groups Admin SDK query syntax:
- email:pattern* - search by email address (supports wildcards)
- name:term - search by group name
- memberKey:email - search by member email
Examples:
- "groups starting with admin" -> email:admin*
- "groups with support in the name" -> name:support*
- "groups containing user@example.com" -> memberKey:user@example.com
Return ONLY the query string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the groups you want to find...',
},
},
{
id: 'maxResults',
@@ -115,6 +131,22 @@ export const GoogleGroupsBlock: BlockConfig = {
placeholder: 'Display name for the group',
required: true,
condition: { field: 'operation', value: 'create_group' },
wandConfig: {
enabled: true,
prompt: `Generate a professional group display name based on the user's description.
The name should be:
- Clear and descriptive
- Appropriate for a workplace setting
- Concise (typically 2-5 words)
Examples:
- "marketing team" -> Marketing Team
- "project managers" -> Project Managers
- "sales leadership" -> Sales Leadership Team
Return ONLY the group name - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the group you want to create...',
},
},
{
id: 'description',
@@ -122,6 +154,17 @@ export const GoogleGroupsBlock: BlockConfig = {
type: 'long-input',
placeholder: 'Optional description for the group',
condition: { field: 'operation', value: ['create_group', 'update_group'] },
wandConfig: {
enabled: true,
prompt: `Generate a professional group description based on the user's request.
The description should:
- Clearly explain the purpose of the group
- Be concise but informative (1-3 sentences)
- Use professional language appropriate for a workplace setting
Return ONLY the description text - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the purpose of this group...',
},
},
{

View File

@@ -83,6 +83,22 @@ export const GoogleSheetsBlock: BlockConfig<GoogleSheetsResponse> = {
'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects (e.g., [{"name":"John", "age":30}, {"name":"Jane", "age":25}])',
condition: { field: 'operation', value: 'write' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate Google Sheets data as a JSON array based on the user's description.
Format options:
1. Array of arrays: [["Header1", "Header2"], ["Value1", "Value2"]]
2. Array of objects: [{"column1": "value1", "column2": "value2"}]
Examples:
- "sales data with product and revenue columns" -> [["Product", "Revenue"], ["Widget A", 1500], ["Widget B", 2300]]
- "list of employees with name and email" -> [{"name": "John Doe", "email": "john@example.com"}, {"name": "Jane Smith", "email": "jane@example.com"}]
Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the data you want to write...',
generationType: 'json-object',
},
},
{
id: 'valueInputOption',
@@ -103,6 +119,22 @@ export const GoogleSheetsBlock: BlockConfig<GoogleSheetsResponse> = {
'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects (e.g., [{"name":"John", "age":30}, {"name":"Jane", "age":25}])',
condition: { field: 'operation', value: 'update' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate Google Sheets data as a JSON array based on the user's description.
Format options:
1. Array of arrays: [["Header1", "Header2"], ["Value1", "Value2"]]
2. Array of objects: [{"column1": "value1", "column2": "value2"}]
Examples:
- "update with new prices" -> [["Product", "Price"], ["Widget A", 29.99], ["Widget B", 49.99]]
- "quarterly targets" -> [{"Q1": 10000, "Q2": 12000, "Q3": 15000, "Q4": 18000}]
Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the data you want to update...',
generationType: 'json-object',
},
},
{
id: 'valueInputOption',
@@ -123,6 +155,22 @@ export const GoogleSheetsBlock: BlockConfig<GoogleSheetsResponse> = {
'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects (e.g., [{"name":"John", "age":30}, {"name":"Jane", "age":25}])',
condition: { field: 'operation', value: 'append' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate Google Sheets data as a JSON array based on the user's description.
Format options:
1. Array of arrays: [["Value1", "Value2"], ["Value3", "Value4"]]
2. Array of objects: [{"column1": "value1", "column2": "value2"}]
Examples:
- "add new sales record" -> [["2024-01-15", "Widget Pro", 5, 249.99]]
- "append customer info" -> [{"name": "Acme Corp", "contact": "John Smith", "status": "Active"}]
Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the data you want to append...',
generationType: 'json-object',
},
},
{
id: 'valueInputOption',

View File

@@ -91,6 +91,17 @@ export const GoogleSlidesBlock: BlockConfig<GoogleSlidesResponse> = {
placeholder: 'Enter slide content',
condition: { field: 'operation', value: 'write' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate slide content based on the user's description.
Create clear, concise content suitable for a presentation slide.
- Use bullet points for lists
- Keep text brief and impactful
- Focus on key points
Return ONLY the slide content - no explanations, no markdown formatting markers, no extra text.`,
placeholder: 'Describe what you want on this slide...',
},
},
// ========== Create Operation Fields ==========
@@ -101,6 +112,22 @@ export const GoogleSlidesBlock: BlockConfig<GoogleSlidesResponse> = {
placeholder: 'Enter title for the new presentation',
condition: { field: 'operation', value: 'create' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a professional presentation title based on the user's description.
The title should be:
- Clear and descriptive
- Professional and engaging
- Concise (typically 3-8 words)
Examples:
- "quarterly sales" -> Q4 2024 Sales Performance Review
- "product launch" -> Introducing Our New Product Line
- "team meeting" -> Weekly Team Sync - Updates & Goals
Return ONLY the title - no explanations, no quotes, no extra text.`,
placeholder: 'Describe your presentation topic...',
},
},
// Folder selector (basic mode)
{
@@ -134,6 +161,16 @@ export const GoogleSlidesBlock: BlockConfig<GoogleSlidesResponse> = {
type: 'long-input',
placeholder: 'Enter initial slide content (optional)',
condition: { field: 'operation', value: 'create' },
wandConfig: {
enabled: true,
prompt: `Generate initial slide content for a new presentation based on the user's description.
Create clear, concise content suitable for a title or introductory slide.
- Keep text brief and impactful
- Focus on the main message or theme
Return ONLY the slide content - no explanations, no markdown formatting markers, no extra text.`,
placeholder: 'Describe the initial slide content...',
},
},
// ========== Replace All Text Operation Fields ==========
@@ -152,6 +189,14 @@ export const GoogleSlidesBlock: BlockConfig<GoogleSlidesResponse> = {
placeholder: 'Text to replace with',
condition: { field: 'operation', value: 'replace_all_text' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate replacement text based on the user's description.
The text should be appropriate for a presentation slide - concise and professional.
Return ONLY the replacement text - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the replacement text...',
},
},
{
id: 'matchCase',
@@ -201,6 +246,28 @@ export const GoogleSlidesBlock: BlockConfig<GoogleSlidesResponse> = {
placeholder: 'JSON array: [{"layoutPlaceholder":{"type":"TITLE"},"objectId":"my_title"}]',
condition: { field: 'operation', value: 'add_slide' },
mode: 'advanced',
wandConfig: {
enabled: true,
prompt: `Generate Google Slides placeholder ID mappings as a JSON array.
Structure:
[
{
"layoutPlaceholder": {"type": "PLACEHOLDER_TYPE", "index": 0},
"objectId": "unique_object_id"
}
]
Placeholder types: TITLE, SUBTITLE, BODY, CENTERED_TITLE, HEADER, FOOTER, SLIDE_NUMBER, DATE_AND_TIME, CHART, TABLE, MEDIA, IMAGE
Examples:
- "title and body placeholders" -> [{"layoutPlaceholder":{"type":"TITLE"},"objectId":"title_1"},{"layoutPlaceholder":{"type":"BODY"},"objectId":"body_1"}]
- "just a title" -> [{"layoutPlaceholder":{"type":"TITLE"},"objectId":"my_title"}]
Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the placeholder mappings you need...',
generationType: 'json-object',
},
},
// ========== Add Image Operation Fields ==========

View File

@@ -90,6 +90,22 @@ export const GoogleVaultBlock: BlockConfig = {
placeholder: 'Name for the export',
condition: { field: 'operation', value: 'create_matters_export' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a descriptive export name for Google Vault based on the user's description.
The name should be:
- Clear and descriptive
- Include relevant identifiers (date, case, scope)
- Professional and concise
Examples:
- "email export for Q4" -> Q4_2024_Email_Export
- "drive files for legal case" -> Legal_Case_Drive_Files_Export
- "john's messages" -> John_Doe_Messages_Export
Return ONLY the export name - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the export...',
},
},
{
id: 'holdName',
@@ -98,6 +114,22 @@ export const GoogleVaultBlock: BlockConfig = {
placeholder: 'Name of the hold',
condition: { field: 'operation', value: 'create_matters_holds' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a descriptive hold name for Google Vault based on the user's description.
The name should be:
- Clear and descriptive
- Include relevant identifiers (case name, scope, date)
- Professional and concise
Examples:
- "hold for investigation" -> Investigation_Hold_2024
- "preserve emails for John" -> John_Doe_Email_Preservation
- "legal hold for project alpha" -> Project_Alpha_Legal_Hold
Return ONLY the hold name - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the hold...',
},
},
{
id: 'corpus',
@@ -169,6 +201,22 @@ export const GoogleVaultBlock: BlockConfig = {
placeholder: 'Enter Matter name',
condition: { field: 'operation', value: 'create_matters' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a descriptive matter name for Google Vault based on the user's description.
The name should be:
- Clear and descriptive
- Professional and suitable for legal/compliance purposes
- Include relevant identifiers if applicable
Examples:
- "investigation into data breach" -> Data_Breach_Investigation_2024
- "lawsuit from acme corp" -> Acme_Corp_Litigation
- "HR complaint case" -> HR_Complaint_Matter_001
Return ONLY the matter name - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the matter...',
},
},
{
id: 'description',
@@ -176,6 +224,17 @@ export const GoogleVaultBlock: BlockConfig = {
type: 'short-input',
placeholder: 'Optional description for the matter',
condition: { field: 'operation', value: 'create_matters' },
wandConfig: {
enabled: true,
prompt: `Generate a professional description for a Google Vault matter based on the user's request.
The description should:
- Clearly explain the purpose and scope of the matter
- Be concise but informative (1-3 sentences)
- Use professional language appropriate for legal/compliance contexts
Return ONLY the description text - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the purpose of this matter...',
},
},
// Optional get specific matter by ID
{

View File

@@ -103,6 +103,19 @@ export const GrafanaBlock: BlockConfig<GrafanaResponse> = {
type: 'short-input',
placeholder: 'Filter dashboards by title',
condition: { field: 'operation', value: 'grafana_list_dashboards' },
wandConfig: {
enabled: true,
prompt: `Generate a Grafana dashboard search query based on the user's description.
The query should be a simple text string to filter dashboards by title.
Examples:
- "production dashboards" -> production
- "kubernetes monitoring" -> kubernetes
- "api performance" -> api performance
Return ONLY the search query - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the dashboards you want to find...',
},
},
{
id: 'tag',
@@ -120,6 +133,22 @@ export const GrafanaBlock: BlockConfig<GrafanaResponse> = {
placeholder: 'Enter dashboard title',
required: true,
condition: { field: 'operation', value: 'grafana_create_dashboard' },
wandConfig: {
enabled: true,
prompt: `Generate a professional Grafana dashboard title based on the user's description.
The title should be:
- Clear and descriptive
- Indicate the purpose or scope of monitoring
- Concise (typically 2-5 words)
Examples:
- "api monitoring" -> API Performance Dashboard
- "kubernetes cluster" -> Kubernetes Cluster Overview
- "database metrics" -> Database Health & Metrics
Return ONLY the title - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the dashboard...',
},
},
{
id: 'folderUid',
@@ -154,6 +183,39 @@ export const GrafanaBlock: BlockConfig<GrafanaResponse> = {
field: 'operation',
value: ['grafana_create_dashboard', 'grafana_update_dashboard'],
},
wandConfig: {
enabled: true,
prompt: `Generate Grafana panel configurations as a JSON array based on the user's description.
Basic panel structure:
[
{
"title": "Panel Title",
"type": "graph|stat|gauge|table|text|heatmap|bargauge",
"gridPos": {"x": 0, "y": 0, "w": 12, "h": 8},
"targets": [
{
"expr": "prometheus_query_here",
"refId": "A"
}
]
}
]
Common panel types:
- "graph" / "timeseries": Line charts for time-series data
- "stat": Single value display
- "gauge": Gauge visualization
- "table": Tabular data
- "bargauge": Bar gauge
Examples:
- "CPU usage panel" -> [{"title":"CPU Usage","type":"timeseries","gridPos":{"x":0,"y":0,"w":12,"h":8},"targets":[{"expr":"100 - (avg(irate(node_cpu_seconds_total{mode=\"idle\"}[5m])) * 100)","refId":"A"}]}]
Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the panels you want to create...',
generationType: 'json-object',
},
},
{
id: 'message',
@@ -187,6 +249,22 @@ export const GrafanaBlock: BlockConfig<GrafanaResponse> = {
field: 'operation',
value: ['grafana_create_alert_rule', 'grafana_update_alert_rule'],
},
wandConfig: {
enabled: true,
prompt: `Generate a professional Grafana alert rule name based on the user's description.
The name should be:
- Clear and descriptive
- Indicate what is being monitored and the condition
- Follow naming conventions (PascalCase or with spaces)
Examples:
- "high cpu alert" -> High CPU Usage Alert
- "disk space warning" -> Low Disk Space Warning
- "api error rate" -> API Error Rate Threshold
Return ONLY the alert title - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the alert...',
},
},
{
id: 'folderUid',
@@ -227,6 +305,48 @@ export const GrafanaBlock: BlockConfig<GrafanaResponse> = {
field: 'operation',
value: ['grafana_create_alert_rule', 'grafana_update_alert_rule'],
},
wandConfig: {
enabled: true,
prompt: `Generate Grafana alert query data as a JSON array based on the user's description.
Structure for alert queries:
[
{
"refId": "A",
"datasourceUid": "datasource_uid",
"model": {
"expr": "prometheus_query",
"refId": "A"
}
},
{
"refId": "B",
"datasourceUid": "-100",
"model": {
"type": "reduce",
"expression": "A",
"reducer": "last"
}
},
{
"refId": "C",
"datasourceUid": "-100",
"model": {
"type": "threshold",
"expression": "B",
"conditions": [{"evaluator": {"type": "gt", "params": [80]}}]
}
}
]
Examples:
- "alert when CPU > 80%" -> Query for CPU metrics with threshold condition
- "memory usage warning" -> Query for memory with reduce and threshold
Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the alert query conditions...',
generationType: 'json-object',
},
},
{
id: 'forDuration',
@@ -279,6 +399,22 @@ export const GrafanaBlock: BlockConfig<GrafanaResponse> = {
field: 'operation',
value: ['grafana_create_annotation', 'grafana_update_annotation'],
},
wandConfig: {
enabled: true,
prompt: `Generate annotation text for Grafana based on the user's description.
The annotation should:
- Clearly describe the event or observation
- Be concise but informative
- Include relevant details (what happened, impact, etc.)
Examples:
- "deployment started" -> Deployment v2.3.1 started - API service
- "high traffic period" -> High traffic period began - 3x normal load
- "config change" -> Configuration update: increased connection pool size to 50
Return ONLY the annotation text - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the annotation...',
},
},
{
id: 'annotationTags',
@@ -324,6 +460,19 @@ export const GrafanaBlock: BlockConfig<GrafanaResponse> = {
field: 'operation',
value: ['grafana_create_annotation', 'grafana_update_annotation'],
},
wandConfig: {
enabled: true,
prompt: `Generate an epoch timestamp in milliseconds based on the user's description.
The timestamp should be a Unix epoch time in milliseconds (13 digits).
Examples:
- "now" -> Current timestamp in milliseconds
- "yesterday" -> Yesterday at 00:00:00 in milliseconds
- "1 hour ago" -> Subtract 3600000 from current time
Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the time (e.g., "now", "1 hour ago", "yesterday at noon")...',
generationType: 'timestamp',
},
},
{
id: 'timeEnd',
@@ -334,6 +483,19 @@ export const GrafanaBlock: BlockConfig<GrafanaResponse> = {
field: 'operation',
value: ['grafana_create_annotation', 'grafana_update_annotation'],
},
wandConfig: {
enabled: true,
prompt: `Generate an epoch timestamp in milliseconds based on the user's description.
The timestamp should be a Unix epoch time in milliseconds (13 digits).
Examples:
- "now" -> Current timestamp in milliseconds
- "in 1 hour" -> Add 3600000 to current time
- "end of today" -> Today at 23:59:59 in milliseconds
Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the end time (e.g., "in 1 hour", "end of today")...',
generationType: 'timestamp',
},
},
{
id: 'annotationId',
@@ -352,6 +514,19 @@ export const GrafanaBlock: BlockConfig<GrafanaResponse> = {
type: 'short-input',
placeholder: 'Filter from time',
condition: { field: 'operation', value: 'grafana_list_annotations' },
wandConfig: {
enabled: true,
prompt: `Generate an epoch timestamp in milliseconds based on the user's description.
The timestamp should be a Unix epoch time in milliseconds (13 digits).
Examples:
- "last week" -> 7 days ago at 00:00:00 in milliseconds
- "beginning of this month" -> First day of current month at 00:00:00
- "24 hours ago" -> Subtract 86400000 from current time
Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the start time (e.g., "last week", "beginning of this month")...',
generationType: 'timestamp',
},
},
{
id: 'to',
@@ -359,6 +534,19 @@ export const GrafanaBlock: BlockConfig<GrafanaResponse> = {
type: 'short-input',
placeholder: 'Filter to time',
condition: { field: 'operation', value: 'grafana_list_annotations' },
wandConfig: {
enabled: true,
prompt: `Generate an epoch timestamp in milliseconds based on the user's description.
The timestamp should be a Unix epoch time in milliseconds (13 digits).
Examples:
- "now" -> Current timestamp in milliseconds
- "end of today" -> Today at 23:59:59 in milliseconds
- "end of last week" -> Last Sunday at 23:59:59 in milliseconds
Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the end time (e.g., "now", "end of today")...',
generationType: 'timestamp',
},
},
// Folder operations
@@ -369,6 +557,22 @@ export const GrafanaBlock: BlockConfig<GrafanaResponse> = {
placeholder: 'Enter folder title',
required: true,
condition: { field: 'operation', value: 'grafana_create_folder' },
wandConfig: {
enabled: true,
prompt: `Generate a Grafana folder title based on the user's description.
The title should be:
- Clear and descriptive
- Indicate the category or scope of dashboards it will contain
- Concise (typically 1-3 words)
Examples:
- "production monitoring" -> Production
- "kubernetes dashboards" -> Kubernetes
- "team alpha metrics" -> Team Alpha
Return ONLY the folder title - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the folder...',
},
},
{
id: 'folderUidNew',

View File

@@ -73,6 +73,19 @@ export const GrainBlock: BlockConfig = {
field: 'operation',
value: ['grain_list_recordings', 'grain_create_hook'],
},
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp based on the user's description.
The timestamp should be in the format: YYYY-MM-DDTHH:MM:SSZ (UTC timezone).
Examples:
- "yesterday" -> Calculate yesterday's date at 00:00:00Z
- "last week" -> Calculate 7 days ago at 00:00:00Z
- "beginning of this month" -> First day of current month at 00:00:00Z
Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the date (e.g., "yesterday", "last week")...',
generationType: 'timestamp',
},
},
// After datetime filter
{
@@ -84,6 +97,19 @@ export const GrainBlock: BlockConfig = {
field: 'operation',
value: ['grain_list_recordings', 'grain_create_hook'],
},
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp based on the user's description.
The timestamp should be in the format: YYYY-MM-DDTHH:MM:SSZ (UTC timezone).
Examples:
- "today" -> Today's date at 00:00:00Z
- "last Monday" -> Calculate last Monday's date at 00:00:00Z
- "beginning of last month" -> First day of previous month at 00:00:00Z
Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the date (e.g., "today", "last Monday")...',
generationType: 'timestamp',
},
},
// Participant scope filter
{
@@ -111,6 +137,21 @@ export const GrainBlock: BlockConfig = {
field: 'operation',
value: ['grain_list_recordings'],
},
wandConfig: {
enabled: true,
prompt: `Generate a search term for finding recordings by title based on the user's description.
The search term should be:
- Keywords or phrases that would appear in recording titles
- Concise and targeted
Examples:
- "meetings with john" -> John
- "weekly standup" -> standup
- "product demo" -> demo product
Return ONLY the search term - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the recordings you want to find...',
},
},
// Team ID filter
{
@@ -217,12 +258,12 @@ export const GrainBlock: BlockConfig = {
value: () => 'grain_webhook',
required: true,
},
...getTrigger('grain_recording_created').subBlocks.slice(1),
...getTrigger('grain_recording_updated').subBlocks.slice(1),
...getTrigger('grain_highlight_created').subBlocks.slice(1),
...getTrigger('grain_highlight_updated').subBlocks.slice(1),
...getTrigger('grain_story_created').subBlocks.slice(1),
...getTrigger('grain_webhook').subBlocks.slice(1),
...getTrigger('grain_recording_created').subBlocks,
...getTrigger('grain_recording_updated').subBlocks,
...getTrigger('grain_highlight_created').subBlocks,
...getTrigger('grain_highlight_updated').subBlocks,
...getTrigger('grain_story_created').subBlocks,
...getTrigger('grain_webhook').subBlocks,
],
tools: {
access: [

View File

@@ -73,6 +73,30 @@ export const GuardrailsBlock: BlockConfig<GuardrailsResponse> = {
field: 'validationType',
value: ['regex'],
},
wandConfig: {
enabled: true,
prompt: `Generate a regular expression pattern based on the user's description.
The regex should be:
- Valid JavaScript regex syntax
- Properly escaped for special characters
- Optimized for the use case
Common patterns:
- Email: ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$
- Phone (US): ^\\+?1?[-.\\s]?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}$
- URL: ^https?:\\/\\/[\\w\\-]+(\\.[\\w\\-]+)+[/#?]?.*$
- Date (YYYY-MM-DD): ^\\d{4}-\\d{2}-\\d{2}$
- UUID: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$
- IP Address: ^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$
Examples:
- "validate email" -> ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$
- "check for numbers only" -> ^\\d+$
- "alphanumeric with underscores" -> ^[a-zA-Z0-9_]+$
Return ONLY the regex pattern - no explanations, no quotes, no forward slashes, no extra text.`,
placeholder: 'Describe the pattern you want to match...',
},
},
{
id: 'knowledgeBaseId',

View File

@@ -10,6 +10,7 @@ export const HumanInTheLoopBlock: BlockConfig<ResponseBlockOutput> = {
'Combines response and start functionality. Sends structured responses and allows workflow to resume from this point.',
category: 'blocks',
bgColor: '#10B981',
docsLink: 'https://docs.sim.ai/blocks/human-in-the-loop',
icon: HumanInTheLoopIcon,
subBlocks: [
// Operation dropdown hidden - block defaults to human approval mode

View File

@@ -125,6 +125,18 @@ export const HunterBlock: BlockConfig<HunterResponse> = {
placeholder: 'Enter search query (e.g., "software companies in San Francisco")',
condition: { field: 'operation', value: 'hunter_discover' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a company discovery search query for Hunter.io based on the user's description.
The query should be optimized for finding companies and should include:
- Industry or business type
- Location if relevant
- Company size or other relevant criteria
Return ONLY the search query text - no explanations.`,
placeholder:
'Describe the companies you want to find (e.g., "fintech startups in NYC", "healthcare companies in Europe")...',
},
},
{
id: 'domain',

View File

@@ -127,6 +127,17 @@ export const IncidentioBlock: BlockConfig<IncidentioResponse> = {
type: 'short-input',
placeholder: 'Enter incident name...',
condition: { field: 'operation', value: 'incidentio_incidents_create' },
wandConfig: {
enabled: true,
prompt: `Generate a concise, descriptive incident name based on the user's description.
The incident name should:
- Be clear and descriptive
- Indicate the nature of the issue
- Be suitable for incident tracking and communication
Return ONLY the incident name - no explanations.`,
placeholder: 'Describe the incident (e.g., "database outage", "API latency issues")...',
},
},
{
id: 'summary',
@@ -137,6 +148,17 @@ export const IncidentioBlock: BlockConfig<IncidentioResponse> = {
field: 'operation',
value: ['incidentio_incidents_create', 'incidentio_incidents_update'],
},
wandConfig: {
enabled: true,
prompt: `Generate an incident summary based on the user's description.
The summary should:
- Clearly describe the impact and scope of the incident
- Include relevant technical details
- Be professional and suitable for stakeholder communication
Return ONLY the summary text - no explanations.`,
placeholder: 'Describe the incident details (e.g., "users unable to login since 2pm")...',
},
},
{
id: 'severity_id',
@@ -281,6 +303,18 @@ export const IncidentioBlock: BlockConfig<IncidentioResponse> = {
placeholder: 'Enter escalation title...',
condition: { field: 'operation', value: 'incidentio_escalations_create' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate an escalation title based on the user's description.
The title should:
- Be concise and urgent
- Clearly indicate the escalation reason
- Be suitable for paging and alerting
Return ONLY the title - no explanations.`,
placeholder:
'Describe the escalation reason (e.g., "critical system down", "security breach detected")...',
},
},
{
id: 'escalation_path_id',
@@ -383,6 +417,26 @@ export const IncidentioBlock: BlockConfig<IncidentioResponse> = {
'JSON configuration with rotations. Example: {"rotations": [{"name": "Primary", "users": [{"id": "user_id"}], "handover_start_at": "2024-01-01T09:00:00Z", "handovers": [{"interval": 1, "interval_type": "weekly"}]}]}',
condition: { field: 'operation', value: 'incidentio_schedules_create' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a JSON schedule configuration for incident.io based on the user's description.
The configuration must follow this structure:
{
"rotations": [
{
"name": "Rotation Name",
"users": [{"id": "user_id"}],
"handover_start_at": "ISO8601 timestamp",
"handovers": [{"interval": number, "interval_type": "daily|weekly|monthly"}]
}
]
}
Return ONLY the JSON object - no explanations or markdown formatting.`,
placeholder:
'Describe the schedule (e.g., "weekly rotation between 3 engineers starting Monday 9am")...',
generationType: 'json-object',
},
},
{
id: 'timezone',
@@ -427,6 +481,18 @@ export const IncidentioBlock: BlockConfig<IncidentioResponse> = {
value: ['incidentio_custom_fields_create', 'incidentio_custom_fields_update'],
},
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a description for a custom field based on the user's description.
The description should:
- Explain what the field is used for
- Provide guidance on how to fill it out
- Be clear and concise
Return ONLY the description text - no explanations.`,
placeholder:
'Describe the custom field purpose (e.g., "tracks affected customer count", "categorizes incident type")...',
},
},
{
id: 'field_type',
@@ -454,6 +520,18 @@ export const IncidentioBlock: BlockConfig<IncidentioResponse> = {
value: ['incidentio_incident_roles_create', 'incidentio_incident_roles_update'],
},
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a description for an incident role based on the user's description.
The description should:
- Explain the role's responsibilities
- Clarify when this role is needed
- Be suitable for incident response documentation
Return ONLY the description text - no explanations.`,
placeholder:
'Describe the role (e.g., "coordinates communication with stakeholders", "leads technical investigation")...',
},
},
{
id: 'instructions',
@@ -465,6 +543,18 @@ export const IncidentioBlock: BlockConfig<IncidentioResponse> = {
value: ['incidentio_incident_roles_create', 'incidentio_incident_roles_update'],
},
required: true,
wandConfig: {
enabled: true,
prompt: `Generate instructions for an incident role based on the user's description.
The instructions should:
- Provide step-by-step guidance for the role
- Include key actions and responsibilities
- Be actionable and clear during an incident
Return ONLY the instructions text - no explanations.`,
placeholder:
'Describe what the role should do (e.g., "manage external communications during outages")...',
},
},
{
id: 'shortform',
@@ -504,6 +594,19 @@ export const IncidentioBlock: BlockConfig<IncidentioResponse> = {
type: 'short-input',
placeholder: 'ISO 8601 format (e.g., 2024-01-01T00:00:00Z)...',
condition: { field: 'operation', value: 'incidentio_schedule_entries_list' },
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp based on the user's description.
The timestamp should be in the format: YYYY-MM-DDTHH:MM:SSZ (UTC timezone).
Examples:
- "beginning of this week" -> Monday of current week at 00:00:00Z
- "last month" -> First day of previous month at 00:00:00Z
- "yesterday" -> Yesterday's date at 00:00:00Z
Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the start date (e.g., "beginning of this week", "last month")...',
generationType: 'timestamp',
},
},
{
id: 'entry_window_end',
@@ -511,6 +614,19 @@ export const IncidentioBlock: BlockConfig<IncidentioResponse> = {
type: 'short-input',
placeholder: 'ISO 8601 format (e.g., 2024-12-31T23:59:59Z)...',
condition: { field: 'operation', value: 'incidentio_schedule_entries_list' },
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp based on the user's description.
The timestamp should be in the format: YYYY-MM-DDTHH:MM:SSZ (UTC timezone).
Examples:
- "end of this week" -> Sunday of current week at 23:59:59Z
- "end of next month" -> Last day of next month at 23:59:59Z
- "tomorrow" -> Tomorrow's date at 23:59:59Z
Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the end date (e.g., "end of this week", "end of next month")...',
generationType: 'timestamp',
},
},
// Schedule Overrides inputs
{
@@ -552,6 +668,19 @@ export const IncidentioBlock: BlockConfig<IncidentioResponse> = {
placeholder: 'ISO 8601 format (e.g., 2024-01-01T00:00:00Z)...',
condition: { field: 'operation', value: 'incidentio_schedule_overrides_create' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp based on the user's description.
The timestamp should be in the format: YYYY-MM-DDTHH:MM:SSZ (UTC timezone).
Examples:
- "now" -> Current date and time in UTC
- "tomorrow at 9am" -> Tomorrow at 09:00:00Z
- "next Monday" -> Next Monday at 00:00:00Z
Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the start time (e.g., "now", "tomorrow at 9am")...',
generationType: 'timestamp',
},
},
{
id: 'end_at',
@@ -560,6 +689,19 @@ export const IncidentioBlock: BlockConfig<IncidentioResponse> = {
placeholder: 'ISO 8601 format (e.g., 2024-12-31T23:59:59Z)...',
condition: { field: 'operation', value: 'incidentio_schedule_overrides_create' },
required: true,
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp based on the user's description.
The timestamp should be in the format: YYYY-MM-DDTHH:MM:SSZ (UTC timezone).
Examples:
- "in 4 hours" -> Current time plus 4 hours
- "tomorrow at 5pm" -> Tomorrow at 17:00:00Z
- "end of next week" -> Next Sunday at 23:59:59Z
Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the end time (e.g., "in 4 hours", "tomorrow at 5pm")...',
generationType: 'timestamp',
},
},
// Escalation Paths inputs
{
@@ -573,6 +715,24 @@ export const IncidentioBlock: BlockConfig<IncidentioResponse> = {
value: 'incidentio_escalation_paths_create',
},
required: true,
wandConfig: {
enabled: true,
prompt: `Generate a JSON array for escalation path configuration based on the user's description.
The array must follow this structure:
[
{
"targets": [{"id": "target_id", "type": "user|schedule", "urgency": "high|low"}],
"time_to_ack_seconds": number
}
]
Each level represents an escalation step with acknowledgment timeout.
Return ONLY the JSON array - no explanations or markdown formatting.`,
placeholder:
'Describe the escalation path (e.g., "page on-call first, then manager after 5 min")...',
generationType: 'json-object',
},
},
{
id: 'path',
@@ -596,6 +756,18 @@ export const IncidentioBlock: BlockConfig<IncidentioResponse> = {
field: 'operation',
value: ['incidentio_escalation_paths_create', 'incidentio_escalation_paths_update'],
},
wandConfig: {
enabled: true,
prompt: `Generate a JSON array for working hours configuration based on the user's description.
The array must follow this structure:
[
{"weekday": "monday|tuesday|wednesday|thursday|friday|saturday|sunday", "start_time": "HH:MM", "end_time": "HH:MM"}
]
Return ONLY the JSON array - no explanations or markdown formatting.`,
placeholder: 'Describe working hours (e.g., "9-5 Monday to Friday", "24/7 coverage")...',
generationType: 'json-object',
},
},
// API Key (common)
{

View File

@@ -130,6 +130,19 @@ export const IntercomBlock: BlockConfig = {
field: 'operation',
value: ['create_contact', 'update_contact'],
},
wandConfig: {
enabled: true,
prompt: `Generate a Unix timestamp in seconds based on the user's description.
The timestamp should be a Unix epoch time in seconds (10 digits).
Examples:
- "yesterday" -> Yesterday at 00:00:00 as Unix timestamp
- "last week" -> 7 days ago at 00:00:00 as Unix timestamp
- "January 1, 2024" -> 1704067200
Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the signup date (e.g., "yesterday", "January 1, 2024")...',
generationType: 'timestamp',
},
},
{
id: 'last_seen_at',
@@ -140,6 +153,19 @@ export const IntercomBlock: BlockConfig = {
field: 'operation',
value: ['create_contact', 'update_contact'],
},
wandConfig: {
enabled: true,
prompt: `Generate a Unix timestamp in seconds based on the user's description.
The timestamp should be a Unix epoch time in seconds (10 digits).
Examples:
- "now" -> Current Unix timestamp
- "1 hour ago" -> Current time minus 3600 seconds
- "today at noon" -> Today at 12:00:00 as Unix timestamp
Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the last seen time (e.g., "now", "1 hour ago")...',
generationType: 'timestamp',
},
},
{
id: 'owner_id',
@@ -173,6 +199,17 @@ export const IntercomBlock: BlockConfig = {
field: 'operation',
value: ['create_contact', 'update_contact'],
},
wandConfig: {
enabled: true,
prompt: `Generate a JSON object for Intercom custom attributes based on the user's description.
The object should contain key-value pairs for custom contact attributes.
Example: {"plan_type": "enterprise", "signup_source": "website", "industry": "technology"}
Return ONLY the JSON object - no explanations or markdown formatting.`,
placeholder:
'Describe the custom attributes (e.g., "enterprise customer, signed up from marketing campaign")...',
generationType: 'json-object',
},
},
{
id: 'contact_company_id',
@@ -194,6 +231,17 @@ export const IntercomBlock: BlockConfig = {
field: 'operation',
value: ['search_contacts', 'search_conversations'],
},
wandConfig: {
enabled: true,
prompt: `Generate a search query for Intercom based on the user's description.
This can be either:
1. A simple text search query
2. A JSON query object for advanced filtering
Return ONLY the query - no explanations.`,
placeholder:
'Describe what you want to search for (e.g., "active users from last week", "open conversations about billing")...',
},
},
{
id: 'sort_field',
@@ -310,6 +358,20 @@ export const IntercomBlock: BlockConfig = {
field: 'operation',
value: ['create_company'],
},
wandConfig: {
enabled: true,
prompt: `Generate a Unix timestamp in seconds based on the user's description.
The timestamp should be a Unix epoch time in seconds (10 digits).
Examples:
- "2 years ago" -> Calculate 2 years ago as Unix timestamp
- "January 2022" -> January 1, 2022 at 00:00:00 as Unix timestamp
- "last year" -> 1 year ago at 00:00:00 as Unix timestamp
Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
placeholder:
'Describe when the company was created (e.g., "2 years ago", "January 2022")...',
generationType: 'timestamp',
},
},
// Conversation fields
{
@@ -396,6 +458,18 @@ export const IntercomBlock: BlockConfig = {
field: 'operation',
value: ['reply_conversation', 'create_message'],
},
wandConfig: {
enabled: true,
prompt: `Generate a message body for Intercom based on the user's description.
The message should:
- Be professional and friendly
- Be clear and concise
- Match the context (support reply, outreach, etc.)
Return ONLY the message text - no explanations.`,
placeholder:
'Describe the message you want to send (e.g., "thank customer for feedback", "follow up on support ticket")...',
},
},
{
id: 'admin_id',
@@ -427,6 +501,19 @@ export const IntercomBlock: BlockConfig = {
field: 'operation',
value: ['reply_conversation'],
},
wandConfig: {
enabled: true,
prompt: `Generate a Unix timestamp in seconds based on the user's description.
The timestamp should be a Unix epoch time in seconds (10 digits).
Examples:
- "now" -> Current Unix timestamp
- "5 minutes ago" -> Current time minus 300 seconds
- "earlier today" -> Today at 09:00:00 as Unix timestamp
Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the reply time (e.g., "now", "5 minutes ago")...',
generationType: 'timestamp',
},
},
// Ticket fields
{
@@ -461,6 +548,16 @@ export const IntercomBlock: BlockConfig = {
field: 'operation',
value: ['create_ticket'],
},
wandConfig: {
enabled: true,
prompt: `Generate a JSON array of contact identifiers for Intercom based on the user's description.
The array should contain contact identifier objects.
Example: [{"id": "contact_id_1"}, {"id": "contact_id_2"}] or [{"email": "user@example.com"}]
Return ONLY the JSON array - no explanations or markdown formatting.`,
placeholder: 'Describe the contacts (e.g., "user with email john@example.com")...',
generationType: 'json-object',
},
},
{
id: 'ticket_attributes',
@@ -472,6 +569,16 @@ export const IntercomBlock: BlockConfig = {
field: 'operation',
value: ['create_ticket'],
},
wandConfig: {
enabled: true,
prompt: `Generate a JSON object for Intercom ticket attributes based on the user's description.
The object should contain the ticket's custom attributes based on your ticket type schema.
Example: {"_default_title_": "Issue title", "_default_description_": "Issue description", "priority": "high"}
Return ONLY the JSON object - no explanations or markdown formatting.`,
placeholder: 'Describe the ticket (e.g., "high priority bug report about login issues")...',
generationType: 'json-object',
},
},
{
id: 'ticket_company_id',
@@ -492,6 +599,19 @@ export const IntercomBlock: BlockConfig = {
field: 'operation',
value: ['create_ticket'],
},
wandConfig: {
enabled: true,
prompt: `Generate a Unix timestamp in seconds based on the user's description.
The timestamp should be a Unix epoch time in seconds (10 digits).
Examples:
- "now" -> Current Unix timestamp
- "when the issue was reported" -> Use current time
- "yesterday" -> Yesterday at 00:00:00 as Unix timestamp
Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the ticket creation time (e.g., "now", "yesterday")...',
generationType: 'timestamp',
},
},
{
id: 'conversation_to_link_id',
@@ -554,6 +674,18 @@ export const IntercomBlock: BlockConfig = {
field: 'operation',
value: ['create_message'],
},
wandConfig: {
enabled: true,
prompt: `Generate an email subject line for Intercom based on the user's description.
The subject should:
- Be concise and attention-grabbing
- Clearly indicate the email purpose
- Be professional
Return ONLY the subject line - no explanations.`,
placeholder:
'Describe the email purpose (e.g., "welcome new customer", "feature announcement")...',
},
},
{
id: 'from_type',
@@ -608,6 +740,19 @@ export const IntercomBlock: BlockConfig = {
field: 'operation',
value: ['create_message'],
},
wandConfig: {
enabled: true,
prompt: `Generate a Unix timestamp in seconds based on the user's description.
The timestamp should be a Unix epoch time in seconds (10 digits).
Examples:
- "now" -> Current Unix timestamp
- "just now" -> Current Unix timestamp
- "a few minutes ago" -> Current time minus 300 seconds
Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the message time (e.g., "now", "just now")...',
generationType: 'timestamp',
},
},
// Pagination fields
{

View File

@@ -186,6 +186,18 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
placeholder: 'Enter new summary for the issue',
dependsOn: ['projectId'],
condition: { field: 'operation', value: ['update', 'write'] },
wandConfig: {
enabled: true,
prompt: `Generate a concise Jira issue summary/title based on the user's description.
The summary should:
- Be clear and descriptive
- Capture the essence of the issue
- Be suitable for issue tracking
Return ONLY the summary text - no explanations.`,
placeholder:
'Describe the issue (e.g., "login page not loading", "add dark mode feature")...',
},
},
{
id: 'description',
@@ -194,6 +206,18 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
placeholder: 'Enter new description for the issue',
dependsOn: ['projectId'],
condition: { field: 'operation', value: ['update', 'write'] },
wandConfig: {
enabled: true,
prompt: `Generate a detailed Jira issue description based on the user's description.
The description should:
- Provide context and details about the issue
- Include steps to reproduce (for bugs) or requirements (for features)
- Be professional and clear
Return ONLY the description text - no explanations.`,
placeholder:
'Describe the issue details (e.g., "users seeing 500 error when clicking submit")...',
},
},
// Write Issue additional fields
{
@@ -227,6 +251,19 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
placeholder: 'YYYY-MM-DD (e.g., 2024-12-31)',
dependsOn: ['projectId'],
condition: { field: 'operation', value: 'write' },
wandConfig: {
enabled: true,
prompt: `Generate a date in YYYY-MM-DD format based on the user's description.
Examples:
- "tomorrow" -> Calculate tomorrow's date
- "next week" -> Calculate 7 days from now
- "end of month" -> Calculate the last day of the current month
- "in 2 weeks" -> Calculate 14 days from now
Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the due date (e.g., "next Friday", "end of month")...',
generationType: 'timestamp',
},
},
{
id: 'reporter',
@@ -296,6 +333,17 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
type: 'long-input',
placeholder: 'Add optional comment for transition',
condition: { field: 'operation', value: 'transition' },
wandConfig: {
enabled: true,
prompt: `Generate a transition comment for a Jira issue based on the user's description.
The comment should:
- Explain the reason for the status change
- Provide any relevant context
- Be professional and informative
Return ONLY the comment text - no explanations.`,
placeholder: 'Describe the transition reason (e.g., "fixed bug", "ready for QA review")...',
},
},
// Search Issues fields
{
@@ -305,6 +353,22 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
required: true,
placeholder: 'Enter JQL query (e.g., project = PROJ AND status = "In Progress")',
condition: { field: 'operation', value: 'search' },
wandConfig: {
enabled: true,
prompt: `Generate a JQL (Jira Query Language) query based on the user's description.
JQL syntax examples:
- project = PROJ
- status = "In Progress"
- assignee = currentUser()
- created >= -7d
- priority = High AND status != Done
- labels in (bug, urgent)
Return ONLY the JQL query - no explanations or markdown formatting.`,
placeholder:
'Describe what you want to search for (e.g., "open bugs assigned to me", "high priority issues from last week")...',
generationType: 'sql-query',
},
},
{
id: 'maxResults',
@@ -321,6 +385,18 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
required: true,
placeholder: 'Enter comment text',
condition: { field: 'operation', value: ['add_comment', 'update_comment'] },
wandConfig: {
enabled: true,
prompt: `Generate a Jira issue comment based on the user's description.
The comment should:
- Be professional and informative
- Provide relevant updates or information
- Be suitable for team collaboration
Return ONLY the comment text - no explanations.`,
placeholder:
'Describe what you want to comment (e.g., "update on investigation", "requesting review")...',
},
},
{
id: 'commentId',
@@ -361,6 +437,18 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
type: 'long-input',
placeholder: 'Enter optional worklog comment',
condition: { field: 'operation', value: ['add_worklog', 'update_worklog'] },
wandConfig: {
enabled: true,
prompt: `Generate a worklog comment for Jira based on the user's description.
The comment should:
- Describe the work that was done
- Be concise but informative
- Be suitable for time tracking records
Return ONLY the comment text - no explanations.`,
placeholder:
'Describe the work done (e.g., "implemented API endpoint", "fixed login bug")...',
},
},
{
id: 'started',
@@ -368,6 +456,20 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
type: 'short-input',
placeholder: 'ISO timestamp (defaults to now)',
condition: { field: 'operation', value: ['add_worklog', 'update_worklog'] },
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp based on the user's description.
The timestamp should be in the format: YYYY-MM-DDTHH:MM:SS.sssZ (UTC timezone).
Examples:
- "now" -> Current timestamp
- "yesterday at 9am" -> Yesterday's date at 09:00:00.000Z
- "last Monday at 2pm" -> Calculate last Monday at 14:00:00.000Z
- "start of today" -> Today's date at 00:00:00.000Z
Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe when the work started (e.g., "yesterday at 9am")...',
generationType: 'timestamp',
},
},
{
id: 'worklogId',
@@ -408,6 +510,18 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
type: 'long-input',
placeholder: 'Add optional comment for the link',
condition: { field: 'operation', value: 'create_link' },
wandConfig: {
enabled: true,
prompt: `Generate a comment for a Jira issue link based on the user's description.
The comment should:
- Explain why the issues are linked
- Provide context for the relationship
- Be concise and clear
Return ONLY the comment text - no explanations.`,
placeholder:
'Describe the relationship (e.g., "blocks deployment", "related to refactoring effort")...',
},
},
{
id: 'linkId',

Some files were not shown because too many files have changed in this diff Show More