mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-11 07:58:06 -05:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a02016e247 | ||
|
|
8620ab255a | ||
|
|
47ddfb639e | ||
|
|
5d48c2780c | ||
|
|
38614fad79 | ||
|
|
6f32aea96b | ||
|
|
98e98496e8 | ||
|
|
659b46fa2f | ||
|
|
fb3d6d4c88 | ||
|
|
ec2cc82b72 | ||
|
|
274d5e3afc | ||
|
|
c552bb9c5f | ||
|
|
ad7b791242 | ||
|
|
ce4893a53c | ||
|
|
7f1ff7fd86 | ||
|
|
517f1a91b6 | ||
|
|
9b2490c4b1 | ||
|
|
dba7514350 | ||
|
|
e94de1dd26 | ||
|
|
a4e874b266 | ||
|
|
ec034f3fc7 | ||
|
|
e425d064c0 | ||
|
|
bcd1a2faf6 | ||
|
|
989a77261c | ||
|
|
71ae27b6cd | ||
|
|
5ab482127d | ||
|
|
b8bc632baa |
@@ -6,6 +6,7 @@ import Link from 'next/link'
|
||||
import { notFound } from 'next/navigation'
|
||||
import { StructuredData } from '@/components/structured-data'
|
||||
import { CodeBlock } from '@/components/ui/code-block'
|
||||
import { CopyPageButton } from '@/components/ui/copy-page-button'
|
||||
import { source } from '@/lib/source'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
@@ -193,8 +194,19 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l
|
||||
component: <CustomFooter />,
|
||||
}}
|
||||
>
|
||||
<DocsTitle>{page.data.title}</DocsTitle>
|
||||
<DocsDescription>{page.data.description}</DocsDescription>
|
||||
<div className='relative'>
|
||||
<div className='absolute top-1 right-0'>
|
||||
<CopyPageButton
|
||||
content={`# ${page.data.title}
|
||||
|
||||
${page.data.description || ''}
|
||||
|
||||
${page.data.content || ''}`}
|
||||
/>
|
||||
</div>
|
||||
<DocsTitle>{page.data.title}</DocsTitle>
|
||||
<DocsDescription>{page.data.description}</DocsDescription>
|
||||
</div>
|
||||
<DocsBody>
|
||||
<MDX
|
||||
components={{
|
||||
|
||||
@@ -36,7 +36,9 @@
|
||||
/* Shift the sidebar slightly left from the content edge for extra breathing room */
|
||||
--sidebar-shift: 90px;
|
||||
--sidebar-offset: max(0px, calc(var(--edge-gutter) - var(--sidebar-shift)));
|
||||
--toc-offset: var(--edge-gutter);
|
||||
/* Shift TOC slightly right to match sidebar spacing for symmetry */
|
||||
--toc-shift: 90px;
|
||||
--toc-offset: max(0px, calc(var(--edge-gutter) - var(--toc-shift)));
|
||||
/* Sidebar and TOC have 20px internal padding - navbar accounts for this directly */
|
||||
/* Extra gap between sidebar/TOC and the main text content */
|
||||
--content-gap: 1.75rem;
|
||||
@@ -107,8 +109,21 @@ aside#nd-sidebar {
|
||||
aside#nd-sidebar {
|
||||
left: var(--sidebar-offset) !important;
|
||||
}
|
||||
[data-toc] {
|
||||
margin-right: var(--toc-offset) !important;
|
||||
/* TOC positioning - target all possible selectors */
|
||||
[data-toc],
|
||||
aside[data-toc],
|
||||
div[data-toc],
|
||||
.fd-toc,
|
||||
#nd-toc,
|
||||
nav[data-toc],
|
||||
aside:has([role="complementary"]) {
|
||||
right: var(--toc-offset) !important;
|
||||
}
|
||||
|
||||
/* Alternative TOC container targeting */
|
||||
[data-docs-page] > aside:last-child,
|
||||
main ~ aside {
|
||||
right: var(--toc-offset) !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,13 +19,13 @@ export function Navbar() {
|
||||
{/* Desktop: Single row layout */}
|
||||
<div className='hidden h-16 w-full items-center lg:flex'>
|
||||
<div
|
||||
className='grid w-full grid-cols-[auto_1fr_auto] items-center'
|
||||
className='relative flex w-full items-center justify-between'
|
||||
style={{
|
||||
paddingLeft: 'calc(var(--sidebar-offset) + 20px)',
|
||||
paddingRight: 'calc(var(--toc-offset) + 20px)',
|
||||
paddingRight: 'calc(var(--toc-offset) + 60px)',
|
||||
}}
|
||||
>
|
||||
{/* Left cluster: translate by sidebar delta to align with sidebar edge */}
|
||||
{/* Left cluster: logo */}
|
||||
<div className='flex items-center'>
|
||||
<Link href='/' className='flex min-w-[100px] items-center'>
|
||||
<Image
|
||||
@@ -38,12 +38,12 @@ export function Navbar() {
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Center cluster: search */}
|
||||
<div className='flex flex-1 items-center justify-center pl-32'>
|
||||
{/* Center cluster: search - absolutely positioned to center */}
|
||||
<div className='-translate-x-1/2 absolute left-1/2 flex items-center justify-center'>
|
||||
<SearchTrigger />
|
||||
</div>
|
||||
|
||||
{/* Right cluster aligns with TOC edge using the same right gutter */}
|
||||
{/* Right cluster aligns with TOC edge */}
|
||||
<div className='flex items-center gap-4'>
|
||||
<Link
|
||||
href='https://sim.ai'
|
||||
|
||||
42
apps/docs/components/ui/copy-page-button.tsx
Normal file
42
apps/docs/components/ui/copy-page-button.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { Check, Copy } from 'lucide-react'
|
||||
|
||||
interface CopyPageButtonProps {
|
||||
content: string
|
||||
}
|
||||
|
||||
export function CopyPageButton({ content }: CopyPageButtonProps) {
|
||||
const [copied, setCopied] = useState(false)
|
||||
|
||||
const handleCopy = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(content)
|
||||
setCopied(true)
|
||||
setTimeout(() => setCopied(false), 2000)
|
||||
} catch (err) {
|
||||
console.error('Failed to copy:', err)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={handleCopy}
|
||||
className='flex items-center gap-1.5 rounded-lg border border-border/40 bg-background px-2.5 py-1.5 text-muted-foreground/60 text-sm transition-all hover:border-border hover:bg-accent/50 hover:text-muted-foreground'
|
||||
aria-label={copied ? 'Copied to clipboard' : 'Copy page content'}
|
||||
>
|
||||
{copied ? (
|
||||
<>
|
||||
<Check className='h-4 w-4' />
|
||||
<span>Copied</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Copy className='h-4 w-4' />
|
||||
<span>Copy page</span>
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
@@ -207,18 +207,18 @@ Populate Clay with data from a JSON file. Enables direct communication and notif
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `webhookURL` | string | Yes | The webhook URL to populate |
|
||||
| `data` | json | Yes | The data to populate |
|
||||
| `authToken` | string | Yes | Auth token for Clay webhook authentication |
|
||||
| `webhookURL` | string | Ja | Die Webhook-URL, die befüllt werden soll |
|
||||
| `data` | json | Ja | Die Daten, die befüllt werden sollen |
|
||||
| `authToken` | string | Nein | Optionaler Auth-Token für die Clay-Webhook-Authentifizierung \(die meisten Webhooks benötigen dies nicht\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | json | Clay populate operation results including response data from Clay webhook |
|
||||
| `data` | json | Antwortdaten vom Clay-Webhook |
|
||||
| `metadata` | object | Webhook-Antwort-Metadaten |
|
||||
|
||||
## Notes
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="discord"
|
||||
color="#E0E0E0"
|
||||
color="#5865F2"
|
||||
icon={true}
|
||||
iconSvg={`<svg className="block-icon"
|
||||
|
||||
@@ -72,6 +72,7 @@ Eine Nachricht an einen Discord-Kanal senden
|
||||
| `channelId` | string | Ja | Die Discord-Kanal-ID, an die die Nachricht gesendet werden soll |
|
||||
| `content` | string | Nein | Der Textinhalt der Nachricht |
|
||||
| `serverId` | string | Ja | Die Discord-Server-ID \(Guild-ID\) |
|
||||
| `files` | file[] | Nein | Dateien, die an die Nachricht angehängt werden sollen |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
|
||||
@@ -85,10 +85,11 @@ Eine Datei zu Google Drive hochladen
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `fileName` | string | Ja | Der Name der hochzuladenden Datei |
|
||||
| `content` | string | Ja | Der Inhalt der hochzuladenden Datei |
|
||||
| `mimeType` | string | Nein | Der MIME-Typ der hochzuladenden Datei |
|
||||
| `file` | file | Nein | Binärdatei zum Hochladen (UserFile-Objekt) |
|
||||
| `content` | string | Nein | Textinhalt zum Hochladen (verwenden Sie entweder diesen ODER file, nicht beides) |
|
||||
| `mimeType` | string | Nein | Der MIME-Typ der hochzuladenden Datei (wird automatisch aus der Datei erkannt, wenn nicht angegeben) |
|
||||
| `folderSelector` | string | Nein | Wählen Sie den Ordner aus, in den die Datei hochgeladen werden soll |
|
||||
| `folderId` | string | Nein | Die ID des Ordners, in den die Datei hochgeladen werden soll \(interne Verwendung\) |
|
||||
| `folderId` | string | Nein | Die ID des Ordners, in den die Datei hochgeladen werden soll (interne Verwendung) |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
|
||||
@@ -135,6 +135,7 @@ Inhalte in einem Microsoft Teams-Chat schreiben oder aktualisieren
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `chatId` | string | Ja | Die ID des Chats, in den geschrieben werden soll |
|
||||
| `content` | string | Ja | Der Inhalt, der in die Nachricht geschrieben werden soll |
|
||||
| `files` | file[] | Nein | Dateien, die der Nachricht angehängt werden sollen |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
@@ -181,9 +182,10 @@ Schreiben oder senden einer Nachricht an einen Microsoft Teams-Kanal
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `teamId` | string | Ja | Die ID des Teams, an das geschrieben werden soll |
|
||||
| `channelId` | string | Ja | Die ID des Kanals, an den geschrieben werden soll |
|
||||
| `content` | string | Ja | Der Inhalt, der an den Kanal gesendet werden soll |
|
||||
| `teamId` | string | Ja | Die ID des Teams, in das geschrieben werden soll |
|
||||
| `channelId` | string | Ja | Die ID des Kanals, in den geschrieben werden soll |
|
||||
| `content` | string | Ja | Der Inhalt, der in den Kanal geschrieben werden soll |
|
||||
| `files` | file[] | Nein | Dateien, die der Nachricht angehängt werden sollen |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
|
||||
@@ -63,9 +63,10 @@ Eine Datei auf OneDrive hochladen
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `fileName` | string | Ja | Der Name der hochzuladenden Datei |
|
||||
| `content` | string | Ja | Der Inhalt der hochzuladenden Datei |
|
||||
| `file` | file | Nein | Die hochzuladende Datei (binär) |
|
||||
| `content` | string | Nein | Der hochzuladende Textinhalt (falls keine Datei bereitgestellt wird) |
|
||||
| `folderSelector` | string | Nein | Wählen Sie den Ordner aus, in den die Datei hochgeladen werden soll |
|
||||
| `manualFolderId` | string | Nein | Manuell eingegebene Ordner-ID \(erweiterter Modus\) |
|
||||
| `manualFolderId` | string | Nein | Manuell eingegebene Ordner-ID (erweiterter Modus) |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
|
||||
@@ -154,10 +154,11 @@ E-Mails über Outlook versenden
|
||||
| `to` | string | Ja | E-Mail-Adresse des Empfängers |
|
||||
| `subject` | string | Ja | E-Mail-Betreff |
|
||||
| `body` | string | Ja | E-Mail-Inhalt |
|
||||
| `replyToMessageId` | string | Nein | Nachrichten-ID für Antworten \(für Threading\) |
|
||||
| `replyToMessageId` | string | Nein | Nachrichten-ID, auf die geantwortet wird \(für Threading\) |
|
||||
| `conversationId` | string | Nein | Konversations-ID für Threading |
|
||||
| `cc` | string | Nein | CC-Empfänger \(durch Kommas getrennt\) |
|
||||
| `bcc` | string | Nein | BCC-Empfänger \(durch Kommas getrennt\) |
|
||||
| `attachments` | file[] | Nein | Dateien, die an die E-Mail angehängt werden sollen |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
@@ -177,10 +178,11 @@ E-Mails mit Outlook erstellen
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `to` | string | Ja | E-Mail-Adresse des Empfängers |
|
||||
| `subject` | string | Ja | Betreff der E-Mail |
|
||||
| `body` | string | Ja | Inhalt der E-Mail |
|
||||
| `cc` | string | Nein | CC-Empfänger \(durch Komma getrennt\) |
|
||||
| `bcc` | string | Nein | BCC-Empfänger \(durch Komma getrennt\) |
|
||||
| `subject` | string | Ja | E-Mail-Betreff |
|
||||
| `body` | string | Ja | E-Mail-Inhalt |
|
||||
| `cc` | string | Nein | CC-Empfänger \(durch Kommas getrennt\) |
|
||||
| `bcc` | string | Nein | BCC-Empfänger \(durch Kommas getrennt\) |
|
||||
| `attachments` | file[] | Nein | Dateien, die an den E-Mail-Entwurf angehängt werden sollen |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
|
||||
@@ -139,7 +139,8 @@ Suche nach ähnlichen Vektoren in einer Qdrant-Sammlung
|
||||
| `collection` | string | Ja | Sammlungsname |
|
||||
| `vector` | array | Ja | Zu suchender Vektor |
|
||||
| `limit` | number | Nein | Anzahl der zurückzugebenden Ergebnisse |
|
||||
| `filter` | object | Nein | Filter für die Suche |
|
||||
| `filter` | object | Nein | Auf die Suche anzuwendender Filter |
|
||||
| `search_return_data` | string | Nein | Aus der Suche zurückzugebende Daten |
|
||||
| `with_payload` | boolean | Nein | Payload in Antwort einschließen |
|
||||
| `with_vector` | boolean | Nein | Vektor in Antwort einschließen |
|
||||
|
||||
@@ -161,7 +162,8 @@ Punkte anhand der ID aus einer Qdrant-Sammlung abrufen
|
||||
| `url` | string | Ja | Qdrant-Basis-URL |
|
||||
| `apiKey` | string | Nein | Qdrant-API-Schlüssel \(optional\) |
|
||||
| `collection` | string | Ja | Sammlungsname |
|
||||
| `ids` | array | Ja | Array von abzurufenden Punkt-IDs |
|
||||
| `ids` | array | Ja | Array von Punkt-IDs zum Abrufen |
|
||||
| `fetch_return_data` | string | Nein | Aus dem Abruf zurückzugebende Daten |
|
||||
| `with_payload` | boolean | Nein | Payload in Antwort einschließen |
|
||||
| `with_vector` | boolean | Nein | Vektor in Antwort einschließen |
|
||||
|
||||
|
||||
@@ -199,6 +199,26 @@ Ein neues Element zu einer SharePoint-Liste hinzufügen
|
||||
| --------- | ---- | ----------- |
|
||||
| `item` | object | Erstelltes SharePoint-Listenelement |
|
||||
|
||||
### `sharepoint_upload_file`
|
||||
|
||||
Dateien in eine SharePoint-Dokumentenbibliothek hochladen
|
||||
|
||||
#### Eingabe
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Nein | Die ID der SharePoint-Website |
|
||||
| `driveId` | string | Nein | Die ID der Dokumentenbibliothek (Laufwerk). Wenn nicht angegeben, wird das Standardlaufwerk verwendet. |
|
||||
| `folderPath` | string | Nein | Optionaler Ordnerpfad innerhalb der Dokumentenbibliothek (z.B. /Documents/Subfolder) |
|
||||
| `fileName` | string | Nein | Optional: Überschreiben des hochgeladenen Dateinamens |
|
||||
| `files` | file[] | Nein | Dateien, die nach SharePoint hochgeladen werden sollen |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `uploadedFiles` | array | Array von hochgeladenen Dateiobjekten |
|
||||
|
||||
## Hinweise
|
||||
|
||||
- Kategorie: `tools`
|
||||
|
||||
@@ -78,7 +78,8 @@ Senden Sie Nachrichten an Slack-Kanäle oder Benutzer über die Slack-API. Unter
|
||||
| `authMethod` | string | Nein | Authentifizierungsmethode: oauth oder bot_token |
|
||||
| `botToken` | string | Nein | Bot-Token für Custom Bot |
|
||||
| `channel` | string | Ja | Ziel-Slack-Kanal \(z.B. #general\) |
|
||||
| `text` | string | Ja | Zu sendender Nachrichtentext \(unterstützt Slack mrkdwn-Formatierung\) |
|
||||
| `text` | string | Ja | Nachrichtentext zum Senden \(unterstützt Slack mrkdwn-Formatierung\) |
|
||||
| `files` | file[] | Nein | Dateien, die an die Nachricht angehängt werden sollen |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
|
||||
@@ -202,6 +202,28 @@ Daten in eine Supabase-Tabelle einfügen oder aktualisieren (Upsert-Operation)
|
||||
| `message` | string | Statusmeldung der Operation |
|
||||
| `results` | array | Array der eingefügten/aktualisierten Datensätze |
|
||||
|
||||
### `supabase_vector_search`
|
||||
|
||||
Ähnlichkeitssuche mit pgvector in einer Supabase-Tabelle durchführen
|
||||
|
||||
#### Eingabe
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Ja | Ihre Supabase-Projekt-ID \(z.B. jdrkgepadsdopsntdlom\) |
|
||||
| `functionName` | string | Ja | Der Name der PostgreSQL-Funktion, die die Vektorsuche durchführt \(z.B. match_documents\) |
|
||||
| `queryEmbedding` | array | Ja | Der Abfragevektor/Embedding, nach dem ähnliche Elemente gesucht werden sollen |
|
||||
| `matchThreshold` | number | Nein | Minimaler Ähnlichkeitsschwellenwert \(0-1\), typischerweise 0,7-0,9 |
|
||||
| `matchCount` | number | Nein | Maximale Anzahl der zurückzugebenden Ergebnisse \(Standard: 10\) |
|
||||
| `apiKey` | string | Ja | Ihr Supabase Service Role Secret Key |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Statusmeldung der Operation |
|
||||
| `results` | array | Array von Datensätzen mit Ähnlichkeitswerten aus der Vektorsuche. Jeder Datensatz enthält ein Ähnlichkeitsfeld \(0-1\), das angibt, wie ähnlich er dem Abfragevektor ist. |
|
||||
|
||||
## Hinweise
|
||||
|
||||
- Kategorie: `tools`
|
||||
|
||||
@@ -190,6 +190,26 @@ Senden Sie Animationen (GIFs) an Telegram-Kanäle oder Benutzer über die Telegr
|
||||
| `message` | string | Erfolgs- oder Fehlermeldung |
|
||||
| `data` | object | Telegram-Nachrichtendaten einschließlich optionaler Medien |
|
||||
|
||||
### `telegram_send_document`
|
||||
|
||||
Senden Sie Dokumente (PDF, ZIP, DOC, etc.) an Telegram-Kanäle oder -Nutzer über die Telegram Bot API.
|
||||
|
||||
#### Eingabe
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `botToken` | string | Ja | Ihr Telegram Bot API-Token |
|
||||
| `chatId` | string | Ja | Ziel-Telegram-Chat-ID |
|
||||
| `files` | file[] | Nein | Zu sendende Dokumentdatei \(PDF, ZIP, DOC, etc.\). Maximale Größe: 50MB |
|
||||
| `caption` | string | Nein | Dokumentbeschreibung \(optional\) |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Erfolgs- oder Fehlermeldung |
|
||||
| `data` | object | Telegram-Nachrichtendaten einschließlich Dokument |
|
||||
|
||||
## Hinweise
|
||||
|
||||
- Kategorie: `tools`
|
||||
|
||||
@@ -59,8 +59,9 @@ Verarbeiten und analysieren Sie Bilder mit fortschrittlichen Vision-Modellen. F
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Ja | API-Schlüssel für den ausgewählten Modellanbieter |
|
||||
| `imageUrl` | string | Ja | Öffentlich zugängliche Bild-URL |
|
||||
| `model` | string | Nein | Zu verwendendes Vision-Modell \(gpt-4o, claude-3-opus-20240229, etc\) |
|
||||
| `imageUrl` | string | Nein | Öffentlich zugängliche Bild-URL |
|
||||
| `imageFile` | file | Nein | Zu analysierende Bilddatei |
|
||||
| `model` | string | Nein | Zu verwendendes Vision-Modell \(gpt-4o, claude-3-opus-20240229, usw.\) |
|
||||
| `prompt` | string | Nein | Benutzerdefinierte Eingabeaufforderung für die Bildanalyse |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
145
apps/docs/content/docs/de/tools/webflow.mdx
Normal file
145
apps/docs/content/docs/de/tools/webflow.mdx
Normal file
@@ -0,0 +1,145 @@
|
||||
---
|
||||
title: Webflow
|
||||
description: Webflow CMS-Sammlungen verwalten
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="webflow"
|
||||
color="#E0E0E0"
|
||||
icon={true}
|
||||
iconSvg={`<svg className="block-icon"
|
||||
|
||||
|
||||
|
||||
viewBox='0 0 1080 674'
|
||||
fill='none'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
>
|
||||
<path
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
d='M1080 0L735.386 673.684H411.695L555.916 394.481H549.445C430.464 548.934 252.942 650.61 -0.000488281 673.684V398.344C-0.000488281 398.344 161.813 388.787 256.938 288.776H-0.000488281V0.0053214H288.771V237.515L295.252 237.489L413.254 0.0053214H631.644V236.009L638.126 235.999L760.555 0H1080Z'
|
||||
fill='#146EF5'
|
||||
/>
|
||||
</svg>`}
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[Webflow](https://webflow.com/) ist eine leistungsstarke visuelle Webdesign-Plattform, mit der Sie responsive Websites ohne Programmierung erstellen können. Sie kombiniert eine visuelle Design-Oberfläche mit einem robusten CMS (Content Management System), das es Ihnen ermöglicht, dynamische Inhalte für Ihre Websites zu erstellen, zu verwalten und zu veröffentlichen.
|
||||
|
||||
Mit Webflow können Sie:
|
||||
|
||||
- **Visuell gestalten**: Erstellen Sie benutzerdefinierte Websites mit einem visuellen Editor, der sauberen, semantischen HTML/CSS-Code generiert
|
||||
- **Inhalte dynamisch verwalten**: Nutzen Sie das CMS, um Sammlungen strukturierter Inhalte wie Blogbeiträge, Produkte, Teammitglieder oder beliebige benutzerdefinierte Daten zu erstellen
|
||||
- **Sofort veröffentlichen**: Stellen Sie Ihre Websites auf Webflows Hosting bereit oder exportieren Sie den Code für benutzerdefiniertes Hosting
|
||||
- **Responsive Designs erstellen**: Bauen Sie Websites, die nahtlos auf Desktop, Tablet und Mobilgeräten funktionieren
|
||||
- **Sammlungen anpassen**: Definieren Sie benutzerdefinierte Felder und Datenstrukturen für Ihre Inhaltstypen
|
||||
- **Inhaltsaktualisierungen automatisieren**: Verwalten Sie Ihre CMS-Inhalte programmgesteuert über APIs
|
||||
|
||||
In Sim ermöglicht die Webflow-Integration Ihren Agenten, nahtlos mit Ihren Webflow-CMS-Sammlungen über API-Authentifizierung zu interagieren. Dies ermöglicht leistungsstarke Automatisierungsszenarien wie das automatische Erstellen von Blogbeiträgen aus KI-generierten Inhalten, das Aktualisieren von Produktinformationen, das Verwalten von Teammitgliederprofilen und das Abrufen von CMS-Elementen für die dynamische Inhaltsgenerierung. Ihre Agenten können vorhandene Elemente auflisten, um Ihre Inhalte zu durchsuchen, bestimmte Elemente nach ID abrufen, neue Einträge erstellen, um frische Inhalte hinzuzufügen, bestehende Elemente aktualisieren, um Informationen aktuell zu halten, und veraltete Inhalte löschen. Diese Integration überbrückt die Lücke zwischen Ihren KI-Workflows und Ihrem Webflow-CMS und ermöglicht automatisierte Inhaltsverwaltung, dynamische Website-Aktualisierungen und optimierte Inhalts-Workflows, die Ihre Websites ohne manuelles Eingreifen frisch und aktuell halten.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
## Gebrauchsanweisung
|
||||
|
||||
Integriert Webflow CMS in den Workflow. Kann Elemente in Webflow CMS-Sammlungen erstellen, abrufen, auflisten, aktualisieren oder löschen. Verwalten Sie Ihre Webflow-Inhalte programmatisch. Kann im Trigger-Modus verwendet werden, um Workflows auszulösen, wenn sich Sammlungselemente ändern oder Formulare übermittelt werden.
|
||||
|
||||
## Tools
|
||||
|
||||
### `webflow_list_items`
|
||||
|
||||
Alle Elemente aus einer Webflow CMS-Sammlung auflisten
|
||||
|
||||
#### Eingabe
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `collectionId` | string | Ja | ID der Sammlung |
|
||||
| `offset` | number | Nein | Offset für Paginierung \(optional\) |
|
||||
| `limit` | number | Nein | Maximale Anzahl der zurückzugebenden Elemente \(optional, Standard: 100\) |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `items` | json | Array von Sammlungselementen |
|
||||
| `metadata` | json | Metadaten über die Abfrage |
|
||||
|
||||
### `webflow_get_item`
|
||||
|
||||
Ein einzelnes Element aus einer Webflow CMS-Sammlung abrufen
|
||||
|
||||
#### Eingabe
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `collectionId` | string | Ja | ID der Sammlung |
|
||||
| `itemId` | string | Ja | ID des abzurufenden Elements |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `item` | json | Das abgerufene Elementobjekt |
|
||||
| `metadata` | json | Metadaten über das abgerufene Element |
|
||||
|
||||
### `webflow_create_item`
|
||||
|
||||
Ein neues Element in einer Webflow CMS-Sammlung erstellen
|
||||
|
||||
#### Eingabe
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `collectionId` | string | Ja | ID der Sammlung |
|
||||
| `fieldData` | json | Ja | Felddaten für das neue Element als JSON-Objekt. Die Schlüssel sollten mit den Feldnamen der Sammlung übereinstimmen. |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `item` | json | Das erstellte Element-Objekt |
|
||||
| `metadata` | json | Metadaten über das erstellte Element |
|
||||
|
||||
### `webflow_update_item`
|
||||
|
||||
Ein vorhandenes Element in einer Webflow CMS-Sammlung aktualisieren
|
||||
|
||||
#### Eingabe
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `collectionId` | string | Ja | ID der Sammlung |
|
||||
| `itemId` | string | Ja | ID des zu aktualisierenden Elements |
|
||||
| `fieldData` | json | Ja | Zu aktualisierende Felddaten als JSON-Objekt. Nur Felder einschließen, die geändert werden sollen. |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `item` | json | Das aktualisierte Element-Objekt |
|
||||
| `metadata` | json | Metadaten über das aktualisierte Element |
|
||||
|
||||
### `webflow_delete_item`
|
||||
|
||||
Ein Element aus einer Webflow CMS-Sammlung löschen
|
||||
|
||||
#### Eingabe
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `collectionId` | string | Ja | ID der Sammlung |
|
||||
| `itemId` | string | Ja | ID des zu löschenden Elements |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Ob die Löschung erfolgreich war |
|
||||
| `metadata` | json | Metadaten über die Löschung |
|
||||
|
||||
## Hinweise
|
||||
|
||||
- Kategorie: `tools`
|
||||
- Typ: `webflow`
|
||||
@@ -214,14 +214,14 @@ Populate Clay with data from a JSON file. Enables direct communication and notif
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `webhookURL` | string | Yes | The webhook URL to populate |
|
||||
| `data` | json | Yes | The data to populate |
|
||||
| `authToken` | string | Yes | Auth token for Clay webhook authentication |
|
||||
| `authToken` | string | No | Optional auth token for Clay webhook authentication \(most webhooks do not require this\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | json | Clay populate operation results including response data from Clay webhook |
|
||||
| `data` | json | Response data from Clay webhook |
|
||||
| `metadata` | object | Webhook response metadata |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="discord"
|
||||
color="#E0E0E0"
|
||||
color="#5865F2"
|
||||
icon={true}
|
||||
iconSvg={`<svg className="block-icon"
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
"typeform",
|
||||
"vision",
|
||||
"wealthbox",
|
||||
"webflow",
|
||||
"webhook",
|
||||
"whatsapp",
|
||||
"wikipedia",
|
||||
|
||||
@@ -143,6 +143,7 @@ Search for similar vectors in a Qdrant collection
|
||||
| `vector` | array | Yes | Vector to search for |
|
||||
| `limit` | number | No | Number of results to return |
|
||||
| `filter` | object | No | Filter to apply to the search |
|
||||
| `search_return_data` | string | No | Data to return from search |
|
||||
| `with_payload` | boolean | No | Include payload in response |
|
||||
| `with_vector` | boolean | No | Include vector in response |
|
||||
|
||||
@@ -165,6 +166,7 @@ Fetch points by ID from a Qdrant collection
|
||||
| `apiKey` | string | No | Qdrant API key \(optional\) |
|
||||
| `collection` | string | Yes | Collection name |
|
||||
| `ids` | array | Yes | Array of point IDs to fetch |
|
||||
| `fetch_return_data` | string | No | Data to return from fetch |
|
||||
| `with_payload` | boolean | No | Include payload in response |
|
||||
| `with_vector` | boolean | No | Include vector in response |
|
||||
|
||||
|
||||
150
apps/docs/content/docs/en/tools/webflow.mdx
Normal file
150
apps/docs/content/docs/en/tools/webflow.mdx
Normal file
@@ -0,0 +1,150 @@
|
||||
---
|
||||
title: Webflow
|
||||
description: Manage Webflow CMS collections
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="webflow"
|
||||
color="#E0E0E0"
|
||||
icon={true}
|
||||
iconSvg={`<svg className="block-icon"
|
||||
|
||||
|
||||
|
||||
viewBox='0 0 1080 674'
|
||||
fill='none'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
>
|
||||
<path
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
d='M1080 0L735.386 673.684H411.695L555.916 394.481H549.445C430.464 548.934 252.942 650.61 -0.000488281 673.684V398.344C-0.000488281 398.344 161.813 388.787 256.938 288.776H-0.000488281V0.0053214H288.771V237.515L295.252 237.489L413.254 0.0053214H631.644V236.009L638.126 235.999L760.555 0H1080Z'
|
||||
fill='#146EF5'
|
||||
/>
|
||||
</svg>`}
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[Webflow](https://webflow.com/) is a powerful visual web design platform that enables you to build responsive websites without writing code. It combines a visual design interface with a robust CMS (Content Management System) that allows you to create, manage, and publish dynamic content for your websites.
|
||||
|
||||
With Webflow, you can:
|
||||
|
||||
- **Design visually**: Create custom websites with a visual editor that generates clean, semantic HTML/CSS code
|
||||
- **Manage content dynamically**: Use the CMS to create collections of structured content like blog posts, products, team members, or any custom data
|
||||
- **Publish instantly**: Deploy your sites to Webflow's hosting or export the code for custom hosting
|
||||
- **Create responsive designs**: Build sites that work seamlessly across desktop, tablet, and mobile devices
|
||||
- **Customize collections**: Define custom fields and data structures for your content types
|
||||
- **Automate content updates**: Programmatically manage your CMS content through APIs
|
||||
|
||||
In Sim, the Webflow integration enables your agents to seamlessly interact with your Webflow CMS collections through API authentication. This allows for powerful automation scenarios such as automatically creating blog posts from AI-generated content, updating product information, managing team member profiles, and retrieving CMS items for dynamic content generation. Your agents can list existing items to browse your content, retrieve specific items by ID, create new entries to add fresh content, update existing items to keep information current, and delete outdated content. This integration bridges the gap between your AI workflows and your Webflow CMS, enabling automated content management, dynamic website updates, and streamlined content workflows that keep your sites fresh and up-to-date without manual intervention.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrates Webflow CMS into the workflow. Can create, get, list, update, or delete items in Webflow CMS collections. Manage your Webflow content programmatically. Can be used in trigger mode to trigger workflows when collection items change or forms are submitted.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `webflow_list_items`
|
||||
|
||||
List all items from a Webflow CMS collection
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `collectionId` | string | Yes | ID of the collection |
|
||||
| `offset` | number | No | Offset for pagination \(optional\) |
|
||||
| `limit` | number | No | Maximum number of items to return \(optional, default: 100\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `items` | json | Array of collection items |
|
||||
| `metadata` | json | Metadata about the query |
|
||||
|
||||
### `webflow_get_item`
|
||||
|
||||
Get a single item from a Webflow CMS collection
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `collectionId` | string | Yes | ID of the collection |
|
||||
| `itemId` | string | Yes | ID of the item to retrieve |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `item` | json | The retrieved item object |
|
||||
| `metadata` | json | Metadata about the retrieved item |
|
||||
|
||||
### `webflow_create_item`
|
||||
|
||||
Create a new item in a Webflow CMS collection
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `collectionId` | string | Yes | ID of the collection |
|
||||
| `fieldData` | json | Yes | Field data for the new item as a JSON object. Keys should match collection field names. |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `item` | json | The created item object |
|
||||
| `metadata` | json | Metadata about the created item |
|
||||
|
||||
### `webflow_update_item`
|
||||
|
||||
Update an existing item in a Webflow CMS collection
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `collectionId` | string | Yes | ID of the collection |
|
||||
| `itemId` | string | Yes | ID of the item to update |
|
||||
| `fieldData` | json | Yes | Field data to update as a JSON object. Only include fields you want to change. |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `item` | json | The updated item object |
|
||||
| `metadata` | json | Metadata about the updated item |
|
||||
|
||||
### `webflow_delete_item`
|
||||
|
||||
Delete an item from a Webflow CMS collection
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `collectionId` | string | Yes | ID of the collection |
|
||||
| `itemId` | string | Yes | ID of the item to delete |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the deletion was successful |
|
||||
| `metadata` | json | Metadata about the deletion |
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- Category: `tools`
|
||||
- Type: `webflow`
|
||||
@@ -207,18 +207,18 @@ Poblar Clay con datos de un archivo JSON. Permite comunicación directa y notifi
|
||||
|
||||
#### Entrada
|
||||
|
||||
| Parámetro | Tipo | Requerido | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `webhookURL` | string | Sí | La URL del webhook para poblar |
|
||||
| `data` | json | Sí | Los datos para poblar |
|
||||
| `authToken` | string | Sí | Token de autenticación para la autenticación del webhook de Clay |
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
| `webhookURL` | string | Sí | La URL del webhook a completar |
|
||||
| `data` | json | Sí | Los datos para completar |
|
||||
| `authToken` | string | No | Token de autenticación opcional para la autenticación del webhook de Clay \(la mayoría de los webhooks no requieren esto\) |
|
||||
|
||||
#### Salida
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | json | Resultados de la operación de poblado de Clay incluyendo datos de respuesta del webhook de Clay |
|
||||
| `data` | json | Datos de respuesta del webhook de Clay |
|
||||
| `metadata` | object | Metadatos de respuesta del webhook |
|
||||
|
||||
## Notas
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="discord"
|
||||
color="#E0E0E0"
|
||||
color="#5865F2"
|
||||
icon={true}
|
||||
iconSvg={`<svg className="block-icon"
|
||||
|
||||
@@ -67,11 +67,12 @@ Enviar un mensaje a un canal de Discord
|
||||
#### Entrada
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `botToken` | string | Sí | El token del bot para autenticación |
|
||||
| `channelId` | string | Sí | El ID del canal de Discord donde enviar el mensaje |
|
||||
| `channelId` | string | Sí | El ID del canal de Discord al que enviar el mensaje |
|
||||
| `content` | string | No | El contenido de texto del mensaje |
|
||||
| `serverId` | string | Sí | El ID del servidor de Discord \(ID del guild\) |
|
||||
| `files` | file[] | No | Archivos para adjuntar al mensaje |
|
||||
|
||||
#### Salida
|
||||
|
||||
|
||||
@@ -85,9 +85,10 @@ Subir un archivo a Google Drive
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
| `fileName` | string | Sí | El nombre del archivo a subir |
|
||||
| `content` | string | Sí | El contenido del archivo a subir |
|
||||
| `mimeType` | string | No | El tipo MIME del archivo a subir |
|
||||
| `folderSelector` | string | No | Selecciona la carpeta donde subir el archivo |
|
||||
| `file` | file | No | Archivo binario para subir \(objeto UserFile\) |
|
||||
| `content` | string | No | Contenido de texto para subir \(use esto O archivo, no ambos\) |
|
||||
| `mimeType` | string | No | El tipo MIME del archivo a subir \(auto-detectado del archivo si no se proporciona\) |
|
||||
| `folderSelector` | string | No | Seleccione la carpeta donde subir el archivo |
|
||||
| `folderId` | string | No | El ID de la carpeta donde subir el archivo \(uso interno\) |
|
||||
|
||||
#### Salida
|
||||
|
||||
@@ -133,8 +133,9 @@ Escribir o actualizar contenido en un chat de Microsoft Teams
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `chatId` | string | Sí | El ID del chat donde escribir |
|
||||
| `content` | string | Sí | El contenido a escribir en el mensaje |
|
||||
| `chatId` | string | Sí | El ID del chat en el que escribir |
|
||||
| `content` | string | Sí | El contenido para escribir en el mensaje |
|
||||
| `files` | file[] | No | Archivos para adjuntar al mensaje |
|
||||
|
||||
#### Salida
|
||||
|
||||
@@ -181,9 +182,10 @@ Escribir o enviar un mensaje a un canal de Microsoft Teams
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `teamId` | string | Sí | El ID del equipo al que escribir |
|
||||
| `channelId` | string | Sí | El ID del canal al que escribir |
|
||||
| `teamId` | string | Sí | El ID del equipo en el que escribir |
|
||||
| `channelId` | string | Sí | El ID del canal en el que escribir |
|
||||
| `content` | string | Sí | El contenido para escribir en el canal |
|
||||
| `files` | file[] | No | Archivos para adjuntar al mensaje |
|
||||
|
||||
#### Salida
|
||||
|
||||
|
||||
@@ -61,11 +61,12 @@ Subir un archivo a OneDrive
|
||||
#### Entrada
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
| `fileName` | string | Sí | El nombre del archivo a subir |
|
||||
| `content` | string | Sí | El contenido del archivo a subir |
|
||||
| `file` | file | No | El archivo a subir \(binario\) |
|
||||
| `content` | string | No | El contenido de texto a subir \(si no se proporciona un archivo\) |
|
||||
| `folderSelector` | string | No | Seleccionar la carpeta donde subir el archivo |
|
||||
| `manualFolderId` | string | No | ID de carpeta introducido manualmente \(modo avanzado\) |
|
||||
| `manualFolderId` | string | No | ID de carpeta ingresado manualmente \(modo avanzado\) |
|
||||
|
||||
#### Salida
|
||||
|
||||
|
||||
@@ -150,14 +150,15 @@ Enviar correos electrónicos usando Outlook
|
||||
#### Entrada
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `to` | string | Sí | Dirección de correo electrónico del destinatario |
|
||||
| `subject` | string | Sí | Asunto del correo electrónico |
|
||||
| `body` | string | Sí | Contenido del cuerpo del correo electrónico |
|
||||
| `replyToMessageId` | string | No | ID del mensaje al que responder (para hilos) |
|
||||
| `replyToMessageId` | string | No | ID del mensaje al que responder \(para hilos\) |
|
||||
| `conversationId` | string | No | ID de conversación para hilos |
|
||||
| `cc` | string | No | Destinatarios en CC (separados por comas) |
|
||||
| `bcc` | string | No | Destinatarios en CCO (separados por comas) |
|
||||
| `cc` | string | No | Destinatarios en CC \(separados por comas\) |
|
||||
| `bcc` | string | No | Destinatarios en CCO \(separados por comas\) |
|
||||
| `attachments` | file[] | No | Archivos para adjuntar al correo electrónico |
|
||||
|
||||
#### Salida
|
||||
|
||||
@@ -181,6 +182,7 @@ Crear borradores de correos electrónicos usando Outlook
|
||||
| `body` | string | Sí | Contenido del cuerpo del correo electrónico |
|
||||
| `cc` | string | No | Destinatarios en CC \(separados por comas\) |
|
||||
| `bcc` | string | No | Destinatarios en CCO \(separados por comas\) |
|
||||
| `attachments` | file[] | No | Archivos para adjuntar al borrador de correo electrónico |
|
||||
|
||||
#### Salida
|
||||
|
||||
|
||||
@@ -133,13 +133,14 @@ Buscar vectores similares en una colección de Qdrant
|
||||
#### Entrada
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
| `url` | string | Sí | URL base de Qdrant |
|
||||
| `apiKey` | string | No | Clave API de Qdrant \(opcional\) |
|
||||
| `collection` | string | Sí | Nombre de la colección |
|
||||
| `vector` | array | Sí | Vector a buscar |
|
||||
| `vector` | array | Sí | Vector para buscar |
|
||||
| `limit` | number | No | Número de resultados a devolver |
|
||||
| `filter` | object | No | Filtro a aplicar a la búsqueda |
|
||||
| `filter` | object | No | Filtro para aplicar a la búsqueda |
|
||||
| `search_return_data` | string | No | Datos a devolver de la búsqueda |
|
||||
| `with_payload` | boolean | No | Incluir payload en la respuesta |
|
||||
| `with_vector` | boolean | No | Incluir vector en la respuesta |
|
||||
|
||||
@@ -157,11 +158,12 @@ Obtener puntos por ID desde una colección de Qdrant
|
||||
#### Entrada
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
| `url` | string | Sí | URL base de Qdrant |
|
||||
| `apiKey` | string | No | Clave API de Qdrant \(opcional\) |
|
||||
| `collection` | string | Sí | Nombre de la colección |
|
||||
| `ids` | array | Sí | Array de IDs de puntos a obtener |
|
||||
| `ids` | array | Sí | Array de IDs de puntos para recuperar |
|
||||
| `fetch_return_data` | string | No | Datos a devolver de la recuperación |
|
||||
| `with_payload` | boolean | No | Incluir payload en la respuesta |
|
||||
| `with_vector` | boolean | No | Incluir vector en la respuesta |
|
||||
|
||||
|
||||
@@ -199,6 +199,26 @@ Añadir un nuevo elemento a una lista de SharePoint
|
||||
| --------- | ---- | ----------- |
|
||||
| `item` | object | Elemento de lista de SharePoint creado |
|
||||
|
||||
### `sharepoint_upload_file`
|
||||
|
||||
Subir archivos a una biblioteca de documentos de SharePoint
|
||||
|
||||
#### Entrada
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | cadena | No | El ID del sitio de SharePoint |
|
||||
| `driveId` | cadena | No | El ID de la biblioteca de documentos \(unidad\). Si no se proporciona, usa la unidad predeterminada. |
|
||||
| `folderPath` | cadena | No | Ruta de carpeta opcional dentro de la biblioteca de documentos \(p. ej., /Documents/Subfolder\) |
|
||||
| `fileName` | cadena | No | Opcional: sobrescribir el nombre del archivo subido |
|
||||
| `files` | archivo[] | No | Archivos para subir a SharePoint |
|
||||
|
||||
#### Salida
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `uploadedFiles` | array | Array de objetos de archivos subidos |
|
||||
|
||||
## Notas
|
||||
|
||||
- Categoría: `tools`
|
||||
|
||||
@@ -79,6 +79,7 @@ Envía mensajes a canales o usuarios de Slack a través de la API de Slack. Comp
|
||||
| `botToken` | string | No | Token del bot para Bot personalizado |
|
||||
| `channel` | string | Sí | Canal de Slack objetivo (p. ej., #general) |
|
||||
| `text` | string | Sí | Texto del mensaje a enviar (admite formato mrkdwn de Slack) |
|
||||
| `files` | file[] | No | Archivos para adjuntar al mensaje |
|
||||
|
||||
#### Salida
|
||||
|
||||
|
||||
@@ -202,6 +202,28 @@ Insertar o actualizar datos en una tabla de Supabase (operación upsert)
|
||||
| `message` | string | Mensaje de estado de la operación |
|
||||
| `results` | array | Array de registros insertados o actualizados |
|
||||
|
||||
### `supabase_vector_search`
|
||||
|
||||
Realizar búsqueda de similitud usando pgvector en una tabla de Supabase
|
||||
|
||||
#### Entrada
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
| `projectId` | string | Sí | ID de tu proyecto Supabase \(p. ej., jdrkgepadsdopsntdlom\) |
|
||||
| `functionName` | string | Sí | Nombre de la función PostgreSQL que realiza la búsqueda vectorial \(p. ej., match_documents\) |
|
||||
| `queryEmbedding` | array | Sí | El vector/embedding de consulta para buscar elementos similares |
|
||||
| `matchThreshold` | number | No | Umbral mínimo de similitud \(0-1\), típicamente 0.7-0.9 |
|
||||
| `matchCount` | number | No | Número máximo de resultados a devolver \(predeterminado: 10\) |
|
||||
| `apiKey` | string | Sí | Tu clave secreta de rol de servicio de Supabase |
|
||||
|
||||
#### Salida
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Mensaje de estado de la operación |
|
||||
| `results` | array | Array de registros con puntuaciones de similitud de la búsqueda vectorial. Cada registro incluye un campo de similitud \(0-1\) que indica cuán similar es al vector de consulta. |
|
||||
|
||||
## Notas
|
||||
|
||||
- Categoría: `tools`
|
||||
|
||||
@@ -190,6 +190,26 @@ Envía animaciones (GIFs) a canales o usuarios de Telegram a través de la API d
|
||||
| `message` | string | Mensaje de éxito o error |
|
||||
| `data` | object | Datos del mensaje de Telegram incluyendo medios opcionales |
|
||||
|
||||
### `telegram_send_document`
|
||||
|
||||
Envía documentos (PDF, ZIP, DOC, etc.) a canales o usuarios de Telegram a través de la API de Bot de Telegram.
|
||||
|
||||
#### Entrada
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
| `botToken` | string | Sí | Tu token de API de Bot de Telegram |
|
||||
| `chatId` | string | Sí | ID del chat de Telegram objetivo |
|
||||
| `files` | file[] | No | Archivo de documento para enviar \(PDF, ZIP, DOC, etc.\). Tamaño máximo: 50MB |
|
||||
| `caption` | string | No | Leyenda del documento \(opcional\) |
|
||||
|
||||
#### Salida
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Mensaje de éxito o error |
|
||||
| `data` | object | Datos del mensaje de Telegram incluyendo documento |
|
||||
|
||||
## Notas
|
||||
|
||||
- Categoría: `tools`
|
||||
|
||||
@@ -57,11 +57,12 @@ Procesa y analiza imágenes utilizando modelos avanzados de visión. Capaz de co
|
||||
#### Entrada
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
| --------- | ---- | ---------- | ----------- |
|
||||
| `apiKey` | string | Sí | Clave API para el proveedor de modelo seleccionado |
|
||||
| `imageUrl` | string | Sí | URL de imagen de acceso público |
|
||||
| `imageUrl` | string | No | URL de imagen accesible públicamente |
|
||||
| `imageFile` | file | No | Archivo de imagen para analizar |
|
||||
| `model` | string | No | Modelo de visión a utilizar \(gpt-4o, claude-3-opus-20240229, etc\) |
|
||||
| `prompt` | string | No | Indicación personalizada para análisis de imágenes |
|
||||
| `prompt` | string | No | Prompt personalizado para análisis de imagen |
|
||||
|
||||
#### Salida
|
||||
|
||||
|
||||
145
apps/docs/content/docs/es/tools/webflow.mdx
Normal file
145
apps/docs/content/docs/es/tools/webflow.mdx
Normal file
@@ -0,0 +1,145 @@
|
||||
---
|
||||
title: Webflow
|
||||
description: Gestionar colecciones CMS de Webflow
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="webflow"
|
||||
color="#E0E0E0"
|
||||
icon={true}
|
||||
iconSvg={`<svg className="block-icon"
|
||||
|
||||
|
||||
|
||||
viewBox='0 0 1080 674'
|
||||
fill='none'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
>
|
||||
<path
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
d='M1080 0L735.386 673.684H411.695L555.916 394.481H549.445C430.464 548.934 252.942 650.61 -0.000488281 673.684V398.344C-0.000488281 398.344 161.813 388.787 256.938 288.776H-0.000488281V0.0053214H288.771V237.515L295.252 237.489L413.254 0.0053214H631.644V236.009L638.126 235.999L760.555 0H1080Z'
|
||||
fill='#146EF5'
|
||||
/>
|
||||
</svg>`}
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[Webflow](https://webflow.com/) es una potente plataforma visual de diseño web que te permite crear sitios web responsivos sin escribir código. Combina una interfaz de diseño visual con un robusto CMS (Sistema de Gestión de Contenidos) que te permite crear, gestionar y publicar contenido dinámico para tus sitios web.
|
||||
|
||||
Con Webflow, puedes:
|
||||
|
||||
- **Diseñar visualmente**: Crear sitios web personalizados con un editor visual que genera código HTML/CSS limpio y semántico
|
||||
- **Gestionar contenido dinámicamente**: Usar el CMS para crear colecciones de contenido estructurado como entradas de blog, productos, miembros del equipo o cualquier dato personalizado
|
||||
- **Publicar instantáneamente**: Implementar tus sitios en el alojamiento de Webflow o exportar el código para alojamiento personalizado
|
||||
- **Crear diseños responsivos**: Construir sitios que funcionen perfectamente en dispositivos de escritorio, tabletas y móviles
|
||||
- **Personalizar colecciones**: Definir campos personalizados y estructuras de datos para tus tipos de contenido
|
||||
- **Automatizar actualizaciones de contenido**: Gestionar programáticamente el contenido de tu CMS a través de APIs
|
||||
|
||||
En Sim, la integración con Webflow permite a tus agentes interactuar sin problemas con tus colecciones CMS de Webflow mediante autenticación API. Esto permite potentes escenarios de automatización como la creación automática de entradas de blog a partir de contenido generado por IA, actualización de información de productos, gestión de perfiles de miembros del equipo y recuperación de elementos CMS para la generación de contenido dinámico. Tus agentes pueden listar elementos existentes para navegar por tu contenido, recuperar elementos específicos por ID, crear nuevas entradas para añadir contenido fresco, actualizar elementos existentes para mantener la información actualizada y eliminar contenido obsoleto. Esta integración cierra la brecha entre tus flujos de trabajo de IA y tu CMS de Webflow, permitiendo la gestión automatizada de contenido, actualizaciones dinámicas del sitio web y flujos de trabajo de contenido optimizados que mantienen tus sitios frescos y actualizados sin intervención manual.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
## Instrucciones de uso
|
||||
|
||||
Integra el CMS de Webflow en el flujo de trabajo. Puede crear, obtener, listar, actualizar o eliminar elementos en las colecciones del CMS de Webflow. Gestiona tu contenido de Webflow de forma programática. Se puede usar en modo de activación para iniciar flujos de trabajo cuando cambian los elementos de la colección o se envían formularios.
|
||||
|
||||
## Herramientas
|
||||
|
||||
### `webflow_list_items`
|
||||
|
||||
Listar todos los elementos de una colección del CMS de Webflow
|
||||
|
||||
#### Entrada
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `collectionId` | string | Sí | ID de la colección |
|
||||
| `offset` | number | No | Desplazamiento para paginación \(opcional\) |
|
||||
| `limit` | number | No | Número máximo de elementos a devolver \(opcional, predeterminado: 100\) |
|
||||
|
||||
#### Salida
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `items` | json | Array de elementos de la colección |
|
||||
| `metadata` | json | Metadatos sobre la consulta |
|
||||
|
||||
### `webflow_get_item`
|
||||
|
||||
Obtener un solo elemento de una colección del CMS de Webflow
|
||||
|
||||
#### Entrada
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `collectionId` | string | Sí | ID de la colección |
|
||||
| `itemId` | string | Sí | ID del elemento a recuperar |
|
||||
|
||||
#### Salida
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `item` | json | El objeto del elemento recuperado |
|
||||
| `metadata` | json | Metadatos sobre el elemento recuperado |
|
||||
|
||||
### `webflow_create_item`
|
||||
|
||||
Crear un nuevo elemento en una colección del CMS de Webflow
|
||||
|
||||
#### Entrada
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `collectionId` | string | Sí | ID de la colección |
|
||||
| `fieldData` | json | Sí | Datos de campo para el nuevo elemento como objeto JSON. Las claves deben coincidir con los nombres de campo de la colección. |
|
||||
|
||||
#### Salida
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `item` | json | El objeto del elemento creado |
|
||||
| `metadata` | json | Metadatos sobre el elemento creado |
|
||||
|
||||
### `webflow_update_item`
|
||||
|
||||
Actualizar un elemento existente en una colección CMS de Webflow
|
||||
|
||||
#### Entrada
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `collectionId` | string | Sí | ID de la colección |
|
||||
| `itemId` | string | Sí | ID del elemento a actualizar |
|
||||
| `fieldData` | json | Sí | Datos de campo para actualizar como objeto JSON. Solo incluye los campos que quieres cambiar. |
|
||||
|
||||
#### Salida
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `item` | json | El objeto del elemento actualizado |
|
||||
| `metadata` | json | Metadatos sobre el elemento actualizado |
|
||||
|
||||
### `webflow_delete_item`
|
||||
|
||||
Eliminar un elemento de una colección CMS de Webflow
|
||||
|
||||
#### Entrada
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `collectionId` | string | Sí | ID de la colección |
|
||||
| `itemId` | string | Sí | ID del elemento a eliminar |
|
||||
|
||||
#### Salida
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Si la eliminación fue exitosa |
|
||||
| `metadata` | json | Metadatos sobre la eliminación |
|
||||
|
||||
## Notas
|
||||
|
||||
- Categoría: `tools`
|
||||
- Tipo: `webflow`
|
||||
@@ -211,14 +211,14 @@ Remplir Clay avec des données provenant d'un fichier JSON. Permet une communica
|
||||
| --------- | ---- | ---------- | ----------- |
|
||||
| `webhookURL` | string | Oui | L'URL du webhook à remplir |
|
||||
| `data` | json | Oui | Les données à remplir |
|
||||
| `authToken` | string | Oui | Jeton d'authentification pour l'authentification du webhook Clay |
|
||||
| `authToken` | string | Non | Jeton d'authentification optionnel pour l'authentification du webhook Clay \(la plupart des webhooks ne nécessitent pas cela\) |
|
||||
|
||||
#### Sortie
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | json | Résultats de l'opération de remplissage Clay incluant les données de réponse du webhook Clay |
|
||||
| `data` | json | Données de réponse du webhook Clay |
|
||||
| `metadata` | object | Métadonnées de réponse du webhook |
|
||||
|
||||
## Notes
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="discord"
|
||||
color="#E0E0E0"
|
||||
color="#5865F2"
|
||||
icon={true}
|
||||
iconSvg={`<svg className="block-icon"
|
||||
|
||||
@@ -72,6 +72,7 @@ Envoyer un message à un canal Discord
|
||||
| `channelId` | chaîne | Oui | L'ID du canal Discord où envoyer le message |
|
||||
| `content` | chaîne | Non | Le contenu textuel du message |
|
||||
| `serverId` | chaîne | Oui | L'ID du serveur Discord \(ID de guilde\) |
|
||||
| `files` | fichier[] | Non | Fichiers à joindre au message |
|
||||
|
||||
#### Sortie
|
||||
|
||||
|
||||
@@ -84,11 +84,12 @@ Téléverser un fichier vers Google Drive
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | ---------- | ----------- |
|
||||
| `fileName` | chaîne | Oui | Le nom du fichier à téléverser |
|
||||
| `content` | chaîne | Oui | Le contenu du fichier à téléverser |
|
||||
| `mimeType` | chaîne | Non | Le type MIME du fichier à téléverser |
|
||||
| `folderSelector` | chaîne | Non | Sélectionnez le dossier où téléverser le fichier |
|
||||
| `folderId` | chaîne | Non | L'ID du dossier où téléverser le fichier \(usage interne\) |
|
||||
| `fileName` | string | Oui | Le nom du fichier à télécharger |
|
||||
| `file` | file | Non | Fichier binaire à télécharger \(objet UserFile\) |
|
||||
| `content` | string | Non | Contenu textuel à télécharger \(utilisez ceci OU fichier, pas les deux\) |
|
||||
| `mimeType` | string | Non | Le type MIME du fichier à télécharger \(détecté automatiquement à partir du fichier si non fourni\) |
|
||||
| `folderSelector` | string | Non | Sélectionnez le dossier dans lequel télécharger le fichier |
|
||||
| `folderId` | string | Non | L'ID du dossier dans lequel télécharger le fichier \(usage interne\) |
|
||||
|
||||
#### Sortie
|
||||
|
||||
|
||||
@@ -135,6 +135,7 @@ Lire le contenu d'un chat Microsoft Teams
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `chatId` | chaîne | Oui | L'ID de la conversation dans laquelle écrire |
|
||||
| `content` | chaîne | Oui | Le contenu à écrire dans le message |
|
||||
| `files` | fichier[] | Non | Fichiers à joindre au message |
|
||||
|
||||
#### Sortie
|
||||
|
||||
@@ -180,10 +181,11 @@ Lire le contenu d'un canal Microsoft Teams
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | ---------- | ----------- |
|
||||
| `teamId` | chaîne | Oui | L'ID de l'équipe à laquelle écrire |
|
||||
| `channelId` | chaîne | Oui | L'ID du canal auquel écrire |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `teamId` | chaîne | Oui | L'ID de l'équipe dans laquelle écrire |
|
||||
| `channelId` | chaîne | Oui | L'ID du canal dans lequel écrire |
|
||||
| `content` | chaîne | Oui | Le contenu à écrire dans le canal |
|
||||
| `files` | fichier[] | Non | Fichiers à joindre au message |
|
||||
|
||||
#### Sortie
|
||||
|
||||
|
||||
@@ -61,11 +61,12 @@ Télécharger un fichier vers OneDrive
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `fileName` | chaîne | Oui | Le nom du fichier à télécharger |
|
||||
| `content` | chaîne | Oui | Le contenu du fichier à télécharger |
|
||||
| `folderSelector` | chaîne | Non | Sélectionnez le dossier où télécharger le fichier |
|
||||
| `manualFolderId` | chaîne | Non | ID du dossier saisi manuellement \(mode avancé\) |
|
||||
| --------- | ---- | ---------- | ----------- |
|
||||
| `fileName` | string | Oui | Le nom du fichier à télécharger |
|
||||
| `file` | file | Non | Le fichier à télécharger \(binaire\) |
|
||||
| `content` | string | Non | Le contenu textuel à télécharger \(si aucun fichier n'est fourni\) |
|
||||
| `folderSelector` | string | Non | Sélectionner le dossier dans lequel télécharger le fichier |
|
||||
| `manualFolderId` | string | Non | ID de dossier saisi manuellement \(mode avancé\) |
|
||||
|
||||
#### Sortie
|
||||
|
||||
|
||||
@@ -150,14 +150,15 @@ Envoyer des e-mails avec Outlook
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
| `to` | chaîne | Oui | Adresse e-mail du destinataire |
|
||||
| `subject` | chaîne | Oui | Objet de l'e-mail |
|
||||
| `body` | chaîne | Oui | Contenu du corps de l'e-mail |
|
||||
| `replyToMessageId` | chaîne | Non | ID du message auquel répondre \(pour le fil de discussion\) |
|
||||
| `conversationId` | chaîne | Non | ID de conversation pour le fil de discussion |
|
||||
| `cc` | chaîne | Non | Destinataires en CC \(séparés par des virgules\) |
|
||||
| `bcc` | chaîne | Non | Destinataires en BCC \(séparés par des virgules\) |
|
||||
| --------- | ---- | ---------- | ----------- |
|
||||
| `to` | string | Oui | Adresse e-mail du destinataire |
|
||||
| `subject` | string | Oui | Objet de l'e-mail |
|
||||
| `body` | string | Oui | Contenu du corps de l'e-mail |
|
||||
| `replyToMessageId` | string | Non | ID du message auquel répondre \(pour le fil de discussion\) |
|
||||
| `conversationId` | string | Non | ID de conversation pour le fil de discussion |
|
||||
| `cc` | string | Non | Destinataires en CC \(séparés par des virgules\) |
|
||||
| `bcc` | string | Non | Destinataires en BCC \(séparés par des virgules\) |
|
||||
| `attachments` | file[] | Non | Fichiers à joindre à l'e-mail |
|
||||
|
||||
#### Sortie
|
||||
|
||||
@@ -175,12 +176,13 @@ Rédiger des e-mails avec Outlook
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| --------- | ---- | ---------- | ----------- |
|
||||
| `to` | string | Oui | Adresse e-mail du destinataire |
|
||||
| `subject` | string | Oui | Objet de l'e-mail |
|
||||
| `body` | string | Oui | Contenu du corps de l'e-mail |
|
||||
| `cc` | string | Non | Destinataires en CC \(séparés par des virgules\) |
|
||||
| `bcc` | string | Non | Destinataires en BCC \(séparés par des virgules\) |
|
||||
| `attachments` | file[] | Non | Fichiers à joindre au brouillon d'e-mail |
|
||||
|
||||
#### Sortie
|
||||
|
||||
|
||||
@@ -133,13 +133,14 @@ Rechercher des vecteurs similaires dans une collection Qdrant
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | ---------- | ----------- |
|
||||
| `url` | chaîne | Oui | URL de base Qdrant |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
| `url` | chaîne | Oui | URL de base de Qdrant |
|
||||
| `apiKey` | chaîne | Non | Clé API Qdrant \(facultative\) |
|
||||
| `collection` | chaîne | Oui | Nom de la collection |
|
||||
| `vector` | tableau | Oui | Vecteur à rechercher |
|
||||
| `limit` | nombre | Non | Nombre de résultats à retourner |
|
||||
| `filter` | objet | Non | Filtre à appliquer à la recherche |
|
||||
| `search_return_data` | chaîne | Non | Données à retourner de la recherche |
|
||||
| `with_payload` | booléen | Non | Inclure la charge utile dans la réponse |
|
||||
| `with_vector` | booléen | Non | Inclure le vecteur dans la réponse |
|
||||
|
||||
@@ -157,11 +158,12 @@ Récupérer des points par ID depuis une collection Qdrant
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | ---------- | ----------- |
|
||||
| `url` | chaîne | Oui | URL de base Qdrant |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
| `url` | chaîne | Oui | URL de base de Qdrant |
|
||||
| `apiKey` | chaîne | Non | Clé API Qdrant \(facultative\) |
|
||||
| `collection` | chaîne | Oui | Nom de la collection |
|
||||
| `ids` | tableau | Oui | Tableau d'identifiants de points à récupérer |
|
||||
| `fetch_return_data` | chaîne | Non | Données à retourner de la récupération |
|
||||
| `with_payload` | booléen | Non | Inclure la charge utile dans la réponse |
|
||||
| `with_vector` | booléen | Non | Inclure le vecteur dans la réponse |
|
||||
|
||||
|
||||
@@ -199,6 +199,26 @@ Ajouter un nouvel élément à une liste SharePoint
|
||||
| --------- | ---- | ----------- |
|
||||
| `item` | object | Élément de liste SharePoint créé |
|
||||
|
||||
### `sharepoint_upload_file`
|
||||
|
||||
Télécharger des fichiers vers une bibliothèque de documents SharePoint
|
||||
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | ---------- | ----------- |
|
||||
| `siteId` | chaîne | Non | L'ID du site SharePoint |
|
||||
| `driveId` | chaîne | Non | L'ID de la bibliothèque de documents \(lecteur\). Si non fourni, utilise le lecteur par défaut. |
|
||||
| `folderPath` | chaîne | Non | Chemin de dossier optionnel dans la bibliothèque de documents \(par exemple, /Documents/Sousdossier\) |
|
||||
| `fileName` | chaîne | Non | Optionnel : remplacer le nom du fichier téléchargé |
|
||||
| `files` | fichier[] | Non | Fichiers à télécharger vers SharePoint |
|
||||
|
||||
#### Sortie
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `uploadedFiles` | tableau | Tableau d'objets de fichiers téléchargés |
|
||||
|
||||
## Remarques
|
||||
|
||||
- Catégorie : `tools`
|
||||
|
||||
@@ -75,11 +75,12 @@ Envoyez des messages aux canaux ou utilisateurs Slack via l'API Slack. Prend en
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
| --------- | ---- | ---------- | ----------- |
|
||||
| `authMethod` | chaîne | Non | Méthode d'authentification : oauth ou bot_token |
|
||||
| `botToken` | chaîne | Non | Jeton du bot pour le Bot personnalisé |
|
||||
| `channel` | chaîne | Oui | Canal Slack cible (par ex., #general) |
|
||||
| `text` | chaîne | Oui | Texte du message à envoyer (prend en charge le formatage mrkdwn de Slack) |
|
||||
| `botToken` | chaîne | Non | Jeton du bot pour Bot personnalisé |
|
||||
| `channel` | chaîne | Oui | Canal Slack cible \(par ex., #general\) |
|
||||
| `text` | chaîne | Oui | Texte du message à envoyer \(prend en charge le formatage mrkdwn de Slack\) |
|
||||
| `files` | fichier[] | Non | Fichiers à joindre au message |
|
||||
|
||||
#### Sortie
|
||||
|
||||
|
||||
@@ -202,6 +202,28 @@ Insérer ou mettre à jour des données dans une table Supabase (opération upse
|
||||
| `message` | string | Message d'état de l'opération |
|
||||
| `results` | array | Tableau des enregistrements insérés ou mis à jour |
|
||||
|
||||
### `supabase_vector_search`
|
||||
|
||||
Effectuer une recherche de similarité en utilisant pgvector dans une table Supabase
|
||||
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
| `projectId` | chaîne | Oui | L'ID de votre projet Supabase (ex. : jdrkgepadsdopsntdlom) |
|
||||
| `functionName` | chaîne | Oui | Le nom de la fonction PostgreSQL qui effectue la recherche vectorielle (ex. : match_documents) |
|
||||
| `queryEmbedding` | tableau | Oui | Le vecteur/embedding de requête pour rechercher des éléments similaires |
|
||||
| `matchThreshold` | nombre | Non | Seuil minimum de similarité (0-1), généralement 0,7-0,9 |
|
||||
| `matchCount` | nombre | Non | Nombre maximum de résultats à retourner (par défaut : 10) |
|
||||
| `apiKey` | chaîne | Oui | Votre clé secrète de rôle de service Supabase |
|
||||
|
||||
#### Sortie
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | chaîne | Message d'état de l'opération |
|
||||
| `results` | tableau | Tableau d'enregistrements avec scores de similarité issus de la recherche vectorielle. Chaque enregistrement inclut un champ de similarité (0-1) indiquant son degré de similarité avec le vecteur de requête. |
|
||||
|
||||
## Notes
|
||||
|
||||
- Catégorie : `tools`
|
||||
|
||||
@@ -190,6 +190,26 @@ Envoyez des animations (GIF) aux canaux ou utilisateurs Telegram via l'API Bot T
|
||||
| `message` | chaîne | Message de succès ou d'erreur |
|
||||
| `data` | objet | Données du message Telegram incluant les médias optionnels |
|
||||
|
||||
### `telegram_send_document`
|
||||
|
||||
Envoyez des documents (PDF, ZIP, DOC, etc.) aux canaux ou utilisateurs Telegram via l'API Bot Telegram.
|
||||
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
| `botToken` | chaîne | Oui | Votre jeton d'API Bot Telegram |
|
||||
| `chatId` | chaîne | Oui | ID du chat Telegram cible |
|
||||
| `files` | fichier[] | Non | Fichier document à envoyer \(PDF, ZIP, DOC, etc.\). Taille max : 50 Mo |
|
||||
| `caption` | chaîne | Non | Légende du document \(facultatif\) |
|
||||
|
||||
#### Sortie
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | chaîne | Message de succès ou d'erreur |
|
||||
| `data` | objet | Données du message Telegram incluant le document |
|
||||
|
||||
## Notes
|
||||
|
||||
- Catégorie : `tools`
|
||||
|
||||
@@ -58,10 +58,11 @@ Traitez et analysez des images en utilisant des modèles de vision avancés. Cap
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | ---------- | ----------- |
|
||||
| `apiKey` | chaîne | Oui | Clé API pour le fournisseur de modèle sélectionné |
|
||||
| `imageUrl` | chaîne | Oui | URL d'image accessible publiquement |
|
||||
| `model` | chaîne | Non | Modèle de vision à utiliser \(gpt-4o, claude-3-opus-20240229, etc\) |
|
||||
| `prompt` | chaîne | Non | Prompt personnalisé pour l'analyse d'image |
|
||||
| `apiKey` | string | Oui | Clé API pour le fournisseur de modèle sélectionné |
|
||||
| `imageUrl` | string | Non | URL d'image accessible publiquement |
|
||||
| `imageFile` | file | Non | Fichier image à analyser |
|
||||
| `model` | string | Non | Modèle de vision à utiliser \(gpt-4o, claude-3-opus-20240229, etc\) |
|
||||
| `prompt` | string | Non | Invite personnalisée pour l'analyse d'image |
|
||||
|
||||
#### Sortie
|
||||
|
||||
|
||||
145
apps/docs/content/docs/fr/tools/webflow.mdx
Normal file
145
apps/docs/content/docs/fr/tools/webflow.mdx
Normal file
@@ -0,0 +1,145 @@
|
||||
---
|
||||
title: Webflow
|
||||
description: Gérer les collections CMS de Webflow
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="webflow"
|
||||
color="#E0E0E0"
|
||||
icon={true}
|
||||
iconSvg={`<svg className="block-icon"
|
||||
|
||||
|
||||
|
||||
viewBox='0 0 1080 674'
|
||||
fill='none'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
>
|
||||
<path
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
d='M1080 0L735.386 673.684H411.695L555.916 394.481H549.445C430.464 548.934 252.942 650.61 -0.000488281 673.684V398.344C-0.000488281 398.344 161.813 388.787 256.938 288.776H-0.000488281V0.0053214H288.771V237.515L295.252 237.489L413.254 0.0053214H631.644V236.009L638.126 235.999L760.555 0H1080Z'
|
||||
fill='#146EF5'
|
||||
/>
|
||||
</svg>`}
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[Webflow](https://webflow.com/) est une plateforme puissante de conception web visuelle qui vous permet de créer des sites web responsifs sans écrire de code. Elle combine une interface de conception visuelle avec un CMS (système de gestion de contenu) robuste qui vous permet de créer, gérer et publier du contenu dynamique pour vos sites web.
|
||||
|
||||
Avec Webflow, vous pouvez :
|
||||
|
||||
- **Concevoir visuellement** : créer des sites web personnalisés avec un éditeur visuel qui génère du code HTML/CSS propre et sémantique
|
||||
- **Gérer du contenu dynamiquement** : utiliser le CMS pour créer des collections de contenu structuré comme des articles de blog, des produits, des membres d'équipe ou toute donnée personnalisée
|
||||
- **Publier instantanément** : déployer vos sites sur l'hébergement de Webflow ou exporter le code pour un hébergement personnalisé
|
||||
- **Créer des designs responsifs** : construire des sites qui fonctionnent parfaitement sur ordinateur, tablette et appareils mobiles
|
||||
- **Personnaliser les collections** : définir des champs personnalisés et des structures de données pour vos types de contenu
|
||||
- **Automatiser les mises à jour de contenu** : gérer programmatiquement votre contenu CMS via des API
|
||||
|
||||
Dans Sim, l'intégration Webflow permet à vos agents d'interagir de manière transparente avec vos collections CMS Webflow grâce à l'authentification API. Cela permet des scénarios d'automatisation puissants tels que la création automatique d'articles de blog à partir de contenu généré par IA, la mise à jour d'informations sur les produits, la gestion des profils des membres de l'équipe et la récupération d'éléments CMS pour la génération de contenu dynamique. Vos agents peuvent lister les éléments existants pour parcourir votre contenu, récupérer des éléments spécifiques par ID, créer de nouvelles entrées pour ajouter du contenu frais, mettre à jour des éléments existants pour maintenir les informations à jour et supprimer du contenu obsolète. Cette intégration comble le fossé entre vos flux de travail IA et votre CMS Webflow, permettant une gestion automatisée du contenu, des mises à jour dynamiques de sites web et des flux de travail de contenu rationalisés qui maintiennent vos sites frais et à jour sans intervention manuelle.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
## Instructions d'utilisation
|
||||
|
||||
Intègre le CMS Webflow dans le flux de travail. Peut créer, obtenir, lister, mettre à jour ou supprimer des éléments dans les collections CMS Webflow. Gérez votre contenu Webflow par programmation. Peut être utilisé en mode déclencheur pour lancer des flux de travail lorsque les éléments de collection changent ou lorsque des formulaires sont soumis.
|
||||
|
||||
## Outils
|
||||
|
||||
### `webflow_list_items`
|
||||
|
||||
Lister tous les éléments d'une collection CMS Webflow
|
||||
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `collectionId` | string | Oui | ID de la collection |
|
||||
| `offset` | number | Non | Décalage pour la pagination \(facultatif\) |
|
||||
| `limit` | number | Non | Nombre maximum d'éléments à retourner \(facultatif, par défaut : 100\) |
|
||||
|
||||
#### Sortie
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `items` | json | Tableau des éléments de la collection |
|
||||
| `metadata` | json | Métadonnées sur la requête |
|
||||
|
||||
### `webflow_get_item`
|
||||
|
||||
Obtenir un seul élément d'une collection CMS Webflow
|
||||
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `collectionId` | string | Oui | ID de la collection |
|
||||
| `itemId` | string | Oui | ID de l'élément à récupérer |
|
||||
|
||||
#### Sortie
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `item` | json | L'objet de l'élément récupéré |
|
||||
| `metadata` | json | Métadonnées sur l'élément récupéré |
|
||||
|
||||
### `webflow_create_item`
|
||||
|
||||
Créer un nouvel élément dans une collection CMS Webflow
|
||||
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | ---------- | ----------- |
|
||||
| `collectionId` | string | Oui | ID de la collection |
|
||||
| `fieldData` | json | Oui | Données de champ pour le nouvel élément sous forme d'objet JSON. Les clés doivent correspondre aux noms des champs de la collection. |
|
||||
|
||||
#### Sortie
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `item` | json | L'objet de l'élément créé |
|
||||
| `metadata` | json | Métadonnées concernant l'élément créé |
|
||||
|
||||
### `webflow_update_item`
|
||||
|
||||
Mettre à jour un élément existant dans une collection CMS Webflow
|
||||
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | ---------- | ----------- |
|
||||
| `collectionId` | string | Oui | ID de la collection |
|
||||
| `itemId` | string | Oui | ID de l'élément à mettre à jour |
|
||||
| `fieldData` | json | Oui | Données de champ à mettre à jour sous forme d'objet JSON. N'incluez que les champs que vous souhaitez modifier. |
|
||||
|
||||
#### Sortie
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `item` | json | L'objet de l'élément mis à jour |
|
||||
| `metadata` | json | Métadonnées concernant l'élément mis à jour |
|
||||
|
||||
### `webflow_delete_item`
|
||||
|
||||
Supprimer un élément d'une collection CMS Webflow
|
||||
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | ---------- | ----------- |
|
||||
| `collectionId` | string | Oui | ID de la collection |
|
||||
| `itemId` | string | Oui | ID de l'élément à supprimer |
|
||||
|
||||
#### Sortie
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Indique si la suppression a réussi |
|
||||
| `metadata` | json | Métadonnées concernant la suppression |
|
||||
|
||||
## Notes
|
||||
|
||||
- Catégorie : `tools`
|
||||
- Type : `webflow`
|
||||
@@ -207,18 +207,18 @@ Populate Clay with data from a JSON file. Enables direct communication and notif
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `webhookURL` | string | Yes | The webhook URL to populate |
|
||||
| `data` | json | Yes | The data to populate |
|
||||
| `authToken` | string | Yes | Auth token for Clay webhook authentication |
|
||||
| `webhookURL` | string | はい | 設定するウェブフックURL |
|
||||
| `data` | json | はい | 設定するデータ |
|
||||
| `authToken` | string | いいえ | Clayウェブフック認証用のオプション認証トークン(ほとんどのウェブフックではこれは不要です) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | json | Clay populate operation results including response data from Clay webhook |
|
||||
| `data` | json | Clayウェブフックからのレスポンスデータ |
|
||||
| `metadata` | object | ウェブフックレスポンスのメタデータ |
|
||||
|
||||
## Notes
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="discord"
|
||||
color="#E0E0E0"
|
||||
color="#5865F2"
|
||||
icon={true}
|
||||
iconSvg={`<svg className="block-icon"
|
||||
|
||||
@@ -72,6 +72,7 @@ Discordチャンネルにメッセージを送信する
|
||||
| `channelId` | string | はい | メッセージを送信するDiscordチャンネルID |
|
||||
| `content` | string | いいえ | メッセージのテキスト内容 |
|
||||
| `serverId` | string | はい | DiscordサーバーID(ギルドID) |
|
||||
| `files` | file[] | いいえ | メッセージに添付するファイル |
|
||||
|
||||
#### 出力
|
||||
|
||||
|
||||
@@ -85,8 +85,9 @@ Google Driveをワークフローに統合します。ファイルの作成、
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `fileName` | string | はい | アップロードするファイルの名前 |
|
||||
| `content` | string | はい | アップロードするファイルの内容 |
|
||||
| `mimeType` | string | いいえ | アップロードするファイルのMIMEタイプ |
|
||||
| `file` | file | いいえ | アップロードするバイナリファイル(UserFileオブジェクト) |
|
||||
| `content` | string | いいえ | アップロードするテキストコンテンツ(fileかこちらのどちらか一方を使用、両方は不可) |
|
||||
| `mimeType` | string | いいえ | アップロードするファイルのMIMEタイプ(指定がない場合はファイルから自動検出) |
|
||||
| `folderSelector` | string | いいえ | ファイルをアップロードするフォルダを選択 |
|
||||
| `folderId` | string | いいえ | ファイルをアップロードするフォルダのID(内部使用) |
|
||||
|
||||
|
||||
@@ -134,7 +134,8 @@ Microsoft Teams チャットでコンテンツを作成または更新する
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `chatId` | string | はい | 書き込み先のチャットID |
|
||||
| `content` | string | はい | メッセージに書き込むコンテンツ |
|
||||
| `content` | string | はい | メッセージに書き込む内容 |
|
||||
| `files` | file[] | いいえ | メッセージに添付するファイル |
|
||||
|
||||
#### 出力
|
||||
|
||||
@@ -179,11 +180,12 @@ Microsoft Teamsチャネルにメッセージを書き込むまたは送信す
|
||||
|
||||
#### 入力
|
||||
|
||||
| パラメータ | 種類 | 必須 | 説明 |
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `teamId` | string | はい | 書き込み先のチームID |
|
||||
| `channelId` | string | はい | 書き込み先のチャネルID |
|
||||
| `content` | string | はい | チャネルに書き込む内容 |
|
||||
| `files` | file[] | いいえ | メッセージに添付するファイル |
|
||||
|
||||
#### 出力
|
||||
|
||||
|
||||
@@ -63,7 +63,8 @@ OneDriveにファイルをアップロードする
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `fileName` | string | はい | アップロードするファイルの名前 |
|
||||
| `content` | string | はい | アップロードするファイルの内容 |
|
||||
| `file` | file | いいえ | アップロードするファイル(バイナリ) |
|
||||
| `content` | string | いいえ | アップロードするテキストコンテンツ(ファイルが提供されていない場合) |
|
||||
| `folderSelector` | string | いいえ | ファイルをアップロードするフォルダを選択 |
|
||||
| `manualFolderId` | string | いいえ | 手動で入力したフォルダID(高度なモード) |
|
||||
|
||||
|
||||
@@ -158,6 +158,7 @@ Outlookを使用してメールを送信する
|
||||
| `conversationId` | string | いいえ | スレッド用の会話ID |
|
||||
| `cc` | string | いいえ | CCの受信者(カンマ区切り) |
|
||||
| `bcc` | string | いいえ | BCCの受信者(カンマ区切り) |
|
||||
| `attachments` | file[] | いいえ | メールに添付するファイル |
|
||||
|
||||
#### 出力
|
||||
|
||||
@@ -181,6 +182,7 @@ Outlookを使用してメールを下書きする
|
||||
| `body` | string | はい | メール本文の内容 |
|
||||
| `cc` | string | いいえ | CCの受信者(カンマ区切り) |
|
||||
| `bcc` | string | いいえ | BCCの受信者(カンマ区切り) |
|
||||
| `attachments` | file[] | いいえ | メールの下書きに添付するファイル |
|
||||
|
||||
#### 出力
|
||||
|
||||
|
||||
@@ -140,6 +140,7 @@ Qdrantコレクション内で類似ベクトルを検索する
|
||||
| `vector` | array | はい | 検索対象のベクトル |
|
||||
| `limit` | number | いいえ | 返す結果の数 |
|
||||
| `filter` | object | いいえ | 検索に適用するフィルター |
|
||||
| `search_return_data` | string | いいえ | 検索から返すデータ |
|
||||
| `with_payload` | boolean | いいえ | レスポンスにペイロードを含める |
|
||||
| `with_vector` | boolean | いいえ | レスポンスにベクトルを含める |
|
||||
|
||||
@@ -162,6 +163,7 @@ QdrantコレクションからIDによってポイントを取得する
|
||||
| `apiKey` | string | いいえ | Qdrant APIキー(オプション) |
|
||||
| `collection` | string | はい | コレクション名 |
|
||||
| `ids` | array | はい | 取得するポイントIDの配列 |
|
||||
| `fetch_return_data` | string | いいえ | 取得から返すデータ |
|
||||
| `with_payload` | boolean | いいえ | レスポンスにペイロードを含める |
|
||||
| `with_vector` | boolean | いいえ | レスポンスにベクトルを含める |
|
||||
|
||||
|
||||
@@ -199,7 +199,27 @@ SharePointリストに新しいアイテムを追加する
|
||||
| --------- | ---- | ----------- |
|
||||
| `item` | object | 作成されたSharePointリストアイテム |
|
||||
|
||||
### `sharepoint_upload_file`
|
||||
|
||||
SharePointドキュメントライブラリにファイルをアップロードする
|
||||
|
||||
#### 入力
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | いいえ | SharePointサイトのID |
|
||||
| `driveId` | string | いいえ | ドキュメントライブラリ(ドライブ)のID。提供されない場合、デフォルトドライブを使用します。 |
|
||||
| `folderPath` | string | いいえ | ドキュメントライブラリ内のオプションのフォルダパス(例:/Documents/Subfolder) |
|
||||
| `fileName` | string | いいえ | オプション:アップロードされるファイル名を上書きする |
|
||||
| `files` | file[] | いいえ | SharePointにアップロードするファイル |
|
||||
|
||||
#### 出力
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `uploadedFiles` | array | アップロードされたファイルオブジェクトの配列 |
|
||||
|
||||
## 注意事項
|
||||
|
||||
- カテゴリー: `tools`
|
||||
- カテゴリ: `tools`
|
||||
- タイプ: `sharepoint`
|
||||
|
||||
@@ -78,7 +78,8 @@ Slack APIを通じてSlackチャンネルまたはユーザーにメッセージ
|
||||
| `authMethod` | string | いいえ | 認証方法:oauth または bot_token |
|
||||
| `botToken` | string | いいえ | カスタムボット用のボットトークン |
|
||||
| `channel` | string | はい | 対象のSlackチャンネル(例:#general) |
|
||||
| `text` | string | はい | 送信するメッセージテキスト(Slack mrkdwnフォーマットをサポート) |
|
||||
| `text` | string | はい | 送信するメッセージテキスト(Slack mrkdwn形式をサポート) |
|
||||
| `files` | file[] | いいえ | メッセージに添付するファイル |
|
||||
|
||||
#### 出力
|
||||
|
||||
|
||||
@@ -202,7 +202,29 @@ Supabaseテーブルにデータを挿入または更新する(アップサー
|
||||
| `message` | string | 操作ステータスメッセージ |
|
||||
| `results` | array | アップサートされたレコードの配列 |
|
||||
|
||||
### `supabase_vector_search`
|
||||
|
||||
Supabaseテーブルでpgvectorを使用して類似性検索を実行する
|
||||
|
||||
#### 入力
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | はい | あなたのSupabaseプロジェクトID(例:jdrkgepadsdopsntdlom) |
|
||||
| `functionName` | string | はい | ベクトル検索を実行するPostgreSQL関数の名前(例:match_documents) |
|
||||
| `queryEmbedding` | array | はい | 類似アイテムを検索するためのクエリベクトル/埋め込み |
|
||||
| `matchThreshold` | number | いいえ | 最小類似度しきい値(0-1)、通常は0.7-0.9 |
|
||||
| `matchCount` | number | いいえ | 返す結果の最大数(デフォルト:10) |
|
||||
| `apiKey` | string | はい | あなたのSupabaseサービスロールシークレットキー |
|
||||
|
||||
#### 出力
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | 操作ステータスメッセージ |
|
||||
| `results` | array | ベクトル検索からの類似度スコア付きレコードの配列。各レコードには、クエリベクトルとの類似度を示す類似度フィールド(0-1)が含まれます。 |
|
||||
|
||||
## 注意事項
|
||||
|
||||
- カテゴリー: `tools`
|
||||
- カテゴリ: `tools`
|
||||
- タイプ: `supabase`
|
||||
|
||||
@@ -189,6 +189,26 @@ Telegram Bot APIを通じてTelegramチャンネルまたはユーザーにア
|
||||
| `message` | string | 成功またはエラーメッセージ |
|
||||
| `data` | object | オプションのメディアを含むTelegramメッセージデータ |
|
||||
|
||||
### `telegram_send_document`
|
||||
|
||||
Telegram Bot APIを通じて、Telegramチャンネルやユーザーにドキュメント(PDF、ZIP、DOCなど)を送信します。
|
||||
|
||||
#### 入力
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `botToken` | string | はい | あなたのTelegram Bot APIトークン |
|
||||
| `chatId` | string | はい | 対象のTelegramチャットID |
|
||||
| `files` | file[] | いいえ | 送信するドキュメントファイル(PDF、ZIP、DOCなど)。最大サイズ:50MB |
|
||||
| `caption` | string | いいえ | ドキュメントのキャプション(任意) |
|
||||
|
||||
#### 出力
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | 成功またはエラーメッセージ |
|
||||
| `data` | object | ドキュメントを含むTelegramメッセージデータ |
|
||||
|
||||
## 注意事項
|
||||
|
||||
- カテゴリー: `tools`
|
||||
|
||||
@@ -59,8 +59,9 @@ Visionをワークフローに統合します。ビジョンモデルで画像
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | はい | 選択したモデルプロバイダーのAPIキー |
|
||||
| `imageUrl` | string | はい | 公開アクセス可能な画像URL |
|
||||
| `model` | string | いいえ | 使用するビジョンモデル \(gpt-4o, claude-3-opus-20240229, など\) |
|
||||
| `imageUrl` | string | いいえ | 公開アクセス可能な画像URL |
|
||||
| `imageFile` | file | いいえ | 分析する画像ファイル |
|
||||
| `model` | string | いいえ | 使用するビジョンモデル(gpt-4o、claude-3-opus-20240229など) |
|
||||
| `prompt` | string | いいえ | 画像分析用のカスタムプロンプト |
|
||||
|
||||
#### 出力
|
||||
|
||||
145
apps/docs/content/docs/ja/tools/webflow.mdx
Normal file
145
apps/docs/content/docs/ja/tools/webflow.mdx
Normal file
@@ -0,0 +1,145 @@
|
||||
---
|
||||
title: Webflow
|
||||
description: Webflow CMSコレクションを管理する
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="webflow"
|
||||
color="#E0E0E0"
|
||||
icon={true}
|
||||
iconSvg={`<svg className="block-icon"
|
||||
|
||||
|
||||
|
||||
viewBox='0 0 1080 674'
|
||||
fill='none'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
>
|
||||
<path
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
d='M1080 0L735.386 673.684H411.695L555.916 394.481H549.445C430.464 548.934 252.942 650.61 -0.000488281 673.684V398.344C-0.000488281 398.344 161.813 388.787 256.938 288.776H-0.000488281V0.0053214H288.771V237.515L295.252 237.489L413.254 0.0053214H631.644V236.009L638.126 235.999L760.555 0H1080Z'
|
||||
fill='#146EF5'
|
||||
/>
|
||||
</svg>`}
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[Webflow](https://webflow.com/)は、コードを書かずに応答性の高いウェブサイトを構築できる強力なビジュアルウェブデザインプラットフォームです。視覚的なデザインインターフェースと堅牢なCMS(コンテンツ管理システム)を組み合わせており、ウェブサイト用の動的コンテンツを作成、管理、公開することができます。
|
||||
|
||||
Webflowでは以下のことが可能です:
|
||||
|
||||
- **視覚的にデザインする**:クリーンでセマンティックなHTML/CSSコードを生成する視覚エディタで、カスタムウェブサイトを作成
|
||||
- **動的にコンテンツを管理する**:CMSを使用してブログ投稿、製品、チームメンバー、またはカスタムデータなどの構造化されたコンテンツのコレクションを作成
|
||||
- **即時に公開する**:サイトをWebflowのホスティングにデプロイするか、カスタムホスティング用にコードをエクスポート
|
||||
- **レスポンシブデザインを作成する**:デスクトップ、タブレット、モバイルデバイスでシームレスに動作するサイトを構築
|
||||
- **コレクションをカスタマイズする**:コンテンツタイプのカスタムフィールドとデータ構造を定義
|
||||
- **コンテンツ更新を自動化する**:APIを通じてCMSコンテンツをプログラムで管理
|
||||
|
||||
Simでは、Webflow統合によりエージェントがAPI認証を通じてWebflow CMSコレクションとシームレスに連携できます。これにより、AIが生成したコンテンツからブログ投稿を自動作成したり、製品情報を更新したり、チームメンバープロフィールを管理したり、動的コンテンツ生成のためにCMSアイテムを取得したりするなど、強力な自動化シナリオが可能になります。エージェントは既存のアイテムをリストしてコンテンツを閲覧したり、IDで特定のアイテムを取得したり、新しいエントリを作成して新鮮なコンテンツを追加したり、既存のアイテムを更新して情報を最新の状態に保ったり、古いコンテンツを削除したりできます。この統合により、AIワークフローとWebflow CMSの間のギャップが埋まり、自動化されたコンテンツ管理、動的なウェブサイト更新、合理化されたコンテンツワークフローが可能になり、手動介入なしにサイトを常に新鮮で最新の状態に保つことができます。
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
## 使用方法
|
||||
|
||||
Webflow CMSをワークフローに統合します。Webflow CMSコレクションのアイテムを作成、取得、一覧表示、更新、または削除できます。Webflowのコンテンツをプログラムで管理します。トリガーモードでは、コレクションアイテムが変更されたり、フォームが送信されたりしたときにワークフローをトリガーするために使用できます。
|
||||
|
||||
## ツール
|
||||
|
||||
### `webflow_list_items`
|
||||
|
||||
Webflow CMSコレクションからすべてのアイテムを一覧表示する
|
||||
|
||||
#### 入力
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `collectionId` | string | はい | コレクションのID |
|
||||
| `offset` | number | いいえ | ページネーション用のオフセット(オプション) |
|
||||
| `limit` | number | いいえ | 返すアイテムの最大数(オプション、デフォルト:100) |
|
||||
|
||||
#### 出力
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `items` | json | コレクションアイテムの配列 |
|
||||
| `metadata` | json | クエリに関するメタデータ |
|
||||
|
||||
### `webflow_get_item`
|
||||
|
||||
Webflow CMSコレクションから単一のアイテムを取得する
|
||||
|
||||
#### 入力
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `collectionId` | string | はい | コレクションのID |
|
||||
| `itemId` | string | はい | 取得するアイテムのID |
|
||||
|
||||
#### 出力
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `item` | json | 取得したアイテムオブジェクト |
|
||||
| `metadata` | json | 取得したアイテムに関するメタデータ |
|
||||
|
||||
### `webflow_create_item`
|
||||
|
||||
Webflow CMSコレクションに新しいアイテムを作成する
|
||||
|
||||
#### 入力
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `collectionId` | string | はい | コレクションのID |
|
||||
| `fieldData` | json | はい | 新しいアイテムのフィールドデータ(JSONオブジェクト形式)。キーはコレクションのフィールド名と一致する必要があります。 |
|
||||
|
||||
#### 出力
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `item` | json | 作成されたアイテムオブジェクト |
|
||||
| `metadata` | json | 作成されたアイテムに関するメタデータ |
|
||||
|
||||
### `webflow_update_item`
|
||||
|
||||
Webflow CMSコレクション内の既存アイテムを更新する
|
||||
|
||||
#### 入力
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `collectionId` | string | はい | コレクションのID |
|
||||
| `itemId` | string | はい | 更新するアイテムのID |
|
||||
| `fieldData` | json | はい | 更新するフィールドデータ(JSONオブジェクト形式)。変更したいフィールドのみを含めてください。 |
|
||||
|
||||
#### 出力
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `item` | json | 更新されたアイテムオブジェクト |
|
||||
| `metadata` | json | 更新されたアイテムに関するメタデータ |
|
||||
|
||||
### `webflow_delete_item`
|
||||
|
||||
Webflow CMSコレクションからアイテムを削除する
|
||||
|
||||
#### 入力
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `collectionId` | string | はい | コレクションのID |
|
||||
| `itemId` | string | はい | 削除するアイテムのID |
|
||||
|
||||
#### 出力
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 削除が成功したかどうか |
|
||||
| `metadata` | json | 削除に関するメタデータ |
|
||||
|
||||
## 注意事項
|
||||
|
||||
- カテゴリー: `tools`
|
||||
- タイプ: `webflow`
|
||||
@@ -209,16 +209,16 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `webhookURL` | string | 是 | 用于填充的 webhook URL |
|
||||
| `webhookURL` | string | 是 | 要填充的 webhook URL |
|
||||
| `data` | json | 是 | 要填充的数据 |
|
||||
| `authToken` | string | 是 | 用于 Clay webhook 认证的授权令牌 |
|
||||
| `authToken` | string | 否 | 用于 Clay webhook 认证的可选身份验证令牌(大多数 webhook 不需要此令牌) |
|
||||
|
||||
#### 输出
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | json | Clay 填充操作结果,包括来自 Clay webhook 的响应数据 |
|
||||
| `data` | json | 来自 Clay webhook 的响应数据 |
|
||||
| `metadata` | object | webhook 响应元数据 |
|
||||
|
||||
## 注意事项
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="discord"
|
||||
color="#E0E0E0"
|
||||
color="#5865F2"
|
||||
icon={true}
|
||||
iconSvg={`<svg className="block-icon"
|
||||
|
||||
@@ -71,7 +71,8 @@ Sim 中的 Discord 组件使用高效的延迟加载,仅在需要时获取数
|
||||
| `botToken` | string | 是 | 用于身份验证的机器人令牌 |
|
||||
| `channelId` | string | 是 | 要发送消息的 Discord 频道 ID |
|
||||
| `content` | string | 否 | 消息的文本内容 |
|
||||
| `serverId` | string | 是 | Discord 服务器 ID(guild ID) |
|
||||
| `serverId` | string | 是 | Discord 服务器 ID \(公会 ID\) |
|
||||
| `files` | file[] | 否 | 要附加到消息的文件 |
|
||||
|
||||
#### 输出
|
||||
|
||||
|
||||
@@ -84,11 +84,12 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `fileName` | string | 是 | 要上传的文件名称 |
|
||||
| `content` | string | 是 | 要上传的文件内容 |
|
||||
| `mimeType` | string | 否 | 要上传文件的 MIME 类型 |
|
||||
| `folderSelector` | string | 否 | 选择上传文件的文件夹 |
|
||||
| `folderId` | string | 否 | 要上传文件的文件夹 ID(内部使用) |
|
||||
| `fileName` | string | 是 | 要上传文件的名称 |
|
||||
| `file` | file | 否 | 要上传的二进制文件 \(UserFile 对象\) |
|
||||
| `content` | string | 否 | 要上传的文本内容 \(使用此项或 file,不可同时使用\) |
|
||||
| `mimeType` | string | 否 | 要上传文件的 MIME 类型 \(如果未提供,将从文件中自动检测\) |
|
||||
| `folderSelector` | string | 否 | 选择要上传文件的文件夹 |
|
||||
| `folderId` | string | 否 | 要上传文件的文件夹 ID \(内部使用\) |
|
||||
|
||||
#### 输出
|
||||
|
||||
|
||||
@@ -135,6 +135,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `chatId` | string | 是 | 要写入的聊天 ID |
|
||||
| `content` | string | 是 | 要写入消息的内容 |
|
||||
| `files` | file[] | 否 | 要附加到消息的文件 |
|
||||
|
||||
#### 输出
|
||||
|
||||
@@ -184,6 +185,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
| `teamId` | string | 是 | 要写入的团队 ID |
|
||||
| `channelId` | string | 是 | 要写入的频道 ID |
|
||||
| `content` | string | 是 | 要写入频道的内容 |
|
||||
| `files` | file[] | 否 | 要附加到消息的文件 |
|
||||
|
||||
#### 输出
|
||||
|
||||
|
||||
@@ -62,10 +62,11 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `fileName` | string | 是 | 要上传的文件名 |
|
||||
| `content` | string | 是 | 要上传的文件内容 |
|
||||
| `folderSelector` | string | 否 | 选择要上传文件的文件夹 |
|
||||
| `manualFolderId` | string | 否 | 手动输入的文件夹 ID(高级模式) |
|
||||
| `fileName` | string | 是 | 要上传文件的名称 |
|
||||
| `file` | file | 否 | 要上传的文件 \(二进制\) |
|
||||
| `content` | string | 否 | 要上传的文本内容 \(如果未提供文件\) |
|
||||
| `folderSelector` | string | 否 | 选择上传文件的文件夹 |
|
||||
| `manualFolderId` | string | 否 | 手动输入的文件夹 ID \(高级模式\) |
|
||||
|
||||
#### 输出
|
||||
|
||||
|
||||
@@ -152,12 +152,13 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `to` | string | 是 | 收件人电子邮件地址 |
|
||||
| `subject` | string | 是 | 电子邮件主题 |
|
||||
| `body` | string | 是 | 电子邮件正文内容 |
|
||||
| `subject` | string | 是 | 邮件主题 |
|
||||
| `body` | string | 是 | 邮件正文内容 |
|
||||
| `replyToMessageId` | string | 否 | 要回复的消息 ID(用于线程化) |
|
||||
| `conversationId` | string | 否 | 用于线程化的会话 ID |
|
||||
| `cc` | string | 否 | 抄送收件人(逗号分隔) |
|
||||
| `bcc` | string | 否 | 密送收件人(逗号分隔) |
|
||||
| `cc` | string | 否 | 抄送收件人(以逗号分隔) |
|
||||
| `bcc` | string | 否 | 密送收件人(以逗号分隔) |
|
||||
| `attachments` | file[] | 否 | 要附加到邮件的文件 |
|
||||
|
||||
#### 输出
|
||||
|
||||
@@ -176,11 +177,12 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `to` | string | 是 | 收件人邮箱地址 |
|
||||
| `to` | string | 是 | 收件人电子邮件地址 |
|
||||
| `subject` | string | 是 | 邮件主题 |
|
||||
| `body` | string | 是 | 邮件正文内容 |
|
||||
| `cc` | string | 否 | 抄送收件人(逗号分隔) |
|
||||
| `bcc` | string | 否 | 密送收件人(逗号分隔) |
|
||||
| `cc` | string | 否 | 抄送收件人(以逗号分隔) |
|
||||
| `bcc` | string | 否 | 密送收件人(以逗号分隔) |
|
||||
| `attachments` | file[] | 否 | 要附加到邮件草稿的文件 |
|
||||
|
||||
#### 输出
|
||||
|
||||
|
||||
@@ -138,8 +138,9 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
| `apiKey` | string | 否 | Qdrant API 密钥(可选)|
|
||||
| `collection` | string | 是 | 集合名称 |
|
||||
| `vector` | array | 是 | 要搜索的向量 |
|
||||
| `limit` | number | 否 | 返回结果的数量 |
|
||||
| `filter` | object | 否 | 应用于搜索的过滤器 |
|
||||
| `limit` | number | 否 | 要返回的结果数量 |
|
||||
| `filter` | object | 否 | 要应用于搜索的过滤器 |
|
||||
| `search_return_data` | string | 否 | 搜索中要返回的数据 |
|
||||
| `with_payload` | boolean | 否 | 在响应中包含有效负载 |
|
||||
| `with_vector` | boolean | 否 | 在响应中包含向量 |
|
||||
|
||||
@@ -162,6 +163,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
| `apiKey` | string | 否 | Qdrant API 密钥(可选)|
|
||||
| `collection` | string | 是 | 集合名称 |
|
||||
| `ids` | array | 是 | 要获取的点 ID 数组 |
|
||||
| `fetch_return_data` | string | 否 | 获取中要返回的数据 |
|
||||
| `with_payload` | boolean | 否 | 在响应中包含有效负载 |
|
||||
| `with_vector` | boolean | 否 | 在响应中包含向量 |
|
||||
|
||||
|
||||
@@ -199,6 +199,26 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
| --------- | ---- | ----------- |
|
||||
| `item` | object | 创建的 SharePoint 列表项 |
|
||||
|
||||
### `sharepoint_upload_file`
|
||||
|
||||
将文件上传到 SharePoint 文档库
|
||||
|
||||
#### 输入
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | 否 | SharePoint 站点的 ID |
|
||||
| `driveId` | string | 否 | 文档库(驱动器)的 ID。如果未提供,则使用默认驱动器。 |
|
||||
| `folderPath` | string | 否 | 文档库中的可选文件夹路径(例如,/Documents/Subfolder) |
|
||||
| `fileName` | string | 否 | 可选:覆盖上传文件的名称 |
|
||||
| `files` | file[] | 否 | 要上传到 SharePoint 的文件 |
|
||||
|
||||
#### 输出
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `uploadedFiles` | array | 上传文件对象的数组 |
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 类别:`tools`
|
||||
|
||||
@@ -78,6 +78,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
| `botToken` | string | 否 | 自定义 Bot 的令牌 |
|
||||
| `channel` | string | 是 | 目标 Slack 频道(例如,#general) |
|
||||
| `text` | string | 是 | 要发送的消息文本(支持 Slack mrkdwn 格式) |
|
||||
| `files` | file[] | 否 | 要附加到消息的文件 |
|
||||
|
||||
#### 输出
|
||||
|
||||
|
||||
@@ -202,6 +202,28 @@ Sim 的 Supabase 集成使您能够轻松地将代理工作流连接到您的 Su
|
||||
| `message` | string | 操作状态消息 |
|
||||
| `results` | array | 已 upsert 的记录数组 |
|
||||
|
||||
### `supabase_vector_search`
|
||||
|
||||
在 Supabase 表中使用 pgvector 执行相似性搜索
|
||||
|
||||
#### 输入
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | 是 | 您的 Supabase 项目 ID \(例如:jdrkgepadsdopsntdlom\) |
|
||||
| `functionName` | string | 是 | 执行向量搜索的 PostgreSQL 函数名称 \(例如:match_documents\) |
|
||||
| `queryEmbedding` | array | 是 | 要搜索相似项的查询向量/嵌入 |
|
||||
| `matchThreshold` | number | 否 | 最小相似度阈值 \(0-1\),通常为 0.7-0.9 |
|
||||
| `matchCount` | number | 否 | 返回结果的最大数量 \(默认值:10\) |
|
||||
| `apiKey` | string | 是 | 您的 Supabase 服务角色密钥 |
|
||||
|
||||
#### 输出
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | 操作状态消息 |
|
||||
| `results` | array | 包含向量搜索相似度分数的记录数组。每条记录包括一个相似度字段 \(0-1\),表示与查询向量的相似程度。 |
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 类别:`tools`
|
||||
|
||||
@@ -189,6 +189,26 @@ Telegram 的主要功能包括:
|
||||
| `message` | 字符串 | 成功或错误消息 |
|
||||
| `data` | 对象 | 包含可选媒体的 Telegram 消息数据 |
|
||||
|
||||
### `telegram_send_document`
|
||||
|
||||
通过 Telegram Bot API 将文档(PDF、ZIP、DOC 等)发送到 Telegram 频道或用户。
|
||||
|
||||
#### 输入
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `botToken` | string | 是 | 您的 Telegram Bot API 令牌 |
|
||||
| `chatId` | string | 是 | 目标 Telegram 聊天 ID |
|
||||
| `files` | file[] | 否 | 要发送的文档文件(PDF、ZIP、DOC 等)。最大大小:50MB |
|
||||
| `caption` | string | 否 | 文档标题(可选) |
|
||||
|
||||
#### 输出
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | 成功或错误消息 |
|
||||
| `data` | object | 包含文档的 Telegram 消息数据 |
|
||||
|
||||
## 注意
|
||||
|
||||
- 类别:`tools`
|
||||
|
||||
@@ -59,9 +59,10 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | 是 | 所选模型提供商的 API 密钥 |
|
||||
| `imageUrl` | string | 是 | 可公开访问的图像 URL |
|
||||
| `model` | string | 否 | 要使用的视觉模型(gpt-4o、claude-3-opus-20240229 等) |
|
||||
| `prompt` | string | 否 | 图像分析的自定义提示 |
|
||||
| `imageUrl` | string | 否 | 可公开访问的图片 URL |
|
||||
| `imageFile` | file | 否 | 要分析的图片文件 |
|
||||
| `model` | string | 否 | 要使用的视觉模型 \(gpt-4o, claude-3-opus-20240229 等\) |
|
||||
| `prompt` | string | 否 | 用于图像分析的自定义提示 |
|
||||
|
||||
#### 输出
|
||||
|
||||
|
||||
144
apps/docs/content/docs/zh/tools/webflow.mdx
Normal file
144
apps/docs/content/docs/zh/tools/webflow.mdx
Normal file
@@ -0,0 +1,144 @@
|
||||
---
|
||||
title: Webflow
|
||||
description: 管理 Webflow CMS 集合
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="webflow"
|
||||
color="#E0E0E0"
|
||||
icon={true}
|
||||
iconSvg={`<svg className="block-icon"
|
||||
|
||||
|
||||
|
||||
viewBox='0 0 1080 674'
|
||||
fill='none'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
>
|
||||
<path
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
d='M1080 0L735.386 673.684H411.695L555.916 394.481H549.445C430.464 548.934 252.942 650.61 -0.000488281 673.684V398.344C-0.000488281 398.344 161.813 388.787 256.938 288.776H-0.000488281V0.0053214H288.771V237.515L295.252 237.489L413.254 0.0053214H631.644V236.009L638.126 235.999L760.555 0H1080Z'
|
||||
fill='#146EF5'
|
||||
/>
|
||||
</svg>`}
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[Webflow](https://webflow.com/) 是一个强大的可视化网页设计平台,能够让您无需编写代码即可构建响应式网站。它结合了可视化设计界面和强大的 CMS(内容管理系统),使您能够为网站创建、管理和发布动态内容。
|
||||
|
||||
使用 Webflow,您可以:
|
||||
|
||||
- **可视化设计**:使用可视化编辑器创建自定义网站,该编辑器会生成干净、语义化的 HTML/CSS 代码
|
||||
- **动态管理内容**:使用 CMS 创建结构化内容集合,例如博客文章、产品、团队成员或任何自定义数据
|
||||
- **即时发布**:将您的网站部署到 Webflow 的托管服务,或导出代码以进行自定义托管
|
||||
- **创建响应式设计**:构建在桌面、平板电脑和移动设备上无缝运行的网站
|
||||
- **自定义集合**:为您的内容类型定义自定义字段和数据结构
|
||||
- **自动更新内容**:通过 API 编程方式管理您的 CMS 内容
|
||||
|
||||
在 Sim 中,Webflow 集成使您的代理能够通过 API 身份验证无缝与 Webflow CMS 集合交互。这支持强大的自动化场景,例如从 AI 生成的内容中自动创建博客文章、更新产品信息、管理团队成员资料以及检索 CMS 项目以生成动态内容。您的代理可以列出现有项目以浏览内容,通过 ID 检索特定项目,创建新条目以添加新内容,更新现有项目以保持信息最新,以及删除过时内容。此集成弥合了您的 AI 工作流与 Webflow CMS 之间的差距,实现了自动化内容管理、动态网站更新和简化的内容工作流,使您的网站无需人工干预即可保持新鲜和最新。{/* MANUAL-CONTENT-END */}
|
||||
|
||||
## 使用说明
|
||||
|
||||
将 Webflow CMS 集成到工作流程中。可以创建、获取、列出、更新或删除 Webflow CMS 集合中的项目。以编程方式管理您的 Webflow 内容。可以在触发模式下使用,当集合项目发生变化或提交表单时触发工作流程。
|
||||
|
||||
## 工具
|
||||
|
||||
### `webflow_list_items`
|
||||
|
||||
列出 Webflow CMS 集合中的所有项目
|
||||
|
||||
#### 输入
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `collectionId` | string | 是 | 集合的 ID |
|
||||
| `offset` | number | 否 | 分页偏移量(可选) |
|
||||
| `limit` | number | 否 | 返回的最大项目数(可选,默认值:100) |
|
||||
|
||||
#### 输出
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `items` | json | 集合项目的数组 |
|
||||
| `metadata` | json | 查询的元数据 |
|
||||
|
||||
### `webflow_get_item`
|
||||
|
||||
从 Webflow CMS 集合中获取单个项目
|
||||
|
||||
#### 输入
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `collectionId` | string | 是 | 集合的 ID |
|
||||
| `itemId` | string | 是 | 要检索的项目 ID |
|
||||
|
||||
#### 输出
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `item` | json | 检索到的项目对象 |
|
||||
| `metadata` | json | 检索到的项目的元数据 |
|
||||
|
||||
### `webflow_create_item`
|
||||
|
||||
在 Webflow CMS 集合中创建一个新项目
|
||||
|
||||
#### 输入
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `collectionId` | string | 是 | 集合的 ID |
|
||||
| `fieldData` | json | 是 | 新项目的字段数据,格式为 JSON 对象。键名应与集合字段名称匹配。 |
|
||||
|
||||
#### 输出
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `item` | json | 创建的项目对象 |
|
||||
| `metadata` | json | 关于创建项目的元数据 |
|
||||
|
||||
### `webflow_update_item`
|
||||
|
||||
更新 Webflow CMS 集合中的现有项目
|
||||
|
||||
#### 输入
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `collectionId` | string | 是 | 集合的 ID |
|
||||
| `itemId` | string | 是 | 要更新项目的 ID |
|
||||
| `fieldData` | json | 是 | 要更新的字段数据,格式为 JSON 对象。仅包含您想更改的字段。 |
|
||||
|
||||
#### 输出
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `item` | json | 更新的项目对象 |
|
||||
| `metadata` | json | 关于更新项目的元数据 |
|
||||
|
||||
### `webflow_delete_item`
|
||||
|
||||
从 Webflow CMS 集合中删除项目
|
||||
|
||||
#### 输入
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `collectionId` | string | 是 | 集合的 ID |
|
||||
| `itemId` | string | 是 | 要删除项目的 ID |
|
||||
|
||||
#### 输出
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 是否删除成功 |
|
||||
| `metadata` | json | 有关删除的元数据 |
|
||||
|
||||
## 注意
|
||||
|
||||
- 类别:`tools`
|
||||
- 类型:`webflow`
|
||||
@@ -439,7 +439,7 @@ checksums:
|
||||
content/9: 10ec1e8eaecc6f3d30cfa63749e968b7
|
||||
content/10: f4b856e774ec5e995c437201d22036ee
|
||||
content/11: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/12: 4e324b7b7dc434eea83f6b5d528ab930
|
||||
content/12: ae0448bae89d0f81d8a28361acc2d67e
|
||||
content/13: bcadfc362b69078beee0088e5936c98b
|
||||
content/14: 0c8b7227f9df8b60ee36d80de899961e
|
||||
content/15: b3f310d5ef115bea5a8b75bf25d7ea9a
|
||||
@@ -594,8 +594,14 @@ checksums:
|
||||
content/47: f18d03fa59e3997a6d951e333d99c822
|
||||
content/48: bcadfc362b69078beee0088e5936c98b
|
||||
content/49: 8779f29ccc257e421d64c071949b81fb
|
||||
content/50: b3f310d5ef115bea5a8b75bf25d7ea9a
|
||||
content/51: e1fa627fb51f09989752a9bddf0ebb58
|
||||
content/50: 8782b269ede5cb41fecbd34072214c6c
|
||||
content/51: 79a5a4e61b55cdb65a6915ef08daeb93
|
||||
content/52: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/53: 7653b942236c8ad404c6962988fda3d3
|
||||
content/54: bcadfc362b69078beee0088e5936c98b
|
||||
content/55: 87cd2cb3a40d59957ca9ad1762c7a816
|
||||
content/56: b3f310d5ef115bea5a8b75bf25d7ea9a
|
||||
content/57: e1fa627fb51f09989752a9bddf0ebb58
|
||||
ac9313ccf1478cdf17fe25081b6b78be:
|
||||
meta/title: df20085ae7dc009c9b6532113064c6bb
|
||||
meta/description: 0a0c4af79216512ddc68df973afed52c
|
||||
@@ -672,8 +678,14 @@ checksums:
|
||||
content/44: b37f6692238f3a45bfcf106f0d192b21
|
||||
content/45: bcadfc362b69078beee0088e5936c98b
|
||||
content/46: 1788748095a805b62a0e21403789dad7
|
||||
content/47: b3f310d5ef115bea5a8b75bf25d7ea9a
|
||||
content/48: d2ca5a540458c526aebabd2f9ff3fd03
|
||||
content/47: dd231637d3d327f0daf546fe92594ac6
|
||||
content/48: 97f32d4cd73f2375eb8540e71300dad1
|
||||
content/49: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/50: ce131f6bcfe8a6aeb340229a107febc3
|
||||
content/51: bcadfc362b69078beee0088e5936c98b
|
||||
content/52: 67cb33e6ab252c7130cac02728311dc8
|
||||
content/53: b3f310d5ef115bea5a8b75bf25d7ea9a
|
||||
content/54: d2ca5a540458c526aebabd2f9ff3fd03
|
||||
51862d3525bcbc53275ead2362197752:
|
||||
meta/title: 64d1e9a0e5ae9fdb055a5366c22433fc
|
||||
meta/description: 1f9e8e75ebe525fc5bb53d9f5e0dd3a8
|
||||
@@ -732,7 +744,7 @@ checksums:
|
||||
content/12: 8ee83eff32425b2c52929284e8485c20
|
||||
content/13: 6cda87dc9837779f4572ed70b87a5654
|
||||
content/14: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/15: 57e7f55217bb573b93ff0e6e37bd58a7
|
||||
content/15: ba09630dea12ec37bbe8a4f490150c8a
|
||||
content/16: bcadfc362b69078beee0088e5936c98b
|
||||
content/17: 1f31e78210417a7f251f29e0b93a8528
|
||||
content/18: 05540cb3028d4d781521c14e5f9e3835
|
||||
@@ -803,8 +815,14 @@ checksums:
|
||||
content/48: 5d7221454e6adf5eed5f0c02b24eb9e0
|
||||
content/49: bcadfc362b69078beee0088e5936c98b
|
||||
content/50: f42ef799c350c30117cb100b98a9d526
|
||||
content/51: b3f310d5ef115bea5a8b75bf25d7ea9a
|
||||
content/52: 6480fe7f4ab32b223c32deab16b6922b
|
||||
content/51: a13d828344becb9f2f7c2a788dda30a5
|
||||
content/52: 5d0ccd6bc37305555c6a48cec645fe48
|
||||
content/53: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/54: bfdd461ff9444c6d81c5fb0b9cee3c97
|
||||
content/55: bcadfc362b69078beee0088e5936c98b
|
||||
content/56: ce194fa4c0560d03b31f53bbe0e1cf94
|
||||
content/57: b3f310d5ef115bea5a8b75bf25d7ea9a
|
||||
content/58: 6480fe7f4ab32b223c32deab16b6922b
|
||||
d36fb89d311a649e49173c0998fad09f:
|
||||
meta/title: 25b7006e238bd277acad0336b59e6581
|
||||
meta/description: 4c972c767abaf134b90c2e9bf6619876
|
||||
@@ -927,13 +945,13 @@ checksums:
|
||||
content/17: f5bef3db56ed3a56395f7ae1fa41ecf3
|
||||
content/18: 7ca733ac5374e92a9cc8ef35e1075fb1
|
||||
content/19: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/20: 8ce52b8ffed51482dff6fa0f2846c498
|
||||
content/20: 0eada001684acb8efe28fcfed38a5163
|
||||
content/21: bcadfc362b69078beee0088e5936c98b
|
||||
content/22: b875ec2f16d200917e9860b49a5a9772
|
||||
content/23: c0c2276dd4207eb2b08d4dc9132e7ec3
|
||||
content/24: 85de953906920f3fb4eafa8fdb918feb
|
||||
content/25: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/26: f63654037687387924343d8f7453f379
|
||||
content/26: b0cf90320ac6b98d5bf00b87052cd76e
|
||||
content/27: bcadfc362b69078beee0088e5936c98b
|
||||
content/28: e62b89406f01af79e2e293d352aa2499
|
||||
content/29: b3f310d5ef115bea5a8b75bf25d7ea9a
|
||||
@@ -1081,13 +1099,13 @@ checksums:
|
||||
content/9: 822829fd1c0500496b72d5a0abafeb41
|
||||
content/10: 4a8b7fc9ab62f88061e4b21ac5b10947
|
||||
content/11: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/12: ba245342edd3cc56355ba84be1d45aef
|
||||
content/12: 9edeb4b9fca066dd5bf11d0c3a2cb8db
|
||||
content/13: bcadfc362b69078beee0088e5936c98b
|
||||
content/14: 558c49a1186440a5a2254dcfbd7b74f7
|
||||
content/15: 64a8e14453f9063bd8d89d43b7833fa8
|
||||
content/16: c5800c27cab920bd65932ffc703e6fc6
|
||||
content/17: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/18: 96832958c23fb2c9cefb35ee9a63cded
|
||||
content/18: c886c72190889c7b55a8f38a294ecf38
|
||||
content/19: bcadfc362b69078beee0088e5936c98b
|
||||
content/20: 9f1a5d98944fc79dbcd0b01e0a82fe28
|
||||
content/21: d80aeca589a53111bf1695cdd331cf5b
|
||||
@@ -1140,7 +1158,7 @@ checksums:
|
||||
content/10: 6df7e096bafa5efda818db1ab65841fd
|
||||
content/11: 646990da68ff77ca02dd172f830f12f0
|
||||
content/12: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/13: 6e6d228bc03c82d986ab0f60470fd1ea
|
||||
content/13: 6979ff1359b3ede098d71e35fabd6a05
|
||||
content/14: bcadfc362b69078beee0088e5936c98b
|
||||
content/15: c25b7418fd96b560b79af512cba4e7f1
|
||||
content/16: 3d44fa28ed12c73c2c9178b8570e516d
|
||||
@@ -1340,7 +1358,7 @@ checksums:
|
||||
content/15: 9a44ad2a020c00d5c50250ee0c543ae2
|
||||
content/16: 3f013e8cf7537231614d510c9bcdf1b2
|
||||
content/17: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/18: e0e86275deb46b5c7b516849de4d70fa
|
||||
content/18: 48389c9abe6b4ab2f836686265618a0e
|
||||
content/19: bcadfc362b69078beee0088e5936c98b
|
||||
content/20: 5e472d191f58641d1b0416383875f22b
|
||||
content/21: 25d767f79c8b785d24657d66091e479b
|
||||
@@ -1352,7 +1370,7 @@ checksums:
|
||||
content/27: 89ae14a70cbc288e4f816b710c7ef48e
|
||||
content/28: 311c355c6e43246e52112ee300df8db5
|
||||
content/29: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/30: f28566e14c56ba74700c17a6ddebc994
|
||||
content/30: 44d1f81db519e167c575e2ac6d34048c
|
||||
content/31: bcadfc362b69078beee0088e5936c98b
|
||||
content/32: d977e473bf5c4e014362b2f7c5e4ae7b
|
||||
content/33: b3f310d5ef115bea5a8b75bf25d7ea9a
|
||||
@@ -1820,7 +1838,7 @@ checksums:
|
||||
content/11: 871d6ecf3f593f3b20f5c3c8ded1ecc6
|
||||
content/12: 30072675a0dffbddc0e85bd3e34764ec
|
||||
content/13: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/14: fb8b01d27288339ac6e87c630abca238
|
||||
content/14: f07ac61557b436d01cbc1fbc2684d39f
|
||||
content/15: bcadfc362b69078beee0088e5936c98b
|
||||
content/16: 1e23d729d38b5da93b6598e73e4ab612
|
||||
content/17: 24991dd332b7a6b8f8091e3e219b542f
|
||||
@@ -2165,7 +2183,7 @@ checksums:
|
||||
meta/title: 06ec7d95ab44931ed9d1925e4063d703
|
||||
meta/description: cc9ab492bdda4a2cb9085537d6e6a0c0
|
||||
content/0: 1b031fb0c62c46b177aeed5c3d3f8f80
|
||||
content/1: 73fb594a22fcf560087b53ea6aa592f6
|
||||
content/1: f744e92fcc234d02bb9b352a2ab1e1e3
|
||||
content/2: b229bf34f0106ccb5af6f0b2a044e21a
|
||||
content/3: 45e7cee1fa342c4d13a1a6cb70733a14
|
||||
content/4: 62db68be640983ea9a383ee162bd8463
|
||||
@@ -2180,7 +2198,7 @@ checksums:
|
||||
content/13: 4a01bece58a2e9ac96bea57ed39e35d5
|
||||
content/14: f49c586052b88ec4bf80d844c0c7ca3e
|
||||
content/15: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/16: f2a31106e2f78ba7e12a78e234ba1e9a
|
||||
content/16: ff83558d232dd95d39838a2ca868dfed
|
||||
content/17: bcadfc362b69078beee0088e5936c98b
|
||||
content/18: 6b7691d4977f1b6520a8b3236b993358
|
||||
content/19: 96f2aa27a8d3f74035575de3da6c47c4
|
||||
@@ -2246,9 +2264,9 @@ checksums:
|
||||
content/11: 3524f0dac9a9152db223bcc2682a842d
|
||||
content/12: 341fbcb79af9a7cb1bf5ac653f51807c
|
||||
content/13: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/14: 85aebdee44deb5b2f03e95112a776839
|
||||
content/14: 6dba14b1346c18cd2342f502371a1042
|
||||
content/15: bcadfc362b69078beee0088e5936c98b
|
||||
content/16: 7062fc4e2ca6974003e0d209f2b52d9f
|
||||
content/16: df68275133be883eac95664c3ed10063
|
||||
content/17: b3f310d5ef115bea5a8b75bf25d7ea9a
|
||||
content/18: 59815ce1d0dddd507d505b42aa01b648
|
||||
44f1f9fe8d5081b7781dc70e012cb531:
|
||||
@@ -4342,3 +4360,47 @@ checksums:
|
||||
content/58: 74af50eceb2a5f514301c497a8e64030
|
||||
content/59: b3f310d5ef115bea5a8b75bf25d7ea9a
|
||||
content/60: 549a9bd7ff92264fbd18c9d6e616594b
|
||||
65891ef7e29a3ad2464222a998549ff5:
|
||||
meta/title: b35b5211a53c68cea3a3f0995099c24b
|
||||
meta/description: c12cfd50357fba2ffebaf79750b7d76d
|
||||
content/0: 1b031fb0c62c46b177aeed5c3d3f8f80
|
||||
content/1: 49bca8f0502318620cbebb686a4fd5b3
|
||||
content/2: 4d30444228f205f237d0c194fe4dd3d9
|
||||
content/3: 8d8c30d8ee25edbf854d7d2ac1e2dbd8
|
||||
content/4: e4d2f4146482f4724c177815103e026b
|
||||
content/5: 68b1bf876a3ba2faf8fba18ae4e1bc19
|
||||
content/6: 821e6394b0a953e2b0842b04ae8f3105
|
||||
content/7: 22ab0fa161f448ca26998e101805e59e
|
||||
content/8: 9c8aa3f09c9b2bd50ea4cdff3598ea4e
|
||||
content/9: 5914baadfaf2ca26d54130a36dd5ed29
|
||||
content/10: 25507380ac7d9c7f8cf9f5256c6a0dbb
|
||||
content/11: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/12: e7fb612c3323c1e6b05eacfcea360d34
|
||||
content/13: bcadfc362b69078beee0088e5936c98b
|
||||
content/14: e5f830d6049ff79a318110098e5e0130
|
||||
content/15: 711e90714806b91f93923018e82ad2e9
|
||||
content/16: 0f3f7d9699d7397cb3a094c3229329ee
|
||||
content/17: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/18: c53b5b8f901066e63fe159ad2fa5e6e0
|
||||
content/19: bcadfc362b69078beee0088e5936c98b
|
||||
content/20: 5f2afdd49c3ac13381401c69d1eca22a
|
||||
content/21: cc4baa9096fafa4c6276f6136412ba66
|
||||
content/22: 676f76e8a7154a576d7fa20b245cef70
|
||||
content/23: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/24: c67c387eb7e274ee7c07b7e1748afce1
|
||||
content/25: bcadfc362b69078beee0088e5936c98b
|
||||
content/26: a6ffebda549ad5b903a66c7d9ac03a20
|
||||
content/27: 0dadd51cde48d6ea75b29ec3ee4ade56
|
||||
content/28: cdc74f6483a0b4e9933ecdd92ed7480f
|
||||
content/29: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/30: 4cda10aa374e1a46d60ad14eeaa79100
|
||||
content/31: bcadfc362b69078beee0088e5936c98b
|
||||
content/32: 5f221421953a0e760ead7388cbf66561
|
||||
content/33: a3c0372590cef72d5d983dbc8dbbc2cb
|
||||
content/34: 1402e53c08bdd8a741f44b2d66fcd003
|
||||
content/35: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/36: 028e579a28e55def4fbc59f39f4610b7
|
||||
content/37: bcadfc362b69078beee0088e5936c98b
|
||||
content/38: 4fe4260da2f137679ce2fa42cffcf56a
|
||||
content/39: b3f310d5ef115bea5a8b75bf25d7ea9a
|
||||
content/40: 89bdbd886b24f2aaec635a2b4119660a
|
||||
|
||||
@@ -71,6 +71,12 @@ export default function SSOForm() {
|
||||
}
|
||||
}
|
||||
|
||||
// Pre-fill email if provided in URL (e.g., from deployed chat SSO)
|
||||
const emailParam = searchParams.get('email')
|
||||
if (emailParam) {
|
||||
setEmail(emailParam)
|
||||
}
|
||||
|
||||
// Check for SSO error from redirect
|
||||
const error = searchParams.get('error')
|
||||
if (error) {
|
||||
|
||||
@@ -20,5 +20,4 @@ INTERNAL_API_SECRET=your_internal_api_secret # Use `openssl rand -hex 32` to gen
|
||||
# If left commented out, emails will be logged to console instead
|
||||
|
||||
# Local AI Models (Optional)
|
||||
# OLLAMA_URL=http://localhost:11434 # URL for local Ollama server - uncomment if using local models
|
||||
|
||||
# OLLAMA_URL=http://localhost:11434 # URL for local Ollama server - uncomment if using local models
|
||||
@@ -1,6 +1,6 @@
|
||||
import { db } from '@sim/db'
|
||||
import { subscription as subscriptionTable, user } from '@sim/db/schema'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { and, eq, or } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { requireStripeClient } from '@/lib/billing/stripe-client'
|
||||
@@ -38,7 +38,10 @@ export async function POST(request: NextRequest) {
|
||||
.where(
|
||||
and(
|
||||
eq(subscriptionTable.referenceId, organizationId),
|
||||
eq(subscriptionTable.status, 'active')
|
||||
or(
|
||||
eq(subscriptionTable.status, 'active'),
|
||||
eq(subscriptionTable.cancelAtPeriodEnd, true)
|
||||
)
|
||||
)
|
||||
)
|
||||
.limit(1)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { db } from '@sim/db'
|
||||
import { chat, workflow } from '@sim/db/schema'
|
||||
import { chat, workflow, workspace } from '@sim/db/schema'
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
@@ -94,11 +94,12 @@ export async function POST(
|
||||
return addCorsHeaders(createErrorResponse('No input provided', 400), request)
|
||||
}
|
||||
|
||||
// Get the workflow for this chat
|
||||
// Get the workflow and workspace owner for this chat
|
||||
const workflowResult = await db
|
||||
.select({
|
||||
isDeployed: workflow.isDeployed,
|
||||
workspaceId: workflow.workspaceId,
|
||||
variables: workflow.variables,
|
||||
})
|
||||
.from(workflow)
|
||||
.where(eq(workflow.id, deployment.workflowId))
|
||||
@@ -109,6 +110,22 @@ export async function POST(
|
||||
return addCorsHeaders(createErrorResponse('Chat workflow is not available', 503), request)
|
||||
}
|
||||
|
||||
let workspaceOwnerId = deployment.userId
|
||||
if (workflowResult[0].workspaceId) {
|
||||
const workspaceData = await db
|
||||
.select({ ownerId: workspace.ownerId })
|
||||
.from(workspace)
|
||||
.where(eq(workspace.id, workflowResult[0].workspaceId))
|
||||
.limit(1)
|
||||
|
||||
if (workspaceData.length === 0) {
|
||||
logger.error(`[${requestId}] Workspace not found for workflow ${deployment.workflowId}`)
|
||||
return addCorsHeaders(createErrorResponse('Workspace not found', 500), request)
|
||||
}
|
||||
|
||||
workspaceOwnerId = workspaceData[0].ownerId
|
||||
}
|
||||
|
||||
try {
|
||||
const selectedOutputs: string[] = []
|
||||
if (deployment.outputConfigs && Array.isArray(deployment.outputConfigs)) {
|
||||
@@ -145,16 +162,19 @@ export async function POST(
|
||||
}
|
||||
}
|
||||
|
||||
const workflowForExecution = {
|
||||
id: deployment.workflowId,
|
||||
userId: deployment.userId,
|
||||
workspaceId: workflowResult[0].workspaceId,
|
||||
isDeployed: true,
|
||||
variables: workflowResult[0].variables || {},
|
||||
}
|
||||
|
||||
const stream = await createStreamingResponse({
|
||||
requestId,
|
||||
workflow: {
|
||||
id: deployment.workflowId,
|
||||
userId: deployment.userId,
|
||||
workspaceId: workflowResult[0].workspaceId,
|
||||
isDeployed: true,
|
||||
},
|
||||
workflow: workflowForExecution,
|
||||
input: workflowInput,
|
||||
executingUserId: deployment.userId,
|
||||
executingUserId: workspaceOwnerId,
|
||||
streamConfig: {
|
||||
selectedOutputs,
|
||||
isSecureMode: true,
|
||||
|
||||
@@ -8,6 +8,7 @@ import { isDev } from '@/lib/environment'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { getEmailDomain } from '@/lib/urls/utils'
|
||||
import { encryptSecret } from '@/lib/utils'
|
||||
import { deployWorkflow } from '@/lib/workflows/db-helpers'
|
||||
import { checkChatAccess } from '@/app/api/chat/utils'
|
||||
import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils'
|
||||
|
||||
@@ -31,7 +32,7 @@ const chatUpdateSchema = z.object({
|
||||
imageUrl: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
authType: z.enum(['public', 'password', 'email']).optional(),
|
||||
authType: z.enum(['public', 'password', 'email', 'sso']).optional(),
|
||||
password: z.string().optional(),
|
||||
allowedEmails: z.array(z.string()).optional(),
|
||||
outputConfigs: z
|
||||
@@ -134,6 +135,22 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise<
|
||||
}
|
||||
}
|
||||
|
||||
// Redeploy the workflow to ensure latest version is active
|
||||
const deployResult = await deployWorkflow({
|
||||
workflowId: existingChat[0].workflowId,
|
||||
deployedBy: session.user.id,
|
||||
})
|
||||
|
||||
if (!deployResult.success) {
|
||||
logger.warn(
|
||||
`Failed to redeploy workflow for chat update: ${deployResult.error}, continuing with chat update`
|
||||
)
|
||||
} else {
|
||||
logger.info(
|
||||
`Redeployed workflow ${existingChat[0].workflowId} for chat update (v${deployResult.version})`
|
||||
)
|
||||
}
|
||||
|
||||
let encryptedPassword
|
||||
|
||||
if (password) {
|
||||
@@ -165,7 +182,7 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise<
|
||||
updateData.allowedEmails = []
|
||||
} else if (authType === 'password') {
|
||||
updateData.allowedEmails = []
|
||||
} else if (authType === 'email') {
|
||||
} else if (authType === 'email' || authType === 'sso') {
|
||||
updateData.password = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ describe('Chat API Route', () => {
|
||||
const mockCreateErrorResponse = vi.fn()
|
||||
const mockEncryptSecret = vi.fn()
|
||||
const mockCheckWorkflowAccessForChatCreation = vi.fn()
|
||||
const mockDeployWorkflow = vi.fn()
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetModules()
|
||||
@@ -76,6 +77,14 @@ describe('Chat API Route', () => {
|
||||
vi.doMock('@/app/api/chat/utils', () => ({
|
||||
checkWorkflowAccessForChatCreation: mockCheckWorkflowAccessForChatCreation,
|
||||
}))
|
||||
|
||||
vi.doMock('@/lib/workflows/db-helpers', () => ({
|
||||
deployWorkflow: mockDeployWorkflow.mockResolvedValue({
|
||||
success: true,
|
||||
version: 1,
|
||||
deployedAt: new Date(),
|
||||
}),
|
||||
}))
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
@@ -236,7 +245,7 @@ describe('Chat API Route', () => {
|
||||
it('should allow chat deployment when user owns workflow directly', async () => {
|
||||
vi.doMock('@/lib/auth', () => ({
|
||||
getSession: vi.fn().mockResolvedValue({
|
||||
user: { id: 'user-id' },
|
||||
user: { id: 'user-id', email: 'user@example.com' },
|
||||
}),
|
||||
}))
|
||||
|
||||
@@ -283,7 +292,7 @@ describe('Chat API Route', () => {
|
||||
it('should allow chat deployment when user has workspace admin permission', async () => {
|
||||
vi.doMock('@/lib/auth', () => ({
|
||||
getSession: vi.fn().mockResolvedValue({
|
||||
user: { id: 'user-id' },
|
||||
user: { id: 'user-id', email: 'user@example.com' },
|
||||
}),
|
||||
}))
|
||||
|
||||
@@ -393,10 +402,10 @@ describe('Chat API Route', () => {
|
||||
expect(mockCheckWorkflowAccessForChatCreation).toHaveBeenCalledWith('workflow-123', 'user-id')
|
||||
})
|
||||
|
||||
it('should reject if workflow is not deployed', async () => {
|
||||
it('should auto-deploy workflow if not already deployed', async () => {
|
||||
vi.doMock('@/lib/auth', () => ({
|
||||
getSession: vi.fn().mockResolvedValue({
|
||||
user: { id: 'user-id' },
|
||||
user: { id: 'user-id', email: 'user@example.com' },
|
||||
}),
|
||||
}))
|
||||
|
||||
@@ -415,6 +424,7 @@ describe('Chat API Route', () => {
|
||||
hasAccess: true,
|
||||
workflow: { userId: 'user-id', workspaceId: null, isDeployed: false },
|
||||
})
|
||||
mockReturning.mockResolvedValue([{ id: 'test-uuid' }])
|
||||
|
||||
const req = new NextRequest('http://localhost:3000/api/chat', {
|
||||
method: 'POST',
|
||||
@@ -423,11 +433,11 @@ describe('Chat API Route', () => {
|
||||
const { POST } = await import('@/app/api/chat/route')
|
||||
const response = await POST(req)
|
||||
|
||||
expect(response.status).toBe(400)
|
||||
expect(mockCreateErrorResponse).toHaveBeenCalledWith(
|
||||
'Workflow must be deployed before creating a chat',
|
||||
400
|
||||
)
|
||||
expect(response.status).toBe(200)
|
||||
expect(mockDeployWorkflow).toHaveBeenCalledWith({
|
||||
workflowId: 'workflow-123',
|
||||
deployedBy: 'user-id',
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -9,6 +9,7 @@ import { isDev } from '@/lib/environment'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { getBaseUrl } from '@/lib/urls/utils'
|
||||
import { encryptSecret } from '@/lib/utils'
|
||||
import { deployWorkflow } from '@/lib/workflows/db-helpers'
|
||||
import { checkWorkflowAccessForChatCreation } from '@/app/api/chat/utils'
|
||||
import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils'
|
||||
|
||||
@@ -27,7 +28,7 @@ const chatSchema = z.object({
|
||||
welcomeMessage: z.string(),
|
||||
imageUrl: z.string().optional(),
|
||||
}),
|
||||
authType: z.enum(['public', 'password', 'email']).default('public'),
|
||||
authType: z.enum(['public', 'password', 'email', 'sso']).default('public'),
|
||||
password: z.string().optional(),
|
||||
allowedEmails: z.array(z.string()).optional().default([]),
|
||||
outputConfigs: z
|
||||
@@ -98,6 +99,13 @@ export async function POST(request: NextRequest) {
|
||||
)
|
||||
}
|
||||
|
||||
if (authType === 'sso' && (!Array.isArray(allowedEmails) || allowedEmails.length === 0)) {
|
||||
return createErrorResponse(
|
||||
'At least one email or domain is required when using SSO access control',
|
||||
400
|
||||
)
|
||||
}
|
||||
|
||||
// Check if identifier is available
|
||||
const existingIdentifier = await db
|
||||
.select()
|
||||
@@ -119,11 +127,20 @@ export async function POST(request: NextRequest) {
|
||||
return createErrorResponse('Workflow not found or access denied', 404)
|
||||
}
|
||||
|
||||
// Verify the workflow is deployed (required for chat deployment)
|
||||
if (!workflowRecord.isDeployed) {
|
||||
return createErrorResponse('Workflow must be deployed before creating a chat', 400)
|
||||
// Always deploy/redeploy the workflow to ensure latest version
|
||||
const result = await deployWorkflow({
|
||||
workflowId,
|
||||
deployedBy: session.user.id,
|
||||
})
|
||||
|
||||
if (!result.success) {
|
||||
return createErrorResponse(result.error || 'Failed to deploy workflow', 500)
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`${workflowRecord.isDeployed ? 'Redeployed' : 'Auto-deployed'} workflow ${workflowId} for chat (v${result.version})`
|
||||
)
|
||||
|
||||
// Encrypt password if provided
|
||||
let encryptedPassword = null
|
||||
if (authType === 'password' && password) {
|
||||
@@ -163,7 +180,7 @@ export async function POST(request: NextRequest) {
|
||||
isActive: true,
|
||||
authType,
|
||||
password: encryptedPassword,
|
||||
allowedEmails: authType === 'email' ? allowedEmails : [],
|
||||
allowedEmails: authType === 'email' || authType === 'sso' ? allowedEmails : [],
|
||||
outputConfigs,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
|
||||
@@ -262,7 +262,67 @@ export async function validateChatAuth(
|
||||
}
|
||||
}
|
||||
|
||||
// Unknown auth type
|
||||
if (authType === 'sso') {
|
||||
if (request.method === 'GET') {
|
||||
return { authorized: false, error: 'auth_required_sso' }
|
||||
}
|
||||
|
||||
try {
|
||||
if (!parsedBody) {
|
||||
return { authorized: false, error: 'SSO authentication is required' }
|
||||
}
|
||||
|
||||
const { email, input, checkSSOAccess } = parsedBody
|
||||
|
||||
if (checkSSOAccess) {
|
||||
if (!email) {
|
||||
return { authorized: false, error: 'Email is required' }
|
||||
}
|
||||
|
||||
const allowedEmails = deployment.allowedEmails || []
|
||||
|
||||
if (allowedEmails.includes(email)) {
|
||||
return { authorized: true }
|
||||
}
|
||||
|
||||
const domain = email.split('@')[1]
|
||||
if (domain && allowedEmails.some((allowed: string) => allowed === `@${domain}`)) {
|
||||
return { authorized: true }
|
||||
}
|
||||
|
||||
return { authorized: false, error: 'Email not authorized for SSO access' }
|
||||
}
|
||||
|
||||
const { auth } = await import('@/lib/auth')
|
||||
const session = await auth.api.getSession({ headers: request.headers })
|
||||
|
||||
if (!session || !session.user) {
|
||||
return { authorized: false, error: 'auth_required_sso' }
|
||||
}
|
||||
|
||||
const userEmail = session.user.email
|
||||
if (!userEmail) {
|
||||
return { authorized: false, error: 'SSO session does not contain email' }
|
||||
}
|
||||
|
||||
const allowedEmails = deployment.allowedEmails || []
|
||||
|
||||
if (allowedEmails.includes(userEmail)) {
|
||||
return { authorized: true }
|
||||
}
|
||||
|
||||
const domain = userEmail.split('@')[1]
|
||||
if (domain && allowedEmails.some((allowed: string) => allowed === `@${domain}`)) {
|
||||
return { authorized: true }
|
||||
}
|
||||
|
||||
return { authorized: false, error: 'Your email is not authorized to access this chat' }
|
||||
} catch (error) {
|
||||
logger.error(`[${requestId}] Error validating SSO:`, error)
|
||||
return { authorized: false, error: 'SSO authentication error' }
|
||||
}
|
||||
}
|
||||
|
||||
return { authorized: false, error: 'Unsupported authentication type' }
|
||||
}
|
||||
|
||||
|
||||
@@ -121,10 +121,24 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
|
||||
if (!isUsingCloudStorage()) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Direct uploads are only available when cloud storage is enabled' },
|
||||
{ status: 400 }
|
||||
logger.info(
|
||||
`Local storage detected - batch presigned URLs not available, client will use API fallback`
|
||||
)
|
||||
return NextResponse.json({
|
||||
files: files.map((file) => ({
|
||||
fileName: file.fileName,
|
||||
presignedUrl: '', // Empty URL signals fallback to API upload
|
||||
fileInfo: {
|
||||
path: '',
|
||||
key: '',
|
||||
name: file.fileName,
|
||||
size: file.fileSize,
|
||||
type: file.contentType,
|
||||
},
|
||||
directUploadSupported: false,
|
||||
})),
|
||||
directUploadSupported: false,
|
||||
})
|
||||
}
|
||||
|
||||
const storageProvider = getStorageProvider()
|
||||
|
||||
@@ -25,7 +25,7 @@ describe('/api/files/presigned', () => {
|
||||
})
|
||||
|
||||
describe('POST', () => {
|
||||
it('should return error when cloud storage is not enabled', async () => {
|
||||
it('should return graceful fallback response when cloud storage is not enabled', async () => {
|
||||
setupFileApiMocks({
|
||||
cloudEnabled: false,
|
||||
storageProvider: 's3',
|
||||
@@ -45,10 +45,14 @@ describe('/api/files/presigned', () => {
|
||||
const response = await POST(request)
|
||||
const data = await response.json()
|
||||
|
||||
expect(response.status).toBe(500)
|
||||
expect(data.error).toBe('Direct uploads are only available when cloud storage is enabled')
|
||||
expect(data.code).toBe('STORAGE_CONFIG_ERROR')
|
||||
expect(response.status).toBe(200)
|
||||
expect(data.directUploadSupported).toBe(false)
|
||||
expect(data.presignedUrl).toBe('')
|
||||
expect(data.fileName).toBe('test.txt')
|
||||
expect(data.fileInfo).toBeDefined()
|
||||
expect(data.fileInfo.name).toBe('test.txt')
|
||||
expect(data.fileInfo.size).toBe(1024)
|
||||
expect(data.fileInfo.type).toBe('text/plain')
|
||||
})
|
||||
|
||||
it('should return error when fileName is missing', async () => {
|
||||
|
||||
@@ -141,9 +141,21 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
|
||||
if (!isUsingCloudStorage()) {
|
||||
throw new StorageConfigError(
|
||||
'Direct uploads are only available when cloud storage is enabled'
|
||||
logger.info(
|
||||
`Local storage detected - presigned URL not available for ${fileName}, client will use API fallback`
|
||||
)
|
||||
return NextResponse.json({
|
||||
fileName,
|
||||
presignedUrl: '', // Empty URL signals fallback to API upload
|
||||
fileInfo: {
|
||||
path: '',
|
||||
key: '',
|
||||
name: fileName,
|
||||
size: fileSize,
|
||||
type: contentType,
|
||||
},
|
||||
directUploadSupported: false,
|
||||
})
|
||||
}
|
||||
|
||||
const storageProvider = getStorageProvider()
|
||||
|
||||
@@ -90,16 +90,38 @@ export const POST = withMcpAuth('read')(
|
||||
)
|
||||
}
|
||||
|
||||
// Parse array arguments based on tool schema
|
||||
// Cast arguments to their expected types based on tool schema
|
||||
if (tool.inputSchema?.properties) {
|
||||
for (const [paramName, paramSchema] of Object.entries(tool.inputSchema.properties)) {
|
||||
const schema = paramSchema as any
|
||||
const value = args[paramName]
|
||||
|
||||
if (value === undefined || value === null) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Cast numbers
|
||||
if (
|
||||
schema.type === 'array' &&
|
||||
args[paramName] !== undefined &&
|
||||
typeof args[paramName] === 'string'
|
||||
(schema.type === 'number' || schema.type === 'integer') &&
|
||||
typeof value === 'string'
|
||||
) {
|
||||
const stringValue = args[paramName].trim()
|
||||
const numValue =
|
||||
schema.type === 'integer' ? Number.parseInt(value) : Number.parseFloat(value)
|
||||
if (!Number.isNaN(numValue)) {
|
||||
args[paramName] = numValue
|
||||
}
|
||||
}
|
||||
// Cast booleans
|
||||
else if (schema.type === 'boolean' && typeof value === 'string') {
|
||||
if (value.toLowerCase() === 'true') {
|
||||
args[paramName] = true
|
||||
} else if (value.toLowerCase() === 'false') {
|
||||
args[paramName] = false
|
||||
}
|
||||
}
|
||||
// Cast arrays
|
||||
else if (schema.type === 'array' && typeof value === 'string') {
|
||||
const stringValue = value.trim()
|
||||
if (stringValue) {
|
||||
try {
|
||||
// Try to parse as JSON first (handles ["item1", "item2"])
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { validateImageUrl } from '@/lib/security/input-validation'
|
||||
import { generateRequestId } from '@/lib/utils'
|
||||
@@ -14,6 +15,12 @@ export async function GET(request: NextRequest) {
|
||||
const imageUrl = url.searchParams.get('url')
|
||||
const requestId = generateRequestId()
|
||||
|
||||
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||
if (!authResult.success) {
|
||||
logger.error(`[${requestId}] Authentication failed for image proxy:`, authResult.error)
|
||||
return new NextResponse('Unauthorized', { status: 401 })
|
||||
}
|
||||
|
||||
if (!imageUrl) {
|
||||
logger.error(`[${requestId}] Missing 'url' parameter`)
|
||||
return new NextResponse('Missing URL parameter', { status: 400 })
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||
import { generateInternalToken } from '@/lib/auth/internal'
|
||||
import { isDev } from '@/lib/environment'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
@@ -242,12 +244,18 @@ export async function GET(request: Request) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
export async function POST(request: NextRequest) {
|
||||
const requestId = generateRequestId()
|
||||
const startTime = new Date()
|
||||
const startTimeISO = startTime.toISOString()
|
||||
|
||||
try {
|
||||
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||
if (!authResult.success) {
|
||||
logger.error(`[${requestId}] Authentication failed for proxy:`, authResult.error)
|
||||
return createErrorResponse('Unauthorized', 401)
|
||||
}
|
||||
|
||||
let requestBody
|
||||
try {
|
||||
requestBody = await request.json()
|
||||
@@ -311,7 +319,6 @@ export async function POST(request: Request) {
|
||||
error: result.error || 'Unknown error',
|
||||
})
|
||||
|
||||
// Let the main executeTool handle error transformation to avoid double transformation
|
||||
throw new Error(result.error || 'Tool execution failed')
|
||||
}
|
||||
|
||||
@@ -319,10 +326,8 @@ export async function POST(request: Request) {
|
||||
const endTimeISO = endTime.toISOString()
|
||||
const duration = endTime.getTime() - startTime.getTime()
|
||||
|
||||
// Add explicit timing information directly to the response
|
||||
const responseWithTimingData = {
|
||||
...result,
|
||||
// Add timing data both at root level and in nested timing object
|
||||
startTime: startTimeISO,
|
||||
endTime: endTimeISO,
|
||||
duration,
|
||||
@@ -335,7 +340,6 @@ export async function POST(request: Request) {
|
||||
|
||||
logger.info(`[${requestId}] Tool executed successfully: ${toolId} (${duration}ms)`)
|
||||
|
||||
// Return the response with CORS headers
|
||||
return formatResponse(responseWithTimingData)
|
||||
} catch (error: any) {
|
||||
logger.error(`[${requestId}] Proxy request failed`, {
|
||||
@@ -344,7 +348,6 @@ export async function POST(request: Request) {
|
||||
name: error instanceof Error ? error.name : undefined,
|
||||
})
|
||||
|
||||
// Add timing information even to error responses
|
||||
const endTime = new Date()
|
||||
const endTimeISO = endTime.toISOString()
|
||||
const duration = endTime.getTime() - startTime.getTime()
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { validateAlphanumericId } from '@/lib/security/input-validation'
|
||||
import { uploadFile } from '@/lib/uploads/storage-client'
|
||||
@@ -6,19 +8,25 @@ import { getBaseUrl } from '@/lib/urls/utils'
|
||||
|
||||
const logger = createLogger('ProxyTTSAPI')
|
||||
|
||||
export async function POST(request: Request) {
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||
if (!authResult.success) {
|
||||
logger.error('Authentication failed for TTS proxy:', authResult.error)
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
const body = await request.json()
|
||||
const { text, voiceId, apiKey, modelId = 'eleven_monolingual_v1' } = body
|
||||
|
||||
if (!text || !voiceId || !apiKey) {
|
||||
return new NextResponse('Missing required parameters', { status: 400 })
|
||||
return NextResponse.json({ error: 'Missing required parameters' }, { status: 400 })
|
||||
}
|
||||
|
||||
const voiceIdValidation = validateAlphanumericId(voiceId, 'voiceId', 255)
|
||||
if (!voiceIdValidation.isValid) {
|
||||
logger.error(`Invalid voice ID: ${voiceIdValidation.error}`)
|
||||
return new NextResponse(voiceIdValidation.error, { status: 400 })
|
||||
return NextResponse.json({ error: voiceIdValidation.error }, { status: 400 })
|
||||
}
|
||||
|
||||
logger.info('Proxying TTS request for voice:', voiceId)
|
||||
@@ -41,16 +49,17 @@ export async function POST(request: Request) {
|
||||
|
||||
if (!response.ok) {
|
||||
logger.error(`Failed to generate TTS: ${response.status} ${response.statusText}`)
|
||||
return new NextResponse(`Failed to generate TTS: ${response.status} ${response.statusText}`, {
|
||||
status: response.status,
|
||||
})
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to generate TTS: ${response.status} ${response.statusText}` },
|
||||
{ status: response.status }
|
||||
)
|
||||
}
|
||||
|
||||
const audioBlob = await response.blob()
|
||||
|
||||
if (audioBlob.size === 0) {
|
||||
logger.error('Empty audio received from ElevenLabs')
|
||||
return new NextResponse('Empty audio received', { status: 422 })
|
||||
return NextResponse.json({ error: 'Empty audio received' }, { status: 422 })
|
||||
}
|
||||
|
||||
const audioBuffer = Buffer.from(await audioBlob.arrayBuffer())
|
||||
@@ -67,11 +76,11 @@ export async function POST(request: Request) {
|
||||
} catch (error) {
|
||||
logger.error('Error proxying TTS:', error)
|
||||
|
||||
return new NextResponse(
|
||||
`Internal Server Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||
return NextResponse.json(
|
||||
{
|
||||
status: 500,
|
||||
}
|
||||
error: `Internal Server Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||
},
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||
import { env } from '@/lib/env'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { validateAlphanumericId } from '@/lib/security/input-validation'
|
||||
@@ -7,6 +8,12 @@ const logger = createLogger('ProxyTTSStreamAPI')
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||
if (!authResult.success) {
|
||||
logger.error('Authentication failed for TTS stream proxy:', authResult.error)
|
||||
return new Response('Unauthorized', { status: 401 })
|
||||
}
|
||||
|
||||
const body = await request.json()
|
||||
const { text, voiceId, modelId = 'eleven_turbo_v2_5' } = body
|
||||
|
||||
|
||||
69
apps/sim/app/api/tools/webflow/collections/route.ts
Normal file
69
apps/sim/app/api/tools/webflow/collections/route.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { getOAuthToken } from '@/app/api/auth/oauth/utils'
|
||||
|
||||
const logger = createLogger('WebflowCollectionsAPI')
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const session = await getSession()
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
const { searchParams } = new URL(request.url)
|
||||
const siteId = searchParams.get('siteId')
|
||||
|
||||
if (!siteId) {
|
||||
return NextResponse.json({ error: 'Missing siteId parameter' }, { status: 400 })
|
||||
}
|
||||
|
||||
const accessToken = await getOAuthToken(session.user.id, 'webflow')
|
||||
|
||||
if (!accessToken) {
|
||||
return NextResponse.json(
|
||||
{ error: 'No Webflow access token found. Please connect your Webflow account.' },
|
||||
{ status: 404 }
|
||||
)
|
||||
}
|
||||
|
||||
const response = await fetch(`https://api.webflow.com/v2/sites/${siteId}/collections`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
accept: 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
logger.error('Failed to fetch Webflow collections', {
|
||||
status: response.status,
|
||||
error: errorData,
|
||||
siteId,
|
||||
})
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to fetch Webflow collections', details: errorData },
|
||||
{ status: response.status }
|
||||
)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const collections = data.collections || []
|
||||
|
||||
const formattedCollections = collections.map((collection: any) => ({
|
||||
id: collection.id,
|
||||
name: collection.displayName || collection.slug || collection.id,
|
||||
}))
|
||||
|
||||
return NextResponse.json({ collections: formattedCollections }, { status: 200 })
|
||||
} catch (error: any) {
|
||||
logger.error('Error fetching Webflow collections', error)
|
||||
return NextResponse.json(
|
||||
{ error: 'Internal server error', details: error.message },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
61
apps/sim/app/api/tools/webflow/sites/route.ts
Normal file
61
apps/sim/app/api/tools/webflow/sites/route.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { getOAuthToken } from '@/app/api/auth/oauth/utils'
|
||||
|
||||
const logger = createLogger('WebflowSitesAPI')
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const session = await getSession()
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
const accessToken = await getOAuthToken(session.user.id, 'webflow')
|
||||
|
||||
if (!accessToken) {
|
||||
return NextResponse.json(
|
||||
{ error: 'No Webflow access token found. Please connect your Webflow account.' },
|
||||
{ status: 404 }
|
||||
)
|
||||
}
|
||||
|
||||
const response = await fetch('https://api.webflow.com/v2/sites', {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
accept: 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
logger.error('Failed to fetch Webflow sites', {
|
||||
status: response.status,
|
||||
error: errorData,
|
||||
})
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to fetch Webflow sites', details: errorData },
|
||||
{ status: response.status }
|
||||
)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const sites = data.sites || []
|
||||
|
||||
const formattedSites = sites.map((site: any) => ({
|
||||
id: site.id,
|
||||
name: site.displayName || site.shortName || site.id,
|
||||
}))
|
||||
|
||||
return NextResponse.json({ sites: formattedSites }, { status: 200 })
|
||||
} catch (error: any) {
|
||||
logger.error('Error fetching Webflow sites', error)
|
||||
return NextResponse.json(
|
||||
{ error: 'Internal server error', details: error.message },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||
import { checkServerSideUsageLimits } from '@/lib/billing'
|
||||
import { getHighestPrioritySubscription } from '@/lib/billing/core/subscription'
|
||||
import { getEffectiveCurrentPeriodCost } from '@/lib/billing/core/usage'
|
||||
import { getUserStorageLimit, getUserStorageUsage } from '@/lib/billing/storage'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { createErrorResponse } from '@/app/api/workflows/utils'
|
||||
import { RateLimiter } from '@/services/queue'
|
||||
@@ -37,9 +38,11 @@ export async function GET(request: NextRequest) {
|
||||
])
|
||||
|
||||
// Usage summary (current period cost + limit + plan)
|
||||
const [usageCheck, effectiveCost] = await Promise.all([
|
||||
const [usageCheck, effectiveCost, storageUsage, storageLimit] = await Promise.all([
|
||||
checkServerSideUsageLimits(authenticatedUserId),
|
||||
getEffectiveCurrentPeriodCost(authenticatedUserId),
|
||||
getUserStorageUsage(authenticatedUserId),
|
||||
getUserStorageLimit(authenticatedUserId),
|
||||
])
|
||||
|
||||
const currentPeriodCost = effectiveCost
|
||||
@@ -66,6 +69,11 @@ export async function GET(request: NextRequest) {
|
||||
limit: usageCheck.limit,
|
||||
plan: userSubscription?.plan || 'free',
|
||||
},
|
||||
storage: {
|
||||
usedBytes: storageUsage,
|
||||
limitBytes: storageLimit,
|
||||
percentUsed: storageLimit > 0 ? (storageUsage / storageLimit) * 100 : 0,
|
||||
},
|
||||
})
|
||||
} catch (error: any) {
|
||||
logger.error('Error checking usage limits:', error)
|
||||
|
||||
@@ -428,6 +428,26 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
// --- End Outlook specific logic ---
|
||||
|
||||
// --- Webflow webhook setup ---
|
||||
if (savedWebhook && provider === 'webflow') {
|
||||
logger.info(
|
||||
`[${requestId}] Webflow provider detected. Attempting to create webhook in Webflow.`
|
||||
)
|
||||
try {
|
||||
await createWebflowWebhookSubscription(request, userId, savedWebhook, requestId)
|
||||
} catch (err) {
|
||||
logger.error(`[${requestId}] Error creating Webflow webhook`, err)
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: 'Failed to create webhook in Webflow',
|
||||
details: err instanceof Error ? err.message : 'Unknown error',
|
||||
},
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
// --- End Webflow specific logic ---
|
||||
|
||||
const status = targetWebhookId ? 200 : 201
|
||||
return NextResponse.json({ webhook: savedWebhook }, { status })
|
||||
} catch (error: any) {
|
||||
@@ -548,3 +568,136 @@ async function createAirtableWebhookSubscription(
|
||||
)
|
||||
}
|
||||
}
|
||||
// Helper function to create the webhook subscription in Webflow
|
||||
async function createWebflowWebhookSubscription(
|
||||
request: NextRequest,
|
||||
userId: string,
|
||||
webhookData: any,
|
||||
requestId: string
|
||||
) {
|
||||
try {
|
||||
const { path, providerConfig } = webhookData
|
||||
const { siteId, triggerId, collectionId, formId } = providerConfig || {}
|
||||
|
||||
if (!siteId) {
|
||||
logger.warn(`[${requestId}] Missing siteId for Webflow webhook creation.`, {
|
||||
webhookId: webhookData.id,
|
||||
})
|
||||
throw new Error('Site ID is required to create Webflow webhook')
|
||||
}
|
||||
|
||||
if (!triggerId) {
|
||||
logger.warn(`[${requestId}] Missing triggerId for Webflow webhook creation.`, {
|
||||
webhookId: webhookData.id,
|
||||
})
|
||||
throw new Error('Trigger type is required to create Webflow webhook')
|
||||
}
|
||||
|
||||
const accessToken = await getOAuthToken(userId, 'webflow')
|
||||
if (!accessToken) {
|
||||
logger.warn(
|
||||
`[${requestId}] Could not retrieve Webflow access token for user ${userId}. Cannot create webhook in Webflow.`
|
||||
)
|
||||
throw new Error(
|
||||
'Webflow account connection required. Please connect your Webflow account in the trigger configuration and try again.'
|
||||
)
|
||||
}
|
||||
|
||||
const notificationUrl = `${getBaseUrl()}/api/webhooks/trigger/${path}`
|
||||
|
||||
// Map trigger IDs to Webflow trigger types
|
||||
const triggerTypeMap: Record<string, string> = {
|
||||
webflow_collection_item_created: 'collection_item_created',
|
||||
webflow_collection_item_changed: 'collection_item_changed',
|
||||
webflow_collection_item_deleted: 'collection_item_deleted',
|
||||
webflow_form_submission: 'form_submission',
|
||||
}
|
||||
|
||||
const webflowTriggerType = triggerTypeMap[triggerId]
|
||||
if (!webflowTriggerType) {
|
||||
logger.warn(`[${requestId}] Invalid triggerId for Webflow: ${triggerId}`, {
|
||||
webhookId: webhookData.id,
|
||||
})
|
||||
throw new Error(`Invalid Webflow trigger type: ${triggerId}`)
|
||||
}
|
||||
|
||||
const webflowApiUrl = `https://api.webflow.com/v2/sites/${siteId}/webhooks`
|
||||
|
||||
const requestBody: any = {
|
||||
triggerType: webflowTriggerType,
|
||||
url: notificationUrl,
|
||||
}
|
||||
|
||||
// Add filter for collection-based triggers
|
||||
if (collectionId && webflowTriggerType.startsWith('collection_item_')) {
|
||||
requestBody.filter = {
|
||||
resource_type: 'collection',
|
||||
resource_id: collectionId,
|
||||
}
|
||||
}
|
||||
|
||||
// Add filter for form submissions
|
||||
if (formId && webflowTriggerType === 'form_submission') {
|
||||
requestBody.filter = {
|
||||
resource_type: 'form',
|
||||
resource_id: formId,
|
||||
}
|
||||
}
|
||||
|
||||
const webflowResponse = await fetch(webflowApiUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
accept: 'application/json',
|
||||
},
|
||||
body: JSON.stringify(requestBody),
|
||||
})
|
||||
|
||||
const responseBody = await webflowResponse.json()
|
||||
|
||||
if (!webflowResponse.ok || responseBody.error) {
|
||||
const errorMessage = responseBody.message || responseBody.error || 'Unknown Webflow API error'
|
||||
logger.error(
|
||||
`[${requestId}] Failed to create webhook in Webflow for webhook ${webhookData.id}. Status: ${webflowResponse.status}`,
|
||||
{ message: errorMessage, response: responseBody }
|
||||
)
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`[${requestId}] Successfully created webhook in Webflow for webhook ${webhookData.id}.`,
|
||||
{
|
||||
webflowWebhookId: responseBody.id || responseBody._id,
|
||||
}
|
||||
)
|
||||
|
||||
// Store the Webflow webhook ID in the providerConfig
|
||||
try {
|
||||
const currentConfig = (webhookData.providerConfig as Record<string, any>) || {}
|
||||
const updatedConfig = {
|
||||
...currentConfig,
|
||||
externalId: responseBody.id || responseBody._id,
|
||||
}
|
||||
await db
|
||||
.update(webhook)
|
||||
.set({ providerConfig: updatedConfig, updatedAt: new Date() })
|
||||
.where(eq(webhook.id, webhookData.id))
|
||||
} catch (dbError: any) {
|
||||
logger.error(
|
||||
`[${requestId}] Failed to store externalId in providerConfig for webhook ${webhookData.id}.`,
|
||||
dbError
|
||||
)
|
||||
// Even if saving fails, the webhook exists in Webflow. Log and continue.
|
||||
}
|
||||
} catch (error: any) {
|
||||
logger.error(
|
||||
`[${requestId}] Exception during Webflow webhook creation for webhook ${webhookData.id}.`,
|
||||
{
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
}
|
||||
)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { apiKey, db, workflow, workflowDeploymentVersion } from '@sim/db'
|
||||
import { and, desc, eq, sql } from 'drizzle-orm'
|
||||
import { and, desc, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { generateRequestId } from '@/lib/utils'
|
||||
import { loadWorkflowFromNormalizedTables } from '@/lib/workflows/db-helpers'
|
||||
import { deployWorkflow } from '@/lib/workflows/db-helpers'
|
||||
import { validateWorkflowPermissions } from '@/lib/workflows/utils'
|
||||
import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils'
|
||||
|
||||
@@ -138,37 +137,7 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
||||
}
|
||||
} catch (_err) {}
|
||||
|
||||
logger.debug(`[${requestId}] Getting current workflow state for deployment`)
|
||||
|
||||
const normalizedData = await loadWorkflowFromNormalizedTables(id)
|
||||
|
||||
if (!normalizedData) {
|
||||
logger.error(`[${requestId}] Failed to load workflow from normalized tables`)
|
||||
return createErrorResponse('Failed to load workflow state', 500)
|
||||
}
|
||||
|
||||
const currentState = {
|
||||
blocks: normalizedData.blocks,
|
||||
edges: normalizedData.edges,
|
||||
loops: normalizedData.loops,
|
||||
parallels: normalizedData.parallels,
|
||||
lastSaved: Date.now(),
|
||||
}
|
||||
|
||||
logger.debug(`[${requestId}] Current state retrieved from normalized tables:`, {
|
||||
blocksCount: Object.keys(currentState.blocks).length,
|
||||
edgesCount: currentState.edges.length,
|
||||
loopsCount: Object.keys(currentState.loops).length,
|
||||
parallelsCount: Object.keys(currentState.parallels).length,
|
||||
})
|
||||
|
||||
if (!currentState || !currentState.blocks) {
|
||||
logger.error(`[${requestId}] Invalid workflow state retrieved`, { currentState })
|
||||
throw new Error('Invalid workflow state: missing blocks')
|
||||
}
|
||||
|
||||
const deployedAt = new Date()
|
||||
logger.debug(`[${requestId}] Proceeding with deployment at ${deployedAt.toISOString()}`)
|
||||
logger.debug(`[${requestId}] Validating API key for deployment`)
|
||||
|
||||
let keyInfo: { name: string; type: 'personal' | 'workspace' } | null = null
|
||||
let matchedKey: {
|
||||
@@ -260,46 +229,20 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
||||
return createErrorResponse('Unable to determine deploying user', 400)
|
||||
}
|
||||
|
||||
await db.transaction(async (tx) => {
|
||||
const [{ maxVersion }] = await tx
|
||||
.select({ maxVersion: sql`COALESCE(MAX("version"), 0)` })
|
||||
.from(workflowDeploymentVersion)
|
||||
.where(eq(workflowDeploymentVersion.workflowId, id))
|
||||
|
||||
const nextVersion = Number(maxVersion) + 1
|
||||
|
||||
await tx
|
||||
.update(workflowDeploymentVersion)
|
||||
.set({ isActive: false })
|
||||
.where(
|
||||
and(
|
||||
eq(workflowDeploymentVersion.workflowId, id),
|
||||
eq(workflowDeploymentVersion.isActive, true)
|
||||
)
|
||||
)
|
||||
|
||||
await tx.insert(workflowDeploymentVersion).values({
|
||||
id: uuidv4(),
|
||||
workflowId: id,
|
||||
version: nextVersion,
|
||||
state: currentState,
|
||||
isActive: true,
|
||||
createdAt: deployedAt,
|
||||
createdBy: actorUserId,
|
||||
})
|
||||
|
||||
const updateData: Record<string, unknown> = {
|
||||
isDeployed: true,
|
||||
deployedAt,
|
||||
deployedState: currentState,
|
||||
}
|
||||
if (providedApiKey && matchedKey) {
|
||||
updateData.pinnedApiKeyId = matchedKey.id
|
||||
}
|
||||
|
||||
await tx.update(workflow).set(updateData).where(eq(workflow.id, id))
|
||||
const deployResult = await deployWorkflow({
|
||||
workflowId: id,
|
||||
deployedBy: actorUserId,
|
||||
pinnedApiKeyId: matchedKey?.id,
|
||||
includeDeployedState: true,
|
||||
workflowName: workflowData!.name,
|
||||
})
|
||||
|
||||
if (!deployResult.success) {
|
||||
return createErrorResponse(deployResult.error || 'Failed to deploy workflow', 500)
|
||||
}
|
||||
|
||||
const deployedAt = deployResult.deployedAt!
|
||||
|
||||
if (matchedKey) {
|
||||
try {
|
||||
await db
|
||||
@@ -313,31 +256,6 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
||||
|
||||
logger.info(`[${requestId}] Workflow deployed successfully: ${id}`)
|
||||
|
||||
// Track workflow deployment
|
||||
try {
|
||||
const { trackPlatformEvent } = await import('@/lib/telemetry/tracer')
|
||||
|
||||
// Aggregate block types to understand which blocks are being used
|
||||
const blockTypeCounts: Record<string, number> = {}
|
||||
for (const block of Object.values(currentState.blocks)) {
|
||||
const blockType = (block as any).type || 'unknown'
|
||||
blockTypeCounts[blockType] = (blockTypeCounts[blockType] || 0) + 1
|
||||
}
|
||||
|
||||
trackPlatformEvent('platform.workflow.deployed', {
|
||||
'workflow.id': id,
|
||||
'workflow.name': workflowData!.name,
|
||||
'workflow.blocks_count': Object.keys(currentState.blocks).length,
|
||||
'workflow.edges_count': currentState.edges.length,
|
||||
'workflow.has_loops': Object.keys(currentState.loops).length > 0,
|
||||
'workflow.has_parallels': Object.keys(currentState.parallels).length > 0,
|
||||
'workflow.api_key_type': keyInfo?.type || 'default',
|
||||
'workflow.block_types': JSON.stringify(blockTypeCounts),
|
||||
})
|
||||
} catch (_e) {
|
||||
// Silently fail
|
||||
}
|
||||
|
||||
const responseApiKeyInfo = keyInfo ? `${keyInfo.name} (${keyInfo.type})` : 'Default key'
|
||||
|
||||
return createSuccessResponse({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { db, workflow, workflowDeploymentVersion } from '@sim/db'
|
||||
import { apiKey, db, workflow, workflowDeploymentVersion } from '@sim/db'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
@@ -19,7 +19,11 @@ export async function POST(
|
||||
const { id, version } = await params
|
||||
|
||||
try {
|
||||
const { error } = await validateWorkflowPermissions(id, requestId, 'admin')
|
||||
const {
|
||||
error,
|
||||
session,
|
||||
workflow: workflowData,
|
||||
} = await validateWorkflowPermissions(id, requestId, 'admin')
|
||||
if (error) {
|
||||
return createErrorResponse(error.message, error.status)
|
||||
}
|
||||
@@ -29,6 +33,52 @@ export async function POST(
|
||||
return createErrorResponse('Invalid version', 400)
|
||||
}
|
||||
|
||||
let providedApiKey: string | null = null
|
||||
try {
|
||||
const parsed = await request.json()
|
||||
if (parsed && typeof parsed.apiKey === 'string' && parsed.apiKey.trim().length > 0) {
|
||||
providedApiKey = parsed.apiKey.trim()
|
||||
}
|
||||
} catch (_err) {}
|
||||
|
||||
let pinnedApiKeyId: string | null = null
|
||||
if (providedApiKey) {
|
||||
const currentUserId = session?.user?.id
|
||||
if (currentUserId) {
|
||||
const [personalKey] = await db
|
||||
.select({ id: apiKey.id })
|
||||
.from(apiKey)
|
||||
.where(
|
||||
and(
|
||||
eq(apiKey.id, providedApiKey),
|
||||
eq(apiKey.userId, currentUserId),
|
||||
eq(apiKey.type, 'personal')
|
||||
)
|
||||
)
|
||||
.limit(1)
|
||||
|
||||
if (personalKey) {
|
||||
pinnedApiKeyId = personalKey.id
|
||||
} else if (workflowData!.workspaceId) {
|
||||
const [workspaceKey] = await db
|
||||
.select({ id: apiKey.id })
|
||||
.from(apiKey)
|
||||
.where(
|
||||
and(
|
||||
eq(apiKey.id, providedApiKey),
|
||||
eq(apiKey.workspaceId, workflowData!.workspaceId),
|
||||
eq(apiKey.type, 'workspace')
|
||||
)
|
||||
)
|
||||
.limit(1)
|
||||
|
||||
if (workspaceKey) {
|
||||
pinnedApiKeyId = workspaceKey.id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const now = new Date()
|
||||
|
||||
await db.transaction(async (tx) => {
|
||||
@@ -57,10 +107,16 @@ export async function POST(
|
||||
throw new Error('Deployment version not found')
|
||||
}
|
||||
|
||||
await tx
|
||||
.update(workflow)
|
||||
.set({ isDeployed: true, deployedAt: now })
|
||||
.where(eq(workflow.id, id))
|
||||
const updateData: Record<string, unknown> = {
|
||||
isDeployed: true,
|
||||
deployedAt: now,
|
||||
}
|
||||
|
||||
if (pinnedApiKeyId) {
|
||||
updateData.pinnedApiKeyId = pinnedApiKeyId
|
||||
}
|
||||
|
||||
await tx.update(workflow).set(updateData).where(eq(workflow.id, id))
|
||||
})
|
||||
|
||||
return createSuccessResponse({ success: true, deployedAt: now })
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
ChatMessageContainer,
|
||||
EmailAuth,
|
||||
PasswordAuth,
|
||||
SSOAuth,
|
||||
VoiceInterface,
|
||||
} from '@/app/chat/components'
|
||||
import { CHAT_ERROR_MESSAGES, CHAT_REQUEST_TIMEOUT_MS } from '@/app/chat/constants'
|
||||
@@ -32,7 +33,7 @@ interface ChatConfig {
|
||||
welcomeMessage?: string
|
||||
headerText?: string
|
||||
}
|
||||
authType?: 'public' | 'password' | 'email'
|
||||
authType?: 'public' | 'password' | 'email' | 'sso'
|
||||
outputConfigs?: Array<{ blockId: string; path?: string }>
|
||||
}
|
||||
|
||||
@@ -119,7 +120,7 @@ export default function ChatClient({ identifier }: { identifier: string }) {
|
||||
const [userHasScrolled, setUserHasScrolled] = useState(false)
|
||||
const isUserScrollingRef = useRef(false)
|
||||
|
||||
const [authRequired, setAuthRequired] = useState<'password' | 'email' | null>(null)
|
||||
const [authRequired, setAuthRequired] = useState<'password' | 'email' | 'sso' | null>(null)
|
||||
|
||||
const [isVoiceFirstMode, setIsVoiceFirstMode] = useState(false)
|
||||
const { isStreamingResponse, abortControllerRef, stopStreaming, handleStreamedResponse } =
|
||||
@@ -222,6 +223,10 @@ export default function ChatClient({ identifier }: { identifier: string }) {
|
||||
setAuthRequired('email')
|
||||
return
|
||||
}
|
||||
if (errorData.error === 'auth_required_sso') {
|
||||
setAuthRequired('sso')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`Failed to load chat configuration: ${response.status}`)
|
||||
@@ -500,6 +505,16 @@ export default function ChatClient({ identifier }: { identifier: string }) {
|
||||
/>
|
||||
)
|
||||
}
|
||||
if (authRequired === 'sso') {
|
||||
return (
|
||||
<SSOAuth
|
||||
identifier={identifier}
|
||||
onAuthSuccess={handleAuthSuccess}
|
||||
title={title}
|
||||
primaryColor={primaryColor}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Loading state while fetching config using the extracted component
|
||||
|
||||
209
apps/sim/app/chat/components/auth/sso/sso-auth.tsx
Normal file
209
apps/sim/app/chat/components/auth/sso/sso-auth.tsx
Normal file
@@ -0,0 +1,209 @@
|
||||
'use client'
|
||||
|
||||
import { type KeyboardEvent, useEffect, useState } from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { quickValidateEmail } from '@/lib/email/validation'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { cn } from '@/lib/utils'
|
||||
import Nav from '@/app/(landing)/components/nav/nav'
|
||||
import { inter } from '@/app/fonts/inter'
|
||||
import { soehne } from '@/app/fonts/soehne/soehne'
|
||||
|
||||
const logger = createLogger('SSOAuth')
|
||||
|
||||
interface SSOAuthProps {
|
||||
identifier: string
|
||||
onAuthSuccess: () => void
|
||||
title?: string
|
||||
primaryColor?: string
|
||||
}
|
||||
|
||||
const validateEmailField = (emailValue: string): string[] => {
|
||||
const errors: string[] = []
|
||||
|
||||
if (!emailValue || !emailValue.trim()) {
|
||||
errors.push('Email is required.')
|
||||
return errors
|
||||
}
|
||||
|
||||
const validation = quickValidateEmail(emailValue.trim().toLowerCase())
|
||||
if (!validation.isValid) {
|
||||
errors.push(validation.reason || 'Please enter a valid email address.')
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
export default function SSOAuth({
|
||||
identifier,
|
||||
onAuthSuccess,
|
||||
title = 'chat',
|
||||
primaryColor = 'var(--brand-primary-hover-hex)',
|
||||
}: SSOAuthProps) {
|
||||
const router = useRouter()
|
||||
const [email, setEmail] = useState('')
|
||||
const [emailErrors, setEmailErrors] = useState<string[]>([])
|
||||
const [showEmailValidationError, setShowEmailValidationError] = useState(false)
|
||||
const [buttonClass, setButtonClass] = useState('auth-button-gradient')
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
const checkCustomBrand = () => {
|
||||
const computedStyle = getComputedStyle(document.documentElement)
|
||||
const brandAccent = computedStyle.getPropertyValue('--brand-accent-hex').trim()
|
||||
|
||||
if (brandAccent && brandAccent !== '#6f3dfa') {
|
||||
setButtonClass('auth-button-custom')
|
||||
} else {
|
||||
setButtonClass('auth-button-gradient')
|
||||
}
|
||||
}
|
||||
|
||||
checkCustomBrand()
|
||||
|
||||
window.addEventListener('resize', checkCustomBrand)
|
||||
const observer = new MutationObserver(checkCustomBrand)
|
||||
observer.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ['style', 'class'],
|
||||
})
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', checkCustomBrand)
|
||||
observer.disconnect()
|
||||
}
|
||||
}, [])
|
||||
|
||||
const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault()
|
||||
handleAuthenticate()
|
||||
}
|
||||
}
|
||||
|
||||
const handleEmailChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const newEmail = e.target.value
|
||||
setEmail(newEmail)
|
||||
setShowEmailValidationError(false)
|
||||
setEmailErrors([])
|
||||
}
|
||||
|
||||
const handleAuthenticate = async () => {
|
||||
const emailValidationErrors = validateEmailField(email)
|
||||
setEmailErrors(emailValidationErrors)
|
||||
setShowEmailValidationError(emailValidationErrors.length > 0)
|
||||
|
||||
if (emailValidationErrors.length > 0) {
|
||||
return
|
||||
}
|
||||
|
||||
setIsLoading(true)
|
||||
|
||||
try {
|
||||
const checkResponse = await fetch(`/api/chat/${identifier}`, {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
},
|
||||
body: JSON.stringify({ email, checkSSOAccess: true }),
|
||||
})
|
||||
|
||||
if (!checkResponse.ok) {
|
||||
const errorData = await checkResponse.json()
|
||||
setEmailErrors([errorData.error || 'Email not authorized for this chat'])
|
||||
setShowEmailValidationError(true)
|
||||
setIsLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
const callbackUrl = `/chat/${identifier}`
|
||||
const ssoUrl = `/sso?email=${encodeURIComponent(email)}&callbackUrl=${encodeURIComponent(callbackUrl)}`
|
||||
router.push(ssoUrl)
|
||||
} catch (error) {
|
||||
logger.error('SSO authentication error:', error)
|
||||
setEmailErrors(['An error occurred during authentication'])
|
||||
setShowEmailValidationError(true)
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='bg-white'>
|
||||
<Nav variant='auth' />
|
||||
<div className='flex min-h-[calc(100vh-120px)] items-center justify-center px-4'>
|
||||
<div className='w-full max-w-[410px]'>
|
||||
<div className='flex flex-col items-center justify-center'>
|
||||
{/* Header */}
|
||||
<div className='space-y-1 text-center'>
|
||||
<h1
|
||||
className={`${soehne.className} font-medium text-[32px] text-black tracking-tight`}
|
||||
>
|
||||
SSO Authentication
|
||||
</h1>
|
||||
<p className={`${inter.className} font-[380] text-[16px] text-muted-foreground`}>
|
||||
This chat requires SSO authentication
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Form */}
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault()
|
||||
handleAuthenticate()
|
||||
}}
|
||||
className={`${inter.className} mt-8 w-full space-y-8`}
|
||||
>
|
||||
<div className='space-y-6'>
|
||||
<div className='space-y-2'>
|
||||
<div className='flex items-center justify-between'>
|
||||
<Label htmlFor='email'>Work Email</Label>
|
||||
</div>
|
||||
<Input
|
||||
id='email'
|
||||
name='email'
|
||||
required
|
||||
type='email'
|
||||
autoCapitalize='none'
|
||||
autoComplete='email'
|
||||
autoCorrect='off'
|
||||
placeholder='Enter your work email'
|
||||
value={email}
|
||||
onChange={handleEmailChange}
|
||||
onKeyDown={handleKeyDown}
|
||||
className={cn(
|
||||
'rounded-[10px] shadow-sm transition-colors focus:border-gray-400 focus:ring-2 focus:ring-gray-100',
|
||||
showEmailValidationError &&
|
||||
emailErrors.length > 0 &&
|
||||
'border-red-500 focus:border-red-500 focus:ring-red-100 focus-visible:ring-red-500'
|
||||
)}
|
||||
autoFocus
|
||||
/>
|
||||
{showEmailValidationError && emailErrors.length > 0 && (
|
||||
<div className='mt-1 space-y-1 text-red-400 text-xs'>
|
||||
{emailErrors.map((error, index) => (
|
||||
<p key={index}>{error}</p>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
type='submit'
|
||||
className={`${buttonClass} flex w-full items-center justify-center gap-2 rounded-[10px] border font-medium text-[15px] text-white transition-all duration-200`}
|
||||
disabled={isLoading}
|
||||
>
|
||||
{isLoading ? 'Redirecting to SSO...' : 'Continue with SSO'}
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
export { default as EmailAuth } from './auth/email/email-auth'
|
||||
export { default as PasswordAuth } from './auth/password/password-auth'
|
||||
export { default as SSOAuth } from './auth/sso/sso-auth'
|
||||
export { ChatErrorState } from './error-state/error-state'
|
||||
export { ChatHeader } from './header/header'
|
||||
export { ChatInput } from './input/input'
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user