mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-10 23:48:09 -05:00
Compare commits
14 Commits
fix/tool
...
fix/tool-o
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
92a08b0a33 | ||
|
|
5d3b216922 | ||
|
|
431f206930 | ||
|
|
7443e28054 | ||
|
|
c962e3b398 | ||
|
|
d5b95cbd33 | ||
|
|
0fb084b9e4 | ||
|
|
95b9ca4670 | ||
|
|
746ff68a2e | ||
|
|
75a5b43252 | ||
|
|
8d0e50fd0d | ||
|
|
f7d1b06d75 | ||
|
|
73940ab390 | ||
|
|
f111dac020 |
@@ -243,6 +243,9 @@ export async function generateMetadata(props: {
|
||||
const baseUrl = 'https://docs.sim.ai'
|
||||
const fullUrl = `${baseUrl}${page.url}`
|
||||
|
||||
const description = page.data.description || ''
|
||||
const ogImageUrl = `${baseUrl}/api/og?title=${encodeURIComponent(page.data.title)}&category=DOCUMENTATION${description ? `&description=${encodeURIComponent(description)}` : ''}`
|
||||
|
||||
return {
|
||||
title: page.data.title,
|
||||
description:
|
||||
@@ -272,12 +275,23 @@ export async function generateMetadata(props: {
|
||||
alternateLocale: ['en', 'es', 'fr', 'de', 'ja', 'zh']
|
||||
.filter((lang) => lang !== params.lang)
|
||||
.map((lang) => (lang === 'en' ? 'en_US' : `${lang}_${lang.toUpperCase()}`)),
|
||||
images: [
|
||||
{
|
||||
url: ogImageUrl,
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: page.data.title,
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary',
|
||||
card: 'summary_large_image',
|
||||
title: page.data.title,
|
||||
description:
|
||||
page.data.description || 'Sim visual workflow builder for AI applications documentation',
|
||||
images: [ogImageUrl],
|
||||
creator: '@simdotai',
|
||||
site: '@simdotai',
|
||||
},
|
||||
robots: {
|
||||
index: true,
|
||||
|
||||
173
apps/docs/app/api/og/route.tsx
Normal file
173
apps/docs/app/api/og/route.tsx
Normal file
@@ -0,0 +1,173 @@
|
||||
import { ImageResponse } from 'next/og'
|
||||
import type { NextRequest } from 'next/server'
|
||||
|
||||
export const runtime = 'edge'
|
||||
|
||||
const TITLE_FONT_SIZE = {
|
||||
large: 64,
|
||||
medium: 56,
|
||||
small: 48,
|
||||
} as const
|
||||
|
||||
function getTitleFontSize(title: string): number {
|
||||
if (title.length > 45) return TITLE_FONT_SIZE.small
|
||||
if (title.length > 30) return TITLE_FONT_SIZE.medium
|
||||
return TITLE_FONT_SIZE.large
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a Google Font dynamically by fetching the CSS and extracting the font URL.
|
||||
*/
|
||||
async function loadGoogleFont(font: string, weights: string, text: string): Promise<ArrayBuffer> {
|
||||
const url = `https://fonts.googleapis.com/css2?family=${font}:wght@${weights}&text=${encodeURIComponent(text)}`
|
||||
const css = await (await fetch(url)).text()
|
||||
const resource = css.match(/src: url\((.+)\) format\('(opentype|truetype)'\)/)
|
||||
|
||||
if (resource) {
|
||||
const response = await fetch(resource[1])
|
||||
if (response.status === 200) {
|
||||
return await response.arrayBuffer()
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('Failed to load font data')
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates dynamic Open Graph images for documentation pages.
|
||||
*/
|
||||
export async function GET(request: NextRequest) {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const title = searchParams.get('title') || 'Documentation'
|
||||
const category = searchParams.get('category') || 'DOCUMENTATION'
|
||||
const description = searchParams.get('description') || ''
|
||||
|
||||
const baseUrl = new URL(request.url).origin
|
||||
const backgroundImageUrl = `${baseUrl}/static/og-background.png`
|
||||
|
||||
const allText = `${title}${category}${description}docs.sim.ai`
|
||||
const fontData = await loadGoogleFont('Geist', '400;500;600', allText)
|
||||
|
||||
return new ImageResponse(
|
||||
<div
|
||||
style={{
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
background: 'linear-gradient(315deg, #1e1e3f 0%, #1a1a2e 40%, #0f0f0f 100%)',
|
||||
position: 'relative',
|
||||
fontFamily: 'Geist',
|
||||
}}
|
||||
>
|
||||
{/* Background texture */}
|
||||
<img
|
||||
src={backgroundImageUrl}
|
||||
alt=''
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
objectFit: 'cover',
|
||||
opacity: 0.04,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Subtle purple glow from bottom right */}
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
width: '50%',
|
||||
height: '100%',
|
||||
background:
|
||||
'radial-gradient(ellipse at bottom right, rgba(112, 31, 252, 0.1) 0%, transparent 50%)',
|
||||
display: 'flex',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
padding: '56px 72px',
|
||||
height: '100%',
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
{/* Logo */}
|
||||
<img src={`${baseUrl}/static/logo.png`} alt='sim' height={32} />
|
||||
|
||||
{/* Category + Title + Description */}
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 12,
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 15,
|
||||
fontWeight: 600,
|
||||
color: '#802fff',
|
||||
letterSpacing: '0.02em',
|
||||
}}
|
||||
>
|
||||
{category}
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
fontSize: getTitleFontSize(title),
|
||||
fontWeight: 600,
|
||||
color: '#ffffff',
|
||||
lineHeight: 1.1,
|
||||
letterSpacing: '-0.02em',
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</span>
|
||||
{description && (
|
||||
<span
|
||||
style={{
|
||||
fontSize: 18,
|
||||
fontWeight: 400,
|
||||
color: '#a1a1aa',
|
||||
lineHeight: 1.4,
|
||||
marginTop: 4,
|
||||
}}
|
||||
>
|
||||
{description.length > 100 ? `${description.slice(0, 100)}...` : description}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<span
|
||||
style={{
|
||||
fontSize: 15,
|
||||
fontWeight: 500,
|
||||
color: '#52525b',
|
||||
}}
|
||||
>
|
||||
docs.sim.ai
|
||||
</span>
|
||||
</div>
|
||||
</div>,
|
||||
{
|
||||
width: 1200,
|
||||
height: 630,
|
||||
fonts: [
|
||||
{
|
||||
name: 'Geist',
|
||||
data: fontData,
|
||||
style: 'normal',
|
||||
},
|
||||
],
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -56,6 +56,14 @@ export const metadata = {
|
||||
title: 'Sim Documentation - Visual Workflow Builder for AI Applications',
|
||||
description:
|
||||
'Comprehensive documentation for Sim - the visual workflow builder for AI applications. Create powerful AI agents, automation workflows, and data processing pipelines.',
|
||||
images: [
|
||||
{
|
||||
url: 'https://docs.sim.ai/api/og?title=Sim%20Documentation&category=DOCUMENTATION',
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: 'Sim Documentation',
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
@@ -64,7 +72,7 @@ export const metadata = {
|
||||
'Comprehensive documentation for Sim - the visual workflow builder for AI applications.',
|
||||
creator: '@simdotai',
|
||||
site: '@simdotai',
|
||||
images: ['/og-image.png'],
|
||||
images: ['https://docs.sim.ai/api/og?title=Sim%20Documentation&category=DOCUMENTATION'],
|
||||
},
|
||||
robots: {
|
||||
index: true,
|
||||
|
||||
@@ -39,9 +39,10 @@ Alle Elemente aus einer Webflow CMS-Sammlung auflisten
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Ja | ID der Webflow-Website |
|
||||
| `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\) |
|
||||
| `offset` | number | Nein | Offset für Paginierung (optional) |
|
||||
| `limit` | number | Nein | Maximale Anzahl der zurückzugebenden Elemente (optional, Standard: 100) |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
@@ -58,6 +59,7 @@ Ein einzelnes Element aus einer Webflow CMS-Sammlung abrufen
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Ja | ID der Webflow-Website |
|
||||
| `collectionId` | string | Ja | ID der Sammlung |
|
||||
| `itemId` | string | Ja | ID des abzurufenden Elements |
|
||||
|
||||
@@ -76,8 +78,9 @@ Ein neues Element in einer Webflow CMS-Sammlung erstellen
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Ja | ID der Webflow-Website |
|
||||
| `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. |
|
||||
| `fieldData` | json | Ja | Felddaten für das neue Element als JSON-Objekt. Schlüssel sollten mit den Sammlungsfeldnamen übereinstimmen. |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
@@ -94,6 +97,7 @@ Ein vorhandenes Element in einer Webflow CMS-Sammlung aktualisieren
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Ja | ID der Webflow-Website |
|
||||
| `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. |
|
||||
@@ -113,6 +117,7 @@ Ein Element aus einer Webflow CMS-Sammlung löschen
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Ja | ID der Webflow-Website |
|
||||
| `collectionId` | string | Ja | ID der Sammlung |
|
||||
| `itemId` | string | Ja | ID des zu löschenden Elements |
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ List all items from a Webflow CMS collection
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | ID of the Webflow site |
|
||||
| `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\) |
|
||||
@@ -61,6 +62,7 @@ Get a single item from a Webflow CMS collection
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | ID of the Webflow site |
|
||||
| `collectionId` | string | Yes | ID of the collection |
|
||||
| `itemId` | string | Yes | ID of the item to retrieve |
|
||||
|
||||
@@ -79,6 +81,7 @@ Create a new item in a Webflow CMS collection
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | ID of the Webflow site |
|
||||
| `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. |
|
||||
|
||||
@@ -97,6 +100,7 @@ Update an existing item in a Webflow CMS collection
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | ID of the Webflow site |
|
||||
| `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. |
|
||||
@@ -116,6 +120,7 @@ Delete an item from a Webflow CMS collection
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | ID of the Webflow site |
|
||||
| `collectionId` | string | Yes | ID of the collection |
|
||||
| `itemId` | string | Yes | ID of the item to delete |
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ Listar todos los elementos de una colección del CMS de Webflow
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Sí | ID del sitio de Webflow |
|
||||
| `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\) |
|
||||
@@ -58,6 +59,7 @@ Obtener un solo elemento de una colección del CMS de Webflow
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Sí | ID del sitio de Webflow |
|
||||
| `collectionId` | string | Sí | ID de la colección |
|
||||
| `itemId` | string | Sí | ID del elemento a recuperar |
|
||||
|
||||
@@ -76,6 +78,7 @@ Crear un nuevo elemento en una colección del CMS de Webflow
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Sí | ID del sitio de Webflow |
|
||||
| `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. |
|
||||
|
||||
@@ -94,6 +97,7 @@ Actualizar un elemento existente en una colección CMS de Webflow
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Sí | ID del sitio de Webflow |
|
||||
| `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. |
|
||||
@@ -113,6 +117,7 @@ Eliminar un elemento de una colección CMS de Webflow
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Sí | ID del sitio de Webflow |
|
||||
| `collectionId` | string | Sí | ID de la colección |
|
||||
| `itemId` | string | Sí | ID del elemento a eliminar |
|
||||
|
||||
|
||||
@@ -38,7 +38,8 @@ Lister tous les éléments d'une collection CMS Webflow
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| --------- | ---- | ---------- | ----------- |
|
||||
| `siteId` | string | Oui | ID du site Webflow |
|
||||
| `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\) |
|
||||
@@ -57,7 +58,8 @@ Obtenir un seul élément d'une collection CMS Webflow
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| --------- | ---- | ---------- | ----------- |
|
||||
| `siteId` | string | Oui | ID du site Webflow |
|
||||
| `collectionId` | string | Oui | ID de la collection |
|
||||
| `itemId` | string | Oui | ID de l'élément à récupérer |
|
||||
|
||||
@@ -76,8 +78,9 @@ Créer un nouvel élément dans une collection CMS Webflow
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | ---------- | ----------- |
|
||||
| `siteId` | string | Oui | ID du site Webflow |
|
||||
| `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. |
|
||||
| `fieldData` | json | Oui | Données des champs pour le nouvel élément sous forme d'objet JSON. Les clés doivent correspondre aux noms des champs de la collection. |
|
||||
|
||||
#### Sortie
|
||||
|
||||
@@ -94,9 +97,10 @@ Mettre à jour un élément existant dans une collection CMS Webflow
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | ---------- | ----------- |
|
||||
| `siteId` | string | Oui | ID du site Webflow |
|
||||
| `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. |
|
||||
| `fieldData` | json | Oui | Données des champs à mettre à jour sous forme d'objet JSON. N'incluez que les champs que vous souhaitez modifier. |
|
||||
|
||||
#### Sortie
|
||||
|
||||
@@ -113,6 +117,7 @@ Supprimer un élément d'une collection CMS Webflow
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | ---------- | ----------- |
|
||||
| `siteId` | string | Oui | ID du site Webflow |
|
||||
| `collectionId` | string | Oui | ID de la collection |
|
||||
| `itemId` | string | Oui | ID de l'élément à supprimer |
|
||||
|
||||
|
||||
@@ -39,9 +39,10 @@ Webflow CMSコレクションからすべてのアイテムを一覧表示する
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | はい | WebflowサイトのID |
|
||||
| `collectionId` | string | はい | コレクションのID |
|
||||
| `offset` | number | いいえ | ページネーション用のオフセット(オプション) |
|
||||
| `limit` | number | いいえ | 返すアイテムの最大数(オプション、デフォルト:100) |
|
||||
| `limit` | number | いいえ | 返す最大アイテム数(オプション、デフォルト:100) |
|
||||
|
||||
#### 出力
|
||||
|
||||
@@ -58,6 +59,7 @@ Webflow CMSコレクションから単一のアイテムを取得する
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | はい | WebflowサイトのID |
|
||||
| `collectionId` | string | はい | コレクションのID |
|
||||
| `itemId` | string | はい | 取得するアイテムのID |
|
||||
|
||||
@@ -76,8 +78,9 @@ Webflow CMSコレクションに新しいアイテムを作成する
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | はい | WebflowサイトのID |
|
||||
| `collectionId` | string | はい | コレクションのID |
|
||||
| `fieldData` | json | はい | 新しいアイテムのフィールドデータ(JSONオブジェクト形式)。キーはコレクションのフィールド名と一致する必要があります。 |
|
||||
| `fieldData` | json | はい | 新しいアイテムのフィールドデータ(JSONオブジェクト)。キーはコレクションフィールド名と一致する必要があります。 |
|
||||
|
||||
#### 出力
|
||||
|
||||
@@ -94,9 +97,10 @@ Webflow CMSコレクション内の既存アイテムを更新する
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | はい | WebflowサイトのID |
|
||||
| `collectionId` | string | はい | コレクションのID |
|
||||
| `itemId` | string | はい | 更新するアイテムのID |
|
||||
| `fieldData` | json | はい | 更新するフィールドデータ(JSONオブジェクト形式)。変更したいフィールドのみを含めてください。 |
|
||||
| `fieldData` | json | はい | 更新するフィールドデータ(JSONオブジェクト)。変更したいフィールドのみを含めてください。 |
|
||||
|
||||
#### 出力
|
||||
|
||||
@@ -113,6 +117,7 @@ Webflow CMSコレクションからアイテムを削除する
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | はい | WebflowサイトのID |
|
||||
| `collectionId` | string | はい | コレクションのID |
|
||||
| `itemId` | string | はい | 削除するアイテムのID |
|
||||
|
||||
|
||||
@@ -38,9 +38,10 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | 是 | Webflow 网站的 ID |
|
||||
| `collectionId` | string | 是 | 集合的 ID |
|
||||
| `offset` | number | 否 | 分页偏移量(可选) |
|
||||
| `limit` | number | 否 | 返回的最大项目数(可选,默认值:100) |
|
||||
| `offset` | number | 否 | 分页的偏移量(可选) |
|
||||
| `limit` | number | 否 | 要返回的最大项目数(可选,默认值:100) |
|
||||
|
||||
#### 输出
|
||||
|
||||
@@ -57,8 +58,9 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | 是 | Webflow 网站的 ID |
|
||||
| `collectionId` | string | 是 | 集合的 ID |
|
||||
| `itemId` | string | 是 | 要检索的项目 ID |
|
||||
| `itemId` | string | 是 | 要检索项目的 ID |
|
||||
|
||||
#### 输出
|
||||
|
||||
@@ -75,8 +77,9 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | 是 | Webflow 网站的 ID |
|
||||
| `collectionId` | string | 是 | 集合的 ID |
|
||||
| `fieldData` | json | 是 | 新项目的字段数据,格式为 JSON 对象。键名应与集合字段名称匹配。 |
|
||||
| `fieldData` | json | 是 | 新项目的字段数据,格式为 JSON 对象。键名应与集合字段名匹配。 |
|
||||
|
||||
#### 输出
|
||||
|
||||
@@ -93,9 +96,10 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | 是 | Webflow 网站的 ID |
|
||||
| `collectionId` | string | 是 | 集合的 ID |
|
||||
| `itemId` | string | 是 | 要更新项目的 ID |
|
||||
| `fieldData` | json | 是 | 要更新的字段数据,格式为 JSON 对象。仅包含您想更改的字段。 |
|
||||
| `fieldData` | json | 是 | 要更新的字段数据,格式为 JSON 对象。仅包含需要更改的字段。 |
|
||||
|
||||
#### 输出
|
||||
|
||||
@@ -112,6 +116,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | 是 | Webflow 网站的 ID |
|
||||
| `collectionId` | string | 是 | 集合的 ID |
|
||||
| `itemId` | string | 是 | 要删除项目的 ID |
|
||||
|
||||
|
||||
@@ -5973,31 +5973,31 @@ checksums:
|
||||
content/9: 5914baadfaf2ca26d54130a36dd5ed29
|
||||
content/10: 25507380ac7d9c7f8cf9f5256c6a0dbb
|
||||
content/11: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/12: e7fb612c3323c1e6b05eacfcea360d34
|
||||
content/12: e034523b05e8c7bd1723ef0ba96c5332
|
||||
content/13: bcadfc362b69078beee0088e5936c98b
|
||||
content/14: e5f830d6049ff79a318110098e5e0130
|
||||
content/15: 711e90714806b91f93923018e82ad2e9
|
||||
content/16: 0f3f7d9699d7397cb3a094c3229329ee
|
||||
content/17: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/18: c53b5b8f901066e63fe159ad2fa5e6e0
|
||||
content/18: 4b0c581b30f4449b0bfa3cdd4af69e02
|
||||
content/19: bcadfc362b69078beee0088e5936c98b
|
||||
content/20: 5f2afdd49c3ac13381401c69d1eca22a
|
||||
content/21: cc4baa9096fafa4c6276f6136412ba66
|
||||
content/22: 676f76e8a7154a576d7fa20b245cef70
|
||||
content/23: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/24: c67c387eb7e274ee7c07b7e1748afce1
|
||||
content/24: d26dd24c5398fd036d1f464ba3789002
|
||||
content/25: bcadfc362b69078beee0088e5936c98b
|
||||
content/26: a6ffebda549ad5b903a66c7d9ac03a20
|
||||
content/27: 0dadd51cde48d6ea75b29ec3ee4ade56
|
||||
content/28: cdc74f6483a0b4e9933ecdd92ed7480f
|
||||
content/29: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/30: 4cda10aa374e1a46d60ad14eeaa79100
|
||||
content/30: cec3953ee52d1d3c8b1a495f9684d35b
|
||||
content/31: bcadfc362b69078beee0088e5936c98b
|
||||
content/32: 5f221421953a0e760ead7388cbf66561
|
||||
content/33: a3c0372590cef72d5d983dbc8dbbc2cb
|
||||
content/34: 1402e53c08bdd8a741f44b2d66fcd003
|
||||
content/35: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/36: 028e579a28e55def4fbc59f39f4610b7
|
||||
content/36: db921b05a9e5ddceb28a4f3f1af2a377
|
||||
content/37: bcadfc362b69078beee0088e5936c98b
|
||||
content/38: 4fe4260da2f137679ce2fa42cffcf56a
|
||||
content/39: b3f310d5ef115bea5a8b75bf25d7ea9a
|
||||
|
||||
BIN
apps/docs/public/static/og-background.png
Normal file
BIN
apps/docs/public/static/og-background.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 583 KiB |
@@ -4,10 +4,13 @@ DATABASE_URL="postgresql://postgres:password@localhost:5432/postgres"
|
||||
# PostgreSQL Port (Optional) - defaults to 5432 if not specified
|
||||
# POSTGRES_PORT=5432
|
||||
|
||||
# Authentication (Required)
|
||||
# Authentication (Required unless DISABLE_AUTH=true)
|
||||
BETTER_AUTH_SECRET=your_secret_key # Use `openssl rand -hex 32` to generate, or visit https://www.better-auth.com/docs/installation
|
||||
BETTER_AUTH_URL=http://localhost:3000
|
||||
|
||||
# Authentication Bypass (Optional - for self-hosted deployments behind private networks)
|
||||
# DISABLE_AUTH=true # Uncomment to bypass authentication entirely. Creates an anonymous session for all requests.
|
||||
|
||||
# NextJS (Required)
|
||||
NEXT_PUBLIC_APP_URL=http://localhost:3000
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use server'
|
||||
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import { isProd } from '@/lib/core/config/environment'
|
||||
import { isProd } from '@/lib/core/config/feature-flags'
|
||||
|
||||
export async function getOAuthProviderStatus() {
|
||||
const githubAvailable = !!(env.GITHUB_CLIENT_ID && env.GITHUB_CLIENT_SECRET)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { getOAuthProviderStatus } from '@/app/(auth)/components/oauth-provider-checker'
|
||||
import LoginForm from '@/app/(auth)/login/login-form'
|
||||
|
||||
// Force dynamic rendering to avoid prerender errors with search params
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
export default async function LoginPage() {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { env, isTruthy } from '@/lib/core/config/env'
|
||||
import { isRegistrationDisabled } from '@/lib/core/config/feature-flags'
|
||||
import { getOAuthProviderStatus } from '@/app/(auth)/components/oauth-provider-checker'
|
||||
import SignupForm from '@/app/(auth)/signup/signup-form'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
export default async function SignupPage() {
|
||||
const { githubAvailable, googleAvailable, isProduction } = await getOAuthProviderStatus()
|
||||
|
||||
if (isTruthy(env.DISABLE_REGISTRATION)) {
|
||||
if (isRegistrationDisabled) {
|
||||
return <div>Registration is disabled, please contact your admin.</div>
|
||||
}
|
||||
|
||||
const { githubAvailable, googleAvailable, isProduction } = await getOAuthProviderStatus()
|
||||
|
||||
return (
|
||||
<SignupForm
|
||||
githubAvailable={githubAvailable}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { isEmailVerificationEnabled, isProd } from '@/lib/core/config/environment'
|
||||
import { isEmailVerificationEnabled, isProd } from '@/lib/core/config/feature-flags'
|
||||
import { hasEmailService } from '@/lib/messaging/email/mailer'
|
||||
import { VerifyContent } from '@/app/(auth)/verify/verify-content'
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
|
||||
const DEFAULT_STARS = '18.6k'
|
||||
const DEFAULT_STARS = '19.4k'
|
||||
|
||||
const logger = createLogger('GitHubStars')
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
SelectValue,
|
||||
} from '@/components/ui/select'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import { isHosted } from '@/lib/core/config/environment'
|
||||
import { isHosted } from '@/lib/core/config/feature-flags'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { quickValidateEmail } from '@/lib/messaging/email/validation'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { isHosted } from '@/lib/core/config/environment'
|
||||
import { isHosted } from '@/lib/core/config/feature-flags'
|
||||
import { soehne } from '@/app/_styles/fonts/soehne/soehne'
|
||||
import Footer from '@/app/(landing)/components/footer/footer'
|
||||
import Nav from '@/app/(landing)/components/nav/nav'
|
||||
|
||||
@@ -7,7 +7,7 @@ import Link from 'next/link'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { GithubIcon } from '@/components/icons'
|
||||
import { useBrandConfig } from '@/lib/branding/branding'
|
||||
import { isHosted } from '@/lib/core/config/environment'
|
||||
import { isHosted } from '@/lib/core/config/feature-flags'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { soehne } from '@/app/_styles/fonts/soehne/soehne'
|
||||
import { getFormattedGitHubStars } from '@/app/(landing)/actions/github'
|
||||
|
||||
@@ -1,6 +1,23 @@
|
||||
import { toNextJsHandler } from 'better-auth/next-js'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { auth } from '@/lib/auth'
|
||||
import { createAnonymousSession, ensureAnonymousUserExists } from '@/lib/auth/anonymous'
|
||||
import { isAuthDisabled } from '@/lib/core/config/feature-flags'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
export const { GET, POST } = toNextJsHandler(auth.handler)
|
||||
const { GET: betterAuthGET, POST: betterAuthPOST } = toNextJsHandler(auth.handler)
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const url = new URL(request.url)
|
||||
const path = url.pathname.replace('/api/auth/', '')
|
||||
|
||||
if (path === 'get-session' && isAuthDisabled) {
|
||||
await ensureAnonymousUserExists()
|
||||
return NextResponse.json(createAnonymousSession())
|
||||
}
|
||||
|
||||
return betterAuthGET(request)
|
||||
}
|
||||
|
||||
export const POST = betterAuthPOST
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import { headers } from 'next/headers'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { auth } from '@/lib/auth'
|
||||
import { isAuthDisabled } from '@/lib/core/config/feature-flags'
|
||||
|
||||
export async function POST() {
|
||||
try {
|
||||
if (isAuthDisabled) {
|
||||
return NextResponse.json({ token: 'anonymous-socket-token' })
|
||||
}
|
||||
|
||||
const hdrs = await headers()
|
||||
const response = await auth.api.generateOneTimeToken({
|
||||
headers: hdrs,
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { db, ssoProvider } from '@sim/db'
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { auth } from '@/lib/auth'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
|
||||
const logger = createLogger('SSO-Providers')
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
export async function GET() {
|
||||
try {
|
||||
const session = await auth.api.getSession({ headers: req.headers })
|
||||
const session = await getSession()
|
||||
|
||||
let providers
|
||||
if (session?.user?.id) {
|
||||
@@ -38,8 +38,6 @@ export async function GET(req: NextRequest) {
|
||||
: ('oidc' as 'oidc' | 'saml'),
|
||||
}))
|
||||
} else {
|
||||
// Unauthenticated users can only see basic info (domain only)
|
||||
// This is needed for SSO login flow to check if a domain has SSO enabled
|
||||
const results = await db
|
||||
.select({
|
||||
domain: ssoProvider.domain,
|
||||
|
||||
@@ -5,7 +5,7 @@ import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { checkAndBillOverageThreshold } from '@/lib/billing/threshold-billing'
|
||||
import { checkInternalApiKey } from '@/lib/copilot/utils'
|
||||
import { isBillingEnabled } from '@/lib/core/config/environment'
|
||||
import { isBillingEnabled } from '@/lib/core/config/feature-flags'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ export async function POST(
|
||||
if ((password || email) && !input) {
|
||||
const response = addCorsHeaders(createSuccessResponse({ authenticated: true }), request)
|
||||
|
||||
setChatAuthCookie(response, deployment.id, deployment.authType)
|
||||
setChatAuthCookie(response, deployment.id, deployment.authType, deployment.password)
|
||||
|
||||
return response
|
||||
}
|
||||
@@ -315,7 +315,7 @@ export async function GET(
|
||||
if (
|
||||
deployment.authType !== 'public' &&
|
||||
authCookie &&
|
||||
validateAuthToken(authCookie.value, deployment.id)
|
||||
validateAuthToken(authCookie.value, deployment.id, deployment.password)
|
||||
) {
|
||||
return addCorsHeaders(
|
||||
createSuccessResponse({
|
||||
|
||||
@@ -6,6 +6,12 @@ import { NextRequest } from 'next/server'
|
||||
*/
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
vi.mock('@/lib/core/config/feature-flags', () => ({
|
||||
isDev: true,
|
||||
isHosted: false,
|
||||
isProd: false,
|
||||
}))
|
||||
|
||||
describe('Chat Edit API Route', () => {
|
||||
const mockSelect = vi.fn()
|
||||
const mockFrom = vi.fn()
|
||||
@@ -24,7 +30,6 @@ describe('Chat Edit API Route', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetModules()
|
||||
|
||||
// Set default return values
|
||||
mockLimit.mockResolvedValue([])
|
||||
mockSelect.mockReturnValue({ from: mockFrom })
|
||||
mockFrom.mockReturnValue({ where: mockWhere })
|
||||
@@ -77,10 +82,6 @@ describe('Chat Edit API Route', () => {
|
||||
getEmailDomain: vi.fn().mockReturnValue('localhost:3000'),
|
||||
}))
|
||||
|
||||
vi.doMock('@/lib/core/config/environment', () => ({
|
||||
isDev: true,
|
||||
}))
|
||||
|
||||
vi.doMock('@/app/api/chat/utils', () => ({
|
||||
checkChatAccess: mockCheckChatAccess,
|
||||
}))
|
||||
@@ -254,7 +255,6 @@ describe('Chat Edit API Route', () => {
|
||||
|
||||
mockCheckChatAccess.mockResolvedValue({ hasAccess: true, chat: mockChat })
|
||||
|
||||
// Reset and reconfigure mockLimit to return the conflict
|
||||
mockLimit.mockReset()
|
||||
mockLimit.mockResolvedValue([{ id: 'other-chat-id', identifier: 'new-identifier' }])
|
||||
mockWhere.mockReturnValue({ limit: mockLimit })
|
||||
@@ -291,7 +291,7 @@ describe('Chat Edit API Route', () => {
|
||||
|
||||
const req = new NextRequest('http://localhost:3000/api/chat/manage/chat-123', {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify({ authType: 'password' }), // No password provided
|
||||
body: JSON.stringify({ authType: 'password' }),
|
||||
})
|
||||
const { PATCH } = await import('@/app/api/chat/manage/[id]/route')
|
||||
const response = await PATCH(req, { params: Promise.resolve({ id: 'chat-123' }) })
|
||||
@@ -316,9 +316,8 @@ describe('Chat Edit API Route', () => {
|
||||
workflowId: 'workflow-123',
|
||||
}
|
||||
|
||||
// User doesn't own chat but has workspace admin access
|
||||
mockCheckChatAccess.mockResolvedValue({ hasAccess: true, chat: mockChat })
|
||||
mockLimit.mockResolvedValueOnce([]) // No identifier conflict
|
||||
mockLimit.mockResolvedValueOnce([])
|
||||
|
||||
const req = new NextRequest('http://localhost:3000/api/chat/manage/chat-123', {
|
||||
method: 'PATCH',
|
||||
@@ -399,7 +398,6 @@ describe('Chat Edit API Route', () => {
|
||||
}),
|
||||
}))
|
||||
|
||||
// User doesn't own chat but has workspace admin access
|
||||
mockCheckChatAccess.mockResolvedValue({ hasAccess: true })
|
||||
mockWhere.mockResolvedValue(undefined)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { eq } from 'drizzle-orm'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { isDev } from '@/lib/core/config/environment'
|
||||
import { isDev } from '@/lib/core/config/feature-flags'
|
||||
import { encryptSecret } from '@/lib/core/security/encryption'
|
||||
import { getEmailDomain } from '@/lib/core/utils/urls'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { NextRequest } from 'next/server'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { z } from 'zod'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { isDev } from '@/lib/core/config/environment'
|
||||
import { isDev } from '@/lib/core/config/feature-flags'
|
||||
import { encryptSecret } from '@/lib/core/security/encryption'
|
||||
import { getBaseUrl } from '@/lib/core/utils/urls'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
|
||||
@@ -44,6 +44,12 @@ vi.mock('@/lib/core/utils/request', () => ({
|
||||
generateRequestId: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/core/config/feature-flags', () => ({
|
||||
isDev: true,
|
||||
isHosted: false,
|
||||
isProd: false,
|
||||
}))
|
||||
|
||||
describe('Chat API Utils', () => {
|
||||
beforeEach(() => {
|
||||
vi.doMock('@/lib/logs/console/logger', () => ({
|
||||
@@ -62,11 +68,6 @@ describe('Chat API Utils', () => {
|
||||
NODE_ENV: 'development',
|
||||
},
|
||||
})
|
||||
|
||||
vi.doMock('@/lib/core/config/environment', () => ({
|
||||
isDev: true,
|
||||
isHosted: false,
|
||||
}))
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import { createHash } from 'crypto'
|
||||
import { db } from '@sim/db'
|
||||
import { chat, workflow } from '@sim/db/schema'
|
||||
import { eq } from 'drizzle-orm'
|
||||
import type { NextRequest, NextResponse } from 'next/server'
|
||||
import { isDev } from '@/lib/core/config/environment'
|
||||
import { isDev } from '@/lib/core/config/feature-flags'
|
||||
import { decryptSecret } from '@/lib/core/security/encryption'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { hasAdminPermission } from '@/lib/workspaces/permissions/utils'
|
||||
|
||||
const logger = createLogger('ChatAuthUtils')
|
||||
|
||||
function hashPassword(encryptedPassword: string): string {
|
||||
return createHash('sha256').update(encryptedPassword).digest('hex').substring(0, 8)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user has permission to create a chat for a specific workflow
|
||||
* Either the user owns the workflow directly OR has admin permission for the workflow's workspace
|
||||
@@ -77,14 +82,20 @@ export async function checkChatAccess(
|
||||
return { hasAccess: false }
|
||||
}
|
||||
|
||||
const encryptAuthToken = (chatId: string, type: string): string => {
|
||||
return Buffer.from(`${chatId}:${type}:${Date.now()}`).toString('base64')
|
||||
function encryptAuthToken(chatId: string, type: string, encryptedPassword?: string | null): string {
|
||||
const pwHash = encryptedPassword ? hashPassword(encryptedPassword) : ''
|
||||
return Buffer.from(`${chatId}:${type}:${Date.now()}:${pwHash}`).toString('base64')
|
||||
}
|
||||
|
||||
export const validateAuthToken = (token: string, chatId: string): boolean => {
|
||||
export function validateAuthToken(
|
||||
token: string,
|
||||
chatId: string,
|
||||
encryptedPassword?: string | null
|
||||
): boolean {
|
||||
try {
|
||||
const decoded = Buffer.from(token, 'base64').toString()
|
||||
const [storedId, _type, timestamp] = decoded.split(':')
|
||||
const parts = decoded.split(':')
|
||||
const [storedId, _type, timestamp, storedPwHash] = parts
|
||||
|
||||
if (storedId !== chatId) {
|
||||
return false
|
||||
@@ -92,20 +103,32 @@ export const validateAuthToken = (token: string, chatId: string): boolean => {
|
||||
|
||||
const createdAt = Number.parseInt(timestamp)
|
||||
const now = Date.now()
|
||||
const expireTime = 24 * 60 * 60 * 1000 // 24 hours
|
||||
const expireTime = 24 * 60 * 60 * 1000
|
||||
|
||||
if (now - createdAt > expireTime) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (encryptedPassword) {
|
||||
const currentPwHash = hashPassword(encryptedPassword)
|
||||
if (storedPwHash !== currentPwHash) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
} catch (_e) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export const setChatAuthCookie = (response: NextResponse, chatId: string, type: string): void => {
|
||||
const token = encryptAuthToken(chatId, type)
|
||||
export function setChatAuthCookie(
|
||||
response: NextResponse,
|
||||
chatId: string,
|
||||
type: string,
|
||||
encryptedPassword?: string | null
|
||||
): void {
|
||||
const token = encryptAuthToken(chatId, type, encryptedPassword)
|
||||
response.cookies.set({
|
||||
name: `chat_auth_${chatId}`,
|
||||
value: token,
|
||||
@@ -113,7 +136,7 @@ export const setChatAuthCookie = (response: NextResponse, chatId: string, type:
|
||||
secure: !isDev,
|
||||
sameSite: 'lax',
|
||||
path: '/',
|
||||
maxAge: 60 * 60 * 24, // 24 hours
|
||||
maxAge: 60 * 60 * 24,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -145,7 +168,7 @@ export async function validateChatAuth(
|
||||
const cookieName = `chat_auth_${deployment.id}`
|
||||
const authCookie = request.cookies.get(cookieName)
|
||||
|
||||
if (authCookie && validateAuthToken(authCookie.value, deployment.id)) {
|
||||
if (authCookie && validateAuthToken(authCookie.value, deployment.id, deployment.password)) {
|
||||
return { authorized: true }
|
||||
}
|
||||
|
||||
@@ -259,8 +282,8 @@ export async function validateChatAuth(
|
||||
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 })
|
||||
const { getSession } = await import('@/lib/auth')
|
||||
const session = await getSession()
|
||||
|
||||
if (!session || !session.user) {
|
||||
return { authorized: false, error: 'auth_required_sso' }
|
||||
|
||||
@@ -2,7 +2,7 @@ import { db } from '@sim/db'
|
||||
import { settings } from '@sim/db/schema'
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { auth } from '@/lib/auth'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
|
||||
const logger = createLogger('CopilotAutoAllowedToolsAPI')
|
||||
@@ -10,9 +10,9 @@ const logger = createLogger('CopilotAutoAllowedToolsAPI')
|
||||
/**
|
||||
* GET - Fetch user's auto-allowed integration tools
|
||||
*/
|
||||
export async function GET(request: NextRequest) {
|
||||
export async function GET() {
|
||||
try {
|
||||
const session = await auth.api.getSession({ headers: request.headers })
|
||||
const session = await getSession()
|
||||
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||
@@ -31,7 +31,6 @@ export async function GET(request: NextRequest) {
|
||||
return NextResponse.json({ autoAllowedTools })
|
||||
}
|
||||
|
||||
// If no settings record exists, create one with empty array
|
||||
await db.insert(settings).values({
|
||||
id: userId,
|
||||
userId,
|
||||
@@ -50,7 +49,7 @@ export async function GET(request: NextRequest) {
|
||||
*/
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const session = await auth.api.getSession({ headers: request.headers })
|
||||
const session = await getSession()
|
||||
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||
@@ -65,13 +64,11 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
const toolId = body.toolId
|
||||
|
||||
// Get existing settings
|
||||
const [existing] = await db.select().from(settings).where(eq(settings.userId, userId)).limit(1)
|
||||
|
||||
if (existing) {
|
||||
const currentTools = (existing.copilotAutoAllowedTools as string[]) || []
|
||||
|
||||
// Add tool if not already present
|
||||
if (!currentTools.includes(toolId)) {
|
||||
const updatedTools = [...currentTools, toolId]
|
||||
await db
|
||||
@@ -89,7 +86,6 @@ export async function POST(request: NextRequest) {
|
||||
return NextResponse.json({ success: true, autoAllowedTools: currentTools })
|
||||
}
|
||||
|
||||
// Create new settings record with the tool
|
||||
await db.insert(settings).values({
|
||||
id: userId,
|
||||
userId,
|
||||
@@ -109,7 +105,7 @@ export async function POST(request: NextRequest) {
|
||||
*/
|
||||
export async function DELETE(request: NextRequest) {
|
||||
try {
|
||||
const session = await auth.api.getSession({ headers: request.headers })
|
||||
const session = await getSession()
|
||||
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||
@@ -123,7 +119,6 @@ export async function DELETE(request: NextRequest) {
|
||||
return NextResponse.json({ error: 'toolId query parameter is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
// Get existing settings
|
||||
const [existing] = await db.select().from(settings).where(eq(settings.userId, userId)).limit(1)
|
||||
|
||||
if (existing) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { auth } from '@/lib/auth'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { db } from '@/../../packages/db'
|
||||
import { settings } from '@/../../packages/db/schema'
|
||||
@@ -32,7 +32,7 @@ const DEFAULT_ENABLED_MODELS: Record<string, boolean> = {
|
||||
// GET - Fetch user's enabled models
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const session = await auth.api.getSession({ headers: request.headers })
|
||||
const session = await getSession()
|
||||
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||
@@ -40,7 +40,6 @@ export async function GET(request: NextRequest) {
|
||||
|
||||
const userId = session.user.id
|
||||
|
||||
// Try to fetch existing settings record
|
||||
const [userSettings] = await db
|
||||
.select()
|
||||
.from(settings)
|
||||
@@ -50,13 +49,11 @@ export async function GET(request: NextRequest) {
|
||||
if (userSettings) {
|
||||
const userModelsMap = (userSettings.copilotEnabledModels as Record<string, boolean>) || {}
|
||||
|
||||
// Merge: start with defaults, then override with user's existing preferences
|
||||
const mergedModels = { ...DEFAULT_ENABLED_MODELS }
|
||||
for (const [modelId, enabled] of Object.entries(userModelsMap)) {
|
||||
mergedModels[modelId] = enabled
|
||||
}
|
||||
|
||||
// If we added any new models, update the database
|
||||
const hasNewModels = Object.keys(DEFAULT_ENABLED_MODELS).some(
|
||||
(key) => !(key in userModelsMap)
|
||||
)
|
||||
@@ -76,7 +73,6 @@ export async function GET(request: NextRequest) {
|
||||
})
|
||||
}
|
||||
|
||||
// If no settings record exists, create one with defaults
|
||||
await db.insert(settings).values({
|
||||
id: userId,
|
||||
userId,
|
||||
@@ -97,7 +93,7 @@ export async function GET(request: NextRequest) {
|
||||
// PUT - Update user's enabled models
|
||||
export async function PUT(request: NextRequest) {
|
||||
try {
|
||||
const session = await auth.api.getSession({ headers: request.headers })
|
||||
const session = await getSession()
|
||||
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||
@@ -110,11 +106,9 @@ export async function PUT(request: NextRequest) {
|
||||
return NextResponse.json({ error: 'enabledModels must be an object' }, { status: 400 })
|
||||
}
|
||||
|
||||
// Check if settings record exists
|
||||
const [existing] = await db.select().from(settings).where(eq(settings.userId, userId)).limit(1)
|
||||
|
||||
if (existing) {
|
||||
// Update existing record
|
||||
await db
|
||||
.update(settings)
|
||||
.set({
|
||||
@@ -123,7 +117,6 @@ export async function PUT(request: NextRequest) {
|
||||
})
|
||||
.where(eq(settings.userId, userId))
|
||||
} else {
|
||||
// Create new settings record
|
||||
await db.insert(settings).values({
|
||||
id: userId,
|
||||
userId,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createContext, Script } from 'vm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { env, isTruthy } from '@/lib/core/config/env'
|
||||
import { isE2bEnabled } from '@/lib/core/config/feature-flags'
|
||||
import { validateProxyUrl } from '@/lib/core/security/input-validation'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { executeInE2B } from '@/lib/execution/e2b'
|
||||
@@ -701,7 +701,6 @@ export async function POST(req: NextRequest) {
|
||||
resolvedCode = codeResolution.resolvedCode
|
||||
const contextVariables = codeResolution.contextVariables
|
||||
|
||||
const e2bEnabled = isTruthy(env.E2B_ENABLED)
|
||||
const lang = isValidCodeLanguage(language) ? language : DEFAULT_CODE_LANGUAGE
|
||||
|
||||
// Extract imports once for JavaScript code (reuse later to avoid double extraction)
|
||||
@@ -722,14 +721,14 @@ export async function POST(req: NextRequest) {
|
||||
}
|
||||
|
||||
// Python always requires E2B
|
||||
if (lang === CodeLanguage.Python && !e2bEnabled) {
|
||||
if (lang === CodeLanguage.Python && !isE2bEnabled) {
|
||||
throw new Error(
|
||||
'Python execution requires E2B to be enabled. Please contact your administrator to enable E2B, or use JavaScript instead.'
|
||||
)
|
||||
}
|
||||
|
||||
// JavaScript with imports requires E2B
|
||||
if (lang === CodeLanguage.JavaScript && hasImports && !e2bEnabled) {
|
||||
if (lang === CodeLanguage.JavaScript && hasImports && !isE2bEnabled) {
|
||||
throw new Error(
|
||||
'JavaScript code with import statements requires E2B to be enabled. Please remove the import statements, or contact your administrator to enable E2B.'
|
||||
)
|
||||
@@ -740,13 +739,13 @@ export async function POST(req: NextRequest) {
|
||||
// - Not a custom tool AND
|
||||
// - (Python OR JavaScript with imports)
|
||||
const useE2B =
|
||||
e2bEnabled &&
|
||||
isE2bEnabled &&
|
||||
!isCustomTool &&
|
||||
(lang === CodeLanguage.Python || (lang === CodeLanguage.JavaScript && hasImports))
|
||||
|
||||
if (useE2B) {
|
||||
logger.info(`[${requestId}] E2B status`, {
|
||||
enabled: e2bEnabled,
|
||||
enabled: isE2bEnabled,
|
||||
hasApiKey: Boolean(process.env.E2B_API_KEY),
|
||||
language: lang,
|
||||
})
|
||||
|
||||
@@ -6,7 +6,7 @@ import { z } from 'zod'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { getPlanPricing } from '@/lib/billing/core/billing'
|
||||
import { requireStripeClient } from '@/lib/billing/stripe-client'
|
||||
import { isBillingEnabled } from '@/lib/core/config/environment'
|
||||
import { isBillingEnabled } from '@/lib/core/config/feature-flags'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
|
||||
const logger = createLogger('OrganizationSeatsAPI')
|
||||
|
||||
@@ -3,7 +3,7 @@ import { NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||
import { generateInternalToken } from '@/lib/auth/internal'
|
||||
import { isDev } from '@/lib/core/config/environment'
|
||||
import { isDev } from '@/lib/core/config/feature-flags'
|
||||
import { createPinnedUrl, validateUrlWithDNS } from '@/lib/core/security/input-validation'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { getBaseUrl } from '@/lib/core/utils/urls'
|
||||
|
||||
@@ -1,26 +1,81 @@
|
||||
import { db } from '@sim/db'
|
||||
import { chat } from '@sim/db/schema'
|
||||
import { eq } from 'drizzle-orm'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import { validateAlphanumericId } from '@/lib/core/security/input-validation'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { validateAuthToken } from '@/app/api/chat/utils'
|
||||
|
||||
const logger = createLogger('ProxyTTSStreamAPI')
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
/**
|
||||
* Validates chat-based authentication for deployed chat voice mode
|
||||
* Checks if the user has a valid chat auth cookie for the given chatId
|
||||
*/
|
||||
async function validateChatAuth(request: NextRequest, chatId: string): Promise<boolean> {
|
||||
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 chatResult = await db
|
||||
.select({
|
||||
id: chat.id,
|
||||
isActive: chat.isActive,
|
||||
authType: chat.authType,
|
||||
password: chat.password,
|
||||
})
|
||||
.from(chat)
|
||||
.where(eq(chat.id, chatId))
|
||||
.limit(1)
|
||||
|
||||
if (chatResult.length === 0 || !chatResult[0].isActive) {
|
||||
logger.warn('Chat not found or inactive for TTS auth:', chatId)
|
||||
return false
|
||||
}
|
||||
|
||||
const body = await request.json()
|
||||
const { text, voiceId, modelId = 'eleven_turbo_v2_5' } = body
|
||||
const chatData = chatResult[0]
|
||||
|
||||
if (chatData.authType === 'public') {
|
||||
return true
|
||||
}
|
||||
|
||||
const cookieName = `chat_auth_${chatId}`
|
||||
const authCookie = request.cookies.get(cookieName)
|
||||
|
||||
if (authCookie && validateAuthToken(authCookie.value, chatId, chatData.password)) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
} catch (error) {
|
||||
logger.error('Error validating chat auth for TTS:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
let body: any
|
||||
try {
|
||||
body = await request.json()
|
||||
} catch {
|
||||
return new Response('Invalid request body', { status: 400 })
|
||||
}
|
||||
|
||||
const { text, voiceId, modelId = 'eleven_turbo_v2_5', chatId } = body
|
||||
|
||||
if (!chatId) {
|
||||
return new Response('chatId is required', { status: 400 })
|
||||
}
|
||||
|
||||
if (!text || !voiceId) {
|
||||
return new Response('Missing required parameters', { status: 400 })
|
||||
}
|
||||
|
||||
const isChatAuthed = await validateChatAuth(request, chatId)
|
||||
if (!isChatAuthed) {
|
||||
logger.warn('Chat authentication failed for TTS, chatId:', chatId)
|
||||
return new Response('Unauthorized', { status: 401 })
|
||||
}
|
||||
|
||||
const voiceIdValidation = validateAlphanumericId(voiceId, 'voiceId', 255)
|
||||
if (!voiceIdValidation.isValid) {
|
||||
logger.error(`Invalid voice ID: ${voiceIdValidation.error}`)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||
import { validateAlphanumericId } from '@/lib/core/security/input-validation'
|
||||
import { getBaseUrl } from '@/lib/core/utils/urls'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { StorageService } from '@/lib/uploads'
|
||||
@@ -147,6 +148,10 @@ export async function POST(request: NextRequest) {
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
const voiceIdValidation = validateAlphanumericId(body.voiceId, 'voiceId')
|
||||
if (!voiceIdValidation.isValid) {
|
||||
return NextResponse.json({ error: voiceIdValidation.error }, { status: 400 })
|
||||
}
|
||||
const result = await synthesizeWithElevenLabs({
|
||||
text,
|
||||
apiKey,
|
||||
|
||||
@@ -42,11 +42,11 @@ describe('Scheduled Workflow Execution API Route', () => {
|
||||
executeScheduleJob: mockExecuteScheduleJob,
|
||||
}))
|
||||
|
||||
vi.doMock('@/lib/core/config/env', () => ({
|
||||
env: {
|
||||
TRIGGER_DEV_ENABLED: false,
|
||||
},
|
||||
isTruthy: vi.fn(() => false),
|
||||
vi.doMock('@/lib/core/config/feature-flags', () => ({
|
||||
isTriggerDevEnabled: false,
|
||||
isHosted: false,
|
||||
isProd: false,
|
||||
isDev: true,
|
||||
}))
|
||||
|
||||
vi.doMock('drizzle-orm', () => ({
|
||||
@@ -119,11 +119,11 @@ describe('Scheduled Workflow Execution API Route', () => {
|
||||
},
|
||||
}))
|
||||
|
||||
vi.doMock('@/lib/core/config/env', () => ({
|
||||
env: {
|
||||
TRIGGER_DEV_ENABLED: true,
|
||||
},
|
||||
isTruthy: vi.fn(() => true),
|
||||
vi.doMock('@/lib/core/config/feature-flags', () => ({
|
||||
isTriggerDevEnabled: true,
|
||||
isHosted: false,
|
||||
isProd: false,
|
||||
isDev: true,
|
||||
}))
|
||||
|
||||
vi.doMock('drizzle-orm', () => ({
|
||||
@@ -191,11 +191,11 @@ describe('Scheduled Workflow Execution API Route', () => {
|
||||
executeScheduleJob: vi.fn().mockResolvedValue(undefined),
|
||||
}))
|
||||
|
||||
vi.doMock('@/lib/core/config/env', () => ({
|
||||
env: {
|
||||
TRIGGER_DEV_ENABLED: false,
|
||||
},
|
||||
isTruthy: vi.fn(() => false),
|
||||
vi.doMock('@/lib/core/config/feature-flags', () => ({
|
||||
isTriggerDevEnabled: false,
|
||||
isHosted: false,
|
||||
isProd: false,
|
||||
isDev: true,
|
||||
}))
|
||||
|
||||
vi.doMock('drizzle-orm', () => ({
|
||||
@@ -250,11 +250,11 @@ describe('Scheduled Workflow Execution API Route', () => {
|
||||
executeScheduleJob: vi.fn().mockResolvedValue(undefined),
|
||||
}))
|
||||
|
||||
vi.doMock('@/lib/core/config/env', () => ({
|
||||
env: {
|
||||
TRIGGER_DEV_ENABLED: false,
|
||||
},
|
||||
isTruthy: vi.fn(() => false),
|
||||
vi.doMock('@/lib/core/config/feature-flags', () => ({
|
||||
isTriggerDevEnabled: false,
|
||||
isHosted: false,
|
||||
isProd: false,
|
||||
isDev: true,
|
||||
}))
|
||||
|
||||
vi.doMock('drizzle-orm', () => ({
|
||||
|
||||
@@ -3,7 +3,7 @@ import { tasks } from '@trigger.dev/sdk'
|
||||
import { and, eq, isNull, lt, lte, not, or } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { verifyCronAuth } from '@/lib/auth/internal'
|
||||
import { env, isTruthy } from '@/lib/core/config/env'
|
||||
import { isTriggerDevEnabled } from '@/lib/core/config/feature-flags'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { executeScheduleJob } from '@/background/schedule-execution'
|
||||
@@ -54,9 +54,7 @@ export async function GET(request: NextRequest) {
|
||||
logger.debug(`[${requestId}] Successfully queried schedules: ${dueSchedules.length} found`)
|
||||
logger.info(`[${requestId}] Processing ${dueSchedules.length} due scheduled workflows`)
|
||||
|
||||
const useTrigger = isTruthy(env.TRIGGER_DEV_ENABLED)
|
||||
|
||||
if (useTrigger) {
|
||||
if (isTriggerDevEnabled) {
|
||||
const triggerPromises = dueSchedules.map(async (schedule) => {
|
||||
const queueTime = schedule.lastQueuedAt ?? queuedAt
|
||||
|
||||
|
||||
@@ -23,13 +23,13 @@ export async function GET() {
|
||||
|
||||
if (!response.ok) {
|
||||
console.warn('GitHub API request failed:', response.status)
|
||||
return NextResponse.json({ stars: formatStarCount(14500) })
|
||||
return NextResponse.json({ stars: formatStarCount(19400) })
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
return NextResponse.json({ stars: formatStarCount(Number(data?.stargazers_count ?? 14500)) })
|
||||
return NextResponse.json({ stars: formatStarCount(Number(data?.stargazers_count ?? 19400)) })
|
||||
} catch (error) {
|
||||
console.warn('Error fetching GitHub stars:', error)
|
||||
return NextResponse.json({ stars: formatStarCount(14500) })
|
||||
return NextResponse.json({ stars: formatStarCount(19400) })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import { isProd } from '@/lib/core/config/environment'
|
||||
import { isProd } from '@/lib/core/config/feature-flags'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
|
||||
const logger = createLogger('TelemetryAPI')
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||
import { validateNumericId } from '@/lib/core/security/input-validation'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
|
||||
@@ -41,6 +42,17 @@ export async function POST(request: NextRequest) {
|
||||
const body = await request.json()
|
||||
const validatedData = DiscordSendMessageSchema.parse(body)
|
||||
|
||||
const channelIdValidation = validateNumericId(validatedData.channelId, 'channelId')
|
||||
if (!channelIdValidation.isValid) {
|
||||
logger.warn(`[${requestId}] Invalid channelId format`, {
|
||||
error: channelIdValidation.error,
|
||||
})
|
||||
return NextResponse.json(
|
||||
{ success: false, error: channelIdValidation.error },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] Sending Discord message`, {
|
||||
channelId: validatedData.channelId,
|
||||
hasFiles: !!(validatedData.files && validatedData.files.length > 0),
|
||||
|
||||
@@ -1,32 +1,55 @@
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { authorizeCredentialUse } from '@/lib/auth/credential-access'
|
||||
import { validateAlphanumericId } from '@/lib/core/security/input-validation'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { getOAuthToken } from '@/app/api/auth/oauth/utils'
|
||||
import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils'
|
||||
|
||||
const logger = createLogger('WebflowCollectionsAPI')
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const session = await getSession()
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||
const requestId = generateRequestId()
|
||||
const body = await request.json()
|
||||
const { credential, workflowId, siteId } = body
|
||||
|
||||
if (!credential) {
|
||||
logger.error('Missing credential in request')
|
||||
return NextResponse.json({ error: 'Credential is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
const { searchParams } = new URL(request.url)
|
||||
const siteId = searchParams.get('siteId')
|
||||
|
||||
if (!siteId) {
|
||||
return NextResponse.json({ error: 'Missing siteId parameter' }, { status: 400 })
|
||||
const siteIdValidation = validateAlphanumericId(siteId, 'siteId')
|
||||
if (!siteIdValidation.isValid) {
|
||||
logger.error('Invalid siteId', { error: siteIdValidation.error })
|
||||
return NextResponse.json({ error: siteIdValidation.error }, { status: 400 })
|
||||
}
|
||||
|
||||
const accessToken = await getOAuthToken(session.user.id, 'webflow')
|
||||
const authz = await authorizeCredentialUse(request as any, {
|
||||
credentialId: credential,
|
||||
workflowId,
|
||||
})
|
||||
if (!authz.ok || !authz.credentialOwnerUserId) {
|
||||
return NextResponse.json({ error: authz.error || 'Unauthorized' }, { status: 403 })
|
||||
}
|
||||
|
||||
const accessToken = await refreshAccessTokenIfNeeded(
|
||||
credential,
|
||||
authz.credentialOwnerUserId,
|
||||
requestId
|
||||
)
|
||||
if (!accessToken) {
|
||||
logger.error('Failed to get access token', {
|
||||
credentialId: credential,
|
||||
userId: authz.credentialOwnerUserId,
|
||||
})
|
||||
return NextResponse.json(
|
||||
{ error: 'No Webflow access token found. Please connect your Webflow account.' },
|
||||
{ status: 404 }
|
||||
{
|
||||
error: 'Could not retrieve access token',
|
||||
authRequired: true,
|
||||
},
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -58,11 +81,11 @@ export async function GET(request: NextRequest) {
|
||||
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({ collections: formattedCollections })
|
||||
} catch (error) {
|
||||
logger.error('Error processing Webflow collections request:', error)
|
||||
return NextResponse.json(
|
||||
{ error: 'Internal server error', details: error.message },
|
||||
{ error: 'Failed to retrieve Webflow collections', details: (error as Error).message },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
|
||||
106
apps/sim/app/api/tools/webflow/items/route.ts
Normal file
106
apps/sim/app/api/tools/webflow/items/route.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { authorizeCredentialUse } from '@/lib/auth/credential-access'
|
||||
import { validateAlphanumericId } from '@/lib/core/security/input-validation'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils'
|
||||
|
||||
const logger = createLogger('WebflowItemsAPI')
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const requestId = generateRequestId()
|
||||
const body = await request.json()
|
||||
const { credential, workflowId, collectionId, search } = body
|
||||
|
||||
if (!credential) {
|
||||
logger.error('Missing credential in request')
|
||||
return NextResponse.json({ error: 'Credential is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
const collectionIdValidation = validateAlphanumericId(collectionId, 'collectionId')
|
||||
if (!collectionIdValidation.isValid) {
|
||||
logger.error('Invalid collectionId', { error: collectionIdValidation.error })
|
||||
return NextResponse.json({ error: collectionIdValidation.error }, { status: 400 })
|
||||
}
|
||||
|
||||
const authz = await authorizeCredentialUse(request as any, {
|
||||
credentialId: credential,
|
||||
workflowId,
|
||||
})
|
||||
if (!authz.ok || !authz.credentialOwnerUserId) {
|
||||
return NextResponse.json({ error: authz.error || 'Unauthorized' }, { status: 403 })
|
||||
}
|
||||
|
||||
const accessToken = await refreshAccessTokenIfNeeded(
|
||||
credential,
|
||||
authz.credentialOwnerUserId,
|
||||
requestId
|
||||
)
|
||||
if (!accessToken) {
|
||||
logger.error('Failed to get access token', {
|
||||
credentialId: credential,
|
||||
userId: authz.credentialOwnerUserId,
|
||||
})
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: 'Could not retrieve access token',
|
||||
authRequired: true,
|
||||
},
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
const response = await fetch(
|
||||
`https://api.webflow.com/v2/collections/${collectionId}/items?limit=100`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
accept: 'application/json',
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
logger.error('Failed to fetch Webflow items', {
|
||||
status: response.status,
|
||||
error: errorData,
|
||||
collectionId,
|
||||
})
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to fetch Webflow items', details: errorData },
|
||||
{ status: response.status }
|
||||
)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const items = data.items || []
|
||||
|
||||
let formattedItems = items.map((item: any) => {
|
||||
const fieldData = item.fieldData || {}
|
||||
const name = fieldData.name || fieldData.title || fieldData.slug || item.id
|
||||
return {
|
||||
id: item.id,
|
||||
name,
|
||||
}
|
||||
})
|
||||
|
||||
if (search) {
|
||||
const searchLower = search.toLowerCase()
|
||||
formattedItems = formattedItems.filter((item: { id: string; name: string }) =>
|
||||
item.name.toLowerCase().includes(searchLower)
|
||||
)
|
||||
}
|
||||
|
||||
return NextResponse.json({ items: formattedItems })
|
||||
} catch (error) {
|
||||
logger.error('Error processing Webflow items request:', error)
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to retrieve Webflow items', details: (error as Error).message },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,48 @@
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { authorizeCredentialUse } from '@/lib/auth/credential-access'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { getOAuthToken } from '@/app/api/auth/oauth/utils'
|
||||
import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils'
|
||||
|
||||
const logger = createLogger('WebflowSitesAPI')
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const session = await getSession()
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||
const requestId = generateRequestId()
|
||||
const body = await request.json()
|
||||
const { credential, workflowId } = body
|
||||
|
||||
if (!credential) {
|
||||
logger.error('Missing credential in request')
|
||||
return NextResponse.json({ error: 'Credential is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
const accessToken = await getOAuthToken(session.user.id, 'webflow')
|
||||
const authz = await authorizeCredentialUse(request as any, {
|
||||
credentialId: credential,
|
||||
workflowId,
|
||||
})
|
||||
if (!authz.ok || !authz.credentialOwnerUserId) {
|
||||
return NextResponse.json({ error: authz.error || 'Unauthorized' }, { status: 403 })
|
||||
}
|
||||
|
||||
const accessToken = await refreshAccessTokenIfNeeded(
|
||||
credential,
|
||||
authz.credentialOwnerUserId,
|
||||
requestId
|
||||
)
|
||||
if (!accessToken) {
|
||||
logger.error('Failed to get access token', {
|
||||
credentialId: credential,
|
||||
userId: authz.credentialOwnerUserId,
|
||||
})
|
||||
return NextResponse.json(
|
||||
{ error: 'No Webflow access token found. Please connect your Webflow account.' },
|
||||
{ status: 404 }
|
||||
{
|
||||
error: 'Could not retrieve access token',
|
||||
authRequired: true,
|
||||
},
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -50,11 +73,11 @@ export async function GET(request: NextRequest) {
|
||||
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({ sites: formattedSites })
|
||||
} catch (error) {
|
||||
logger.error('Error processing Webflow sites request:', error)
|
||||
return NextResponse.json(
|
||||
{ error: 'Internal server error', details: error.message },
|
||||
{ error: 'Failed to retrieve Webflow sites', details: (error as Error).message },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -35,19 +35,18 @@
|
||||
* GET /api/v1/admin/organizations/:id - Get organization details
|
||||
* PATCH /api/v1/admin/organizations/:id - Update organization
|
||||
* GET /api/v1/admin/organizations/:id/members - List organization members
|
||||
* POST /api/v1/admin/organizations/:id/members - Add/update member in organization
|
||||
* POST /api/v1/admin/organizations/:id/members - Add/update member (validates seat availability)
|
||||
* GET /api/v1/admin/organizations/:id/members/:mid - Get member details
|
||||
* PATCH /api/v1/admin/organizations/:id/members/:mid - Update member role
|
||||
* DELETE /api/v1/admin/organizations/:id/members/:mid - Remove member
|
||||
* GET /api/v1/admin/organizations/:id/billing - Get org billing summary
|
||||
* PATCH /api/v1/admin/organizations/:id/billing - Update org usage limit
|
||||
* GET /api/v1/admin/organizations/:id/seats - Get seat analytics
|
||||
* PATCH /api/v1/admin/organizations/:id/seats - Update seat count
|
||||
*
|
||||
* Subscriptions:
|
||||
* GET /api/v1/admin/subscriptions - List all subscriptions
|
||||
* GET /api/v1/admin/subscriptions/:id - Get subscription details
|
||||
* PATCH /api/v1/admin/subscriptions/:id - Update subscription
|
||||
* DELETE /api/v1/admin/subscriptions/:id - Cancel subscription (?atPeriodEnd=true for scheduled)
|
||||
*/
|
||||
|
||||
export type { AdminAuthFailure, AdminAuthResult, AdminAuthSuccess } from '@/app/api/v1/admin/auth'
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
* POST /api/v1/admin/organizations/[id]/members
|
||||
*
|
||||
* Add a user to an organization with full billing logic.
|
||||
* Validates seat availability before adding (uses same logic as invitation flow):
|
||||
* - Team plans: checks seats column
|
||||
* - Enterprise plans: checks metadata.seats
|
||||
* Handles Pro usage snapshot and subscription cancellation like the invitation flow.
|
||||
* If user is already a member, updates their role if different.
|
||||
*
|
||||
@@ -29,6 +32,7 @@ import { db } from '@sim/db'
|
||||
import { member, organization, user, userStats } from '@sim/db/schema'
|
||||
import { count, eq } from 'drizzle-orm'
|
||||
import { addUserToOrganization } from '@/lib/billing/organizations/membership'
|
||||
import { requireStripeClient } from '@/lib/billing/stripe-client'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { withAdminAuthParams } from '@/app/api/v1/admin/middleware'
|
||||
import {
|
||||
@@ -223,6 +227,29 @@ export const POST = withAdminAuthParams<RouteParams>(async (request, context) =>
|
||||
return badRequestResponse(result.error || 'Failed to add member')
|
||||
}
|
||||
|
||||
// Sync Pro subscription cancellation with Stripe (same as invitation flow)
|
||||
if (result.billingActions.proSubscriptionToCancel?.stripeSubscriptionId) {
|
||||
try {
|
||||
const stripe = requireStripeClient()
|
||||
await stripe.subscriptions.update(
|
||||
result.billingActions.proSubscriptionToCancel.stripeSubscriptionId,
|
||||
{ cancel_at_period_end: true }
|
||||
)
|
||||
logger.info('Admin API: Synced Pro cancellation with Stripe', {
|
||||
userId: body.userId,
|
||||
subscriptionId: result.billingActions.proSubscriptionToCancel.subscriptionId,
|
||||
stripeSubscriptionId: result.billingActions.proSubscriptionToCancel.stripeSubscriptionId,
|
||||
})
|
||||
} catch (stripeError) {
|
||||
logger.error('Admin API: Failed to sync Pro cancellation with Stripe', {
|
||||
userId: body.userId,
|
||||
subscriptionId: result.billingActions.proSubscriptionToCancel.subscriptionId,
|
||||
stripeSubscriptionId: result.billingActions.proSubscriptionToCancel.stripeSubscriptionId,
|
||||
error: stripeError,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const data: AdminMember = {
|
||||
id: result.memberId!,
|
||||
userId: body.userId,
|
||||
|
||||
@@ -4,26 +4,12 @@
|
||||
* Get organization seat analytics including member activity.
|
||||
*
|
||||
* Response: AdminSingleResponse<AdminSeatAnalytics>
|
||||
*
|
||||
* PATCH /api/v1/admin/organizations/[id]/seats
|
||||
*
|
||||
* Update organization seat count with Stripe sync (matches user flow).
|
||||
*
|
||||
* Body:
|
||||
* - seats: number - New seat count (positive integer)
|
||||
*
|
||||
* Response: AdminSingleResponse<{ success: true, seats: number, plan: string, stripeUpdated?: boolean }>
|
||||
*/
|
||||
|
||||
import { db } from '@sim/db'
|
||||
import { organization, subscription } from '@sim/db/schema'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { requireStripeClient } from '@/lib/billing/stripe-client'
|
||||
import { getOrganizationSeatAnalytics } from '@/lib/billing/validation/seat-management'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { withAdminAuthParams } from '@/app/api/v1/admin/middleware'
|
||||
import {
|
||||
badRequestResponse,
|
||||
internalErrorResponse,
|
||||
notFoundResponse,
|
||||
singleResponse,
|
||||
@@ -75,122 +61,3 @@ export const GET = withAdminAuthParams<RouteParams>(async (_, context) => {
|
||||
return internalErrorResponse('Failed to get organization seats')
|
||||
}
|
||||
})
|
||||
|
||||
export const PATCH = withAdminAuthParams<RouteParams>(async (request, context) => {
|
||||
const { id: organizationId } = await context.params
|
||||
|
||||
try {
|
||||
const body = await request.json()
|
||||
|
||||
if (typeof body.seats !== 'number' || body.seats < 1 || !Number.isInteger(body.seats)) {
|
||||
return badRequestResponse('seats must be a positive integer')
|
||||
}
|
||||
|
||||
const [orgData] = await db
|
||||
.select({ id: organization.id })
|
||||
.from(organization)
|
||||
.where(eq(organization.id, organizationId))
|
||||
.limit(1)
|
||||
|
||||
if (!orgData) {
|
||||
return notFoundResponse('Organization')
|
||||
}
|
||||
|
||||
const [subData] = await db
|
||||
.select()
|
||||
.from(subscription)
|
||||
.where(and(eq(subscription.referenceId, organizationId), eq(subscription.status, 'active')))
|
||||
.limit(1)
|
||||
|
||||
if (!subData) {
|
||||
return notFoundResponse('Subscription')
|
||||
}
|
||||
|
||||
const newSeatCount = body.seats
|
||||
let stripeUpdated = false
|
||||
|
||||
if (subData.plan === 'enterprise') {
|
||||
const currentMetadata = (subData.metadata as Record<string, unknown>) || {}
|
||||
const newMetadata = {
|
||||
...currentMetadata,
|
||||
seats: newSeatCount,
|
||||
}
|
||||
|
||||
await db
|
||||
.update(subscription)
|
||||
.set({ metadata: newMetadata })
|
||||
.where(eq(subscription.id, subData.id))
|
||||
|
||||
logger.info(`Admin API: Updated enterprise seats for organization ${organizationId}`, {
|
||||
seats: newSeatCount,
|
||||
})
|
||||
} else if (subData.plan === 'team') {
|
||||
if (subData.stripeSubscriptionId) {
|
||||
const stripe = requireStripeClient()
|
||||
|
||||
const stripeSubscription = await stripe.subscriptions.retrieve(subData.stripeSubscriptionId)
|
||||
|
||||
if (stripeSubscription.status !== 'active') {
|
||||
return badRequestResponse('Stripe subscription is not active')
|
||||
}
|
||||
|
||||
const subscriptionItem = stripeSubscription.items.data[0]
|
||||
if (!subscriptionItem) {
|
||||
return internalErrorResponse('No subscription item found in Stripe subscription')
|
||||
}
|
||||
|
||||
const currentSeats = subData.seats || 1
|
||||
|
||||
logger.info('Admin API: Updating Stripe subscription quantity', {
|
||||
organizationId,
|
||||
stripeSubscriptionId: subData.stripeSubscriptionId,
|
||||
subscriptionItemId: subscriptionItem.id,
|
||||
currentSeats,
|
||||
newSeatCount,
|
||||
})
|
||||
|
||||
await stripe.subscriptions.update(subData.stripeSubscriptionId, {
|
||||
items: [
|
||||
{
|
||||
id: subscriptionItem.id,
|
||||
quantity: newSeatCount,
|
||||
},
|
||||
],
|
||||
proration_behavior: 'create_prorations',
|
||||
})
|
||||
|
||||
stripeUpdated = true
|
||||
}
|
||||
|
||||
await db
|
||||
.update(subscription)
|
||||
.set({ seats: newSeatCount })
|
||||
.where(eq(subscription.id, subData.id))
|
||||
|
||||
logger.info(`Admin API: Updated team seats for organization ${organizationId}`, {
|
||||
seats: newSeatCount,
|
||||
stripeUpdated,
|
||||
})
|
||||
} else {
|
||||
await db
|
||||
.update(subscription)
|
||||
.set({ seats: newSeatCount })
|
||||
.where(eq(subscription.id, subData.id))
|
||||
|
||||
logger.info(`Admin API: Updated seats for organization ${organizationId}`, {
|
||||
seats: newSeatCount,
|
||||
plan: subData.plan,
|
||||
})
|
||||
}
|
||||
|
||||
return singleResponse({
|
||||
success: true,
|
||||
seats: newSeatCount,
|
||||
plan: subData.plan,
|
||||
stripeUpdated,
|
||||
})
|
||||
} catch (error) {
|
||||
logger.error('Admin API: Failed to update organization seats', { error, organizationId })
|
||||
return internalErrorResponse('Failed to update organization seats')
|
||||
}
|
||||
})
|
||||
|
||||
@@ -5,28 +5,28 @@
|
||||
*
|
||||
* Response: AdminSingleResponse<AdminSubscription>
|
||||
*
|
||||
* PATCH /api/v1/admin/subscriptions/[id]
|
||||
* DELETE /api/v1/admin/subscriptions/[id]
|
||||
*
|
||||
* Update subscription details with optional side effects.
|
||||
* Cancel a subscription by triggering Stripe cancellation.
|
||||
* The Stripe webhook handles all cleanup (same as platform cancellation):
|
||||
* - Updates subscription status to canceled
|
||||
* - Bills final period overages
|
||||
* - Resets usage
|
||||
* - Restores member Pro subscriptions (for team/enterprise)
|
||||
* - Deletes organization (for team/enterprise)
|
||||
* - Syncs usage limits to free tier
|
||||
*
|
||||
* Body:
|
||||
* - plan?: string - New plan (free, pro, team, enterprise)
|
||||
* - status?: string - New status (active, canceled, etc.)
|
||||
* - seats?: number - Seat count (for team plans)
|
||||
* - metadata?: object - Subscription metadata (for enterprise)
|
||||
* - periodStart?: string - Period start (ISO date)
|
||||
* - periodEnd?: string - Period end (ISO date)
|
||||
* - cancelAtPeriodEnd?: boolean - Cancel at period end flag
|
||||
* - syncLimits?: boolean - Sync usage limits for affected users (default: false)
|
||||
* - reason?: string - Reason for the change (for audit logging)
|
||||
* Query Parameters:
|
||||
* - atPeriodEnd?: boolean - Schedule cancellation at period end instead of immediate (default: false)
|
||||
* - reason?: string - Reason for cancellation (for audit logging)
|
||||
*
|
||||
* Response: AdminSingleResponse<AdminSubscription & { sideEffects }>
|
||||
* Response: { success: true, message: string, subscriptionId: string, atPeriodEnd: boolean }
|
||||
*/
|
||||
|
||||
import { db } from '@sim/db'
|
||||
import { member, subscription } from '@sim/db/schema'
|
||||
import { subscription } from '@sim/db/schema'
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { syncUsageLimitsFromSubscription } from '@/lib/billing/core/usage'
|
||||
import { requireStripeClient } from '@/lib/billing/stripe-client'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { withAdminAuthParams } from '@/app/api/v1/admin/middleware'
|
||||
import {
|
||||
@@ -43,9 +43,6 @@ interface RouteParams {
|
||||
id: string
|
||||
}
|
||||
|
||||
const VALID_PLANS = ['free', 'pro', 'team', 'enterprise']
|
||||
const VALID_STATUSES = ['active', 'canceled', 'past_due', 'unpaid', 'trialing', 'incomplete']
|
||||
|
||||
export const GET = withAdminAuthParams<RouteParams>(async (_, context) => {
|
||||
const { id: subscriptionId } = await context.params
|
||||
|
||||
@@ -69,14 +66,13 @@ export const GET = withAdminAuthParams<RouteParams>(async (_, context) => {
|
||||
}
|
||||
})
|
||||
|
||||
export const PATCH = withAdminAuthParams<RouteParams>(async (request, context) => {
|
||||
export const DELETE = withAdminAuthParams<RouteParams>(async (request, context) => {
|
||||
const { id: subscriptionId } = await context.params
|
||||
const url = new URL(request.url)
|
||||
const atPeriodEnd = url.searchParams.get('atPeriodEnd') === 'true'
|
||||
const reason = url.searchParams.get('reason') || 'Admin cancellation (no reason provided)'
|
||||
|
||||
try {
|
||||
const body = await request.json()
|
||||
const syncLimits = body.syncLimits === true
|
||||
const reason = body.reason || 'Admin update (no reason provided)'
|
||||
|
||||
const [existing] = await db
|
||||
.select()
|
||||
.from(subscription)
|
||||
@@ -87,150 +83,70 @@ export const PATCH = withAdminAuthParams<RouteParams>(async (request, context) =
|
||||
return notFoundResponse('Subscription')
|
||||
}
|
||||
|
||||
const updateData: Record<string, unknown> = {}
|
||||
const warnings: string[] = []
|
||||
|
||||
if (body.plan !== undefined) {
|
||||
if (!VALID_PLANS.includes(body.plan)) {
|
||||
return badRequestResponse(`plan must be one of: ${VALID_PLANS.join(', ')}`)
|
||||
}
|
||||
if (body.plan !== existing.plan) {
|
||||
warnings.push(
|
||||
`Plan change from ${existing.plan} to ${body.plan}. This does NOT update Stripe - manual sync required.`
|
||||
)
|
||||
}
|
||||
updateData.plan = body.plan
|
||||
if (existing.status === 'canceled') {
|
||||
return badRequestResponse('Subscription is already canceled')
|
||||
}
|
||||
|
||||
if (body.status !== undefined) {
|
||||
if (!VALID_STATUSES.includes(body.status)) {
|
||||
return badRequestResponse(`status must be one of: ${VALID_STATUSES.join(', ')}`)
|
||||
}
|
||||
if (body.status !== existing.status) {
|
||||
warnings.push(
|
||||
`Status change from ${existing.status} to ${body.status}. This does NOT update Stripe - manual sync required.`
|
||||
)
|
||||
}
|
||||
updateData.status = body.status
|
||||
if (!existing.stripeSubscriptionId) {
|
||||
return badRequestResponse('Subscription has no Stripe subscription ID')
|
||||
}
|
||||
|
||||
if (body.seats !== undefined) {
|
||||
if (typeof body.seats !== 'number' || body.seats < 1 || !Number.isInteger(body.seats)) {
|
||||
return badRequestResponse('seats must be a positive integer')
|
||||
}
|
||||
updateData.seats = body.seats
|
||||
const stripe = requireStripeClient()
|
||||
|
||||
if (atPeriodEnd) {
|
||||
// Schedule cancellation at period end
|
||||
await stripe.subscriptions.update(existing.stripeSubscriptionId, {
|
||||
cancel_at_period_end: true,
|
||||
})
|
||||
|
||||
// Update DB (webhooks don't sync cancelAtPeriodEnd)
|
||||
await db
|
||||
.update(subscription)
|
||||
.set({ cancelAtPeriodEnd: true })
|
||||
.where(eq(subscription.id, subscriptionId))
|
||||
|
||||
logger.info('Admin API: Scheduled subscription cancellation at period end', {
|
||||
subscriptionId,
|
||||
stripeSubscriptionId: existing.stripeSubscriptionId,
|
||||
plan: existing.plan,
|
||||
referenceId: existing.referenceId,
|
||||
periodEnd: existing.periodEnd,
|
||||
reason,
|
||||
})
|
||||
|
||||
return singleResponse({
|
||||
success: true,
|
||||
message: 'Subscription scheduled to cancel at period end.',
|
||||
subscriptionId,
|
||||
stripeSubscriptionId: existing.stripeSubscriptionId,
|
||||
atPeriodEnd: true,
|
||||
periodEnd: existing.periodEnd?.toISOString() ?? null,
|
||||
})
|
||||
}
|
||||
|
||||
if (body.metadata !== undefined) {
|
||||
if (typeof body.metadata !== 'object' || body.metadata === null) {
|
||||
return badRequestResponse('metadata must be an object')
|
||||
}
|
||||
updateData.metadata = {
|
||||
...((existing.metadata as Record<string, unknown>) || {}),
|
||||
...body.metadata,
|
||||
}
|
||||
}
|
||||
// Immediate cancellation
|
||||
await stripe.subscriptions.cancel(existing.stripeSubscriptionId, {
|
||||
prorate: true,
|
||||
invoice_now: true,
|
||||
})
|
||||
|
||||
if (body.periodStart !== undefined) {
|
||||
const date = new Date(body.periodStart)
|
||||
if (Number.isNaN(date.getTime())) {
|
||||
return badRequestResponse('periodStart must be a valid ISO date')
|
||||
}
|
||||
updateData.periodStart = date
|
||||
}
|
||||
|
||||
if (body.periodEnd !== undefined) {
|
||||
const date = new Date(body.periodEnd)
|
||||
if (Number.isNaN(date.getTime())) {
|
||||
return badRequestResponse('periodEnd must be a valid ISO date')
|
||||
}
|
||||
updateData.periodEnd = date
|
||||
}
|
||||
|
||||
if (body.cancelAtPeriodEnd !== undefined) {
|
||||
if (typeof body.cancelAtPeriodEnd !== 'boolean') {
|
||||
return badRequestResponse('cancelAtPeriodEnd must be a boolean')
|
||||
}
|
||||
updateData.cancelAtPeriodEnd = body.cancelAtPeriodEnd
|
||||
}
|
||||
|
||||
if (Object.keys(updateData).length === 0) {
|
||||
return badRequestResponse('No valid fields to update')
|
||||
}
|
||||
|
||||
const [updated] = await db
|
||||
.update(subscription)
|
||||
.set(updateData)
|
||||
.where(eq(subscription.id, subscriptionId))
|
||||
.returning()
|
||||
|
||||
const sideEffects: {
|
||||
limitsSynced: boolean
|
||||
usersAffected: string[]
|
||||
errors: string[]
|
||||
} = {
|
||||
limitsSynced: false,
|
||||
usersAffected: [],
|
||||
errors: [],
|
||||
}
|
||||
|
||||
if (syncLimits) {
|
||||
try {
|
||||
const referenceId = updated.referenceId
|
||||
|
||||
if (['free', 'pro'].includes(updated.plan)) {
|
||||
await syncUsageLimitsFromSubscription(referenceId)
|
||||
sideEffects.usersAffected.push(referenceId)
|
||||
sideEffects.limitsSynced = true
|
||||
} else if (['team', 'enterprise'].includes(updated.plan)) {
|
||||
const members = await db
|
||||
.select({ userId: member.userId })
|
||||
.from(member)
|
||||
.where(eq(member.organizationId, referenceId))
|
||||
|
||||
for (const m of members) {
|
||||
try {
|
||||
await syncUsageLimitsFromSubscription(m.userId)
|
||||
sideEffects.usersAffected.push(m.userId)
|
||||
} catch (memberError) {
|
||||
sideEffects.errors.push(`Failed to sync limits for user ${m.userId}`)
|
||||
logger.error('Admin API: Failed to sync limits for member', {
|
||||
userId: m.userId,
|
||||
error: memberError,
|
||||
})
|
||||
}
|
||||
}
|
||||
sideEffects.limitsSynced = members.length > 0
|
||||
}
|
||||
|
||||
logger.info('Admin API: Synced usage limits after subscription update', {
|
||||
subscriptionId,
|
||||
usersAffected: sideEffects.usersAffected.length,
|
||||
})
|
||||
} catch (syncError) {
|
||||
sideEffects.errors.push('Failed to sync usage limits')
|
||||
logger.error('Admin API: Failed to sync usage limits', {
|
||||
subscriptionId,
|
||||
error: syncError,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(`Admin API: Updated subscription ${subscriptionId}`, {
|
||||
fields: Object.keys(updateData),
|
||||
previousPlan: existing.plan,
|
||||
previousStatus: existing.status,
|
||||
syncLimits,
|
||||
logger.info('Admin API: Triggered immediate subscription cancellation on Stripe', {
|
||||
subscriptionId,
|
||||
stripeSubscriptionId: existing.stripeSubscriptionId,
|
||||
plan: existing.plan,
|
||||
referenceId: existing.referenceId,
|
||||
reason,
|
||||
})
|
||||
|
||||
return singleResponse({
|
||||
...toAdminSubscription(updated),
|
||||
sideEffects,
|
||||
warnings,
|
||||
success: true,
|
||||
message: 'Subscription cancellation triggered. Webhook will complete cleanup.',
|
||||
subscriptionId,
|
||||
stripeSubscriptionId: existing.stripeSubscriptionId,
|
||||
atPeriodEnd: false,
|
||||
})
|
||||
} catch (error) {
|
||||
logger.error('Admin API: Failed to update subscription', { error, subscriptionId })
|
||||
return internalErrorResponse('Failed to update subscription')
|
||||
logger.error('Admin API: Failed to cancel subscription', { error, subscriptionId })
|
||||
return internalErrorResponse('Failed to cancel subscription')
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { authenticateApiKeyFromHeader, updateApiKeyLastUsed } from '@/lib/api-key/service'
|
||||
import { ANONYMOUS_USER_ID } from '@/lib/auth/constants'
|
||||
import { isAuthDisabled } from '@/lib/core/config/feature-flags'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
|
||||
const logger = createLogger('V1Auth')
|
||||
@@ -13,6 +15,14 @@ export interface AuthResult {
|
||||
}
|
||||
|
||||
export async function authenticateV1Request(request: NextRequest): Promise<AuthResult> {
|
||||
if (isAuthDisabled) {
|
||||
return {
|
||||
authenticated: true,
|
||||
userId: ANONYMOUS_USER_ID,
|
||||
keyType: 'personal',
|
||||
}
|
||||
}
|
||||
|
||||
const apiKey = request.headers.get('x-api-key')
|
||||
|
||||
if (!apiKey) {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { type NextRequest, NextResponse } from 'next/server'
|
||||
import OpenAI, { AzureOpenAI } from 'openai'
|
||||
import { checkAndBillOverageThreshold } from '@/lib/billing/threshold-billing'
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import { getCostMultiplier, isBillingEnabled } from '@/lib/core/config/environment'
|
||||
import { getCostMultiplier, isBillingEnabled } from '@/lib/core/config/feature-flags'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { getModelPricing } from '@/providers/utils'
|
||||
|
||||
@@ -3,7 +3,7 @@ import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { validate as uuidValidate, v4 as uuidv4 } from 'uuid'
|
||||
import { z } from 'zod'
|
||||
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||
import { env, isTruthy } from '@/lib/core/config/env'
|
||||
import { isTriggerDevEnabled } from '@/lib/core/config/feature-flags'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { SSE_HEADERS } from '@/lib/core/utils/sse'
|
||||
import { getBaseUrl } from '@/lib/core/utils/urls'
|
||||
@@ -236,9 +236,8 @@ type AsyncExecutionParams = {
|
||||
*/
|
||||
async function handleAsyncExecution(params: AsyncExecutionParams): Promise<NextResponse> {
|
||||
const { requestId, workflowId, userId, input, triggerType } = params
|
||||
const useTrigger = isTruthy(env.TRIGGER_DEV_ENABLED)
|
||||
|
||||
if (!useTrigger) {
|
||||
if (!isTriggerDevEnabled) {
|
||||
logger.warn(`[${requestId}] Async mode requested but TRIGGER_DEV_ENABLED is false`)
|
||||
return NextResponse.json(
|
||||
{ error: 'Async execution is not enabled. Set TRIGGER_DEV_ENABLED=true to use async mode.' },
|
||||
|
||||
@@ -39,6 +39,7 @@ interface ChatConfig {
|
||||
|
||||
interface AudioStreamingOptions {
|
||||
voiceId: string
|
||||
chatId?: string
|
||||
onError: (error: Error) => void
|
||||
}
|
||||
|
||||
@@ -62,16 +63,19 @@ function fileToBase64(file: File): Promise<string> {
|
||||
* Creates an audio stream handler for text-to-speech conversion
|
||||
* @param streamTextToAudio - Function to stream text to audio
|
||||
* @param voiceId - The voice ID to use for TTS
|
||||
* @param chatId - Optional chat ID for deployed chat authentication
|
||||
* @returns Audio stream handler function or undefined
|
||||
*/
|
||||
function createAudioStreamHandler(
|
||||
streamTextToAudio: (text: string, options: AudioStreamingOptions) => Promise<void>,
|
||||
voiceId: string
|
||||
voiceId: string,
|
||||
chatId?: string
|
||||
) {
|
||||
return async (text: string) => {
|
||||
try {
|
||||
await streamTextToAudio(text, {
|
||||
voiceId,
|
||||
chatId,
|
||||
onError: (error: Error) => {
|
||||
logger.error('Audio streaming error:', error)
|
||||
},
|
||||
@@ -113,7 +117,7 @@ export default function ChatClient({ identifier }: { identifier: string }) {
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null)
|
||||
const messagesContainerRef = useRef<HTMLDivElement>(null)
|
||||
const [starCount, setStarCount] = useState('3.4k')
|
||||
const [starCount, setStarCount] = useState('19.4k')
|
||||
const [conversationId, setConversationId] = useState('')
|
||||
|
||||
const [showScrollButton, setShowScrollButton] = useState(false)
|
||||
@@ -391,7 +395,11 @@ export default function ChatClient({ identifier }: { identifier: string }) {
|
||||
// Use the streaming hook with audio support
|
||||
const shouldPlayAudio = isVoiceInput || isVoiceFirstMode
|
||||
const audioHandler = shouldPlayAudio
|
||||
? createAudioStreamHandler(streamTextToAudio, DEFAULT_VOICE_SETTINGS.voiceId)
|
||||
? createAudioStreamHandler(
|
||||
streamTextToAudio,
|
||||
DEFAULT_VOICE_SETTINGS.voiceId,
|
||||
chatConfig?.id
|
||||
)
|
||||
: undefined
|
||||
|
||||
logger.info('Starting to handle streamed response:', { shouldPlayAudio })
|
||||
|
||||
@@ -68,7 +68,6 @@ export function VoiceInterface({
|
||||
messages = [],
|
||||
className,
|
||||
}: VoiceInterfaceProps) {
|
||||
// Simple state machine
|
||||
const [state, setState] = useState<'idle' | 'listening' | 'agent_speaking'>('idle')
|
||||
const [isInitialized, setIsInitialized] = useState(false)
|
||||
const [isMuted, setIsMuted] = useState(false)
|
||||
@@ -76,12 +75,10 @@ export function VoiceInterface({
|
||||
const [permissionStatus, setPermissionStatus] = useState<'prompt' | 'granted' | 'denied'>(
|
||||
'prompt'
|
||||
)
|
||||
|
||||
// Current turn transcript (subtitle)
|
||||
const [currentTranscript, setCurrentTranscript] = useState('')
|
||||
|
||||
// State tracking
|
||||
const currentStateRef = useRef<'idle' | 'listening' | 'agent_speaking'>('idle')
|
||||
const isCallEndedRef = useRef(false)
|
||||
|
||||
useEffect(() => {
|
||||
currentStateRef.current = state
|
||||
@@ -98,12 +95,10 @@ export function VoiceInterface({
|
||||
const isSupported =
|
||||
typeof window !== 'undefined' && !!(window.SpeechRecognition || window.webkitSpeechRecognition)
|
||||
|
||||
// Update muted ref
|
||||
useEffect(() => {
|
||||
isMutedRef.current = isMuted
|
||||
}, [isMuted])
|
||||
|
||||
// Timeout to handle cases where agent doesn't provide audio response
|
||||
const setResponseTimeout = useCallback(() => {
|
||||
if (responseTimeoutRef.current) {
|
||||
clearTimeout(responseTimeoutRef.current)
|
||||
@@ -113,7 +108,7 @@ export function VoiceInterface({
|
||||
if (currentStateRef.current === 'listening') {
|
||||
setState('idle')
|
||||
}
|
||||
}, 5000) // 5 second timeout (increased from 3)
|
||||
}, 5000)
|
||||
}, [])
|
||||
|
||||
const clearResponseTimeout = useCallback(() => {
|
||||
@@ -123,14 +118,12 @@ export function VoiceInterface({
|
||||
}
|
||||
}, [])
|
||||
|
||||
// Sync with external state
|
||||
useEffect(() => {
|
||||
if (isPlayingAudio && state !== 'agent_speaking') {
|
||||
clearResponseTimeout() // Clear timeout since agent is responding
|
||||
clearResponseTimeout()
|
||||
setState('agent_speaking')
|
||||
setCurrentTranscript('')
|
||||
|
||||
// Mute microphone immediately
|
||||
setIsMuted(true)
|
||||
if (mediaStreamRef.current) {
|
||||
mediaStreamRef.current.getAudioTracks().forEach((track) => {
|
||||
@@ -138,7 +131,6 @@ export function VoiceInterface({
|
||||
})
|
||||
}
|
||||
|
||||
// Stop speech recognition completely
|
||||
if (recognitionRef.current) {
|
||||
try {
|
||||
recognitionRef.current.abort()
|
||||
@@ -150,7 +142,6 @@ export function VoiceInterface({
|
||||
setState('idle')
|
||||
setCurrentTranscript('')
|
||||
|
||||
// Re-enable microphone
|
||||
setIsMuted(false)
|
||||
if (mediaStreamRef.current) {
|
||||
mediaStreamRef.current.getAudioTracks().forEach((track) => {
|
||||
@@ -160,7 +151,6 @@ export function VoiceInterface({
|
||||
}
|
||||
}, [isPlayingAudio, state, clearResponseTimeout])
|
||||
|
||||
// Audio setup
|
||||
const setupAudio = useCallback(async () => {
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({
|
||||
@@ -175,7 +165,6 @@ export function VoiceInterface({
|
||||
setPermissionStatus('granted')
|
||||
mediaStreamRef.current = stream
|
||||
|
||||
// Setup audio context for visualization
|
||||
if (!audioContextRef.current) {
|
||||
const AudioContext = window.AudioContext || window.webkitAudioContext
|
||||
audioContextRef.current = new AudioContext()
|
||||
@@ -194,7 +183,6 @@ export function VoiceInterface({
|
||||
source.connect(analyser)
|
||||
analyserRef.current = analyser
|
||||
|
||||
// Start visualization
|
||||
const updateVisualization = () => {
|
||||
if (!analyserRef.current) return
|
||||
|
||||
@@ -223,7 +211,6 @@ export function VoiceInterface({
|
||||
}
|
||||
}, [])
|
||||
|
||||
// Speech recognition setup
|
||||
const setupSpeechRecognition = useCallback(() => {
|
||||
if (!isSupported) return
|
||||
|
||||
@@ -259,14 +246,11 @@ export function VoiceInterface({
|
||||
}
|
||||
}
|
||||
|
||||
// Update live transcript
|
||||
setCurrentTranscript(interimTranscript || finalTranscript)
|
||||
|
||||
// Send final transcript (but keep listening state until agent responds)
|
||||
if (finalTranscript.trim()) {
|
||||
setCurrentTranscript('') // Clear transcript
|
||||
setCurrentTranscript('')
|
||||
|
||||
// Stop recognition to avoid interference while waiting for response
|
||||
if (recognitionRef.current) {
|
||||
try {
|
||||
recognitionRef.current.stop()
|
||||
@@ -275,7 +259,6 @@ export function VoiceInterface({
|
||||
}
|
||||
}
|
||||
|
||||
// Start timeout in case agent doesn't provide audio response
|
||||
setResponseTimeout()
|
||||
|
||||
onVoiceTranscript?.(finalTranscript)
|
||||
@@ -283,13 +266,14 @@ export function VoiceInterface({
|
||||
}
|
||||
|
||||
recognition.onend = () => {
|
||||
if (isCallEndedRef.current) return
|
||||
|
||||
const currentState = currentStateRef.current
|
||||
|
||||
// Only restart recognition if we're in listening state and not muted
|
||||
if (currentState === 'listening' && !isMutedRef.current) {
|
||||
// Add a delay to avoid immediate restart after sending transcript
|
||||
setTimeout(() => {
|
||||
// Double-check state hasn't changed during delay
|
||||
if (isCallEndedRef.current) return
|
||||
|
||||
if (
|
||||
recognitionRef.current &&
|
||||
currentStateRef.current === 'listening' &&
|
||||
@@ -301,14 +285,12 @@ export function VoiceInterface({
|
||||
logger.debug('Error restarting speech recognition:', error)
|
||||
}
|
||||
}
|
||||
}, 1000) // Longer delay to give agent time to respond
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
|
||||
recognition.onerror = (event: SpeechRecognitionErrorEvent) => {
|
||||
// Filter out "aborted" errors - these are expected when we intentionally stop recognition
|
||||
if (event.error === 'aborted') {
|
||||
// Ignore
|
||||
return
|
||||
}
|
||||
|
||||
@@ -320,7 +302,6 @@ export function VoiceInterface({
|
||||
recognitionRef.current = recognition
|
||||
}, [isSupported, onVoiceTranscript, setResponseTimeout])
|
||||
|
||||
// Start/stop listening
|
||||
const startListening = useCallback(() => {
|
||||
if (!isInitialized || isMuted || state !== 'idle') {
|
||||
return
|
||||
@@ -351,17 +332,12 @@ export function VoiceInterface({
|
||||
}
|
||||
}, [])
|
||||
|
||||
// Handle interrupt
|
||||
const handleInterrupt = useCallback(() => {
|
||||
if (state === 'agent_speaking') {
|
||||
// Clear any subtitle timeouts and text
|
||||
// (No longer needed after removing subtitle system)
|
||||
|
||||
onInterrupt?.()
|
||||
setState('listening')
|
||||
setCurrentTranscript('')
|
||||
|
||||
// Unmute microphone for user input
|
||||
setIsMuted(false)
|
||||
if (mediaStreamRef.current) {
|
||||
mediaStreamRef.current.getAudioTracks().forEach((track) => {
|
||||
@@ -369,7 +345,6 @@ export function VoiceInterface({
|
||||
})
|
||||
}
|
||||
|
||||
// Start listening immediately
|
||||
if (recognitionRef.current) {
|
||||
try {
|
||||
recognitionRef.current.start()
|
||||
@@ -380,14 +355,13 @@ export function VoiceInterface({
|
||||
}
|
||||
}, [state, onInterrupt])
|
||||
|
||||
// Handle call end with proper cleanup
|
||||
const handleCallEnd = useCallback(() => {
|
||||
// Stop everything immediately
|
||||
isCallEndedRef.current = true
|
||||
|
||||
setState('idle')
|
||||
setCurrentTranscript('')
|
||||
setIsMuted(false)
|
||||
|
||||
// Stop speech recognition
|
||||
if (recognitionRef.current) {
|
||||
try {
|
||||
recognitionRef.current.abort()
|
||||
@@ -396,17 +370,11 @@ export function VoiceInterface({
|
||||
}
|
||||
}
|
||||
|
||||
// Clear timeouts
|
||||
clearResponseTimeout()
|
||||
|
||||
// Stop audio playback and streaming immediately
|
||||
onInterrupt?.()
|
||||
|
||||
// Call the original onCallEnd
|
||||
onCallEnd?.()
|
||||
}, [onCallEnd, onInterrupt, clearResponseTimeout])
|
||||
|
||||
// Keyboard handler
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.code === 'Space') {
|
||||
@@ -419,7 +387,6 @@ export function VoiceInterface({
|
||||
return () => document.removeEventListener('keydown', handleKeyDown)
|
||||
}, [handleInterrupt])
|
||||
|
||||
// Mute toggle
|
||||
const toggleMute = useCallback(() => {
|
||||
if (state === 'agent_speaking') {
|
||||
handleInterrupt()
|
||||
@@ -442,7 +409,6 @@ export function VoiceInterface({
|
||||
}
|
||||
}, [isMuted, state, handleInterrupt, stopListening, startListening])
|
||||
|
||||
// Initialize
|
||||
useEffect(() => {
|
||||
if (isSupported) {
|
||||
setupSpeechRecognition()
|
||||
@@ -450,47 +416,40 @@ export function VoiceInterface({
|
||||
}
|
||||
}, [isSupported, setupSpeechRecognition, setupAudio])
|
||||
|
||||
// Auto-start listening when ready
|
||||
useEffect(() => {
|
||||
if (isInitialized && !isMuted && state === 'idle') {
|
||||
startListening()
|
||||
}
|
||||
}, [isInitialized, isMuted, state, startListening])
|
||||
|
||||
// Cleanup when call ends or component unmounts
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
// Stop speech recognition
|
||||
isCallEndedRef.current = true
|
||||
|
||||
if (recognitionRef.current) {
|
||||
try {
|
||||
recognitionRef.current.abort()
|
||||
} catch (error) {
|
||||
} catch (_e) {
|
||||
// Ignore
|
||||
}
|
||||
recognitionRef.current = null
|
||||
}
|
||||
|
||||
// Stop media stream
|
||||
if (mediaStreamRef.current) {
|
||||
mediaStreamRef.current.getTracks().forEach((track) => {
|
||||
track.stop()
|
||||
})
|
||||
mediaStreamRef.current.getTracks().forEach((track) => track.stop())
|
||||
mediaStreamRef.current = null
|
||||
}
|
||||
|
||||
// Stop audio context
|
||||
if (audioContextRef.current) {
|
||||
audioContextRef.current.close()
|
||||
audioContextRef.current = null
|
||||
}
|
||||
|
||||
// Cancel animation frame
|
||||
if (animationFrameRef.current) {
|
||||
cancelAnimationFrame(animationFrameRef.current)
|
||||
animationFrameRef.current = null
|
||||
}
|
||||
|
||||
// Clear timeouts
|
||||
if (responseTimeoutRef.current) {
|
||||
clearTimeout(responseTimeoutRef.current)
|
||||
responseTimeoutRef.current = null
|
||||
@@ -498,7 +457,6 @@ export function VoiceInterface({
|
||||
}
|
||||
}, [])
|
||||
|
||||
// Get status text
|
||||
const getStatusText = () => {
|
||||
switch (state) {
|
||||
case 'listening':
|
||||
@@ -510,7 +468,6 @@ export function VoiceInterface({
|
||||
}
|
||||
}
|
||||
|
||||
// Get button content
|
||||
const getButtonContent = () => {
|
||||
if (state === 'agent_speaking') {
|
||||
return (
|
||||
@@ -524,9 +481,7 @@ export function VoiceInterface({
|
||||
|
||||
return (
|
||||
<div className={cn('fixed inset-0 z-[100] flex flex-col bg-white text-gray-900', className)}>
|
||||
{/* Main content */}
|
||||
<div className='flex flex-1 flex-col items-center justify-center px-8'>
|
||||
{/* Voice visualization */}
|
||||
<div className='relative mb-16'>
|
||||
<ParticlesVisualization
|
||||
audioLevels={audioLevels}
|
||||
@@ -538,7 +493,6 @@ export function VoiceInterface({
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Live transcript - subtitle style */}
|
||||
<div className='mb-16 flex h-24 items-center justify-center'>
|
||||
{currentTranscript && (
|
||||
<div className='max-w-2xl px-8'>
|
||||
@@ -549,17 +503,14 @@ export function VoiceInterface({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Status */}
|
||||
<p className='mb-8 text-center text-gray-600 text-lg'>
|
||||
{getStatusText()}
|
||||
{isMuted && <span className='ml-2 text-gray-400 text-sm'>(Muted)</span>}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Controls */}
|
||||
<div className='px-8 pb-12'>
|
||||
<div className='flex items-center justify-center space-x-12'>
|
||||
{/* End call */}
|
||||
<Button
|
||||
onClick={handleCallEnd}
|
||||
variant='outline'
|
||||
@@ -569,7 +520,6 @@ export function VoiceInterface({
|
||||
<Phone className='h-6 w-6 rotate-[135deg]' />
|
||||
</Button>
|
||||
|
||||
{/* Mic/Stop button */}
|
||||
<Button
|
||||
onClick={toggleMute}
|
||||
variant='outline'
|
||||
|
||||
@@ -14,6 +14,7 @@ declare global {
|
||||
interface AudioStreamingOptions {
|
||||
voiceId: string
|
||||
modelId?: string
|
||||
chatId?: string
|
||||
onAudioStart?: () => void
|
||||
onAudioEnd?: () => void
|
||||
onError?: (error: Error) => void
|
||||
@@ -76,7 +77,14 @@ export function useAudioStreaming(sharedAudioContextRef?: RefObject<AudioContext
|
||||
}
|
||||
|
||||
const { text, options } = item
|
||||
const { voiceId, modelId = 'eleven_turbo_v2_5', onAudioStart, onAudioEnd, onError } = options
|
||||
const {
|
||||
voiceId,
|
||||
modelId = 'eleven_turbo_v2_5',
|
||||
chatId,
|
||||
onAudioStart,
|
||||
onAudioEnd,
|
||||
onError,
|
||||
} = options
|
||||
|
||||
try {
|
||||
const audioContext = getAudioContext()
|
||||
@@ -93,6 +101,7 @@ export function useAudioStreaming(sharedAudioContextRef?: RefObject<AudioContext
|
||||
text,
|
||||
voiceId,
|
||||
modelId,
|
||||
chatId,
|
||||
}),
|
||||
signal: abortControllerRef.current?.signal,
|
||||
})
|
||||
|
||||
@@ -47,12 +47,16 @@ export function FileSelectorInput({
|
||||
const [projectIdValueFromStore] = useSubBlockValue(blockId, 'projectId')
|
||||
const [planIdValueFromStore] = useSubBlockValue(blockId, 'planId')
|
||||
const [teamIdValueFromStore] = useSubBlockValue(blockId, 'teamId')
|
||||
const [siteIdValueFromStore] = useSubBlockValue(blockId, 'siteId')
|
||||
const [collectionIdValueFromStore] = useSubBlockValue(blockId, 'collectionId')
|
||||
|
||||
const connectedCredential = previewContextValues?.credential ?? connectedCredentialFromStore
|
||||
const domainValue = previewContextValues?.domain ?? domainValueFromStore
|
||||
const projectIdValue = previewContextValues?.projectId ?? projectIdValueFromStore
|
||||
const planIdValue = previewContextValues?.planId ?? planIdValueFromStore
|
||||
const teamIdValue = previewContextValues?.teamId ?? teamIdValueFromStore
|
||||
const siteIdValue = previewContextValues?.siteId ?? siteIdValueFromStore
|
||||
const collectionIdValue = previewContextValues?.collectionId ?? collectionIdValueFromStore
|
||||
|
||||
const normalizedCredentialId =
|
||||
typeof connectedCredential === 'string'
|
||||
@@ -75,6 +79,8 @@ export function FileSelectorInput({
|
||||
projectId: (projectIdValue as string) || undefined,
|
||||
planId: (planIdValue as string) || undefined,
|
||||
teamId: (teamIdValue as string) || undefined,
|
||||
siteId: (siteIdValue as string) || undefined,
|
||||
collectionId: (collectionIdValue as string) || undefined,
|
||||
})
|
||||
}, [
|
||||
subBlock,
|
||||
@@ -84,6 +90,8 @@ export function FileSelectorInput({
|
||||
projectIdValue,
|
||||
planIdValue,
|
||||
teamIdValue,
|
||||
siteIdValue,
|
||||
collectionIdValue,
|
||||
])
|
||||
|
||||
const missingCredential = !normalizedCredentialId
|
||||
@@ -97,6 +105,10 @@ export function FileSelectorInput({
|
||||
!selectorResolution.context.projectId
|
||||
const missingPlan =
|
||||
selectorResolution?.key === 'microsoft.planner' && !selectorResolution.context.planId
|
||||
const missingSite =
|
||||
selectorResolution?.key === 'webflow.collections' && !selectorResolution.context.siteId
|
||||
const missingCollection =
|
||||
selectorResolution?.key === 'webflow.items' && !selectorResolution.context.collectionId
|
||||
|
||||
const disabledReason =
|
||||
finalDisabled ||
|
||||
@@ -105,6 +117,8 @@ export function FileSelectorInput({
|
||||
missingDomain ||
|
||||
missingProject ||
|
||||
missingPlan ||
|
||||
missingSite ||
|
||||
missingCollection ||
|
||||
!selectorResolution?.key
|
||||
|
||||
if (!selectorResolution?.key) {
|
||||
|
||||
@@ -43,14 +43,12 @@ export function ProjectSelectorInput({
|
||||
|
||||
// Use previewContextValues if provided (for tools inside agent blocks), otherwise use store values
|
||||
const connectedCredential = previewContextValues?.credential ?? connectedCredentialFromStore
|
||||
const linearCredential = previewContextValues?.credential ?? connectedCredentialFromStore
|
||||
const linearTeamId = previewContextValues?.teamId ?? linearTeamIdFromStore
|
||||
const jiraDomain = previewContextValues?.domain ?? jiraDomainFromStore
|
||||
|
||||
// Derive provider from serviceId using OAuth config
|
||||
const serviceId = subBlock.serviceId || ''
|
||||
const effectiveProviderId = useMemo(() => getProviderIdFromServiceId(serviceId), [serviceId])
|
||||
const isLinear = serviceId === 'linear'
|
||||
|
||||
const { isForeignCredential } = useForeignCredential(
|
||||
effectiveProviderId,
|
||||
@@ -65,7 +63,6 @@ export function ProjectSelectorInput({
|
||||
})
|
||||
|
||||
// Jira/Discord upstream fields - use values from previewContextValues or store
|
||||
const jiraCredential = connectedCredential
|
||||
const domain = (jiraDomain as string) || ''
|
||||
|
||||
// Verify Jira credential belongs to current user; if not, treat as absent
|
||||
@@ -84,19 +81,11 @@ export function ProjectSelectorInput({
|
||||
const selectorResolution = useMemo(() => {
|
||||
return resolveSelectorForSubBlock(subBlock, {
|
||||
workflowId: workflowIdFromUrl || undefined,
|
||||
credentialId: (isLinear ? linearCredential : jiraCredential) as string | undefined,
|
||||
credentialId: (connectedCredential as string) || undefined,
|
||||
domain,
|
||||
teamId: (linearTeamId as string) || undefined,
|
||||
})
|
||||
}, [
|
||||
subBlock,
|
||||
workflowIdFromUrl,
|
||||
isLinear,
|
||||
linearCredential,
|
||||
jiraCredential,
|
||||
domain,
|
||||
linearTeamId,
|
||||
])
|
||||
}, [subBlock, workflowIdFromUrl, connectedCredential, domain, linearTeamId])
|
||||
|
||||
const missingCredential = !selectorResolution?.context.credentialId
|
||||
|
||||
|
||||
@@ -14,7 +14,10 @@ import {
|
||||
usePopoverContext,
|
||||
} from '@/components/emcn'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { extractFieldsFromSchema, findActiveOutputSchema } from '@/lib/core/utils/response-format'
|
||||
import {
|
||||
extractFieldsFromSchema,
|
||||
parseResponseFormatSafely,
|
||||
} from '@/lib/core/utils/response-format'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { getBlockOutputPaths, getBlockOutputType } from '@/lib/workflows/blocks/block-outputs'
|
||||
import { TRIGGER_TYPES } from '@/lib/workflows/triggers/triggers'
|
||||
@@ -542,9 +545,8 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
|
||||
const normalizedBlockName = normalizeBlockName(blockName)
|
||||
|
||||
const mergedSubBlocks = getMergedSubBlocks(activeSourceBlockId)
|
||||
|
||||
// Use generalized schema detection to find active output schema
|
||||
const activeOutputSchema = findActiveOutputSchema(blockConfig, mergedSubBlocks)
|
||||
const responseFormatValue = mergedSubBlocks?.responseFormat?.value
|
||||
const responseFormat = parseResponseFormatSafely(responseFormatValue, activeSourceBlockId)
|
||||
|
||||
let blockTags: string[]
|
||||
|
||||
@@ -574,8 +576,8 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
|
||||
} else {
|
||||
blockTags = [normalizedBlockName]
|
||||
}
|
||||
} else if (activeOutputSchema) {
|
||||
const schemaFields = extractFieldsFromSchema(activeOutputSchema.schema)
|
||||
} else if (responseFormat) {
|
||||
const schemaFields = extractFieldsFromSchema(responseFormat)
|
||||
if (schemaFields.length > 0) {
|
||||
blockTags = schemaFields.map((field) => `${normalizedBlockName}.${field.name}`)
|
||||
} else {
|
||||
@@ -881,8 +883,8 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
|
||||
const normalizedBlockName = normalizeBlockName(blockName)
|
||||
|
||||
const mergedSubBlocks = getMergedSubBlocks(accessibleBlockId)
|
||||
|
||||
const activeOutputSchema = findActiveOutputSchema(blockConfig, mergedSubBlocks)
|
||||
const responseFormatValue = mergedSubBlocks?.responseFormat?.value
|
||||
const responseFormat = parseResponseFormatSafely(responseFormatValue, accessibleBlockId)
|
||||
|
||||
let blockTags: string[]
|
||||
|
||||
@@ -933,8 +935,8 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
|
||||
} else {
|
||||
blockTags = [normalizedBlockName]
|
||||
}
|
||||
} else if (activeOutputSchema) {
|
||||
const schemaFields = extractFieldsFromSchema(activeOutputSchema.schema)
|
||||
} else if (responseFormat) {
|
||||
const schemaFields = extractFieldsFromSchema(responseFormat)
|
||||
if (schemaFields.length > 0) {
|
||||
blockTags = schemaFields.map((field) => `${normalizedBlockName}.${field.name}`)
|
||||
} else {
|
||||
|
||||
@@ -47,12 +47,16 @@ export function FileSelectorInput({
|
||||
const [projectIdValueFromStore] = useSubBlockValue(blockId, 'projectId')
|
||||
const [planIdValueFromStore] = useSubBlockValue(blockId, 'planId')
|
||||
const [teamIdValueFromStore] = useSubBlockValue(blockId, 'teamId')
|
||||
const [siteIdValueFromStore] = useSubBlockValue(blockId, 'siteId')
|
||||
const [collectionIdValueFromStore] = useSubBlockValue(blockId, 'collectionId')
|
||||
|
||||
const connectedCredential = previewContextValues?.credential ?? connectedCredentialFromStore
|
||||
const domainValue = previewContextValues?.domain ?? domainValueFromStore
|
||||
const projectIdValue = previewContextValues?.projectId ?? projectIdValueFromStore
|
||||
const planIdValue = previewContextValues?.planId ?? planIdValueFromStore
|
||||
const teamIdValue = previewContextValues?.teamId ?? teamIdValueFromStore
|
||||
const siteIdValue = previewContextValues?.siteId ?? siteIdValueFromStore
|
||||
const collectionIdValue = previewContextValues?.collectionId ?? collectionIdValueFromStore
|
||||
|
||||
const normalizedCredentialId =
|
||||
typeof connectedCredential === 'string'
|
||||
@@ -75,6 +79,8 @@ export function FileSelectorInput({
|
||||
projectId: (projectIdValue as string) || undefined,
|
||||
planId: (planIdValue as string) || undefined,
|
||||
teamId: (teamIdValue as string) || undefined,
|
||||
siteId: (siteIdValue as string) || undefined,
|
||||
collectionId: (collectionIdValue as string) || undefined,
|
||||
})
|
||||
}, [
|
||||
subBlock,
|
||||
@@ -84,6 +90,8 @@ export function FileSelectorInput({
|
||||
projectIdValue,
|
||||
planIdValue,
|
||||
teamIdValue,
|
||||
siteIdValue,
|
||||
collectionIdValue,
|
||||
])
|
||||
|
||||
const missingCredential = !normalizedCredentialId
|
||||
@@ -97,6 +105,10 @@ export function FileSelectorInput({
|
||||
!selectorResolution?.context.projectId
|
||||
const missingPlan =
|
||||
selectorResolution?.key === 'microsoft.planner' && !selectorResolution?.context.planId
|
||||
const missingSite =
|
||||
selectorResolution?.key === 'webflow.collections' && !selectorResolution?.context.siteId
|
||||
const missingCollection =
|
||||
selectorResolution?.key === 'webflow.items' && !selectorResolution?.context.collectionId
|
||||
|
||||
const disabledReason =
|
||||
finalDisabled ||
|
||||
@@ -105,6 +117,8 @@ export function FileSelectorInput({
|
||||
missingDomain ||
|
||||
missingProject ||
|
||||
missingPlan ||
|
||||
missingSite ||
|
||||
missingCollection ||
|
||||
!selectorResolution?.key
|
||||
|
||||
if (!selectorResolution?.key) {
|
||||
|
||||
@@ -579,8 +579,10 @@ const WorkflowContent = React.memo(() => {
|
||||
const node = nodeIndex.get(id)
|
||||
if (!node) return false
|
||||
|
||||
// If dropping outside containers, ignore blocks that are inside a container
|
||||
if (!containerAtPoint && blocks[id]?.data?.parentId) return false
|
||||
const blockParentId = blocks[id]?.data?.parentId
|
||||
const dropParentId = containerAtPoint?.loopId
|
||||
if (dropParentId !== blockParentId) return false
|
||||
|
||||
return true
|
||||
})
|
||||
.map(([id, block]) => {
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
} from '@/components/emcn'
|
||||
import { Input, Skeleton } from '@/components/ui'
|
||||
import { signOut, useSession } from '@/lib/auth/auth-client'
|
||||
import { ANONYMOUS_USER_ID } from '@/lib/auth/constants'
|
||||
import { useBrandConfig } from '@/lib/branding/branding'
|
||||
import { getEnv, isTruthy } from '@/lib/core/config/env'
|
||||
import { getBaseUrl } from '@/lib/core/utils/urls'
|
||||
@@ -59,6 +60,7 @@ export function General({ onOpenChange }: GeneralProps) {
|
||||
const isLoading = isProfileLoading || isSettingsLoading
|
||||
|
||||
const isTrainingEnabled = isTruthy(getEnv('NEXT_PUBLIC_COPILOT_TRAINING_ENABLED'))
|
||||
const isAuthDisabled = session?.user?.id === ANONYMOUS_USER_ID
|
||||
|
||||
const [isSuperUser, setIsSuperUser] = useState(false)
|
||||
const [loadingSuperUser, setLoadingSuperUser] = useState(true)
|
||||
@@ -461,10 +463,12 @@ export function General({ onOpenChange }: GeneralProps) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className='mt-auto flex items-center gap-[8px]'>
|
||||
<Button onClick={handleSignOut}>Sign out</Button>
|
||||
<Button onClick={() => setShowResetPasswordModal(true)}>Reset password</Button>
|
||||
</div>
|
||||
{!isAuthDisabled && (
|
||||
<div className='mt-auto flex items-center gap-[8px]'>
|
||||
<Button onClick={handleSignOut}>Sign out</Button>
|
||||
<Button onClick={() => setShowResetPasswordModal(true)}>Reset password</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Password Reset Confirmation Modal */}
|
||||
<Modal open={showResetPasswordModal} onOpenChange={setShowResetPasswordModal}>
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Button, Combobox, Input, Switch, Textarea } from '@/components/emcn'
|
||||
import { Skeleton } from '@/components/ui'
|
||||
import { useSession } from '@/lib/auth/auth-client'
|
||||
import { getSubscriptionStatus } from '@/lib/billing/client/utils'
|
||||
import { isBillingEnabled } from '@/lib/core/config/environment'
|
||||
import { isBillingEnabled } from '@/lib/core/config/feature-flags'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { getBaseUrl } from '@/lib/core/utils/urls'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
|
||||
@@ -26,7 +26,7 @@ import { McpIcon } from '@/components/icons'
|
||||
import { useSession } from '@/lib/auth/auth-client'
|
||||
import { getSubscriptionStatus } from '@/lib/billing/client'
|
||||
import { getEnv, isTruthy } from '@/lib/core/config/env'
|
||||
import { isHosted } from '@/lib/core/config/environment'
|
||||
import { isHosted } from '@/lib/core/config/feature-flags'
|
||||
import { getUserRole } from '@/lib/workspaces/organization'
|
||||
import {
|
||||
ApiKeys,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AgentIcon } from '@/components/icons'
|
||||
import { isHosted } from '@/lib/core/config/environment'
|
||||
import { isHosted } from '@/lib/core/config/feature-flags'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
@@ -290,9 +290,6 @@ The JSON object MUST have the following top-level properties: 'name' (string), '
|
||||
The 'schema' object must define the structure and MUST contain 'type': 'object', 'properties': {...}, 'additionalProperties': false, and 'required': [...].
|
||||
Inside 'properties', use standard JSON Schema properties (type, description, enum, items for arrays, etc.).
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax for dynamic schema names or descriptions.
|
||||
|
||||
Current schema: {context}
|
||||
|
||||
Do not include any explanations, markdown formatting, or other text outside the JSON object.
|
||||
|
||||
@@ -87,52 +87,6 @@ export const AirtableBlock: BlockConfig<AirtableResponse> = {
|
||||
placeholder: 'For Create: `[{ "fields": { ... } }]`\n',
|
||||
condition: { field: 'operation', value: ['create', 'updateMultiple'] },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Airtable API developer. Generate Airtable records JSON array for create or bulk update operations.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.name>\`, \`<function1.result.email>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_STATUS}}\`)
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array of record objects. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### RECORD STRUCTURE
|
||||
For creating records:
|
||||
- Each record has a **fields** object containing field name/value pairs
|
||||
- Field names must match exactly with your Airtable base
|
||||
|
||||
For updating multiple records:
|
||||
- Each record needs an **id** field with the record ID
|
||||
- Plus a **fields** object with fields to update
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Create single record**: "Add a new contact John Doe with email"
|
||||
→ [{"fields": {"Name": "John Doe", "Email": "john@example.com"}}]
|
||||
|
||||
**With variables**: "Create record from previous block data"
|
||||
→ [{"fields": {"Name": <agent1.name>, "Email": <agent1.email>, "Status": "{{DEFAULT_STATUS}}"}}]
|
||||
|
||||
**Bulk update**: "Update status for two records"
|
||||
→ [
|
||||
{"id": "recABC123", "fields": {"Status": "Complete"}},
|
||||
{"id": "recDEF456", "fields": {"Status": "In Progress"}}
|
||||
]
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY a valid JSON array - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the records you want to create or update...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'fields',
|
||||
@@ -141,49 +95,6 @@ Return ONLY a valid JSON array - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Fields to update: `{ "Field Name": "New Value" }`',
|
||||
condition: { field: 'operation', value: 'update' },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Airtable API developer. Generate Airtable fields JSON object for updating a single record.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.new_status>\`, \`<function1.result.price>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_CATEGORY}}\`)
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON object with field name/value pairs. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
|
||||
|
||||
### FIELD VALUES
|
||||
- **Text fields**: String values
|
||||
- **Number fields**: Numeric values
|
||||
- **Checkbox**: true or false
|
||||
- **Single select**: String matching option name
|
||||
- **Multi-select**: Array of option names
|
||||
- **Linked records**: Array of record IDs
|
||||
- **Date**: ISO date string
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Update text fields**: "Update name and email"
|
||||
→ {"Name": "Jane Doe", "Email": "jane@example.com"}
|
||||
|
||||
**With variables**: "Update fields from previous block"
|
||||
→ {"Status": <agent1.new_status>, "Updated By": "{{SYSTEM_USER}}"}
|
||||
|
||||
**Update status**: "Mark as completed"
|
||||
→ {"Status": "Completed", "Completed Date": "2024-01-15"}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the fields you want to update...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
...getTrigger('airtable_webhook').subBlocks,
|
||||
],
|
||||
|
||||
@@ -58,35 +58,24 @@ export const ApiBlock: BlockConfig<RequestResponse> = {
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert JSON programmer. Generate ONLY the raw JSON object based on the user's request.
|
||||
prompt: `You are an expert JSON programmer.
|
||||
Generate ONLY the raw JSON object based on the user's request.
|
||||
The output MUST be a single, valid JSON object, starting with { and ending with }.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
Current body: {context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.response>\`, \`<function1.result.data>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{API_KEY}}\`, \`{{BASE_URL}}\`)
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY valid JSON. The output MUST be a single, valid JSON object, starting with { and ending with }.
|
||||
Do not include any explanations, markdown formatting, or other text outside the JSON object.
|
||||
|
||||
### EXAMPLES
|
||||
You have access to the following variables you can use to generate the JSON body:
|
||||
- 'params' (object): Contains input parameters derived from the JSON schema. Access these directly using the parameter name wrapped in angle brackets, e.g., '<paramName>'. Do NOT use 'params.paramName'.
|
||||
- 'environmentVariables' (object): Contains environment variables. Reference these using the double curly brace syntax: '{{ENV_VAR_NAME}}'. Do NOT use 'environmentVariables.VAR_NAME' or env.
|
||||
|
||||
**Simple request body**: "Send user data"
|
||||
→ {"name": "John Doe", "email": "john@example.com", "active": true}
|
||||
|
||||
**With variables**: "Send data from previous agent"
|
||||
→ {"name": <agent1.name>, "email": <agent1.email>, "apiKey": "{{API_KEY}}"}
|
||||
|
||||
**Nested data**: "Send order with items"
|
||||
→ {"orderId": <function1.order_id>, "items": <agent1.cart_items>, "total": <function1.total>}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON - no explanations, no markdown.`,
|
||||
Example:
|
||||
{
|
||||
"name": "<block.agent.response.content>",
|
||||
"age": <block.function.output.age>,
|
||||
"success": true
|
||||
}`,
|
||||
placeholder: 'Describe the API request body you need...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
|
||||
@@ -46,62 +46,6 @@ export const ApifyBlock: BlockConfig<RunActorResult> = {
|
||||
language: 'json',
|
||||
placeholder: '{\n "startUrl": "https://example.com",\n "maxPages": 10\n}',
|
||||
required: false,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Apify developer. Generate JSON input for Apify actors based on the user's request.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.url>\`, \`<function1.result.query>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{APIFY_PROXY_PASSWORD}}\`)
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY valid JSON input for the Apify actor. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
|
||||
|
||||
### COMMON APIFY ACTOR INPUT PATTERNS
|
||||
|
||||
**Web Scraping Actors**:
|
||||
- startUrls: Array of URLs to start scraping from
|
||||
- maxRequestsPerCrawl: Maximum number of pages to scrape
|
||||
- proxyConfiguration: Proxy settings
|
||||
|
||||
**E-commerce Scrapers**:
|
||||
- searchTerms: Array of search queries
|
||||
- maxItems: Maximum products to return
|
||||
- categoryUrls: URLs of categories to scrape
|
||||
|
||||
**Social Media Scrapers**:
|
||||
- handles: Array of usernames/handles
|
||||
- resultsLimit: Maximum posts/items to return
|
||||
- startDate/endDate: Date range filters
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Web scraper**: "Scrape product pages from example.com, max 50 pages"
|
||||
→ {
|
||||
"startUrls": [{"url": "https://example.com/products"}],
|
||||
"maxRequestsPerCrawl": 50,
|
||||
"proxyConfiguration": {"useApifyProxy": true}
|
||||
}
|
||||
|
||||
**With variables**: "Scrape URL from previous block"
|
||||
→ {
|
||||
"startUrls": [{"url": <agent1.target_url>}],
|
||||
"maxRequestsPerCrawl": <function1.max_pages>,
|
||||
"proxyConfiguration": {"useApifyProxy": true}
|
||||
}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the input for the Apify actor...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'timeout',
|
||||
|
||||
@@ -64,30 +64,6 @@ export const ApolloBlock: BlockConfig<ApolloResponse> = {
|
||||
type: 'code',
|
||||
placeholder: '["CEO", "VP of Sales"]',
|
||||
condition: { field: 'operation', value: 'people_search' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `Generate a JSON array of job titles for Apollo people search.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.job_title>\`).
|
||||
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### EXAMPLES
|
||||
["CEO", "VP of Sales"]
|
||||
["Software Engineer", "Senior Developer"]
|
||||
[<agent1.target_titles>]
|
||||
|
||||
Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: 'Describe the job titles to search for...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'person_locations',
|
||||
@@ -95,30 +71,6 @@ Return ONLY valid JSON array - no explanations.`,
|
||||
type: 'code',
|
||||
placeholder: '["San Francisco, CA", "New York, NY"]',
|
||||
condition: { field: 'operation', value: 'people_search' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `Generate a JSON array of locations for Apollo people search. Use "City, State" or "City, Country" format.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.location>\`).
|
||||
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### EXAMPLES
|
||||
["San Francisco, CA", "New York, NY"]
|
||||
["London, UK", "Paris, France"]
|
||||
[<agent1.target_location>]
|
||||
|
||||
Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: 'Describe the locations to search for...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'organization_names',
|
||||
@@ -126,30 +78,6 @@ Return ONLY valid JSON array - no explanations.`,
|
||||
type: 'code',
|
||||
placeholder: '["Company A", "Company B"]',
|
||||
condition: { field: 'operation', value: 'people_search' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `Generate a JSON array of company names for Apollo people search.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.company>\`).
|
||||
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### EXAMPLES
|
||||
["Acme Corp", "Tech Solutions Inc"]
|
||||
["Google", "Microsoft", "Apple"]
|
||||
[<agent1.target_companies>]
|
||||
|
||||
Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: 'Describe the companies to search for...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'person_seniorities',
|
||||
@@ -157,30 +85,6 @@ Return ONLY valid JSON array - no explanations.`,
|
||||
type: 'code',
|
||||
placeholder: '["senior", "manager", "director"]',
|
||||
condition: { field: 'operation', value: 'people_search' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `Generate a JSON array of seniority levels for Apollo people search. Common values: "senior", "manager", "director", "vp", "c_level", "owner", "founder".
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.seniority>\`).
|
||||
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### EXAMPLES
|
||||
["senior", "manager", "director"]
|
||||
["vp", "c_level"]
|
||||
["founder", "owner"]
|
||||
|
||||
Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: 'Describe the seniority levels to search for...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'contact_stage_ids',
|
||||
@@ -188,28 +92,6 @@ Return ONLY valid JSON array - no explanations.`,
|
||||
type: 'code',
|
||||
placeholder: '["stage_id_1", "stage_id_2"]',
|
||||
condition: { field: 'operation', value: 'contact_search' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `Generate a JSON array of Apollo contact stage IDs for filtering contacts by their pipeline stage.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.stage_id>\`).
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array of stage ID strings. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### EXAMPLES
|
||||
["stage_id_1", "stage_id_2"]
|
||||
[<agent1.selected_stage>]
|
||||
|
||||
Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: 'Enter or describe the contact stage IDs...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
|
||||
// People Enrich Fields
|
||||
@@ -298,29 +180,6 @@ Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: '[{"first_name": "John", "last_name": "Doe", "email": "john@example.com"}]',
|
||||
condition: { field: 'operation', value: 'people_bulk_enrich' },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `Generate a JSON array of people objects for Apollo bulk enrich. Each object should have first_name, last_name, and optionally email, organization_name, or domain.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.first_name>\`).
|
||||
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### EXAMPLES
|
||||
[{"first_name": "John", "last_name": "Doe", "email": "john@example.com"}]
|
||||
[{"first_name": <agent1.first_name>, "last_name": <agent1.last_name>, "email": <agent1.email>}]
|
||||
|
||||
Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: 'Describe the people you want to enrich...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'organizations',
|
||||
@@ -329,29 +188,6 @@ Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: '[{"organization_name": "Company A", "domain": "companya.com"}]',
|
||||
condition: { field: 'operation', value: 'organization_bulk_enrich' },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `Generate a JSON array of organization objects for Apollo bulk enrich. Each object should have organization_name and/or domain.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.company_name>\`).
|
||||
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### EXAMPLES
|
||||
[{"organization_name": "Acme Corp", "domain": "acme.com"}]
|
||||
[{"organization_name": <agent1.company>, "domain": <agent1.domain>}]
|
||||
|
||||
Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: 'Describe the organizations you want to enrich...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
|
||||
// Organization Search Fields
|
||||
@@ -361,30 +197,6 @@ Return ONLY valid JSON array - no explanations.`,
|
||||
type: 'code',
|
||||
placeholder: '["San Francisco, CA"]',
|
||||
condition: { field: 'operation', value: 'organization_search' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `Generate a JSON array of location strings for Apollo organization search.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.location>\`).
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array of location strings. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### EXAMPLES
|
||||
["San Francisco, CA"]
|
||||
["New York, NY", "Los Angeles, CA", "Chicago, IL"]
|
||||
["United States", "Canada"]
|
||||
[<agent1.target_location>]
|
||||
|
||||
Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: 'Describe the locations to search for...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'organization_num_employees_ranges',
|
||||
@@ -392,40 +204,6 @@ Return ONLY valid JSON array - no explanations.`,
|
||||
type: 'code',
|
||||
placeholder: '["1-10", "11-50", "51-200"]',
|
||||
condition: { field: 'operation', value: 'organization_search' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `Generate a JSON array of employee count range strings for Apollo organization search.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.company_size>\`).
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array of employee range strings. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### AVAILABLE RANGES
|
||||
Apollo uses these standard employee count ranges:
|
||||
- "1-10" (Micro)
|
||||
- "11-50" (Small)
|
||||
- "51-200" (Medium)
|
||||
- "201-500" (Mid-Market)
|
||||
- "501-1000" (Large)
|
||||
- "1001-5000" (Enterprise)
|
||||
- "5001-10000" (Large Enterprise)
|
||||
- "10001+" (Global Enterprise)
|
||||
|
||||
### EXAMPLES
|
||||
["1-10", "11-50"]
|
||||
["51-200", "201-500", "501-1000"]
|
||||
["1001-5000", "5001-10000", "10001+"]
|
||||
|
||||
Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: 'Describe the company sizes to search for...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'q_organization_keyword_tags',
|
||||
@@ -433,30 +211,6 @@ Return ONLY valid JSON array - no explanations.`,
|
||||
type: 'code',
|
||||
placeholder: '["saas", "b2b", "enterprise"]',
|
||||
condition: { field: 'operation', value: 'organization_search' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `Generate a JSON array of keyword/industry tag strings for Apollo organization search.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.industry>\`).
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array of keyword strings. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### EXAMPLES
|
||||
["saas", "b2b", "enterprise"]
|
||||
["fintech", "healthcare", "ai"]
|
||||
["e-commerce", "retail", "marketplace"]
|
||||
[<agent1.target_industry>]
|
||||
|
||||
Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: 'Describe the industry keywords to search for...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'q_organization_name',
|
||||
@@ -533,25 +287,6 @@ Return ONLY valid JSON array - no explanations.`,
|
||||
'[{"first_name": "John", "last_name": "Doe", "email": "john@example.com", "title": "CEO"}]',
|
||||
condition: { field: 'operation', value: 'contact_bulk_create' },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `Generate a JSON array of contact objects for Apollo bulk create. Each object must have first_name and last_name, and optionally email, title, account_id, owner_id.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### EXAMPLES
|
||||
[{"first_name": "John", "last_name": "Doe", "email": "john@example.com", "title": "CEO"}]
|
||||
[{"first_name": "Jane", "last_name": "Smith", "title": "Manager", "account_id": "123"}]
|
||||
|
||||
Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: 'Describe the contacts you want to create...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'contacts',
|
||||
@@ -560,25 +295,6 @@ Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: '[{"id": "contact_id_1", "first_name": "John", "last_name": "Doe"}]',
|
||||
condition: { field: 'operation', value: 'contact_bulk_update' },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `Generate a JSON array of contact objects for Apollo bulk update. Each object must have id, and fields to update like first_name, last_name, email, title.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### EXAMPLES
|
||||
[{"id": "contact_id_1", "first_name": "John", "last_name": "Doe"}]
|
||||
[{"id": "contact_id_2", "email": "newemail@example.com", "title": "Senior Manager"}]
|
||||
|
||||
Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: 'Describe the contacts you want to update...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'run_dedupe',
|
||||
@@ -640,28 +356,6 @@ Return ONLY valid JSON array - no explanations.`,
|
||||
type: 'code',
|
||||
placeholder: '["stage_id_1", "stage_id_2"]',
|
||||
condition: { field: 'operation', value: 'account_search' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `Generate a JSON array of Apollo account stage IDs for filtering accounts by their pipeline stage.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.stage_id>\`).
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array of stage ID strings. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### EXAMPLES
|
||||
["stage_id_1", "stage_id_2"]
|
||||
[<agent1.selected_stage>]
|
||||
|
||||
Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: 'Enter or describe the account stage IDs...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
|
||||
// Account Bulk Operations
|
||||
@@ -673,29 +367,6 @@ Return ONLY valid JSON array - no explanations.`,
|
||||
'[{"name": "Company A", "website_url": "https://companya.com", "phone": "+1234567890"}]',
|
||||
condition: { field: 'operation', value: 'account_bulk_create' },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `Generate a JSON array of account objects for Apollo bulk create. Each object must have name, and optionally website_url, phone, owner_id.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.company_name>\`).
|
||||
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### EXAMPLES
|
||||
[{"name": "Acme Corp", "website_url": "https://acme.com", "phone": "+1234567890"}]
|
||||
[{"name": <agent1.company>, "website_url": <agent1.website>}]
|
||||
|
||||
Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: 'Describe the accounts you want to create...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'accounts',
|
||||
@@ -704,29 +375,6 @@ Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: '[{"id": "account_id_1", "name": "Updated Company Name"}]',
|
||||
condition: { field: 'operation', value: 'account_bulk_update' },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `Generate a JSON array of account objects for Apollo bulk update. Each object must have id, and fields to update like name, website_url, phone.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.account_id>\`).
|
||||
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### EXAMPLES
|
||||
[{"id": "account_id_1", "name": "Updated Company Name"}]
|
||||
[{"id": <agent1.account_id>, "name": <agent1.new_name>}]
|
||||
|
||||
Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: 'Describe the accounts you want to update...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
|
||||
// Opportunity Fields
|
||||
@@ -805,28 +453,6 @@ Return ONLY valid JSON array - no explanations.`,
|
||||
type: 'code',
|
||||
placeholder: '["account_id_1", "account_id_2"]',
|
||||
condition: { field: 'operation', value: 'opportunity_search' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `Generate a JSON array of Apollo account IDs for filtering opportunities.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.account_id>\`).
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array of account ID strings. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### EXAMPLES
|
||||
["account_id_1", "account_id_2"]
|
||||
[<agent1.account_id>]
|
||||
|
||||
Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: 'Enter or describe the account IDs...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'stage_ids',
|
||||
@@ -834,28 +460,6 @@ Return ONLY valid JSON array - no explanations.`,
|
||||
type: 'code',
|
||||
placeholder: '["stage_id_1", "stage_id_2"]',
|
||||
condition: { field: 'operation', value: 'opportunity_search' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `Generate a JSON array of Apollo opportunity stage IDs for filtering opportunities.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.stage_id>\`).
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array of stage ID strings. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### EXAMPLES
|
||||
["stage_id_1", "stage_id_2"]
|
||||
[<agent1.selected_stages>]
|
||||
|
||||
Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: 'Enter or describe the stage IDs...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'owner_ids',
|
||||
@@ -863,28 +467,6 @@ Return ONLY valid JSON array - no explanations.`,
|
||||
type: 'code',
|
||||
placeholder: '["user_id_1", "user_id_2"]',
|
||||
condition: { field: 'operation', value: 'opportunity_search' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `Generate a JSON array of Apollo user/owner IDs for filtering opportunities by owner.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.owner_id>\`).
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array of user ID strings. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### EXAMPLES
|
||||
["user_id_1", "user_id_2"]
|
||||
[<agent1.assigned_owner>]
|
||||
|
||||
Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: 'Enter or describe the owner IDs...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
|
||||
// Sequence Search Fields
|
||||
@@ -918,29 +500,6 @@ Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: '["contact_id_1", "contact_id_2"]',
|
||||
condition: { field: 'operation', value: 'sequence_add' },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `Generate a JSON array of Apollo contact IDs to add to a sequence.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.contact_id>\`).
|
||||
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### EXAMPLES
|
||||
["contact_id_1", "contact_id_2"]
|
||||
[<agent1.contact_ids>]
|
||||
|
||||
Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: 'Describe the contact IDs to add...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
|
||||
// Task Fields
|
||||
|
||||
@@ -119,79 +119,12 @@ export const AsanaBlock: BlockConfig<AsanaResponse> = {
|
||||
id: 'notes',
|
||||
title: 'Task Notes',
|
||||
type: 'long-input',
|
||||
|
||||
placeholder: 'Enter task notes or description',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['create_task', 'update_task'],
|
||||
},
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert at writing Asana task descriptions. Create clear, actionable task notes.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.details>\`, \`<function1.result>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{PROJECT_NAME}}\`)
|
||||
|
||||
### ASANA FORMATTING
|
||||
Asana supports rich text:
|
||||
- **bold** with double asterisks
|
||||
- _italic_ with underscores
|
||||
- Bullet points with -
|
||||
- Numbered lists with 1.
|
||||
- Links automatically detected
|
||||
|
||||
### GUIDELINES
|
||||
1. **Clarity**: Be specific about what needs to be done
|
||||
2. **Context**: Include relevant background
|
||||
3. **Subtasks**: Break down complex tasks
|
||||
4. **Dependencies**: Note any blockers or dependencies
|
||||
5. **Resources**: Link to relevant documents or references
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Task description**: "Write notes for implementing user authentication"
|
||||
→ **Objective**
|
||||
Implement user authentication using OAuth 2.0.
|
||||
|
||||
**Requirements**
|
||||
- Support Google and GitHub OAuth providers
|
||||
- Implement token refresh mechanism
|
||||
- Add session management
|
||||
|
||||
**Acceptance Criteria**
|
||||
- Users can sign in with Google
|
||||
- Users can sign in with GitHub
|
||||
- Sessions persist across browser restarts
|
||||
- Token refresh works automatically
|
||||
|
||||
**Resources**
|
||||
- Design doc: [link]
|
||||
- API specs: [link]
|
||||
|
||||
**With variables**: "Create task from request"
|
||||
→ **Request Details**
|
||||
- Submitted by: <agent1.user_name>
|
||||
- Priority: <function1.priority>
|
||||
- Due: <function1.due_date>
|
||||
|
||||
**Description**
|
||||
<agent1.request_description>
|
||||
|
||||
**Next Steps**
|
||||
- Review requirements
|
||||
- Create implementation plan
|
||||
- Schedule kickoff meeting
|
||||
|
||||
### REMEMBER
|
||||
Be specific and actionable. Include acceptance criteria when relevant.`,
|
||||
placeholder: 'Describe the task notes...',
|
||||
generationType: 'markdown-content',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'assignee',
|
||||
|
||||
@@ -30,47 +30,6 @@ export const ClayBlock: BlockConfig<ClayPopulateResponse> = {
|
||||
JSON: Best for populating multiple columns.
|
||||
Plain Text: Best for populating a table in free-form style.
|
||||
`,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Clay developer. Generate data to populate a Clay workbook table.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.name>\`, \`<function1.result.email>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_SOURCE}}\`)
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY valid JSON or plain text data. Do not include any explanations, markdown formatting, or comments.
|
||||
|
||||
### DATA GUIDELINES
|
||||
1. **JSON Format**: Best for structured data with multiple columns
|
||||
- Use objects for single rows: {"column1": "value1", "column2": "value2"}
|
||||
- Use arrays for multiple rows: [{"col1": "val1"}, {"col1": "val2"}]
|
||||
2. **Plain Text**: Best for free-form single column data
|
||||
3. **Column Names**: Should match your Clay table column names
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Single record**: "Add a lead with name and email"
|
||||
→ {"name": "John Doe", "email": "john@example.com", "company": "Acme Inc"}
|
||||
|
||||
**Multiple records**: "Add multiple leads"
|
||||
→ [{"name": "John", "email": "john@example.com"}, {"name": "Jane", "email": "jane@example.com"}]
|
||||
|
||||
**With variables**: "Populate with data from previous block"
|
||||
→ {"name": <agent1.name>, "email": <agent1.email>, "source": "{{DEFAULT_SOURCE}}"}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY the data - no explanations.`,
|
||||
placeholder: 'Describe the data you want to populate...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'authToken',
|
||||
|
||||
@@ -116,85 +116,6 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
|
||||
type: 'long-input',
|
||||
placeholder: 'Enter content for the page',
|
||||
condition: { field: 'operation', value: ['create', 'update'] },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert at writing Confluence documentation. Create clear, professional documentation content.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.content>\`, \`<function1.result>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{PROJECT_NAME}}\`)
|
||||
|
||||
### CONFLUENCE FORMATTING
|
||||
Confluence uses wiki markup or can accept HTML:
|
||||
- h1. h2. h3. for headers
|
||||
- *bold* and _italic_
|
||||
- * bullet lists
|
||||
- # numbered lists
|
||||
- {code}code blocks{code}
|
||||
- [link text|URL]
|
||||
- {info}, {warning}, {note} macros
|
||||
|
||||
### GUIDELINES
|
||||
1. **Structure**: Use clear hierarchy with headers
|
||||
2. **Documentation Style**: Write for clarity and completeness
|
||||
3. **Tables**: Use tables for structured data
|
||||
4. **Code Examples**: Include code samples where relevant
|
||||
5. **Links**: Reference related pages and resources
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Technical documentation**: "Write API endpoint documentation"
|
||||
→ h1. User API Endpoints
|
||||
|
||||
h2. Overview
|
||||
This document describes the User API endpoints available in the system.
|
||||
|
||||
h2. Authentication
|
||||
All endpoints require a valid API key in the Authorization header.
|
||||
|
||||
h2. Endpoints
|
||||
|
||||
h3. GET /api/users
|
||||
Retrieves a list of all users.
|
||||
|
||||
*Parameters:*
|
||||
| Name | Type | Required | Description |
|
||||
| limit | integer | No | Maximum results (default: 100) |
|
||||
| offset | integer | No | Pagination offset |
|
||||
|
||||
*Response:*
|
||||
{code:json}
|
||||
{
|
||||
"users": [...],
|
||||
"total": 150
|
||||
}
|
||||
{code}
|
||||
|
||||
{info}Rate limit: 100 requests per minute{info}
|
||||
|
||||
**With variables**: "Create page from template"
|
||||
→ h1. <agent1.title>
|
||||
|
||||
h2. Overview
|
||||
<agent1.summary>
|
||||
|
||||
h2. Details
|
||||
<function1.content>
|
||||
|
||||
h2. Related Pages
|
||||
* [Related Page 1]
|
||||
* [Related Page 2]
|
||||
|
||||
### REMEMBER
|
||||
Use Confluence wiki markup. Write clear, professional documentation.`,
|
||||
placeholder: 'Describe the Confluence content...',
|
||||
generationType: 'markdown-content',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'parentId',
|
||||
|
||||
@@ -54,47 +54,6 @@ export const DatadogBlock: BlockConfig<DatadogResponse> = {
|
||||
]`,
|
||||
condition: { field: 'operation', value: 'datadog_submit_metrics' },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Datadog developer. Generate Datadog metrics series JSON array.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.metric_value>\`, \`<function1.result.tags>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DD_SERVICE}}\`, \`{{ENVIRONMENT}}\`)
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the metrics series as a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### METRICS GUIDELINES
|
||||
1. **Array Format**: Always return a JSON array, even for single metric
|
||||
2. **Metric Types**: gauge, count, rate, histogram, distribution
|
||||
3. **Points**: Array of {timestamp, value} objects (timestamp in Unix seconds)
|
||||
4. **Tags**: Array of strings in "key:value" format
|
||||
5. **Metric Names**: Use dot notation (e.g., "custom.app.response_time")
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Single gauge metric**: "Submit response time metric"
|
||||
→ [{"metric": "custom.app.response_time", "type": "gauge", "points": [{"timestamp": 1704067200, "value": 0.85}], "tags": ["env:production"]}]
|
||||
|
||||
**Multiple metrics**: "Submit CPU and memory metrics"
|
||||
→ [{"metric": "system.cpu.usage", "type": "gauge", "points": [{"timestamp": 1704067200, "value": 75.5}], "tags": ["env:production"]}, {"metric": "system.memory.used", "type": "gauge", "points": [{"timestamp": 1704067200, "value": 8192}], "tags": ["env:production"]}]
|
||||
|
||||
**With multiple points**: "Submit metric with multiple data points"
|
||||
→ [{"metric": "custom.app.requests", "type": "count", "points": [{"timestamp": 1704067200, "value": 100}, {"timestamp": 1704067300, "value": 150}], "tags": ["env:production", "service:api"]}]
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON array - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the metrics you want to submit...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
|
||||
// ========================
|
||||
@@ -239,50 +198,6 @@ Return ONLY valid JSON array - no explanations, no markdown, no extra text.`,
|
||||
type: 'code',
|
||||
placeholder: '{"notify_no_data": true, "thresholds": {"critical": 90}}',
|
||||
condition: { field: 'operation', value: 'datadog_create_monitor' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Datadog developer. Generate Datadog monitor options JSON.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.threshold>\`, \`<function1.result.interval>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{ALERT_THRESHOLD}}\`)
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the options as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
|
||||
|
||||
### OPTIONS GUIDELINES
|
||||
1. **Thresholds**: Define warning and critical thresholds
|
||||
2. **Notify No Data**: Whether to notify when no data is received
|
||||
3. **Evaluation Delay**: Delay before evaluation (in seconds)
|
||||
4. **Renotify Interval**: Minutes between renotifications
|
||||
5. **Timeout**: Evaluation timeout (in seconds)
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Basic thresholds**: "Set critical threshold at 90"
|
||||
→ {"thresholds": {"critical": 90}}
|
||||
|
||||
**With warning**: "Set warning at 70 and critical at 90"
|
||||
→ {"thresholds": {"warning": 70, "critical": 90}}
|
||||
|
||||
**Full options**: "Monitor with thresholds, notify no data, and renotify every 60 minutes"
|
||||
→ {"notify_no_data": true, "thresholds": {"warning": 70, "critical": 90}, "renotify_interval": 60}
|
||||
|
||||
**With evaluation delay**: "Monitor with 5 minute evaluation delay"
|
||||
→ {"thresholds": {"critical": 90}, "evaluation_delay": 300}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the monitor options you need...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
|
||||
// ========================
|
||||
@@ -351,61 +266,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'service:web-app status:error',
|
||||
condition: { field: 'operation', value: 'datadog_query_logs' },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Datadog developer. Generate Datadog log search queries.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax.
|
||||
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the log search query string. Do not include any explanations, markdown formatting, comments, or additional text.
|
||||
|
||||
### LOG QUERY SYNTAX
|
||||
|
||||
**Basic Filters**:
|
||||
- service:my-service
|
||||
- status:error
|
||||
- host:my-host
|
||||
|
||||
**Text Search**:
|
||||
- "exact phrase"
|
||||
- message:*error*
|
||||
|
||||
**Comparisons**:
|
||||
- @duration:>1000
|
||||
- @status_code:>=400
|
||||
|
||||
**Combining**:
|
||||
- service:api AND status:error
|
||||
- status:error OR status:warn
|
||||
- NOT service:healthcheck
|
||||
|
||||
**Facets**:
|
||||
- @http.url:"/api/*"
|
||||
- @user.id:12345
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Simple**: "Find errors in the API service"
|
||||
→ service:api status:error
|
||||
|
||||
**With text**: "Find logs containing 'timeout'"
|
||||
→ service:api "timeout"
|
||||
|
||||
**Complex**: "Find API errors except healthcheck"
|
||||
→ service:api status:error NOT @http.url:"/health*"
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY the query string - no explanations.`,
|
||||
placeholder: 'Describe the logs you want to find...',
|
||||
generationType: 'sql-query',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'logFrom',
|
||||
@@ -448,48 +308,6 @@ Return ONLY the query string - no explanations.`,
|
||||
]`,
|
||||
condition: { field: 'operation', value: 'datadog_send_logs' },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Datadog developer. Generate Datadog logs JSON array.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.message>\`, \`<function1.result.user_id>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DD_SERVICE}}\`, \`{{ENVIRONMENT}}\`)
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the logs as a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### LOGS GUIDELINES
|
||||
1. **Array Format**: Always return a JSON array, even for single log
|
||||
2. **Message**: Required field with the log message
|
||||
3. **Service**: Application/service name
|
||||
4. **ddsource**: Source identifier (e.g., "custom", "nginx", "python")
|
||||
5. **ddtags**: Comma-separated tags in "key:value" format
|
||||
6. **Additional Fields**: Add any custom fields as needed
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Single log**: "Send application start log"
|
||||
→ [{"message": "Application started successfully", "service": "my-app", "ddsource": "custom", "ddtags": "env:production"}]
|
||||
|
||||
**Multiple logs**: "Send error and info logs"
|
||||
→ [{"message": "Error occurred", "service": "api", "ddsource": "custom", "ddtags": "env:production,level:error"}, {"message": "Request processed", "service": "api", "ddsource": "custom", "ddtags": "env:production,level:info"}]
|
||||
|
||||
**With custom fields**: "Send log with user context"
|
||||
→ [{"message": "User logged in", "service": "auth", "ddsource": "custom", "ddtags": "env:production", "user_id": "123", "ip": "192.168.1.1"}]
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON array - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the logs you want to send...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
|
||||
// ========================
|
||||
|
||||
@@ -131,63 +131,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
field: 'operation',
|
||||
value: ['discord_send_message', 'discord_edit_message', 'discord_execute_webhook'],
|
||||
},
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert at writing Discord messages. Compose engaging messages using Discord's markdown formatting.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.user>\`, \`<function1.result>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{SERVER_NAME}}\`)
|
||||
|
||||
### DISCORD MARKDOWN FORMATTING
|
||||
- **bold** with double asterisks
|
||||
- *italic* with single asterisks
|
||||
- __underline__ with double underscores
|
||||
- ~~strikethrough~~ with double tildes
|
||||
- \`inline code\` with backticks
|
||||
- \`\`\`code block\`\`\` with triple backticks
|
||||
- > quote with >
|
||||
- >>> multi-line quote with >>>
|
||||
- ||spoiler|| with double pipes
|
||||
|
||||
### GUIDELINES
|
||||
1. **Tone**: Match community/server tone (casual, friendly, or professional)
|
||||
2. **Formatting**: Use markdown for emphasis
|
||||
3. **Emojis**: Use Discord emojis where appropriate
|
||||
4. **Length**: Discord has a 2000 character limit
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Announcement**: "Announce a new feature"
|
||||
→ 🎉 **New Feature Alert!**
|
||||
|
||||
We've just released a brand new feature that you're going to love!
|
||||
|
||||
**What's New:**
|
||||
• Faster performance
|
||||
• Better UI
|
||||
• New customization options
|
||||
|
||||
Check it out and let us know what you think!
|
||||
|
||||
**With variables**: "Send alert message"
|
||||
→ ⚠️ **Alert**
|
||||
|
||||
Status: <function1.status>
|
||||
Service: <agent1.service_name>
|
||||
|
||||
Please check the dashboard for more details.
|
||||
|
||||
### REMEMBER
|
||||
Use Discord markdown. Keep messages engaging and within 2000 characters.`,
|
||||
placeholder: 'Describe the Discord message you want to send...',
|
||||
generationType: 'message-content',
|
||||
},
|
||||
},
|
||||
// Emoji - for reaction operations
|
||||
{
|
||||
|
||||
@@ -65,49 +65,6 @@ export const DynamoDBBlock: BlockConfig<DynamoDBResponse> = {
|
||||
placeholder: '{\n "pk": "user#123"\n}',
|
||||
condition: { field: 'operation', value: 'get' },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert DynamoDB developer. Generate DynamoDB primary key JSON based on the user's request.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.user_id>\`, \`<function1.result.order_id>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{TABLE_PREFIX}}\`)
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the key as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
|
||||
|
||||
### KEY GUIDELINES
|
||||
1. **Primary Key**: Must include partition key (pk) and optionally sort key (sk)
|
||||
2. **Data Types**: Use strings, numbers, or binary for key values
|
||||
3. **Format**: Simple key-value pairs matching your table schema
|
||||
4. **Required**: Partition key is always required, sort key only if table has composite key
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Simple partition key**: "Get item with partition key user#123"
|
||||
→ {"pk": "user#123"}
|
||||
|
||||
**Composite key**: "Get item with pk user#123 and sk order#456"
|
||||
→ {"pk": "user#123", "sk": "order#456"}
|
||||
|
||||
**Numeric key**: "Get item with id 12345"
|
||||
→ {"id": 12345}
|
||||
|
||||
**String key**: "Get user by email"
|
||||
→ {"email": "user@example.com"}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the key you want to use...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'key',
|
||||
@@ -116,43 +73,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
placeholder: '{\n "pk": "user#123"\n}',
|
||||
condition: { field: 'operation', value: 'update' },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert DynamoDB developer. Generate DynamoDB primary key JSON for UPDATE operations.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.user_id>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the key as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
|
||||
|
||||
### KEY GUIDELINES
|
||||
1. **Primary Key**: Must include partition key (pk) and optionally sort key (sk)
|
||||
2. **Data Types**: Use strings, numbers, or binary for key values
|
||||
3. **Format**: Simple key-value pairs matching your table schema
|
||||
4. **Required**: Partition key is always required, sort key only if table has composite key
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Simple partition key**: "Update item with partition key user#123"
|
||||
→ {"pk": "user#123"}
|
||||
|
||||
**Composite key**: "Update item with pk user#123 and sk order#456"
|
||||
→ {"pk": "user#123", "sk": "order#456"}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the key you want to use...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'key',
|
||||
@@ -161,46 +81,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
placeholder: '{\n "pk": "user#123"\n}',
|
||||
condition: { field: 'operation', value: 'delete' },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert DynamoDB developer. Generate DynamoDB primary key JSON for DELETE operations.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.item_id>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the key as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
|
||||
|
||||
### ⚠️ DELETION WARNING ⚠️
|
||||
DELETIONS ARE PERMANENT! Be extremely careful and specific with your key criteria.
|
||||
|
||||
### KEY GUIDELINES
|
||||
1. **Primary Key**: Must include partition key (pk) and optionally sort key (sk)
|
||||
2. **Data Types**: Use strings, numbers, or binary for key values
|
||||
3. **Format**: Simple key-value pairs matching your table schema
|
||||
4. **Required**: Partition key is always required, sort key only if table has composite key
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Simple partition key**: "Delete item with partition key user#123"
|
||||
→ {"pk": "user#123"}
|
||||
|
||||
**Composite key**: "Delete item with pk user#123 and sk order#456"
|
||||
→ {"pk": "user#123", "sk": "order#456"}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the key you want to use...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
// Consistent read for get
|
||||
{
|
||||
@@ -223,49 +103,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
'{\n "pk": "user#123",\n "name": "John Doe",\n "email": "john@example.com"\n}',
|
||||
condition: { field: 'operation', value: 'put' },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert DynamoDB developer. Generate DynamoDB item JSON for PUT operations.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.name>\`, \`<function1.result.email>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_STATUS}}\`)
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the item as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
|
||||
|
||||
### ITEM GUIDELINES
|
||||
1. **Primary Key**: Must include partition key (pk) and optionally sort key (sk)
|
||||
2. **Attributes**: Include all attributes you want to store
|
||||
3. **Data Types**: Use strings, numbers, booleans, lists, maps, sets, null
|
||||
4. **Nested Data**: Use maps (objects) for nested structures
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Simple item**: "Create user with pk, name, and email"
|
||||
→ {"pk": "user#123", "name": "John Doe", "email": "john@example.com"}
|
||||
|
||||
**With composite key**: "Create order with pk, sk, total, and status"
|
||||
→ {"pk": "user#123", "sk": "order#456", "total": 99.99, "status": "pending"}
|
||||
|
||||
**Complex nested**: "Create user with profile and preferences"
|
||||
→ {"pk": "user#123", "name": "John", "profile": {"age": 30, "city": "NYC"}, "preferences": {"theme": "dark", "notifications": true}}
|
||||
|
||||
**With lists**: "Create product with tags and categories"
|
||||
→ {"pk": "product#123", "name": "Widget", "tags": ["electronics", "gadget"], "categories": ["tech", "home"]}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the item you want to create...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
// Key condition expression for query
|
||||
{
|
||||
@@ -315,42 +152,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
type: 'code',
|
||||
placeholder: '{\n "#name": "name"\n}',
|
||||
condition: { field: 'operation', value: 'query' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert DynamoDB developer. Generate DynamoDB Expression Attribute Names JSON.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax for dynamic field names.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the expression attribute names as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
|
||||
|
||||
### EXPRESSION ATTRIBUTE NAMES GUIDELINES
|
||||
1. **Purpose**: Maps placeholder names (starting with #) to actual attribute names
|
||||
2. **Reserved Words**: Use when attribute names are DynamoDB reserved words (name, status, data, etc.)
|
||||
3. **Format**: Keys start with #, values are actual attribute names
|
||||
4. **Query/Scan/Update**: Used in KeyConditionExpression, FilterExpression, UpdateExpression
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Single attribute**: "Map #name to name attribute"
|
||||
→ {"#name": "name"}
|
||||
|
||||
**Multiple attributes**: "Map #status and #date to status and created_date"
|
||||
→ {"#status": "status", "#date": "created_date"}
|
||||
|
||||
**Reserved words**: "Map #data to data attribute (reserved word)"
|
||||
→ {"#data": "data"}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the attribute name mappings...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'expressionAttributeNames',
|
||||
@@ -358,39 +159,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
type: 'code',
|
||||
placeholder: '{\n "#name": "name"\n}',
|
||||
condition: { field: 'operation', value: 'scan' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert DynamoDB developer. Generate DynamoDB Expression Attribute Names JSON for SCAN operations.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax for dynamic field names.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the expression attribute names as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
|
||||
|
||||
### EXPRESSION ATTRIBUTE NAMES GUIDELINES
|
||||
1. **Purpose**: Maps placeholder names (starting with #) to actual attribute names
|
||||
2. **Reserved Words**: Use when attribute names are DynamoDB reserved words
|
||||
3. **Format**: Keys start with #, values are actual attribute names
|
||||
4. **Scan**: Used in FilterExpression and ProjectionExpression
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Single attribute**: "Map #name to name attribute"
|
||||
→ {"#name": "name"}
|
||||
|
||||
**Multiple attributes**: "Map #status and #date"
|
||||
→ {"#status": "status", "#date": "created_date"}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the attribute name mappings...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'expressionAttributeNames',
|
||||
@@ -398,39 +166,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
type: 'code',
|
||||
placeholder: '{\n "#name": "name"\n}',
|
||||
condition: { field: 'operation', value: 'update' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert DynamoDB developer. Generate DynamoDB Expression Attribute Names JSON for UPDATE operations.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax for dynamic field names.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the expression attribute names as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
|
||||
|
||||
### EXPRESSION ATTRIBUTE NAMES GUIDELINES
|
||||
1. **Purpose**: Maps placeholder names (starting with #) to actual attribute names
|
||||
2. **Reserved Words**: Use when attribute names are DynamoDB reserved words
|
||||
3. **Format**: Keys start with #, values are actual attribute names
|
||||
4. **Update**: Used in UpdateExpression and ConditionExpression
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Single attribute**: "Map #name to name attribute"
|
||||
→ {"#name": "name"}
|
||||
|
||||
**Multiple attributes**: "Map #status and #date"
|
||||
→ {"#status": "status", "#date": "created_date"}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the attribute name mappings...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
// Expression attribute values for query, scan, update
|
||||
{
|
||||
@@ -439,49 +174,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
type: 'code',
|
||||
placeholder: '{\n ":pk": "user#123",\n ":name": "Jane"\n}',
|
||||
condition: { field: 'operation', value: 'query' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert DynamoDB developer. Generate DynamoDB Expression Attribute Values JSON for QUERY operations.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.user_id>\`, \`<function1.result.status>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the expression attribute values as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
|
||||
|
||||
### EXPRESSION ATTRIBUTE VALUES GUIDELINES
|
||||
1. **Purpose**: Provides values for placeholders (starting with :) in expressions
|
||||
2. **Format**: Keys start with :, values are the actual values
|
||||
3. **Data Types**: Use strings, numbers, booleans, lists, maps, sets, null
|
||||
4. **Query**: Used in KeyConditionExpression and FilterExpression
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Simple values**: "Values for pk and status"
|
||||
→ {":pk": "user#123", ":status": "active"}
|
||||
|
||||
**Numeric values**: "Values for min and max"
|
||||
→ {":min": 18, ":max": 65}
|
||||
|
||||
**Mixed types**: "Values for pk, count, and active"
|
||||
→ {":pk": "user#123", ":count": 10, ":active": true}
|
||||
|
||||
**List values**: "Values for tags"
|
||||
→ {":tags": ["tag1", "tag2"]}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the attribute values...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'expressionAttributeValues',
|
||||
@@ -489,39 +181,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
type: 'code',
|
||||
placeholder: '{\n ":status": "active"\n}',
|
||||
condition: { field: 'operation', value: 'scan' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert DynamoDB developer. Generate DynamoDB Expression Attribute Values JSON for SCAN operations.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the expression attribute values as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
|
||||
|
||||
### EXPRESSION ATTRIBUTE VALUES GUIDELINES
|
||||
1. **Purpose**: Provides values for placeholders (starting with :) in expressions
|
||||
2. **Format**: Keys start with :, values are the actual values
|
||||
3. **Data Types**: Use strings, numbers, booleans, lists, maps, sets, null
|
||||
4. **Scan**: Used in FilterExpression
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Simple values**: "Values for status"
|
||||
→ {":status": "active"}
|
||||
|
||||
**Numeric values**: "Values for min and max"
|
||||
→ {":min": 18, ":max": 65}
|
||||
|
||||
**Mixed types**: "Values for status and count"
|
||||
→ {":status": "active", ":count": 10}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the attribute values...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'expressionAttributeValues',
|
||||
@@ -529,39 +188,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
type: 'code',
|
||||
placeholder: '{\n ":name": "Jane Doe"\n}',
|
||||
condition: { field: 'operation', value: 'update' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert DynamoDB developer. Generate DynamoDB Expression Attribute Values JSON for UPDATE operations.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the expression attribute values as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
|
||||
|
||||
### EXPRESSION ATTRIBUTE VALUES GUIDELINES
|
||||
1. **Purpose**: Provides values for placeholders (starting with :) in expressions
|
||||
2. **Format**: Keys start with :, values are the actual values
|
||||
3. **Data Types**: Use strings, numbers, booleans, lists, maps, sets, null
|
||||
4. **Update**: Used in UpdateExpression and ConditionExpression
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Simple values**: "Values for name and status"
|
||||
→ {":name": "Jane Doe", ":status": "active"}
|
||||
|
||||
**Numeric values**: "Values for increment"
|
||||
→ {":inc": 1}
|
||||
|
||||
**Mixed types**: "Values for name, count, and active"
|
||||
→ {":name": "Jane", ":count": 5, ":active": true}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the attribute values...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
// Index name for query
|
||||
{
|
||||
|
||||
@@ -174,45 +174,6 @@ export const ElasticsearchBlock: BlockConfig<ElasticsearchResponse> = {
|
||||
placeholder: '{ "field": "value", "another_field": 123 }',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'elasticsearch_index_document' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Elasticsearch developer. Generate Elasticsearch document JSON based on the user's request.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.name>\`, \`<function1.result.email>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_STATUS}}\`)
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the document as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
|
||||
|
||||
### DOCUMENT GUIDELINES
|
||||
1. **Structure**: Use proper JSON object structure with field-value pairs
|
||||
2. **Data Types**: Use appropriate types (strings, numbers, booleans, arrays, nested objects)
|
||||
3. **Naming**: Use lowercase field names with underscores (snake_case) following Elasticsearch conventions
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Simple document**: "Create a user document with name, email, and age"
|
||||
→ {"name": "John Doe", "email": "john@example.com", "age": 30}
|
||||
|
||||
**With variables**: "Create a document from previous block"
|
||||
→ {"name": <agent1.name>, "email": <agent1.email>, "source": "{{DATA_SOURCE}}"}
|
||||
|
||||
**With nested data**: "Create a product with name, price, and category details"
|
||||
→ {"name": "Laptop", "price": 999.99, "category": {"name": "Electronics", "id": "cat-123"}}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the document you want to create...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
|
||||
// Document body - for update (partial)
|
||||
@@ -223,43 +184,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
placeholder: '{ "field_to_update": "new_value" }',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'elasticsearch_update_document' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Elasticsearch developer. Generate a partial document JSON for updating an existing document.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.new_status>\`, \`<function1.result.price>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_STATUS}}\`)
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the partial document as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object with only the fields you want to update.
|
||||
|
||||
### UPDATE GUIDELINES
|
||||
1. **Partial Updates**: Only include fields that need to be updated
|
||||
2. **Nested Updates**: Use dot notation for nested fields or include the full nested object
|
||||
3. **Arrays**: Replace entire arrays, or use scripts for array manipulation
|
||||
4. **Data Types**: Maintain consistent data types with existing document
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Simple update**: "Update the email and status fields"
|
||||
→ {"email": "newemail@example.com", "status": "active"}
|
||||
|
||||
**With variables**: "Update fields from previous block"
|
||||
→ {"status": <agent1.new_status>, "updated_by": "{{SYSTEM_USER}}"}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON with fields to update - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe what fields you want to update...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
|
||||
// Search query
|
||||
@@ -269,72 +193,6 @@ Return ONLY valid JSON with fields to update - no explanations, no markdown, no
|
||||
type: 'code',
|
||||
placeholder: '{ "match": { "field": "search term" } }',
|
||||
condition: { field: 'operation', value: 'elasticsearch_search' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Elasticsearch developer. Generate Elasticsearch Query DSL queries based on the user's request.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.search_term>\`, \`<function1.result.status>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_STATUS}}\`)
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the query as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON query object.
|
||||
|
||||
### QUERY GUIDELINES
|
||||
1. **Query DSL**: Use Elasticsearch Query DSL syntax
|
||||
2. **Performance**: Structure queries efficiently
|
||||
3. **Relevance**: Use appropriate query types for the use case
|
||||
4. **Combining**: Use bool queries to combine multiple conditions
|
||||
|
||||
### COMMON QUERY TYPES
|
||||
|
||||
**Match Query** (full-text search):
|
||||
{"match": {"field": "search text"}}
|
||||
|
||||
**Term Query** (exact match):
|
||||
{"term": {"status": "active"}}
|
||||
|
||||
**Range Query**:
|
||||
{"range": {"age": {"gte": 18, "lte": 65}}}
|
||||
|
||||
**Bool Query** (combine queries):
|
||||
{"bool": {"must": [{"match": {"title": "elasticsearch"}}], "filter": [{"term": {"status": "published"}}]}}
|
||||
|
||||
**Multi-Match** (search multiple fields):
|
||||
{"multi_match": {"query": "search text", "fields": ["title", "content"]}}
|
||||
|
||||
**Match Phrase** (exact phrase):
|
||||
{"match_phrase": {"description": "exact phrase here"}}
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Simple search**: "Find documents with 'elasticsearch' in the title"
|
||||
→ {"match": {"title": "elasticsearch"}}
|
||||
|
||||
**Multiple conditions**: "Find active users over 18"
|
||||
→ {"bool": {"must": [{"term": {"status": "active"}}, {"range": {"age": {"gt": 18}}}]}}
|
||||
|
||||
**Text search with filters**: "Search for 'laptop' in products that are in stock and priced under 1000"
|
||||
→ {"bool": {"must": [{"match": {"name": "laptop"}}], "filter": [{"term": {"in_stock": true}}, {"range": {"price": {"lt": 1000}}]}]}}
|
||||
|
||||
**Complex bool query**: "Find published articles about 'python' or 'javascript' written in 2024"
|
||||
→ {"bool": {"must": [{"term": {"status": "published"}}, {"range": {"published_date": {"gte": "2024-01-01", "lte": "2024-12-31"}}], "should": [{"match": {"tags": "python"}}, {"match": {"tags": "javascript"}}], "minimum_should_match": 1}}
|
||||
|
||||
**Fuzzy search**: "Find documents similar to 'elastiksearch'"
|
||||
→ {"fuzzy": {"title": {"value": "elastiksearch", "fuzziness": "AUTO"}}}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON query - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe what you want to search for...',
|
||||
generationType: 'elasticsearch-query',
|
||||
},
|
||||
},
|
||||
|
||||
// Count query
|
||||
@@ -344,45 +202,6 @@ Return ONLY valid JSON query - no explanations, no markdown, no extra text.`,
|
||||
type: 'code',
|
||||
placeholder: '{ "match": { "field": "value" } }',
|
||||
condition: { field: 'operation', value: 'elasticsearch_count' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Elasticsearch developer. Generate Elasticsearch Query DSL queries for counting documents.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax.
|
||||
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the query as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON query object.
|
||||
|
||||
### COUNT QUERY GUIDELINES
|
||||
1. **Query DSL**: Use Elasticsearch Query DSL syntax (same as search queries)
|
||||
2. **Efficiency**: Use filters instead of queries when possible for better performance
|
||||
3. **Structure**: Same query structure as search queries
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Count by term**: "Count active documents"
|
||||
→ {"term": {"status": "active"}}
|
||||
|
||||
**Count with range**: "Count users between 18 and 65"
|
||||
→ {"range": {"age": {"gte": 18, "lte": 65}}}
|
||||
|
||||
**Count with multiple conditions**: "Count published articles from 2024"
|
||||
→ {"bool": {"must": [{"term": {"status": "published"}}, {"range": {"published_date": {"gte": "2024-01-01", "lte": "2024-12-31"}}}]}}
|
||||
|
||||
**Count matching text**: "Count documents containing 'error'"
|
||||
→ {"match": {"message": "error"}}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON query - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe what documents you want to count...',
|
||||
generationType: 'elasticsearch-query',
|
||||
},
|
||||
},
|
||||
|
||||
// Search size
|
||||
@@ -410,45 +229,6 @@ Return ONLY valid JSON query - no explanations, no markdown, no extra text.`,
|
||||
type: 'code',
|
||||
placeholder: '[{ "field": { "order": "asc" } }]',
|
||||
condition: { field: 'operation', value: 'elasticsearch_search' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Elasticsearch developer. Generate Elasticsearch sort specifications as JSON arrays.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the sort specification as a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### SORT GUIDELINES
|
||||
1. **Array Format**: Always return a JSON array, even for single field sorts
|
||||
2. **Order**: Use "asc" for ascending, "desc" for descending
|
||||
3. **Multiple Fields**: Array order determines sort priority
|
||||
4. **Nested Fields**: Use dot notation for nested fields
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Single field ascending**: "Sort by name"
|
||||
→ [{"name": {"order": "asc"}}]
|
||||
|
||||
**Single field descending**: "Sort by date newest first"
|
||||
→ [{"created_at": {"order": "desc"}}]
|
||||
|
||||
**Multiple fields**: "Sort by category then price"
|
||||
→ [{"category": {"order": "asc"}}, {"price": {"order": "asc"}}]
|
||||
|
||||
**Complex sort**: "Sort by status, then by date descending, then by score"
|
||||
→ [{"status": {"order": "asc"}}, {"date": {"order": "desc"}}, {"score": {"order": "desc"}}]
|
||||
|
||||
**Nested field**: "Sort by customer name"
|
||||
→ [{"customer.name": {"order": "asc"}}]
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON array - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe how you want to sort the results...',
|
||||
generationType: 'elasticsearch-sort',
|
||||
},
|
||||
},
|
||||
|
||||
// Source includes
|
||||
@@ -484,72 +264,6 @@ Return ONLY valid JSON array - no explanations, no markdown, no extra text.`,
|
||||
'{ "index": { "_index": "my-index", "_id": "1" } }\n{ "field": "value" }\n{ "delete": { "_index": "my-index", "_id": "2" } }',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'elasticsearch_bulk' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Elasticsearch developer. Generate Elasticsearch bulk operations in NDJSON (Newline Delimited JSON) format.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the bulk operations as NDJSON (each JSON object on a new line). Do not include any explanations, markdown formatting, comments, or additional text. Just the raw NDJSON.
|
||||
|
||||
### BULK OPERATION FORMAT
|
||||
Bulk operations use NDJSON format where:
|
||||
- Each operation action is on one line (index, create, update, delete)
|
||||
- Each document follows on the next line (for index/create/update)
|
||||
- Each pair is separated by a newline
|
||||
|
||||
### OPERATION TYPES
|
||||
|
||||
**Index** (create or replace):
|
||||
{"index": {"_index": "my-index", "_id": "1"}}
|
||||
{"field": "value", "another": 123}
|
||||
|
||||
**Create** (only if doesn't exist):
|
||||
{"create": {"_index": "my-index", "_id": "2"}}
|
||||
{"field": "value"}
|
||||
|
||||
**Update** (partial update):
|
||||
{"update": {"_index": "my-index", "_id": "3"}}
|
||||
{"doc": {"field": "new_value"}}
|
||||
|
||||
**Delete**:
|
||||
{"delete": {"_index": "my-index", "_id": "4"}}
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Index multiple documents**: "Index two user documents"
|
||||
→ {"index": {"_index": "users", "_id": "1"}}
|
||||
{"name": "John", "email": "john@example.com"}
|
||||
{"index": {"_index": "users", "_id": "2"}}
|
||||
{"name": "Jane", "email": "jane@example.com"}
|
||||
|
||||
**Mixed operations**: "Index one document and delete another"
|
||||
→ {"index": {"_index": "products", "_id": "new-1"}}
|
||||
{"name": "Widget", "price": 19.99}
|
||||
{"delete": {"_index": "products", "_id": "old-1"}}
|
||||
|
||||
**Update operations**: "Update two documents"
|
||||
→ {"update": {"_index": "users", "_id": "1"}}
|
||||
{"doc": {"status": "active"}}
|
||||
{"update": {"_index": "users", "_id": "2"}}
|
||||
{"doc": {"status": "inactive"}}
|
||||
|
||||
**Bulk create**: "Create three products"
|
||||
→ {"create": {"_index": "products"}}
|
||||
{"name": "Product A", "price": 10}
|
||||
{"create": {"_index": "products"}}
|
||||
{"name": "Product B", "price": 20}
|
||||
{"create": {"_index": "products"}}
|
||||
{"name": "Product C", "price": 30}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY NDJSON format (each JSON object on its own line) - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the bulk operations you want to perform...',
|
||||
generationType: 'elasticsearch-bulk',
|
||||
},
|
||||
},
|
||||
|
||||
// Index settings
|
||||
@@ -559,50 +273,6 @@ Return ONLY NDJSON format (each JSON object on its own line) - no explanations,
|
||||
type: 'code',
|
||||
placeholder: '{ "number_of_shards": 1, "number_of_replicas": 1 }',
|
||||
condition: { field: 'operation', value: 'elasticsearch_create_index' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Elasticsearch developer. Generate Elasticsearch index settings as JSON.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the settings as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
|
||||
|
||||
### SETTINGS GUIDELINES
|
||||
1. **Shards**: number_of_shards determines how data is split (typically 1-5 for small indices)
|
||||
2. **Replicas**: number_of_replicas for redundancy (0 for development, 1+ for production)
|
||||
3. **Analysis**: Custom analyzers, tokenizers, filters
|
||||
4. **Refresh**: Control when changes become searchable
|
||||
|
||||
### COMMON SETTINGS
|
||||
|
||||
**Basic settings**:
|
||||
{"number_of_shards": 1, "number_of_replicas": 1}
|
||||
|
||||
**With refresh interval**:
|
||||
{"number_of_shards": 1, "number_of_replicas": 1, "refresh_interval": "30s"}
|
||||
|
||||
**With analysis**:
|
||||
{"number_of_shards": 1, "number_of_replicas": 1, "analysis": {"analyzer": {"custom": {"type": "standard"}}}}
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Simple settings**: "Create index with 1 shard and 1 replica"
|
||||
→ {"number_of_shards": 1, "number_of_replicas": 1}
|
||||
|
||||
**Production settings**: "Create index with 3 shards, 2 replicas, and 10s refresh"
|
||||
→ {"number_of_shards": 3, "number_of_replicas": 2, "refresh_interval": "10s"}
|
||||
|
||||
**Development settings**: "Create index with no replicas for testing"
|
||||
→ {"number_of_shards": 1, "number_of_replicas": 0}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the index settings you need...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
|
||||
// Index mappings
|
||||
@@ -612,65 +282,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
type: 'code',
|
||||
placeholder: '{ "properties": { "field": { "type": "text" } } }',
|
||||
condition: { field: 'operation', value: 'elasticsearch_create_index' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Elasticsearch developer. Generate Elasticsearch index mappings as JSON.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the mappings as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
|
||||
|
||||
### MAPPING GUIDELINES
|
||||
1. **Properties**: Define field types and behaviors
|
||||
2. **Field Types**: text (full-text), keyword (exact), long/integer (numbers), date, boolean, object, nested
|
||||
3. **Analysis**: text fields are analyzed, keyword fields are not
|
||||
4. **Nested**: Use nested type for arrays of objects that need independent querying
|
||||
|
||||
### COMMON FIELD TYPES
|
||||
|
||||
**Text** (full-text search):
|
||||
{"properties": {"title": {"type": "text"}}}
|
||||
|
||||
**Keyword** (exact match, sorting):
|
||||
{"properties": {"status": {"type": "keyword"}}}
|
||||
|
||||
**Number**:
|
||||
{"properties": {"age": {"type": "integer"}, "price": {"type": "float"}}}
|
||||
|
||||
**Date**:
|
||||
{"properties": {"created_at": {"type": "date"}}}
|
||||
|
||||
**Boolean**:
|
||||
{"properties": {"active": {"type": "boolean"}}}
|
||||
|
||||
**Object** (nested structure):
|
||||
{"properties": {"user": {"type": "object", "properties": {"name": {"type": "text"}, "email": {"type": "keyword"}}}}}
|
||||
|
||||
**Nested** (array of objects):
|
||||
{"properties": {"tags": {"type": "nested", "properties": {"name": {"type": "keyword"}, "value": {"type": "text"}}}}}
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Simple mapping**: "Create mapping for user with name, email, and age"
|
||||
→ {"properties": {"name": {"type": "text"}, "email": {"type": "keyword"}, "age": {"type": "integer"}}}
|
||||
|
||||
**Product mapping**: "Create mapping for product with name, price, description, and tags"
|
||||
→ {"properties": {"name": {"type": "text"}, "price": {"type": "float"}, "description": {"type": "text"}, "tags": {"type": "keyword"}}}
|
||||
|
||||
**Complex nested**: "Create mapping for order with customer and items"
|
||||
→ {"properties": {"order_id": {"type": "keyword"}, "customer": {"type": "object", "properties": {"name": {"type": "text"}, "email": {"type": "keyword"}}}, "items": {"type": "nested", "properties": {"product": {"type": "text"}, "quantity": {"type": "integer"}, "price": {"type": "float"}}}, "created_at": {"type": "date"}}}
|
||||
|
||||
**With analyzers**: "Create text field with custom analyzer"
|
||||
→ {"properties": {"content": {"type": "text", "analyzer": "standard"}}}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the index mapping you need...',
|
||||
generationType: 'elasticsearch-mapping',
|
||||
},
|
||||
},
|
||||
|
||||
// Refresh option
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ChartBarIcon } from '@/components/icons'
|
||||
import { isHosted } from '@/lib/core/config/environment'
|
||||
import { isHosted } from '@/lib/core/config/feature-flags'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type { BlockConfig, ParamType } from '@/blocks/types'
|
||||
import type { ProviderId } from '@/providers/types'
|
||||
|
||||
@@ -551,46 +551,9 @@ export const GitHubBlock: BlockConfig<GitHubResponse> = {
|
||||
{
|
||||
id: 'required_status_checks',
|
||||
title: 'Required Status Checks',
|
||||
type: 'long-input',
|
||||
type: 'short-input',
|
||||
placeholder: 'JSON: {"strict":true,"contexts":["ci/test"]}',
|
||||
condition: { field: 'operation', value: 'github_update_branch_protection' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert GitHub developer. Generate required status checks JSON for branch protection.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax.
|
||||
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY valid JSON. Do not include any explanations, markdown formatting, or comments.
|
||||
|
||||
### STATUS CHECKS STRUCTURE
|
||||
{
|
||||
"strict": boolean, // Require branches to be up to date before merging
|
||||
"contexts": string[] // Array of required status check names
|
||||
}
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**CI required**: "Require CI to pass"
|
||||
→ {"strict": true, "contexts": ["ci/test"]}
|
||||
|
||||
**Multiple checks**: "Require tests and linting"
|
||||
→ {"strict": true, "contexts": ["ci/test", "ci/lint", "ci/build"]}
|
||||
|
||||
**No strict**: "Just require checks, not up-to-date"
|
||||
→ {"strict": false, "contexts": ["ci/test"]}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON - no explanations.`,
|
||||
placeholder: 'Describe the required status checks...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'enforce_admins',
|
||||
@@ -605,47 +568,9 @@ Return ONLY valid JSON - no explanations.`,
|
||||
{
|
||||
id: 'required_pull_request_reviews',
|
||||
title: 'Required PR Reviews',
|
||||
type: 'long-input',
|
||||
type: 'short-input',
|
||||
placeholder: 'JSON: {"required_approving_review_count":1}',
|
||||
condition: { field: 'operation', value: 'github_update_branch_protection' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert GitHub developer. Generate required PR reviews JSON for branch protection.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY valid JSON. Do not include any explanations, markdown formatting, or comments.
|
||||
|
||||
### PR REVIEWS STRUCTURE
|
||||
{
|
||||
"required_approving_review_count": number, // Number of approvals needed (1-6)
|
||||
"dismiss_stale_reviews": boolean, // Dismiss approvals when new commits pushed
|
||||
"require_code_owner_reviews": boolean, // Require review from code owners
|
||||
"require_last_push_approval": boolean // Require approval of last push
|
||||
}
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Single approval**: "Require one approval"
|
||||
→ {"required_approving_review_count": 1}
|
||||
|
||||
**Strict reviews**: "Require 2 approvals, dismiss stale"
|
||||
→ {"required_approving_review_count": 2, "dismiss_stale_reviews": true}
|
||||
|
||||
**Code owners**: "Require code owner review"
|
||||
→ {"required_approving_review_count": 1, "require_code_owner_reviews": true, "dismiss_stale_reviews": true}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON - no explanations.`,
|
||||
placeholder: 'Describe the PR review requirements...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
// Issue operations parameters
|
||||
{
|
||||
@@ -662,91 +587,6 @@ Return ONLY valid JSON - no explanations.`,
|
||||
type: 'long-input',
|
||||
placeholder: 'Enter issue description (optional)',
|
||||
condition: { field: 'operation', value: 'github_create_issue' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert at writing GitHub issue descriptions. Create clear, well-structured issues using GitHub markdown.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.error>\`, \`<function1.result>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{REPO_URL}}\`)
|
||||
|
||||
### GITHUB MARKDOWN
|
||||
- **bold** with double asterisks
|
||||
- *italic* with asterisks
|
||||
- \`inline code\` with backticks
|
||||
- \`\`\`language code block\`\`\`
|
||||
- - [ ] task lists
|
||||
- > blockquotes
|
||||
- [link text](url)
|
||||
- @mentions for users
|
||||
- #123 for issue references
|
||||
|
||||
### GUIDELINES
|
||||
1. **Problem Statement**: Clearly describe the issue
|
||||
2. **Reproduction Steps**: For bugs, include steps to reproduce
|
||||
3. **Expected vs Actual**: What should happen vs what happens
|
||||
4. **Environment**: Include relevant version/environment info
|
||||
5. **Screenshots/Logs**: Reference any attachments
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Bug report**: "Create a bug report for API rate limiting"
|
||||
→ ## Description
|
||||
API requests are being rate limited incorrectly, causing valid requests to fail.
|
||||
|
||||
## Steps to Reproduce
|
||||
1. Make 10 API requests within 1 minute
|
||||
2. Observe that requests start failing after 5 requests
|
||||
3. Check response headers for rate limit info
|
||||
|
||||
## Expected Behavior
|
||||
Rate limit should be 100 requests per minute as documented.
|
||||
|
||||
## Actual Behavior
|
||||
Rate limit appears to be 5 requests per minute.
|
||||
|
||||
## Environment
|
||||
- API Version: v2.1
|
||||
- Client: Node.js SDK v3.0.0
|
||||
- Region: US-East
|
||||
|
||||
## Logs
|
||||
\`\`\`
|
||||
Error: Rate limit exceeded (5/5)
|
||||
X-RateLimit-Remaining: 0
|
||||
\`\`\`
|
||||
|
||||
**With variables**: "Create issue from error"
|
||||
→ ## Automated Issue Report
|
||||
|
||||
### Error Details
|
||||
- Service: <agent1.service>
|
||||
- Error: <function1.error_message>
|
||||
- Timestamp: <function1.timestamp>
|
||||
|
||||
### Stack Trace
|
||||
\`\`\`
|
||||
<agent1.stack_trace>
|
||||
\`\`\`
|
||||
|
||||
### Impact
|
||||
<agent1.impact_description>
|
||||
|
||||
### Suggested Actions
|
||||
- [ ] Investigate root cause
|
||||
- [ ] Implement fix
|
||||
- [ ] Add tests
|
||||
|
||||
### REMEMBER
|
||||
Use GitHub markdown. Include all relevant technical details.`,
|
||||
placeholder: 'Describe the GitHub issue...',
|
||||
generationType: 'markdown-content',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'labels',
|
||||
@@ -1009,45 +849,6 @@ Use GitHub markdown. Include all relevant technical details.`,
|
||||
type: 'long-input',
|
||||
placeholder: 'JSON: {"key":"value"}',
|
||||
condition: { field: 'operation', value: 'github_trigger_workflow' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert GitHub Actions developer. Generate workflow inputs JSON for triggering a workflow dispatch.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.version>\`, \`<function1.result.tag>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEPLOY_ENV}}\`)
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY valid JSON. Do not include any explanations, markdown formatting, or comments.
|
||||
|
||||
### WORKFLOW INPUTS GUIDELINES
|
||||
1. Keys must match the input names defined in the workflow's workflow_dispatch trigger
|
||||
2. Values can be strings, numbers, or booleans depending on input type
|
||||
3. Required inputs must be provided
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Simple deploy**: "Deploy to production"
|
||||
→ {"environment": "production", "dry_run": false}
|
||||
|
||||
**Version release**: "Release version 1.2.3"
|
||||
→ {"version": "1.2.3", "create_tag": true}
|
||||
|
||||
**With variables**: "Deploy the version from previous block"
|
||||
→ {"version": <agent1.version>, "environment": "{{DEPLOY_ENV}}"}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON - no explanations.`,
|
||||
placeholder: 'Describe the workflow inputs...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'workflow_id',
|
||||
|
||||
@@ -76,52 +76,6 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
|
||||
placeholder: 'Email content',
|
||||
condition: { field: 'operation', value: ['send_gmail', 'draft_gmail'] },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert email writer. Compose professional, clear, and effective email content.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.customer_name>\`, \`<function1.result.order_id>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{COMPANY_NAME}}\`, \`{{SUPPORT_EMAIL}}\`)
|
||||
|
||||
### GUIDELINES
|
||||
1. **Tone**: Match the appropriate tone (formal, friendly, urgent) based on context
|
||||
2. **Structure**: Use clear paragraphs, bullet points where helpful
|
||||
3. **Brevity**: Be concise but complete
|
||||
4. **Call to Action**: Include clear next steps when appropriate
|
||||
5. **Personalization**: Use variable references for dynamic content
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Professional inquiry**: "Ask about project status"
|
||||
→ Hi [Name],
|
||||
|
||||
I hope this email finds you well. I wanted to follow up on the project we discussed last week.
|
||||
|
||||
Could you provide an update on the current status and expected timeline? Please let me know if there are any blockers I can help address.
|
||||
|
||||
Best regards
|
||||
|
||||
**With variables**: "Send order confirmation"
|
||||
→ Dear <agent1.customer_name>,
|
||||
|
||||
Thank you for your order #<function1.order_id>!
|
||||
|
||||
Your order has been confirmed and will be shipped within 2-3 business days.
|
||||
|
||||
Best,
|
||||
{{COMPANY_NAME}}
|
||||
|
||||
### REMEMBER
|
||||
Write the email body only - no subject line unless specifically requested.`,
|
||||
placeholder: 'Describe the email you want to write...',
|
||||
generationType: 'email-content',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'contentType',
|
||||
|
||||
@@ -74,60 +74,6 @@ export const GoogleCalendarBlock: BlockConfig<GoogleCalendarResponse> = {
|
||||
type: 'long-input',
|
||||
placeholder: 'Event description',
|
||||
condition: { field: 'operation', value: 'create' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert at writing calendar event descriptions. Create clear, informative event details.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.agenda>\`, \`<function1.result>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{MEETING_LINK}}\`)
|
||||
|
||||
### GUIDELINES
|
||||
1. **Purpose**: Clearly state the meeting purpose
|
||||
2. **Agenda**: Include key discussion points
|
||||
3. **Preparation**: Note any required preparation
|
||||
4. **Links**: Include relevant meeting links and documents
|
||||
5. **Contact**: Add organizer contact if needed
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Team meeting**: "Write description for weekly team sync"
|
||||
→ Weekly Team Sync
|
||||
|
||||
Agenda:
|
||||
• Project status updates
|
||||
• Blockers and dependencies
|
||||
• Upcoming deadlines
|
||||
• Open discussion
|
||||
|
||||
Meeting Link: {{MEETING_LINK}}
|
||||
|
||||
Please come prepared with your updates for the week.
|
||||
|
||||
**With variables**: "Create event from request"
|
||||
→ <agent1.meeting_title>
|
||||
|
||||
Purpose: <agent1.purpose>
|
||||
|
||||
Attendees: <function1.attendees>
|
||||
|
||||
Agenda:
|
||||
<agent1.agenda_items>
|
||||
|
||||
Documents: <function1.doc_links>
|
||||
|
||||
Questions? Contact <agent1.organizer_email>
|
||||
|
||||
### REMEMBER
|
||||
Be concise. Include all necessary details for attendees.`,
|
||||
placeholder: 'Describe the event details...',
|
||||
generationType: 'markdown-content',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'location',
|
||||
|
||||
@@ -107,73 +107,6 @@ export const GoogleDocsBlock: BlockConfig<GoogleDocsResponse> = {
|
||||
placeholder: 'Enter document content',
|
||||
condition: { field: 'operation', value: 'write' },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert document writer. Create clear, professional content for Google Docs.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.data>\`, \`<function1.result>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{COMPANY_NAME}}\`)
|
||||
|
||||
### GUIDELINES
|
||||
1. **Structure**: Use clear headings and sections
|
||||
2. **Professional Tone**: Write clearly and professionally
|
||||
3. **Formatting**: Use paragraphs, lists, and emphasis
|
||||
4. **Completeness**: Include all relevant information
|
||||
5. **Readability**: Keep sentences and paragraphs concise
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Report**: "Write a weekly status report"
|
||||
→ Weekly Status Report
|
||||
Week of January 15, 2024
|
||||
|
||||
Executive Summary
|
||||
This week saw significant progress on our key initiatives with completion of Phase 1 development.
|
||||
|
||||
Accomplishments
|
||||
• Completed user authentication module
|
||||
• Deployed staging environment
|
||||
• Conducted security review
|
||||
|
||||
In Progress
|
||||
• Database optimization (75% complete)
|
||||
• API documentation updates
|
||||
• Performance testing
|
||||
|
||||
Blockers
|
||||
• Awaiting design approval for dashboard redesign
|
||||
|
||||
Next Week's Priorities
|
||||
1. Complete database optimization
|
||||
2. Begin Phase 2 development
|
||||
3. Stakeholder review meeting
|
||||
|
||||
**With variables**: "Create document from data"
|
||||
→ <agent1.document_title>
|
||||
|
||||
Date: <function1.date>
|
||||
Author: {{AUTHOR_NAME}}
|
||||
|
||||
Overview
|
||||
<agent1.summary>
|
||||
|
||||
Details
|
||||
<function1.content>
|
||||
|
||||
Next Steps
|
||||
<agent1.action_items>
|
||||
|
||||
### REMEMBER
|
||||
Write clear, professional content. Structure with headings and lists.`,
|
||||
placeholder: 'Describe the document content...',
|
||||
generationType: 'markdown-content',
|
||||
},
|
||||
},
|
||||
// Content Field for create operation
|
||||
{
|
||||
@@ -182,30 +115,6 @@ Write clear, professional content. Structure with headings and lists.`,
|
||||
type: 'long-input',
|
||||
placeholder: 'Enter document content',
|
||||
condition: { field: 'operation', value: 'create' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert document writer. Create clear, professional content for Google Docs.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax
|
||||
|
||||
### GUIDELINES
|
||||
1. Use clear headings and sections
|
||||
2. Write professionally and concisely
|
||||
3. Use bullet points and numbered lists
|
||||
4. Include all relevant information
|
||||
|
||||
### REMEMBER
|
||||
Write clear, professional content. Structure with headings and lists.`,
|
||||
placeholder: 'Describe the document content...',
|
||||
generationType: 'markdown-content',
|
||||
},
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
|
||||
@@ -83,48 +83,6 @@ export const GoogleSheetsBlock: BlockConfig<GoogleSheetsResponse> = {
|
||||
'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects (e.g., [{"name":"John", "age":30}, {"name":"Jane", "age":25}])',
|
||||
condition: { field: 'operation', value: 'write' },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert at Google Sheets data formatting. Generate JSON data for writing to Google Sheets.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.data>\`, \`<function1.result.rows>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### DATA FORMATS
|
||||
|
||||
**Array of Arrays** (Row-based):
|
||||
[["Header1", "Header2"], ["Value1", "Value2"]]
|
||||
|
||||
**Array of Objects** (Column-mapped):
|
||||
[{"name": "John", "age": 30}, {"name": "Jane", "age": 25}]
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Simple write**: "Write headers and two rows of data"
|
||||
→ [["Name", "Email", "Age"], ["John Doe", "john@example.com", 30], ["Jane Smith", "jane@example.com", 25]]
|
||||
|
||||
**With variables**: "Write data from previous block"
|
||||
→ [["Name", "Status"], [<agent1.name>, <agent1.status>]]
|
||||
|
||||
**Object format**: "Write user records"
|
||||
→ [{"name": "John", "email": "john@example.com"}, {"name": "Jane", "email": "jane@example.com"}]
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON array - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the data you want to write...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'valueInputOption',
|
||||
@@ -145,37 +103,6 @@ Return ONLY valid JSON array - no explanations, no markdown, no extra text.`,
|
||||
'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects (e.g., [{"name":"John", "age":30}, {"name":"Jane", "age":25}])',
|
||||
condition: { field: 'operation', value: 'update' },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert at Google Sheets data formatting. Generate JSON data for updating Google Sheets.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax.
|
||||
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### DATA FORMATS
|
||||
**Array of Arrays** (Row-based): [["Value1", "Value2"], ["Value3", "Value4"]]
|
||||
**Array of Objects** (Column-mapped): [{"col1": "val1"}, {"col1": "val2"}]
|
||||
|
||||
### EXAMPLES
|
||||
**Update cells**: "Update with new values"
|
||||
→ [["Updated Name", "Updated Email"], ["John Doe", "john.new@example.com"]]
|
||||
|
||||
**With variables**: "Update with data from previous block"
|
||||
→ [[<agent1.name>, <agent1.email>]]
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: 'Describe the data you want to update...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'valueInputOption',
|
||||
@@ -196,37 +123,6 @@ Return ONLY valid JSON array - no explanations.`,
|
||||
'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects (e.g., [{"name":"John", "age":30}, {"name":"Jane", "age":25}])',
|
||||
condition: { field: 'operation', value: 'append' },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert at Google Sheets data formatting. Generate JSON data for appending to Google Sheets.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax.
|
||||
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### DATA FORMATS
|
||||
**Array of Arrays** (Row-based): [["Value1", "Value2"], ["Value3", "Value4"]]
|
||||
**Array of Objects** (Column-mapped): [{"col1": "val1"}, {"col1": "val2"}]
|
||||
|
||||
### EXAMPLES
|
||||
**Append rows**: "Add new rows to the sheet"
|
||||
→ [["New Entry", "new@email.com", "Active"]]
|
||||
|
||||
**With variables**: "Append data from previous block"
|
||||
→ [[<agent1.name>, <agent1.email>, <agent1.status>]]
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: 'Describe the data you want to append...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'valueInputOption',
|
||||
|
||||
@@ -201,44 +201,6 @@ export const GoogleSlidesBlock: BlockConfig<GoogleSlidesResponse> = {
|
||||
placeholder: 'JSON array: [{"layoutPlaceholder":{"type":"TITLE"},"objectId":"my_title"}]',
|
||||
condition: { field: 'operation', value: 'add_slide' },
|
||||
mode: 'advanced',
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Google Slides API developer. Generate placeholder ID mappings JSON for adding slides.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax for dynamic object IDs.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, or comments.
|
||||
|
||||
### PLACEHOLDER TYPES
|
||||
- TITLE: Main title placeholder
|
||||
- SUBTITLE: Subtitle placeholder
|
||||
- BODY: Main body text placeholder
|
||||
- CENTERED_TITLE: Centered title
|
||||
- HEADER: Header area
|
||||
- FOOTER: Footer area
|
||||
|
||||
### FORMAT
|
||||
Each mapping needs: "layoutPlaceholder" with "type", and "objectId" (your custom ID)
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Title and body**: "Map title and body placeholders"
|
||||
→ [{"layoutPlaceholder": {"type": "TITLE"}, "objectId": "my_title_1"}, {"layoutPlaceholder": {"type": "BODY"}, "objectId": "my_body_1"}]
|
||||
|
||||
**Just title**: "Map only the title placeholder"
|
||||
→ [{"layoutPlaceholder": {"type": "TITLE"}, "objectId": "slide_title"}]
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: 'Describe the placeholder mappings...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
|
||||
// ========== Add Image Operation Fields ==========
|
||||
|
||||
@@ -154,58 +154,6 @@ export const GrafanaBlock: BlockConfig<GrafanaResponse> = {
|
||||
field: 'operation',
|
||||
value: ['grafana_create_dashboard', 'grafana_update_dashboard'],
|
||||
},
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Grafana developer. Generate Grafana panel configuration JSON array.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.metric_name>\`, \`<function1.result.query>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DATASOURCE_UID}}\`, \`{{DASHBOARD_TITLE}}\`)
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array of panel objects. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### PANEL STRUCTURE
|
||||
Each panel typically has:
|
||||
- **id**: Unique panel ID (number)
|
||||
- **type**: Panel type (graph, stat, gauge, table, text, etc.)
|
||||
- **title**: Panel title
|
||||
- **gridPos**: Position {x, y, w, h}
|
||||
- **targets**: Array of data source queries
|
||||
- **options**: Panel-specific options
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Simple stat panel**: "Create a panel showing total users"
|
||||
→ [{
|
||||
"id": 1,
|
||||
"type": "stat",
|
||||
"title": "Total Users",
|
||||
"gridPos": {"x": 0, "y": 0, "w": 6, "h": 4},
|
||||
"targets": [{"refId": "A", "expr": "sum(users_total)"}]
|
||||
}]
|
||||
|
||||
**With variables**: "Create panel with dynamic title and query"
|
||||
→ [{
|
||||
"id": 1,
|
||||
"type": "timeseries",
|
||||
"title": <agent1.panel_title>,
|
||||
"gridPos": {"x": 0, "y": 0, "w": 12, "h": 8},
|
||||
"targets": [{"refId": "A", "expr": <agent1.prometheus_query>}]
|
||||
}]
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY a valid JSON array - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the panels you want to create...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'message',
|
||||
@@ -279,60 +227,6 @@ Return ONLY a valid JSON array - no explanations, no markdown, no extra text.`,
|
||||
field: 'operation',
|
||||
value: ['grafana_create_alert_rule', 'grafana_update_alert_rule'],
|
||||
},
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Grafana developer. Generate Grafana alert rule query data JSON array.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.query>\`, \`<function1.result.threshold>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{PROMETHEUS_UID}}\`, \`{{ALERT_THRESHOLD}}\`)
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array of query/expression data objects. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### QUERY DATA STRUCTURE
|
||||
Each query/expression object has:
|
||||
- **refId**: Reference ID (A, B, C, etc.)
|
||||
- **queryType**: Type of query
|
||||
- **relativeTimeRange**: Time range {from, to} in seconds
|
||||
- **datasourceUid**: Data source UID
|
||||
- **model**: Query model specific to the data source
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Prometheus query**: "Alert when CPU is above 80%"
|
||||
→ [
|
||||
{
|
||||
"refId": "A",
|
||||
"queryType": "instant",
|
||||
"relativeTimeRange": {"from": 600, "to": 0},
|
||||
"datasourceUid": "prometheus",
|
||||
"model": {"expr": "avg(cpu_usage_percent) > 80", "intervalMs": 1000}
|
||||
}
|
||||
]
|
||||
|
||||
**With variables**: "Alert with dynamic threshold"
|
||||
→ [
|
||||
{
|
||||
"refId": "A",
|
||||
"relativeTimeRange": {"from": 600, "to": 0},
|
||||
"datasourceUid": "{{PROMETHEUS_UID}}",
|
||||
"model": {"expr": <agent1.prometheus_query>, "intervalMs": 1000}
|
||||
}
|
||||
]
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY a valid JSON array - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the alert query data...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'forDuration',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ShieldCheckIcon } from '@/components/icons'
|
||||
import { isHosted } from '@/lib/core/config/environment'
|
||||
import { isHosted } from '@/lib/core/config/feature-flags'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { getHostedModels, getProviderIcon } from '@/providers/utils'
|
||||
import { useProvidersStore } from '@/stores/providers/store'
|
||||
|
||||
@@ -126,13 +126,6 @@ export const HubSpotBlock: BlockConfig<HubSpotResponse> = {
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.name>\`, \`<function1.result.email>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_OWNER_ID}}\`)
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the JSON object with HubSpot properties. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object that can be used directly in HubSpot API create/update operations.
|
||||
|
||||
@@ -319,13 +312,6 @@ Return ONLY the JSON object with properties - no explanations, no markdown, no e
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.email>\`, \`<function1.result.status>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_LIFECYCLE_STAGE}}\`)
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the JSON array of filter groups. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array that can be used directly in HubSpot API search operations.
|
||||
|
||||
|
||||
@@ -160,46 +160,6 @@ export const IntercomBlock: BlockConfig = {
|
||||
field: 'operation',
|
||||
value: ['create_contact', 'update_contact'],
|
||||
},
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Intercom developer. Generate custom attributes JSON for Intercom contacts.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.plan_type>\`, \`<function1.result.company>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_PLAN}}\`)
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
|
||||
|
||||
### CUSTOM ATTRIBUTES GUIDELINES
|
||||
1. **Types**: Strings, numbers, booleans, dates (as Unix timestamps)
|
||||
2. **Naming**: Use snake_case or lowercase with underscores
|
||||
3. **Limits**: String values max 255 characters
|
||||
4. **Pre-defined**: Must be created in Intercom first if strict mode is enabled
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**User attributes**: "Set plan and company info"
|
||||
→ {"plan_type": "enterprise", "company_name": "Acme Corp", "seats": 50}
|
||||
|
||||
**With variables**: "Use data from previous block"
|
||||
→ {"subscription_id": <agent1.sub_id>, "mrr": <function1.monthly_revenue>, "industry": <agent1.company_industry>}
|
||||
|
||||
**Boolean flags**: "Set feature flags"
|
||||
→ {"has_completed_onboarding": true, "beta_tester": false, "vip_customer": true}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the custom attributes...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'query',
|
||||
@@ -211,46 +171,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
field: 'operation',
|
||||
value: ['search_contacts', 'search_conversations'],
|
||||
},
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Intercom developer. Generate Intercom search queries.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax.
|
||||
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the search query (JSON object for advanced search, or text for simple search). Do not include any explanations.
|
||||
|
||||
### INTERCOM SEARCH QUERY GUIDELINES
|
||||
1. **Simple Text**: Just enter text for basic search
|
||||
2. **Advanced**: JSON object with "query" containing filter/operator structure
|
||||
3. **Operators**: field, operator (=, !=, IN, NIN, <, >, ~, !~, ^, $), value
|
||||
4. **Combining**: Use AND/OR operators to combine conditions
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Simple search**: "Find contacts with example.com email"
|
||||
→ example.com
|
||||
|
||||
**By email**: "Find contact with specific email"
|
||||
→ {"query": {"field": "email", "operator": "=", "value": "user@example.com"}}
|
||||
|
||||
**Multiple conditions**: "Find VIP customers from Acme"
|
||||
→ {"query": {"operator": "AND", "value": [{"field": "custom_attributes.vip_customer", "operator": "=", "value": true}, {"field": "custom_attributes.company_name", "operator": "=", "value": "Acme"}]}}
|
||||
|
||||
**With variables**: "Search for contact by email from previous block"
|
||||
→ {"query": {"field": "email", "operator": "=", "value": <agent1.email>}}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY the query - no explanations.`,
|
||||
placeholder: 'Describe what you want to search for...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
// Company fields
|
||||
{
|
||||
|
||||
@@ -193,81 +193,6 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
placeholder: 'Enter new description for the issue',
|
||||
dependsOn: ['projectId'],
|
||||
condition: { field: 'operation', value: ['update', 'write'] },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert at writing Jira issue descriptions. Create clear, well-structured descriptions.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.error>\`, \`<function1.result>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{ENVIRONMENT}}\`)
|
||||
|
||||
### JIRA FORMATTING
|
||||
Jira uses its own wiki-style markup:
|
||||
- *bold* or **bold**
|
||||
- _italic_
|
||||
- h1. Header 1, h2. Header 2, etc.
|
||||
- * bullet points
|
||||
- # numbered lists
|
||||
- {code}code block{code}
|
||||
- [link text|URL]
|
||||
|
||||
### GUIDELINES
|
||||
1. **Structure**: Use headers and bullet points
|
||||
2. **Context**: Include background and impact
|
||||
3. **Acceptance Criteria**: Define completion criteria
|
||||
4. **Technical Details**: Include relevant technical info
|
||||
5. **Reproducibility**: For bugs, include steps to reproduce
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Bug ticket**: "Write a bug description for API timeout"
|
||||
→ h2. Description
|
||||
API requests to the user service are timing out under high load.
|
||||
|
||||
h2. Steps to Reproduce
|
||||
# Send 100+ concurrent requests to /api/users
|
||||
# Observe response times
|
||||
# Note timeout errors after ~30 seconds
|
||||
|
||||
h2. Expected Behavior
|
||||
All requests should complete within 5 seconds.
|
||||
|
||||
h2. Actual Behavior
|
||||
~20% of requests timeout with 504 errors.
|
||||
|
||||
h2. Impact
|
||||
* Affects user registration flow
|
||||
* Estimated 500 users impacted daily
|
||||
|
||||
h2. Technical Details
|
||||
* Endpoint: /api/users
|
||||
* Environment: Production
|
||||
* Load balancer logs attached
|
||||
|
||||
**With variables**: "Create ticket from alert"
|
||||
→ h2. Description
|
||||
Automated ticket from monitoring alert.
|
||||
|
||||
h2. Alert Details
|
||||
* Service: <agent1.service>
|
||||
* Error: <function1.error_message>
|
||||
* Time: <function1.timestamp>
|
||||
|
||||
h2. Actions Required
|
||||
* Investigate root cause
|
||||
* Implement fix
|
||||
* Update monitoring
|
||||
|
||||
### REMEMBER
|
||||
Use Jira wiki markup. Be thorough and structured.`,
|
||||
placeholder: 'Describe the Jira issue...',
|
||||
generationType: 'markdown-content',
|
||||
},
|
||||
},
|
||||
// Delete Issue fields
|
||||
{
|
||||
@@ -314,77 +239,6 @@ Use Jira wiki markup. Be thorough and structured.`,
|
||||
required: true,
|
||||
placeholder: 'Enter JQL query (e.g., project = PROJ AND status = "In Progress")',
|
||||
condition: { field: 'operation', value: 'search' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Jira administrator. Generate JQL (Jira Query Language) queries based on the user's request.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.project_key>\`, \`<function1.result.assignee>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_PROJECT}}\`)
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the JQL query. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JQL query string.
|
||||
|
||||
### JQL SYNTAX GUIDE
|
||||
|
||||
**Field Operators**:
|
||||
- **=** : Equals - \`status = "In Progress"\`
|
||||
- **!=** : Not equals - \`status != Done\`
|
||||
- **~** : Contains - \`summary ~ "bug"\`
|
||||
- **!~** : Does not contain - \`summary !~ "test"\`
|
||||
- **>**, **>=**, **<**, **<=** : Comparisons - \`created > -7d\`
|
||||
- **IN** : In list - \`status IN ("Open", "In Progress")\`
|
||||
- **NOT IN** : Not in list - \`priority NOT IN (Low, Lowest)\`
|
||||
- **IS** : Is empty/null - \`assignee IS EMPTY\`
|
||||
- **IS NOT** : Is not empty - \`resolution IS NOT EMPTY\`
|
||||
- **WAS** : Historical value - \`status WAS "Open"\`
|
||||
- **CHANGED** : Field changed - \`status CHANGED\`
|
||||
|
||||
**Logical Operators**:
|
||||
- **AND** : Both conditions - \`project = PROJ AND status = Open\`
|
||||
- **OR** : Either condition - \`priority = High OR priority = Critical\`
|
||||
- **NOT** : Negate - \`NOT status = Closed\`
|
||||
|
||||
**Common Fields**:
|
||||
- project, issuetype, status, priority, resolution
|
||||
- assignee, reporter, creator, watcher
|
||||
- created, updated, resolved, due, duedate
|
||||
- summary, description, labels, components
|
||||
- fixVersion, affectedVersion, sprint
|
||||
|
||||
**Date Functions**:
|
||||
- \`startOfDay()\`, \`endOfDay()\`
|
||||
- \`startOfWeek()\`, \`endOfWeek()\`
|
||||
- \`startOfMonth()\`, \`endOfMonth()\`
|
||||
- Relative: \`-7d\` (7 days ago), \`-2w\` (2 weeks ago)
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Simple query**: "Find all open bugs in project PROJ"
|
||||
→ project = PROJ AND issuetype = Bug AND status = Open
|
||||
|
||||
**With assignee**: "Find my tasks in progress"
|
||||
→ assignee = currentUser() AND status = "In Progress"
|
||||
|
||||
**Date range**: "Find issues created this week"
|
||||
→ created >= startOfWeek() AND created <= endOfWeek()
|
||||
|
||||
**Complex query**: "Find high priority unresolved issues assigned to me or unassigned"
|
||||
→ priority IN (High, Critical) AND resolution IS EMPTY AND (assignee = currentUser() OR assignee IS EMPTY)
|
||||
|
||||
**With variables**: "Search for issues in a specific project"
|
||||
→ project = <agent1.project_key> AND status != Done
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY the JQL query - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the issues you want to search for...',
|
||||
generationType: 'sql-query',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'maxResults',
|
||||
@@ -908,7 +762,6 @@ Return ONLY the JQL query - no explanations, no markdown, no extra text.`,
|
||||
outputs: {
|
||||
// Common outputs across all Jira operations
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
success: { type: 'boolean', description: 'Whether the operation was successful' },
|
||||
|
||||
// jira_retrieve (read) outputs
|
||||
issueKey: { type: 'string', description: 'Issue key (e.g., PROJ-123)' },
|
||||
|
||||
@@ -333,73 +333,6 @@ export const LinearBlock: BlockConfig<LinearResponse> = {
|
||||
'linear_update_project',
|
||||
],
|
||||
},
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert at writing Linear issue and project descriptions. Create clear, well-structured descriptions using markdown.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.error_message>\`, \`<function1.result>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{REPO_URL}}\`)
|
||||
|
||||
### GUIDELINES
|
||||
1. **Structure**: Use headers, bullet points, and sections
|
||||
2. **Context**: Include relevant background information
|
||||
3. **Acceptance Criteria**: For issues, define what "done" looks like
|
||||
4. **Technical Details**: Include relevant technical information
|
||||
5. **Links**: Reference related issues, PRs, or documentation
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Bug report**: "Create a bug report for login issues"
|
||||
→ ## Description
|
||||
Users are experiencing intermittent login failures when using SSO authentication.
|
||||
|
||||
## Steps to Reproduce
|
||||
1. Navigate to login page
|
||||
2. Click "Sign in with SSO"
|
||||
3. Complete authentication
|
||||
4. Observe error message
|
||||
|
||||
## Expected Behavior
|
||||
User should be logged in and redirected to dashboard.
|
||||
|
||||
## Actual Behavior
|
||||
Error message "Authentication failed" appears approximately 30% of the time.
|
||||
|
||||
## Technical Details
|
||||
- Browser: Chrome 120+
|
||||
- Environment: Production
|
||||
- Error code: AUTH_SSO_TIMEOUT
|
||||
|
||||
**With variables**: "Create issue from error"
|
||||
→ ## Description
|
||||
Automated issue created from error detection.
|
||||
|
||||
## Error Details
|
||||
- Message: <agent1.error_message>
|
||||
- Timestamp: <function1.timestamp>
|
||||
- Service: <function1.service_name>
|
||||
|
||||
## Stack Trace
|
||||
\`\`\`
|
||||
<agent1.stack_trace>
|
||||
\`\`\`
|
||||
|
||||
## Next Steps
|
||||
- [ ] Investigate root cause
|
||||
- [ ] Implement fix
|
||||
- [ ] Add monitoring
|
||||
|
||||
### REMEMBER
|
||||
Use markdown formatting. Be thorough but concise.`,
|
||||
placeholder: 'Describe the issue or project...',
|
||||
generationType: 'markdown-content',
|
||||
},
|
||||
},
|
||||
// Comment body
|
||||
{
|
||||
|
||||
@@ -49,79 +49,6 @@ export const LinkedInBlock: BlockConfig<LinkedInResponse> = {
|
||||
value: 'share_post',
|
||||
},
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert LinkedIn content creator. Write engaging, professional posts optimized for LinkedIn.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.topic>\`, \`<function1.result>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{COMPANY_NAME}}\`)
|
||||
|
||||
### LINKEDIN BEST PRACTICES
|
||||
1. **Hook**: Start with an attention-grabbing first line
|
||||
2. **Formatting**: Use line breaks for readability
|
||||
3. **Length**: 1,300 characters max, but 150-300 often performs best
|
||||
4. **Hashtags**: Use 3-5 relevant hashtags at the end
|
||||
5. **CTA**: Include a call-to-action or question
|
||||
6. **Emojis**: Use sparingly for visual interest
|
||||
|
||||
### GUIDELINES
|
||||
- Write in first person for authenticity
|
||||
- Share insights, not just promotions
|
||||
- Ask questions to drive engagement
|
||||
- Use numbers and data when relevant
|
||||
- Break up text with white space
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Thought leadership**: "Write a post about AI in business"
|
||||
→ AI isn't replacing jobs.
|
||||
|
||||
It's replacing tasks.
|
||||
|
||||
The professionals who thrive in 2024 will be those who:
|
||||
|
||||
→ Learn to work WITH AI, not against it
|
||||
→ Focus on skills AI can't replicate (creativity, empathy, leadership)
|
||||
→ Continuously adapt and upskill
|
||||
|
||||
I've seen teams 10x their productivity by embracing AI tools.
|
||||
|
||||
But here's what nobody talks about: the human skills matter MORE now, not less.
|
||||
|
||||
What's your take? Is your company embracing AI or resisting it?
|
||||
|
||||
#AI #FutureOfWork #Leadership #Technology #Innovation
|
||||
|
||||
**With variables**: "Announce a product launch"
|
||||
→ Exciting news! 🚀
|
||||
|
||||
We just launched <agent1.product_name> - and I couldn't be more proud of our team.
|
||||
|
||||
What makes it special:
|
||||
|
||||
✅ <agent1.feature_1>
|
||||
✅ <agent1.feature_2>
|
||||
✅ <agent1.feature_3>
|
||||
|
||||
This has been months in the making, and seeing it live is incredible.
|
||||
|
||||
Check it out: <function1.product_url>
|
||||
|
||||
What features would you like to see next? 👇
|
||||
|
||||
#ProductLaunch #{{COMPANY_NAME}} #Innovation
|
||||
|
||||
### REMEMBER
|
||||
Write professional but personable content. Use line breaks and keep it scannable.`,
|
||||
placeholder: 'Describe the LinkedIn post you want to create...',
|
||||
generationType: 'social-post',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'visibility',
|
||||
|
||||
@@ -395,45 +395,6 @@ export const MailchimpBlock: BlockConfig = {
|
||||
field: 'operation',
|
||||
value: ['add_member', 'add_or_update_member', 'update_member'],
|
||||
},
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Mailchimp developer. Generate merge fields JSON for Mailchimp members.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.first_name>\`, \`<function1.result.phone>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_SOURCE}}\`)
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
|
||||
|
||||
### MERGE FIELDS GUIDELINES
|
||||
1. **Standard Fields**: FNAME, LNAME, PHONE, ADDRESS, BIRTHDAY
|
||||
2. **Custom Fields**: Use your audience's custom merge field tags
|
||||
3. **Format**: Key is merge tag (uppercase), value is the data
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Basic info**: "Add first name and last name"
|
||||
→ {"FNAME": "John", "LNAME": "Doe"}
|
||||
|
||||
**With address**: "Add full contact details"
|
||||
→ {"FNAME": "John", "LNAME": "Doe", "PHONE": "555-1234", "ADDRESS": {"addr1": "123 Main St", "city": "Boston", "state": "MA", "zip": "02101"}}
|
||||
|
||||
**With variables**: "Use data from previous block"
|
||||
→ {"FNAME": <agent1.first_name>, "LNAME": <agent1.last_name>, "EMAIL": <agent1.email>}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the merge fields you want to set...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'interests',
|
||||
@@ -458,42 +419,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
field: 'operation',
|
||||
value: ['add_member_tags', 'remove_member_tags'],
|
||||
},
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Mailchimp developer. Generate tags JSON for Mailchimp members.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax.
|
||||
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array of tag objects. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### TAGS GUIDELINES
|
||||
1. **Format**: Array of objects with "name" and "status" fields
|
||||
2. **Status**: "active" to add tag, "inactive" to remove tag
|
||||
3. **Name**: The tag name string
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Add tags**: "Add VIP and Newsletter tags"
|
||||
→ [{"name": "VIP", "status": "active"}, {"name": "Newsletter", "status": "active"}]
|
||||
|
||||
**Remove tags**: "Remove Inactive tag"
|
||||
→ [{"name": "Inactive", "status": "inactive"}]
|
||||
|
||||
**With variables**: "Add tag from previous block"
|
||||
→ [{"name": <agent1.tag_name>, "status": "active"}]
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: 'Describe the tags you want to add or remove...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
// Campaign fields
|
||||
{
|
||||
@@ -976,50 +901,6 @@ Return ONLY valid JSON array - no explanations.`,
|
||||
field: 'operation',
|
||||
value: ['create_batch_operation'],
|
||||
},
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Mailchimp developer. Generate batch operations JSON for Mailchimp API.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.email>\`, \`<function1.result.list_id>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{LIST_ID}}\`)
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array of operation objects. Do not include any explanations, markdown formatting, or comments.
|
||||
|
||||
### BATCH OPERATION STRUCTURE
|
||||
Each operation object should have:
|
||||
- **method**: HTTP method (GET, POST, PUT, PATCH, DELETE)
|
||||
- **path**: API endpoint path (e.g., /lists/{list_id}/members)
|
||||
- **operation_id**: Unique identifier for the operation (optional)
|
||||
- **params**: Query parameters (optional)
|
||||
- **body**: Request body as JSON string (for POST/PUT/PATCH)
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Add multiple members**: "Add 3 members to a list"
|
||||
→ [
|
||||
{"method": "POST", "path": "/lists/abc123/members", "body": "{\\"email_address\\":\\"john@example.com\\",\\"status\\":\\"subscribed\\"}"},
|
||||
{"method": "POST", "path": "/lists/abc123/members", "body": "{\\"email_address\\":\\"jane@example.com\\",\\"status\\":\\"subscribed\\"}"},
|
||||
{"method": "POST", "path": "/lists/abc123/members", "body": "{\\"email_address\\":\\"bob@example.com\\",\\"status\\":\\"subscribed\\"}"}
|
||||
]
|
||||
|
||||
**Update multiple members**: "Update member statuses"
|
||||
→ [
|
||||
{"method": "PATCH", "path": "/lists/abc123/members/hash1", "body": "{\\"status\\":\\"unsubscribed\\"}"},
|
||||
{"method": "PATCH", "path": "/lists/abc123/members/hash2", "body": "{\\"status\\":\\"unsubscribed\\"}"}
|
||||
]
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: 'Describe the batch operations you want to perform...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
// Pagination and filtering
|
||||
{
|
||||
|
||||
@@ -44,51 +44,6 @@ export const Mem0Block: BlockConfig<Mem0Response> = {
|
||||
value: 'add',
|
||||
},
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert at creating Mem0 memory messages. Generate a JSON array of message objects for storing memories.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.content>\`, \`<function1.result>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{USER_NAME}}\`)
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array of message objects. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### MESSAGE STRUCTURE
|
||||
Each message must have:
|
||||
- **role**: Either "user" or "assistant"
|
||||
- **content**: The message text/content
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Single user memory**: "Remember that the user's favorite color is blue"
|
||||
→ [{"role": "user", "content": "My favorite color is blue"}]
|
||||
|
||||
**With variables**: "Store the conversation from previous agent"
|
||||
→ [
|
||||
{"role": "user", "content": <agent1.user_message>},
|
||||
{"role": "assistant", "content": <agent1.response>}
|
||||
]
|
||||
|
||||
**Multiple facts**: "Remember user likes coffee and works remotely"
|
||||
→ [
|
||||
{"role": "user", "content": "I love drinking coffee every morning"},
|
||||
{"role": "user", "content": "I work remotely from home"}
|
||||
]
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY a valid JSON array of message objects - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the memories you want to store...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'query',
|
||||
|
||||
@@ -95,45 +95,6 @@ export const MicrosoftExcelBlock: BlockConfig<MicrosoftExcelResponse> = {
|
||||
'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects (e.g., [{"name":"John", "age":30}, {"name":"Jane", "age":25}])',
|
||||
condition: { field: 'operation', value: 'write' },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert at Microsoft Excel data formatting. Generate JSON data for writing to Excel.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.data>\`, \`<function1.result.rows>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, or comments.
|
||||
|
||||
### DATA FORMATS
|
||||
|
||||
**Array of Arrays** (Row-based):
|
||||
[["Header1", "Header2"], ["Value1", "Value2"]]
|
||||
|
||||
**Array of Objects** (Column-mapped):
|
||||
[{"name": "John", "age": 30}, {"name": "Jane", "age": 25}]
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Simple write**: "Write headers and data rows"
|
||||
→ [["Name", "Email", "Age"], ["John Doe", "john@example.com", 30], ["Jane Smith", "jane@example.com", 25]]
|
||||
|
||||
**With variables**: "Write data from previous block"
|
||||
→ [["Name", "Status"], [<agent1.name>, <agent1.status>]]
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: 'Describe the data you want to write...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'valueInputOption',
|
||||
@@ -153,33 +114,6 @@ Return ONLY valid JSON array - no explanations.`,
|
||||
'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects (e.g., [{"name":"John", "age":30}, {"name":"Jane", "age":25}])',
|
||||
condition: { field: 'operation', value: 'update' },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert at Microsoft Excel data formatting. Generate JSON data for updating Excel.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax.
|
||||
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, or comments.
|
||||
|
||||
### EXAMPLES
|
||||
**Update cells**: "Update with new values"
|
||||
→ [["Updated Name", "Updated Email"], ["John Doe", "john.new@example.com"]]
|
||||
|
||||
**With variables**: "Update with data from previous block"
|
||||
→ [[<agent1.name>, <agent1.email>]]
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: 'Describe the data you want to update...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'valueInputOption',
|
||||
@@ -199,33 +133,6 @@ Return ONLY valid JSON array - no explanations.`,
|
||||
'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects (e.g., [{"name":"John", "age":30}, {"name":"Jane", "age":25}])',
|
||||
condition: { field: 'operation', value: 'table_add' },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert at Microsoft Excel data formatting. Generate JSON data for adding rows to an Excel table.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax.
|
||||
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, or comments.
|
||||
|
||||
### EXAMPLES
|
||||
**Add rows**: "Add new data rows to the table"
|
||||
→ [["John Doe", "john@example.com", "Active"], ["Jane Smith", "jane@example.com", "Active"]]
|
||||
|
||||
**With variables**: "Add data from previous block"
|
||||
→ [[<agent1.name>, <agent1.email>, <agent1.status>]]
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON array - no explanations.`,
|
||||
placeholder: 'Describe the data you want to add...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
|
||||
@@ -245,44 +245,6 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
|
||||
type: 'long-input',
|
||||
placeholder: 'Enter checklist as JSON object (optional)',
|
||||
condition: { field: 'operation', value: ['update_task_details'] },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Microsoft Planner developer. Generate checklist JSON for Planner task details.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.item_title>\`, \`<function1.result.status>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY valid JSON. Do not include any explanations, markdown formatting, or comments.
|
||||
|
||||
### CHECKLIST FORMAT
|
||||
Microsoft Planner checklists use a specific JSON format where each item has a unique key (typically a GUID):
|
||||
{
|
||||
"item-id-1": {"@odata.type": "#microsoft.graph.plannerChecklistItem", "title": "Item 1", "isChecked": false},
|
||||
"item-id-2": {"@odata.type": "#microsoft.graph.plannerChecklistItem", "title": "Item 2", "isChecked": true}
|
||||
}
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Simple checklist**: "Add two items - Review code and Write tests"
|
||||
→ {"item1": {"@odata.type": "#microsoft.graph.plannerChecklistItem", "title": "Review code", "isChecked": false}, "item2": {"@odata.type": "#microsoft.graph.plannerChecklistItem", "title": "Write tests", "isChecked": false}}
|
||||
|
||||
**With variables**: "Add items from previous block"
|
||||
→ {"item1": {"@odata.type": "#microsoft.graph.plannerChecklistItem", "title": <agent1.task_item>, "isChecked": false}}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON - no explanations.`,
|
||||
placeholder: 'Describe the checklist items...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
|
||||
// References for task details
|
||||
@@ -292,41 +254,6 @@ Return ONLY valid JSON - no explanations.`,
|
||||
type: 'long-input',
|
||||
placeholder: 'Enter references as JSON object (optional)',
|
||||
condition: { field: 'operation', value: ['update_task_details'] },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Microsoft Planner developer. Generate references JSON for Planner task details.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.url>\`, \`<function1.result.link>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DOCS_BASE_URL}}\`)
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY valid JSON. Do not include any explanations, markdown formatting, or comments.
|
||||
|
||||
### REFERENCES FORMAT
|
||||
Microsoft Planner references use URL-encoded keys pointing to the reference URL:
|
||||
{
|
||||
"https%3A//example%2Ecom/doc": {"@odata.type": "#microsoft.graph.plannerExternalReference", "alias": "Doc Name", "type": "Other"},
|
||||
"https%3A//github%2Ecom/repo": {"@odata.type": "#microsoft.graph.plannerExternalReference", "alias": "GitHub Repo", "type": "Other"}
|
||||
}
|
||||
|
||||
Note: The key must be URL-encoded (: becomes %3A, . becomes %2E, / becomes %2F)
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Add document link**: "Link to documentation"
|
||||
→ {"https%3A%2F%2Fdocs%2Eexample%2Ecom%2Fguide": {"@odata.type": "#microsoft.graph.plannerExternalReference", "alias": "User Guide", "type": "Other"}}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON with URL-encoded keys - no explanations.`,
|
||||
placeholder: 'Describe the references to add...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
|
||||
// Preview Type
|
||||
|
||||
@@ -216,62 +216,6 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
|
||||
],
|
||||
},
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert at writing Microsoft Teams messages. Compose professional, clear messages for workplace communication.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.update>\`, \`<function1.result>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{TEAM_NAME}}\`)
|
||||
|
||||
### GUIDELINES
|
||||
1. **Tone**: Professional workplace communication
|
||||
2. **Structure**: Use clear paragraphs and bullet points
|
||||
3. **Mentions**: Use @mentions when needed to notify team members
|
||||
4. **Brevity**: Be concise but informative
|
||||
5. **Action Items**: Clearly state any required actions
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Team update**: "Share project status update"
|
||||
→ 📊 Project Status Update
|
||||
|
||||
Hi team,
|
||||
|
||||
Here's our weekly progress update:
|
||||
|
||||
✅ Completed:
|
||||
• Feature A development
|
||||
• Code review for PR #123
|
||||
|
||||
🔄 In Progress:
|
||||
• Testing phase
|
||||
• Documentation updates
|
||||
|
||||
📅 Next Steps:
|
||||
• Release scheduled for Friday
|
||||
|
||||
Let me know if you have questions!
|
||||
|
||||
**With variables**: "Share automated alert"
|
||||
→ ⚠️ System Alert
|
||||
|
||||
Service: <agent1.service_name>
|
||||
Status: <function1.status>
|
||||
Time: <function1.timestamp>
|
||||
|
||||
Please review and take action if needed.
|
||||
|
||||
### REMEMBER
|
||||
Write professional workplace messages. Be clear and actionable.`,
|
||||
placeholder: 'Describe the Teams message you want to send...',
|
||||
generationType: 'message-content',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'reactionType',
|
||||
|
||||
@@ -101,13 +101,6 @@ export const MongoDBBlock: BlockConfig<MongoDBResponse> = {
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.user_id>\`, \`<function1.result.status>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_STATUS}}\`)
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the MongoDB query filter as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object that can be used directly in a MongoDB find() operation.
|
||||
|
||||
@@ -231,13 +224,6 @@ Return ONLY the MongoDB query filter as valid JSON - no explanations, no markdow
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.status>\`, \`<function1.result.limit>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_LIMIT}}\`)
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the aggregation pipeline as a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
@@ -478,13 +464,10 @@ Return ONLY the JSON array pipeline - no explanations, no markdown, no extra tex
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax.
|
||||
|
||||
### EXAMPLES
|
||||
Newest first: {"createdAt": -1}
|
||||
Alphabetical: {"name": 1}
|
||||
Dynamic field: {<agent1.sort_field>: <agent1.sort_order>}
|
||||
Multiple fields: {"category": 1, "price": -1}
|
||||
|
||||
Use 1 for ascending, -1 for descending. Return ONLY valid JSON.`,
|
||||
placeholder: 'Describe how you want to sort the results...',
|
||||
@@ -506,13 +489,9 @@ Use 1 for ascending, -1 for descending. Return ONLY valid JSON.`,
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax.
|
||||
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
|
||||
|
||||
### EXAMPLES
|
||||
Simple user: [{"name": "John Doe", "email": "john@example.com", "active": true}]
|
||||
With variables: [{"name": <agent1.name>, "email": <agent1.email>, "source": "{{DATA_SOURCE}}"}]
|
||||
With nested data: [{"user": {"name": "Jane", "profile": {"age": 25, "city": "NYC"}}, "status": "active"}]
|
||||
Multiple docs: [{"name": "User1", "type": "admin"}, {"name": "User2", "type": "user"}]
|
||||
|
||||
Return ONLY valid JSON array - no explanations.`,
|
||||
@@ -535,11 +514,6 @@ Return ONLY valid JSON array - no explanations.`,
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.user_id>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the MongoDB query filter as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object that will identify which documents to update.
|
||||
|
||||
@@ -731,11 +705,6 @@ Generate the MongoDB update operation that safely and accurately fulfills the us
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.user_id>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the MongoDB query filter as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object that will identify which documents to delete.
|
||||
|
||||
|
||||
@@ -256,47 +256,6 @@ Return ONLY the SQL query - no explanations, no markdown, no extra text.`,
|
||||
placeholder: '{\n "name": "John Doe",\n "email": "john@example.com",\n "active": true\n}',
|
||||
condition: { field: 'operation', value: 'insert' },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert MySQL developer. Generate JSON data for INSERT operations.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.content>\`, \`<function1.result.id>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{API_KEY}}\`, \`{{DATABASE_NAME}}\`)
|
||||
|
||||
Do NOT wrap variable references in quotes - they will be resolved before execution.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the data as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
|
||||
|
||||
### DATA GUIDELINES
|
||||
1. **Structure**: Use field-value pairs matching your table columns
|
||||
2. **Data Types**: Use appropriate types (strings, numbers, booleans, null)
|
||||
3. **Naming**: Use column names exactly as they appear in your table
|
||||
4. **Required Fields**: Include all required (NOT NULL) columns
|
||||
5. **MySQL Types**: Use 1/0 or true/false for booleans, ISO date strings for dates
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Simple insert**: "Insert user with name, email, and active status"
|
||||
→ {"name": "John Doe", "email": "john@example.com", "active": true}
|
||||
|
||||
**With variables**: "Insert user data from previous agent block"
|
||||
→ {"name": <agent1.name>, "email": <agent1.email>, "created_by": "{{SYSTEM_USER}}"}
|
||||
|
||||
**With dates**: "Insert order with customer_id, total, and order_date"
|
||||
→ {"customer_id": 123, "total": 99.99, "order_date": "2024-01-15"}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the data you want to insert...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
// Set clause for updates
|
||||
{
|
||||
@@ -306,46 +265,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
placeholder: '{\n "name": "Jane Doe",\n "email": "jane@example.com"\n}',
|
||||
condition: { field: 'operation', value: 'update' },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert MySQL developer. Generate JSON data for UPDATE operations.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.content>\`, \`<function1.result.id>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{API_KEY}}\`, \`{{DEFAULT_STATUS}}\`)
|
||||
|
||||
Do NOT wrap variable references in quotes - they will be resolved before execution.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the data as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object containing only the fields to update.
|
||||
|
||||
### DATA GUIDELINES
|
||||
1. **Structure**: Include only the fields you want to update
|
||||
2. **Data Types**: Use appropriate types (strings, numbers, booleans, null)
|
||||
3. **Naming**: Use column names exactly as they appear in your table
|
||||
4. **Partial Updates**: You don't need to include all columns, only those being changed
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Update single field**: "Update user's email"
|
||||
→ {"email": "newemail@example.com"}
|
||||
|
||||
**With variables**: "Update user with data from previous block"
|
||||
→ {"email": <agent1.new_email>, "updated_at": <function1.timestamp>}
|
||||
|
||||
**Set to null**: "Clear the user's phone number"
|
||||
→ {"phone": null}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the fields you want to update...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
// Where clause for update/delete
|
||||
{
|
||||
|
||||
@@ -92,13 +92,6 @@ export const Neo4jBlock: BlockConfig<Neo4jResponse> = {
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.user_id>\`, \`<function1.result.name>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_LIMIT}}\`)
|
||||
|
||||
Use parameters ($paramName) for values in Cypher queries and define them in the Parameters field.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY the Cypher query. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw Cypher query.
|
||||
|
||||
|
||||
@@ -103,73 +103,6 @@ export const NotionBlock: BlockConfig<NotionResponse> = {
|
||||
value: 'notion_write',
|
||||
},
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert at writing Notion content. Create well-structured, readable content using markdown.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.data>\`, \`<function1.result>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{TEAM_NAME}}\`)
|
||||
|
||||
### NOTION FORMATTING
|
||||
Notion supports markdown:
|
||||
- **bold** and *italic*
|
||||
- # ## ### headers
|
||||
- - bullet lists
|
||||
- 1. numbered lists
|
||||
- - [ ] todo items
|
||||
- > callouts/quotes
|
||||
- \`code\` and \`\`\`code blocks\`\`\`
|
||||
- [links](url)
|
||||
- --- dividers
|
||||
|
||||
### GUIDELINES
|
||||
1. **Structure**: Use headers to organize content
|
||||
2. **Scannable**: Use bullet points and short paragraphs
|
||||
3. **Visual**: Use callouts and dividers for emphasis
|
||||
4. **Actionable**: Include todo items where relevant
|
||||
5. **Links**: Reference related pages or resources
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Meeting notes**: "Write meeting notes template"
|
||||
→ # Meeting Notes
|
||||
|
||||
## Date
|
||||
<function1.date>
|
||||
|
||||
## Attendees
|
||||
- <agent1.attendees>
|
||||
|
||||
## Agenda
|
||||
1. Review previous action items
|
||||
2. Project updates
|
||||
3. New business
|
||||
|
||||
## Discussion Points
|
||||
- Point 1
|
||||
- Point 2
|
||||
|
||||
## Action Items
|
||||
- [ ] Action 1 - @owner
|
||||
- [ ] Action 2 - @owner
|
||||
|
||||
## Next Steps
|
||||
Schedule follow-up for next week.
|
||||
|
||||
---
|
||||
*Notes taken by {{USER_NAME}}*
|
||||
|
||||
### REMEMBER
|
||||
Use markdown formatting. Create scannable, well-organized content.`,
|
||||
placeholder: 'Describe the content you want to write...',
|
||||
generationType: 'markdown-content',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'content',
|
||||
@@ -181,38 +114,6 @@ Use markdown formatting. Create scannable, well-organized content.`,
|
||||
value: 'notion_create_page',
|
||||
},
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert at writing Notion page content. Create well-structured, readable content using markdown.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax
|
||||
|
||||
### NOTION FORMATTING
|
||||
- **bold** and *italic*
|
||||
- # ## ### headers
|
||||
- - bullet lists and 1. numbered lists
|
||||
- - [ ] todo items
|
||||
- > callouts/quotes
|
||||
- \`code\` and \`\`\`code blocks\`\`\`
|
||||
|
||||
### GUIDELINES
|
||||
1. Use headers to organize content
|
||||
2. Keep paragraphs short and scannable
|
||||
3. Use bullet points for lists
|
||||
4. Include action items where relevant
|
||||
|
||||
### REMEMBER
|
||||
Use markdown. Create well-organized, readable content.`,
|
||||
placeholder: 'Describe the page content...',
|
||||
generationType: 'markdown-content',
|
||||
},
|
||||
},
|
||||
// Query Database Fields
|
||||
{
|
||||
@@ -230,55 +131,6 @@ Use markdown. Create well-organized, readable content.`,
|
||||
placeholder: 'Enter filter conditions as JSON (optional)',
|
||||
condition: { field: 'operation', value: 'notion_query_database' },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Notion API developer. Generate Notion database filter JSON based on the user's request.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.status>\`, \`<function1.result.date>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_STATUS}}\`)
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY valid JSON filter. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
|
||||
|
||||
### NOTION FILTER SYNTAX
|
||||
Filters use property-based conditions with operators:
|
||||
- **equals, does_not_equal**: Exact match
|
||||
- **contains, does_not_contain**: Text contains
|
||||
- **starts_with, ends_with**: Text patterns
|
||||
- **is_empty, is_not_empty**: Existence check
|
||||
- **greater_than, less_than, etc.**: Comparisons for numbers/dates
|
||||
|
||||
Compound filters use:
|
||||
- **and**: Array of conditions (all must match)
|
||||
- **or**: Array of conditions (any must match)
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Simple filter**: "Filter where Status is Done"
|
||||
→ {"property": "Status", "status": {"equals": "Done"}}
|
||||
|
||||
**With variables**: "Filter by status from previous block"
|
||||
→ {"property": "Status", "status": {"equals": <agent1.selected_status>}}
|
||||
|
||||
**AND conditions**: "Status is Active and Priority is High"
|
||||
→ {"and": [
|
||||
{"property": "Status", "status": {"equals": "Active"}},
|
||||
{"property": "Priority", "select": {"equals": "High"}}
|
||||
]}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the filter conditions...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'sorts',
|
||||
@@ -286,49 +138,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
type: 'long-input',
|
||||
placeholder: 'Enter sort criteria as JSON array (optional)',
|
||||
condition: { field: 'operation', value: 'notion_query_database' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Notion API developer. Generate Notion database sort criteria JSON array.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.sort_field>\`, \`<function1.result.direction>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_SORT_FIELD}}\`)
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array of sort objects. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
|
||||
|
||||
### SORT STRUCTURE
|
||||
Each sort object has:
|
||||
- **property**: Property name to sort by
|
||||
- **direction**: "ascending" or "descending"
|
||||
|
||||
Or for timestamp sorts:
|
||||
- **timestamp**: "created_time" or "last_edited_time"
|
||||
- **direction**: "ascending" or "descending"
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Sort by property**: "Sort by Name ascending"
|
||||
→ [{"property": "Name", "direction": "ascending"}]
|
||||
|
||||
**With variables**: "Sort by field from previous block"
|
||||
→ [{"property": <agent1.sort_field>, "direction": <agent1.sort_direction>}]
|
||||
|
||||
**Sort by date**: "Sort by created time, newest first"
|
||||
→ [{"timestamp": "created_time", "direction": "descending"}]
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY a valid JSON array - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe how you want to sort...',
|
||||
generationType: 'json-array',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'pageSize',
|
||||
@@ -379,63 +188,6 @@ Return ONLY a valid JSON array - no explanations, no markdown, no extra text.`,
|
||||
type: 'long-input',
|
||||
placeholder: 'Enter database properties as JSON object',
|
||||
condition: { field: 'operation', value: 'notion_create_database' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert Notion API developer. Generate Notion database properties schema JSON.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.property_name>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_STATUS_OPTIONS}}\`)
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY valid JSON properties object. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
|
||||
|
||||
### PROPERTY TYPES
|
||||
Common Notion property types:
|
||||
- **title**: Primary name field
|
||||
- **rich_text**: Multi-line text
|
||||
- **number**: Numeric value
|
||||
- **select**: Single select dropdown
|
||||
- **multi_select**: Multiple select tags
|
||||
- **date**: Date/datetime
|
||||
- **checkbox**: Boolean checkbox
|
||||
- **url**: URL link
|
||||
- **email**: Email address
|
||||
- **phone_number**: Phone number
|
||||
- **status**: Status field with groups
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Basic task database**: "Create properties for a task tracker"
|
||||
→ {
|
||||
"Name": {"title": {}},
|
||||
"Status": {"status": {}},
|
||||
"Due Date": {"date": {}},
|
||||
"Priority": {"select": {"options": [{"name": "High"}, {"name": "Medium"}, {"name": "Low"}]}},
|
||||
"Completed": {"checkbox": {}}
|
||||
}
|
||||
|
||||
**Contact database**: "Create properties for contacts"
|
||||
→ {
|
||||
"Name": {"title": {}},
|
||||
"Email": {"email": {}},
|
||||
"Phone": {"phone_number": {}},
|
||||
"Company": {"rich_text": {}},
|
||||
"Website": {"url": {}}
|
||||
}
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the database properties...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
|
||||
@@ -92,38 +92,10 @@ export const OneDriveBlock: BlockConfig<OneDriveResponse> = {
|
||||
},
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert at Excel data formatting. Generate JSON data for creating an Excel file in OneDrive.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.data>\`, \`<function1.result.rows>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax
|
||||
|
||||
Do NOT wrap variable references in quotes for non-string values.
|
||||
|
||||
### CRITICAL INSTRUCTION
|
||||
Return ONLY a valid JSON array of arrays. Do not include any explanations, markdown formatting, or comments.
|
||||
|
||||
### DATA FORMAT
|
||||
Use array of arrays format where each inner array represents a row:
|
||||
[["Header1", "Header2"], ["Value1", "Value2"], ["Value3", "Value4"]]
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Simple table**: "Create a table with names and emails"
|
||||
→ [["Name", "Email"], ["John Doe", "john@example.com"], ["Jane Smith", "jane@example.com"]]
|
||||
|
||||
**With variables**: "Create table from previous block data"
|
||||
→ [["Name", "Status"], [<agent1.name>, <agent1.status>]]
|
||||
|
||||
### REMEMBER
|
||||
Return ONLY valid JSON array of arrays - no explanations.`,
|
||||
prompt:
|
||||
'Generate a JSON array of arrays that can be written directly into an Excel worksheet.',
|
||||
placeholder: 'Describe the table you want to generate...',
|
||||
generationType: 'json-array',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
required: false,
|
||||
},
|
||||
|
||||
@@ -94,53 +94,6 @@ export const OutlookBlock: BlockConfig<OutlookResponse> = {
|
||||
placeholder: 'Email content',
|
||||
condition: { field: 'operation', value: ['send_outlook', 'draft_outlook'] },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
maintainHistory: true,
|
||||
prompt: `You are an expert email writer. Compose professional, clear, and effective email content.
|
||||
|
||||
### CONTEXT
|
||||
{context}
|
||||
|
||||
### VARIABLE RESOLUTION
|
||||
You can reference variables from previous blocks and environment variables:
|
||||
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.customer_name>\`, \`<function1.result.details>\`)
|
||||
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{COMPANY_NAME}}\`)
|
||||
|
||||
### GUIDELINES
|
||||
1. **Tone**: Match the appropriate tone (formal, friendly, urgent) based on context
|
||||
2. **Structure**: Use clear paragraphs, bullet points where helpful
|
||||
3. **Brevity**: Be concise but complete
|
||||
4. **Call to Action**: Include clear next steps when appropriate
|
||||
|
||||
### EXAMPLES
|
||||
|
||||
**Meeting request**: "Request a meeting to discuss Q4 planning"
|
||||
→ Hi,
|
||||
|
||||
I'd like to schedule a meeting to discuss our Q4 planning and goals.
|
||||
|
||||
Would you be available this week for a 30-minute call? I'm flexible on timing and can adjust to your schedule.
|
||||
|
||||
Please let me know what works best for you.
|
||||
|
||||
Thanks
|
||||
|
||||
**With variables**: "Follow up on support ticket"
|
||||
→ Dear <agent1.customer_name>,
|
||||
|
||||
Thank you for contacting us regarding ticket #<function1.ticket_id>.
|
||||
|
||||
Our team has reviewed your request and we're working on a resolution. You can expect an update within 24 hours.
|
||||
|
||||
Best regards,
|
||||
{{COMPANY_NAME}} Support
|
||||
|
||||
### REMEMBER
|
||||
Write the email body only - no subject line unless specifically requested.`,
|
||||
placeholder: 'Describe the email you want to write...',
|
||||
generationType: 'email-content',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'contentType',
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user