mirror of
https://github.com/simstudioai/sim.git
synced 2026-02-19 02:34:37 -05:00
added advanced fields for vercel and youtube, added cloudflare and dataverse block
This commit is contained in:
@@ -4407,6 +4407,161 @@ export function DatadogIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function MicrosoftDataverseIcon(props: SVGProps<SVGSVGElement>) {
|
||||
const id = useId()
|
||||
const clip0 = `dataverse_clip0_${id}`
|
||||
const clip1 = `dataverse_clip1_${id}`
|
||||
const clip2 = `dataverse_clip2_${id}`
|
||||
const paint0 = `dataverse_paint0_${id}`
|
||||
const paint1 = `dataverse_paint1_${id}`
|
||||
const paint2 = `dataverse_paint2_${id}`
|
||||
const paint3 = `dataverse_paint3_${id}`
|
||||
const paint4 = `dataverse_paint4_${id}`
|
||||
const paint5 = `dataverse_paint5_${id}`
|
||||
const paint6 = `dataverse_paint6_${id}`
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
width='96'
|
||||
height='96'
|
||||
viewBox='0 0 96 96'
|
||||
fill='none'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
>
|
||||
<g clipPath={`url(#${clip0})`}>
|
||||
<g clipPath={`url(#${clip1})`}>
|
||||
<g clipPath={`url(#${clip2})`}>
|
||||
<path
|
||||
d='M13.8776 21.8242C29.1033 8.13791 49.7501 8.1861 62.955 18.9134C74.9816 28.6836 77.4697 44.3159 70.851 55.7801C64.2321 67.2443 52.5277 70.1455 39.5011 62.6247L31.7286 76.087L31.7234 76.0862C27.4181 83.5324 17.8937 86.0828 10.4437 81.7817C7.45394 80.0556 5.25322 77.4879 3.96665 74.551L3.96096 74.5511C-4.07832 55.7804 0.200745 34.1184 13.8776 21.8242Z'
|
||||
fill={`url(#${paint0})`}
|
||||
/>
|
||||
<path
|
||||
d='M13.8776 21.8242C29.1033 8.13791 49.7501 8.1861 62.955 18.9134C74.9816 28.6836 77.4697 44.3159 70.851 55.7801C64.2321 67.2443 52.5277 70.1455 39.5011 62.6247L31.7286 76.087L31.7234 76.0862C27.4181 83.5324 17.8937 86.0828 10.4437 81.7817C7.45394 80.0556 5.25322 77.4879 3.96665 74.551L3.96096 74.5511C-4.07832 55.7804 0.200745 34.1184 13.8776 21.8242Z'
|
||||
fill={`url(#${paint1})`}
|
||||
fillOpacity='0.8'
|
||||
/>
|
||||
<path
|
||||
d='M85.4327 14.2231C88.4528 15.9668 90.6686 18.569 91.9494 21.5433L91.9533 21.5444C99.9406 40.2943 95.6533 61.9068 81.9983 74.1814C66.7726 87.8677 46.1257 87.8196 32.9209 77.0923C20.8945 67.3221 18.4062 51.6897 25.0249 40.2256C31.6438 28.7614 43.3482 25.8601 56.3748 33.381L64.1434 19.9255L64.1482 19.9249C68.4516 12.4736 77.9805 9.92084 85.4327 14.2231Z'
|
||||
fill={`url(#${paint2})`}
|
||||
/>
|
||||
<path
|
||||
d='M85.4327 14.2231C88.4528 15.9668 90.6686 18.569 91.9494 21.5433L91.9533 21.5444C99.9406 40.2943 95.6533 61.9068 81.9983 74.1814C66.7726 87.8677 46.1257 87.8196 32.9209 77.0923C20.8945 67.3221 18.4062 51.6897 25.0249 40.2256C31.6438 28.7614 43.3482 25.8601 56.3748 33.381L64.1434 19.9255L64.1482 19.9249C68.4516 12.4736 77.9805 9.92084 85.4327 14.2231Z'
|
||||
fill={`url(#${paint3})`}
|
||||
fillOpacity='0.9'
|
||||
/>
|
||||
<path
|
||||
d='M39.5041 62.6261C52.5307 70.1469 64.2352 67.2456 70.8541 55.7814C77.2488 44.7055 75.1426 29.7389 64.147 19.9271L56.3791 33.3814L39.5041 62.6261Z'
|
||||
fill={`url(#${paint4})`}
|
||||
/>
|
||||
<path
|
||||
d='M56.3794 33.3815C43.3528 25.8607 31.6482 28.762 25.0294 40.2262C18.6347 51.3021 20.7409 66.2687 31.7364 76.0806L39.5043 62.6262L56.3794 33.3815Z'
|
||||
fill={`url(#${paint5})`}
|
||||
/>
|
||||
<path
|
||||
d='M33.3215 56.4453C37.9837 64.5204 48.3094 67.2872 56.3846 62.625C64.4598 57.9628 67.2266 47.6371 62.5643 39.5619C57.9021 31.4867 47.5764 28.72 39.5013 33.3822C31.4261 38.0444 28.6593 48.3701 33.3215 56.4453Z'
|
||||
fill={`url(#${paint6})`}
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<radialGradient
|
||||
id={paint0}
|
||||
cx='0'
|
||||
cy='0'
|
||||
r='1'
|
||||
gradientUnits='userSpaceOnUse'
|
||||
gradientTransform='translate(46.0001 49.4996) rotate(-148.717) scale(46.2195 47.5359)'
|
||||
>
|
||||
<stop offset='0.465088' stopColor='#09442A' />
|
||||
<stop offset='0.70088' stopColor='#136C6C' />
|
||||
<stop offset='1' stopColor='#22918B' />
|
||||
</radialGradient>
|
||||
<radialGradient
|
||||
id={paint1}
|
||||
cx='0'
|
||||
cy='0'
|
||||
r='1'
|
||||
gradientUnits='userSpaceOnUse'
|
||||
gradientTransform='translate(50.0001 32.4996) rotate(123.57) scale(66.0095 46.5498)'
|
||||
>
|
||||
<stop offset='0.718705' stopColor='#1A7F7C' stopOpacity='0' />
|
||||
<stop offset='1' stopColor='#16BBDA' />
|
||||
</radialGradient>
|
||||
<radialGradient
|
||||
id={paint2}
|
||||
cx='0'
|
||||
cy='0'
|
||||
r='1'
|
||||
gradientUnits='userSpaceOnUse'
|
||||
gradientTransform='translate(50.4999 44.5001) rotate(30.75) scale(45.9618 44.5095)'
|
||||
>
|
||||
<stop offset='0.358097' stopColor='#136C6C' />
|
||||
<stop offset='0.789474' stopColor='#42B870' />
|
||||
<stop offset='1' stopColor='#76D45E' />
|
||||
</radialGradient>
|
||||
<radialGradient
|
||||
id={paint3}
|
||||
cx='0'
|
||||
cy='0'
|
||||
r='1'
|
||||
gradientTransform='matrix(42.5 -36.0002 31.1824 36.8127 49.4998 55.5001)'
|
||||
gradientUnits='userSpaceOnUse'
|
||||
>
|
||||
<stop offset='0.583166' stopColor='#76D45E' stopOpacity='0' />
|
||||
<stop offset='1' stopColor='#C8F5B7' />
|
||||
</radialGradient>
|
||||
<radialGradient
|
||||
id={paint4}
|
||||
cx='0'
|
||||
cy='0'
|
||||
r='1'
|
||||
gradientUnits='userSpaceOnUse'
|
||||
gradientTransform='translate(47.5 48) rotate(-58.9042) scale(32.6898)'
|
||||
>
|
||||
<stop offset='0.486266' stopColor='#22918B' />
|
||||
<stop offset='0.729599' stopColor='#42B870' />
|
||||
<stop offset='1' stopColor='#43E5CA' />
|
||||
</radialGradient>
|
||||
<radialGradient
|
||||
id={paint5}
|
||||
cx='0'
|
||||
cy='0'
|
||||
r='1'
|
||||
gradientUnits='userSpaceOnUse'
|
||||
gradientTransform='translate(47.3833 49.0077) rotate(119.859) scale(31.1328 29.4032)'
|
||||
>
|
||||
<stop offset='0.459553' stopColor='#08494E' />
|
||||
<stop offset='0.742242' stopColor='#1A7F7C' />
|
||||
<stop offset='1' stopColor='#309C61' />
|
||||
</radialGradient>
|
||||
<radialGradient
|
||||
id={paint6}
|
||||
cx='0'
|
||||
cy='0'
|
||||
r='1'
|
||||
gradientUnits='userSpaceOnUse'
|
||||
gradientTransform='translate(52.5 40) rotate(120.784) scale(27.3542)'
|
||||
>
|
||||
<stop stopColor='#C8F5B7' />
|
||||
<stop offset='0.24583' stopColor='#98F0B0' />
|
||||
<stop offset='0.643961' stopColor='#52D17C' />
|
||||
<stop offset='1' stopColor='#119FC5' />
|
||||
</radialGradient>
|
||||
<clipPath id={clip0}>
|
||||
<rect width='96' height='96' fill='white' />
|
||||
</clipPath>
|
||||
<clipPath id={clip1}>
|
||||
<rect width='96' height='96' fill='white' />
|
||||
</clipPath>
|
||||
<clipPath id={clip2}>
|
||||
<rect width='95.9998' height='96' fill='white' />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function KalshiIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 78 20' fill='currentColor' xmlns='http://www.w3.org/2000/svg'>
|
||||
@@ -5547,3 +5702,18 @@ export function VercelIcon(props: SVGProps<SVGSVGElement>) {
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function CloudflareIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'>
|
||||
<path
|
||||
fill='#f38020'
|
||||
d='M331 326c11-26-4-38-19-38l-148-2c-4 0-4-6 1-7l150-2c17-1 37-15 43-33 0 0 10-21 9-24a97 97 0 0 0-187-11c-38-25-78 9-69 46-48 3-65 46-60 72 0 1 1 2 3 2h274c1 0 3-1 3-3z'
|
||||
/>
|
||||
<path
|
||||
fill='#faae40'
|
||||
d='M381 224c-4 0-6-1-7 1l-5 21c-5 16 3 30 20 31l32 2c4 0 4 6-1 7l-33 1c-36 4-46 39-46 39 0 2 0 3 2 3h113l3-2a81 81 0 0 0-78-103'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
CirclebackIcon,
|
||||
ClayIcon,
|
||||
ClerkIcon,
|
||||
CloudflareIcon,
|
||||
ConfluenceIcon,
|
||||
CursorIcon,
|
||||
DatadogIcon,
|
||||
@@ -71,6 +72,7 @@ import {
|
||||
MailgunIcon,
|
||||
MailServerIcon,
|
||||
Mem0Icon,
|
||||
MicrosoftDataverseIcon,
|
||||
MicrosoftExcelIcon,
|
||||
MicrosoftOneDriveIcon,
|
||||
MicrosoftPlannerIcon,
|
||||
@@ -156,6 +158,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
circleback: CirclebackIcon,
|
||||
clay: ClayIcon,
|
||||
clerk: ClerkIcon,
|
||||
cloudflare: CloudflareIcon,
|
||||
confluence_v2: ConfluenceIcon,
|
||||
cursor_v2: CursorIcon,
|
||||
datadog: DatadogIcon,
|
||||
@@ -209,6 +212,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
mailgun: MailgunIcon,
|
||||
mem0: Mem0Icon,
|
||||
memory: BrainIcon,
|
||||
microsoft_dataverse: MicrosoftDataverseIcon,
|
||||
microsoft_excel_v2: MicrosoftExcelIcon,
|
||||
microsoft_planner: MicrosoftPlannerIcon,
|
||||
microsoft_teams: MicrosoftTeamsIcon,
|
||||
|
||||
424
apps/docs/content/docs/en/tools/cloudflare.mdx
Normal file
424
apps/docs/content/docs/en/tools/cloudflare.mdx
Normal file
@@ -0,0 +1,424 @@
|
||||
---
|
||||
title: Cloudflare
|
||||
description: Manage DNS, domains, certificates, and cache
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="cloudflare"
|
||||
color="#F5F6FA"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[Cloudflare](https://cloudflare.com/) is a global cloud platform that provides content delivery, domain management, cybersecurity, and performance services for websites and applications.
|
||||
|
||||
In Sim, the Cloudflare integration empowers your agents to automate the management of DNS records, SSL/TLS certificates, domains (zones), cache, zone settings, and more through easy-to-use API tools. Agents can securely list and edit domains, update DNS records, monitor analytics, and manage security and performance—all as part of your automated workflows.
|
||||
|
||||
With Cloudflare, you can:
|
||||
|
||||
- **Manage DNS and Domains**: List all your domains (zones), view zone details, and fully control DNS records from your automated agent workflows.
|
||||
- **Handle SSL/TLS Certificates and Settings**: Issue, renew, or list certificates and adjust security and performance settings for your sites.
|
||||
- **Purge Cache and Analyze Traffic**: Instantly purge edge cache and review real-time DNS analytics directly within your Sim agent processes.
|
||||
- **Automate Security and Operations**: Use agents to programmatically manage zones, update settings, and streamline repetitive Cloudflare tasks.
|
||||
|
||||
This integration enables streamlined, secure management of your site's infrastructure from within Sim. Your agents can integrate Cloudflare operations directly into processes—keeping DNS records up-to-date, responding to security events, improving site performance, and automating large-scale site and account administration.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Cloudflare into the workflow. Manage zones (domains), DNS records, SSL/TLS certificates, zone settings, DNS analytics, and cache purging via the Cloudflare API.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `cloudflare_list_zones`
|
||||
|
||||
Lists all zones (domains) in the Cloudflare account.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `name` | string | No | Filter zones by domain name \(e.g., "example.com"\) |
|
||||
| `status` | string | No | Filter by zone status \(e.g., "active", "pending", "initializing"\) |
|
||||
| `page` | number | No | Page number for pagination \(default: 1\) |
|
||||
| `per_page` | number | No | Number of zones per page \(default: 20, max: 50\) |
|
||||
| `apiKey` | string | Yes | Cloudflare API Token |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `zones` | array | List of zones/domains |
|
||||
| ↳ `id` | string | Zone ID |
|
||||
| ↳ `name` | string | Domain name |
|
||||
| ↳ `status` | string | Zone status \(active, pending, initializing, moved, deleted, deactivated\) |
|
||||
| ↳ `paused` | boolean | Whether the zone is paused |
|
||||
| ↳ `type` | string | Zone type \(full or partial\) |
|
||||
| ↳ `name_servers` | array | Assigned Cloudflare name servers |
|
||||
| ↳ `original_name_servers` | array | Original name servers before moving to Cloudflare |
|
||||
| ↳ `created_on` | string | ISO 8601 date when the zone was created |
|
||||
| ↳ `modified_on` | string | ISO 8601 date when the zone was last modified |
|
||||
| ↳ `plan` | object | Zone plan information |
|
||||
| ↳ `id` | string | Plan identifier |
|
||||
| ↳ `name` | string | Plan name |
|
||||
| `total_count` | number | Total number of zones matching the query |
|
||||
|
||||
### `cloudflare_get_zone`
|
||||
|
||||
Gets details for a specific zone (domain) by its ID.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `zoneId` | string | Yes | The zone ID to retrieve details for |
|
||||
| `apiKey` | string | Yes | Cloudflare API Token |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Zone ID |
|
||||
| `name` | string | Domain name |
|
||||
| `status` | string | Zone status \(active, pending, initializing, moved, deleted, deactivated\) |
|
||||
| `paused` | boolean | Whether the zone is paused |
|
||||
| `type` | string | Zone type \(full or partial\) |
|
||||
| `name_servers` | array | Assigned Cloudflare name servers |
|
||||
| `original_name_servers` | array | Original name servers before moving to Cloudflare |
|
||||
| `created_on` | string | ISO 8601 date when the zone was created |
|
||||
| `modified_on` | string | ISO 8601 date when the zone was last modified |
|
||||
| `plan` | object | Zone plan information |
|
||||
| ↳ `id` | string | Plan identifier |
|
||||
| ↳ `name` | string | Plan name |
|
||||
|
||||
### `cloudflare_create_zone`
|
||||
|
||||
Adds a new zone (domain) to the Cloudflare account.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `name` | string | Yes | The domain name to add \(e.g., "example.com"\) |
|
||||
| `accountId` | string | Yes | The Cloudflare account ID |
|
||||
| `type` | string | No | Zone type: "full" \(Cloudflare manages DNS\) or "partial" \(CNAME setup\) |
|
||||
| `apiKey` | string | Yes | Cloudflare API Token |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Created zone ID |
|
||||
| `name` | string | Domain name |
|
||||
| `status` | string | Zone status \(active, pending, initializing, moved, deleted, deactivated\) |
|
||||
| `paused` | boolean | Whether the zone is paused |
|
||||
| `type` | string | Zone type \(full or partial\) |
|
||||
| `name_servers` | array | Assigned Cloudflare name servers |
|
||||
| `original_name_servers` | array | Original name servers before moving to Cloudflare |
|
||||
| `created_on` | string | ISO 8601 date when the zone was created |
|
||||
| `modified_on` | string | ISO 8601 date when the zone was last modified |
|
||||
| `plan` | object | Zone plan information |
|
||||
| ↳ `id` | string | Plan identifier |
|
||||
| ↳ `name` | string | Plan name |
|
||||
|
||||
### `cloudflare_delete_zone`
|
||||
|
||||
Deletes a zone (domain) from the Cloudflare account.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `zoneId` | string | Yes | The zone ID to delete |
|
||||
| `apiKey` | string | Yes | Cloudflare API Token |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Deleted zone ID |
|
||||
|
||||
### `cloudflare_list_dns_records`
|
||||
|
||||
Lists DNS records for a specific zone.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `zoneId` | string | Yes | The zone ID to list DNS records for |
|
||||
| `type` | string | No | Filter by record type \(e.g., "A", "AAAA", "CNAME", "MX", "TXT"\) |
|
||||
| `name` | string | No | Filter by record name \(substring match\) |
|
||||
| `content` | string | No | Filter by record content \(substring match\) |
|
||||
| `page` | number | No | Page number for pagination \(default: 1\) |
|
||||
| `per_page` | number | No | Number of records per page \(default: 100, max: 5000000\) |
|
||||
| `apiKey` | string | Yes | Cloudflare API Token |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `records` | array | List of DNS records |
|
||||
| ↳ `id` | string | Unique identifier for the DNS record |
|
||||
| ↳ `zone_id` | string | The ID of the zone the record belongs to |
|
||||
| ↳ `zone_name` | string | The name of the zone |
|
||||
| ↳ `type` | string | Record type \(A, AAAA, CNAME, MX, TXT, etc.\) |
|
||||
| ↳ `name` | string | Record name \(e.g., example.com\) |
|
||||
| ↳ `content` | string | Record content \(e.g., IP address\) |
|
||||
| ↳ `proxiable` | boolean | Whether the record can be proxied |
|
||||
| ↳ `proxied` | boolean | Whether Cloudflare proxy is enabled |
|
||||
| ↳ `ttl` | number | TTL in seconds \(1 = automatic\) |
|
||||
| ↳ `locked` | boolean | Whether the record is locked |
|
||||
| ↳ `priority` | number | MX/SRV record priority |
|
||||
| ↳ `comment` | string | Comment associated with the record |
|
||||
| ↳ `tags` | array | Tags associated with the record |
|
||||
| ↳ `created_on` | string | ISO 8601 timestamp when the record was created |
|
||||
| ↳ `modified_on` | string | ISO 8601 timestamp when the record was last modified |
|
||||
| `total_count` | number | Total number of DNS records matching the query |
|
||||
|
||||
### `cloudflare_create_dns_record`
|
||||
|
||||
Creates a new DNS record for a zone.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `zoneId` | string | Yes | The zone ID to create the DNS record in |
|
||||
| `type` | string | Yes | DNS record type \(e.g., "A", "AAAA", "CNAME", "MX", "TXT", "NS", "SRV"\) |
|
||||
| `name` | string | Yes | DNS record name \(e.g., "example.com" or "subdomain.example.com"\) |
|
||||
| `content` | string | Yes | DNS record content \(e.g., IP address for A records, target for CNAME\) |
|
||||
| `ttl` | number | No | Time to live in seconds \(1 = automatic, default: 1\) |
|
||||
| `proxied` | boolean | No | Whether to enable Cloudflare proxy \(default: false\) |
|
||||
| `priority` | number | No | Priority for MX and SRV records |
|
||||
| `comment` | string | No | Comment for the DNS record |
|
||||
| `apiKey` | string | Yes | Cloudflare API Token |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Unique identifier for the created DNS record |
|
||||
| `zone_id` | string | The ID of the zone the record belongs to |
|
||||
| `zone_name` | string | The name of the zone |
|
||||
| `type` | string | DNS record type \(A, AAAA, CNAME, MX, TXT, etc.\) |
|
||||
| `name` | string | DNS record hostname |
|
||||
| `content` | string | DNS record value \(e.g., IP address, target hostname\) |
|
||||
| `proxiable` | boolean | Whether the record can be proxied through Cloudflare |
|
||||
| `proxied` | boolean | Whether Cloudflare proxy is enabled |
|
||||
| `ttl` | number | Time to live in seconds \(1 = automatic\) |
|
||||
| `locked` | boolean | Whether the record is locked |
|
||||
| `priority` | number | Priority for MX and SRV records |
|
||||
| `comment` | string | Comment associated with the record |
|
||||
| `tags` | array | Tags associated with the record |
|
||||
| `created_on` | string | ISO 8601 timestamp when the record was created |
|
||||
| `modified_on` | string | ISO 8601 timestamp when the record was last modified |
|
||||
|
||||
### `cloudflare_update_dns_record`
|
||||
|
||||
Updates an existing DNS record for a zone.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `zoneId` | string | Yes | The zone ID containing the DNS record |
|
||||
| `recordId` | string | Yes | The DNS record ID to update |
|
||||
| `type` | string | No | DNS record type \(e.g., "A", "AAAA", "CNAME", "MX", "TXT"\) |
|
||||
| `name` | string | No | DNS record name |
|
||||
| `content` | string | No | DNS record content \(e.g., IP address\) |
|
||||
| `ttl` | number | No | Time to live in seconds \(1 = automatic\) |
|
||||
| `proxied` | boolean | No | Whether to enable Cloudflare proxy |
|
||||
| `priority` | number | No | Priority for MX and SRV records |
|
||||
| `comment` | string | No | Comment for the DNS record |
|
||||
| `apiKey` | string | Yes | Cloudflare API Token |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Unique identifier for the updated DNS record |
|
||||
| `zone_id` | string | The ID of the zone the record belongs to |
|
||||
| `zone_name` | string | The name of the zone |
|
||||
| `type` | string | DNS record type \(A, AAAA, CNAME, MX, TXT, etc.\) |
|
||||
| `name` | string | DNS record hostname |
|
||||
| `content` | string | DNS record value \(e.g., IP address, target hostname\) |
|
||||
| `proxiable` | boolean | Whether the record can be proxied through Cloudflare |
|
||||
| `proxied` | boolean | Whether Cloudflare proxy is enabled |
|
||||
| `ttl` | number | Time to live in seconds \(1 = automatic\) |
|
||||
| `locked` | boolean | Whether the record is locked |
|
||||
| `priority` | number | Priority for MX and SRV records |
|
||||
| `comment` | string | Comment associated with the record |
|
||||
| `tags` | array | Tags associated with the record |
|
||||
| `created_on` | string | ISO 8601 timestamp when the record was created |
|
||||
| `modified_on` | string | ISO 8601 timestamp when the record was last modified |
|
||||
|
||||
### `cloudflare_delete_dns_record`
|
||||
|
||||
Deletes a DNS record from a zone.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `zoneId` | string | Yes | The zone ID containing the DNS record |
|
||||
| `recordId` | string | Yes | The DNS record ID to delete |
|
||||
| `apiKey` | string | Yes | Cloudflare API Token |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Deleted record ID |
|
||||
|
||||
### `cloudflare_list_certificates`
|
||||
|
||||
Lists SSL/TLS certificate packs for a zone.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `zoneId` | string | Yes | The zone ID to list certificates for |
|
||||
| `status` | string | No | Filter certificate packs by status \(e.g., "all", "active", "pending"\) |
|
||||
| `apiKey` | string | Yes | Cloudflare API Token |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `certificates` | array | List of SSL/TLS certificate packs |
|
||||
| ↳ `id` | string | Certificate pack ID |
|
||||
| ↳ `type` | string | Certificate type \(e.g., "universal", "advanced"\) |
|
||||
| ↳ `hosts` | array | Hostnames covered by this certificate pack |
|
||||
| ↳ `primary_certificate` | string | ID of the primary certificate in the pack |
|
||||
| ↳ `status` | string | Certificate pack status \(e.g., "active", "pending"\) |
|
||||
| ↳ `certificates` | array | Individual certificates within the pack |
|
||||
| ↳ `id` | string | Certificate ID |
|
||||
| ↳ `hosts` | array | Hostnames covered by this certificate |
|
||||
| ↳ `issuer` | string | Certificate issuer |
|
||||
| ↳ `signature` | string | Signature algorithm \(e.g., "ECDSAWithSHA256"\) |
|
||||
| ↳ `status` | string | Certificate status |
|
||||
| ↳ `bundle_method` | string | Bundle method \(e.g., "ubiquitous"\) |
|
||||
| ↳ `zone_id` | string | Zone ID the certificate belongs to |
|
||||
| ↳ `uploaded_on` | string | Upload date \(ISO 8601\) |
|
||||
| ↳ `modified_on` | string | Last modified date \(ISO 8601\) |
|
||||
| ↳ `expires_on` | string | Expiration date \(ISO 8601\) |
|
||||
| ↳ `priority` | number | Certificate priority order |
|
||||
| ↳ `geo_restrictions` | object | Geographic restrictions for the certificate |
|
||||
| ↳ `label` | string | Geographic restriction label |
|
||||
| ↳ `cloudflare_branding` | boolean | Whether Cloudflare branding is enabled on the certificate |
|
||||
| ↳ `validation_method` | string | Validation method \(e.g., "txt", "http", "cname"\) |
|
||||
| ↳ `validity_days` | number | Validity period in days |
|
||||
| ↳ `certificate_authority` | string | Certificate authority \(e.g., "lets_encrypt", "google"\) |
|
||||
| ↳ `created_on` | string | Creation date \(ISO 8601\) |
|
||||
| `total_count` | number | Total number of certificate packs |
|
||||
|
||||
### `cloudflare_get_zone_settings`
|
||||
|
||||
Gets all settings for a zone including SSL mode, minification, caching level, and security settings.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `zoneId` | string | Yes | The zone ID to get settings for |
|
||||
| `apiKey` | string | Yes | Cloudflare API Token |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `settings` | array | List of zone settings |
|
||||
| ↳ `id` | string | Setting identifier \(e.g., ssl, minify, cache_level, security_level, always_use_https\) |
|
||||
| ↳ `value` | json | Setting value - may be a string, number, boolean, or object depending on the setting |
|
||||
| ↳ `editable` | boolean | Whether the setting can be modified for the current zone plan |
|
||||
| ↳ `modified_on` | string | ISO 8601 timestamp when the setting was last modified |
|
||||
| ↳ `time_remaining` | number | Seconds remaining until the setting can be modified again \(only present for rate-limited settings\) |
|
||||
|
||||
### `cloudflare_update_zone_setting`
|
||||
|
||||
Updates a specific zone setting such as SSL mode, security level, cache level, minification, or other configuration.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `zoneId` | string | Yes | The zone ID to update settings for |
|
||||
| `settingId` | string | Yes | Setting to update \(e.g., "ssl", "security_level", "cache_level", "minify", "always_use_https", "browser_cache_ttl", "http3", "min_tls_version", "ciphers"\) |
|
||||
| `value` | string | Yes | New value for the setting as a string or JSON string for complex values \(e.g., "full" for SSL, "medium" for security_level, "aggressive" for cache_level, \'\{"css":"on","html":"on","js":"on"\}\' for minify, \'\["ECDHE-RSA-AES128-GCM-SHA256"\]\' for ciphers\) |
|
||||
| `apiKey` | string | Yes | Cloudflare API Token |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Setting identifier \(e.g., ssl, minify, cache_level\) |
|
||||
| `value` | json | Updated setting value - may be a string, number, boolean, or object depending on the setting |
|
||||
| `editable` | boolean | Whether the setting can be modified for the current zone plan |
|
||||
| `modified_on` | string | ISO 8601 timestamp when the setting was last modified |
|
||||
| `time_remaining` | number | Seconds remaining until the setting can be modified again \(only present for rate-limited settings\) |
|
||||
|
||||
### `cloudflare_dns_analytics`
|
||||
|
||||
Gets DNS analytics report for a zone including query counts and trends.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `zoneId` | string | Yes | The zone ID to get DNS analytics for |
|
||||
| `since` | string | No | Start date for analytics \(ISO 8601, e.g., "2024-01-01T00:00:00Z"\) or relative \(e.g., "-6h"\) |
|
||||
| `until` | string | No | End date for analytics \(ISO 8601, e.g., "2024-01-31T23:59:59Z"\) or relative \(e.g., "now"\) |
|
||||
| `metrics` | string | No | Comma-separated metrics to retrieve \(e.g., "queryCount,uncachedCount,staleCount,responseTimeAvg,responseTimeMedian,responseTime90th,responseTime99th"\) |
|
||||
| `dimensions` | string | No | Comma-separated dimensions to group by \(e.g., "queryName,queryType,responseCode,responseCached,coloName,origin,dayOfWeek,tcp,ipVersion"\) |
|
||||
| `filters` | string | No | Filters to apply to the data \(e.g., "queryType==A"\) |
|
||||
| `sort` | string | No | Sort order for the result set. Fields must be included in metrics or dimensions \(e.g., "+queryCount" or "-responseTimeAvg"\) |
|
||||
| `limit` | number | No | Maximum number of results to return |
|
||||
| `apiKey` | string | Yes | Cloudflare API Token |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `totals` | object | Aggregate DNS analytics totals for the entire queried period |
|
||||
| ↳ `queryCount` | number | Total number of DNS queries |
|
||||
| ↳ `uncachedCount` | number | Number of uncached DNS queries |
|
||||
| ↳ `staleCount` | number | Number of stale DNS queries |
|
||||
| ↳ `responseTimeAvg` | number | Average response time in milliseconds |
|
||||
| ↳ `responseTimeMedian` | number | Median response time in milliseconds |
|
||||
| ↳ `responseTime90th` | number | 90th percentile response time in milliseconds |
|
||||
| ↳ `responseTime99th` | number | 99th percentile response time in milliseconds |
|
||||
| `data` | array | Raw analytics data rows returned by the Cloudflare DNS analytics report |
|
||||
| ↳ `dimensions` | array | Dimension values for this data row, parallel to the requested dimensions list |
|
||||
| ↳ `metrics` | array | Metric values for this data row, parallel to the requested metrics list |
|
||||
| `data_lag` | number | Processing lag in seconds before analytics data becomes available |
|
||||
| `rows` | number | Total number of rows in the result set |
|
||||
|
||||
### `cloudflare_purge_cache`
|
||||
|
||||
Purges cached content for a zone. Can purge everything or specific files/tags/hosts/prefixes.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `zoneId` | string | Yes | The zone ID to purge cache for |
|
||||
| `purge_everything` | boolean | No | Set to true to purge all cached content. Mutually exclusive with files, tags, hosts, and prefixes |
|
||||
| `files` | string | No | Comma-separated list of URLs to purge from cache |
|
||||
| `tags` | string | No | Comma-separated list of cache tags to purge \(Enterprise only\) |
|
||||
| `hosts` | string | No | Comma-separated list of hostnames to purge \(Enterprise only\) |
|
||||
| `prefixes` | string | No | Comma-separated list of URL prefixes to purge \(Enterprise only\) |
|
||||
| `apiKey` | string | Yes | Cloudflare API Token |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Purge request identifier returned by Cloudflare |
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"circleback",
|
||||
"clay",
|
||||
"clerk",
|
||||
"cloudflare",
|
||||
"confluence",
|
||||
"cursor",
|
||||
"datadog",
|
||||
@@ -68,6 +69,7 @@
|
||||
"mailgun",
|
||||
"mem0",
|
||||
"memory",
|
||||
"microsoft_dataverse",
|
||||
"microsoft_excel",
|
||||
"microsoft_planner",
|
||||
"microsoft_teams",
|
||||
|
||||
221
apps/docs/content/docs/en/tools/microsoft_dataverse.mdx
Normal file
221
apps/docs/content/docs/en/tools/microsoft_dataverse.mdx
Normal file
@@ -0,0 +1,221 @@
|
||||
---
|
||||
title: Microsoft Dataverse
|
||||
description: Manage records in Microsoft Dataverse tables
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="microsoft_dataverse"
|
||||
color="#E0E0E0"
|
||||
/>
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Microsoft Dataverse into your workflow. Create, read, update, delete, upsert, associate, and query records in Dataverse tables using the Web API. Works with Dynamics 365, Power Platform, and custom Dataverse environments.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `microsoft_dataverse_associate`
|
||||
|
||||
Associate two records in Microsoft Dataverse via a navigation property. Creates a relationship between a source record and a target record. Supports both collection-valued (POST) and single-valued (PUT) navigation properties.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `environmentUrl` | string | Yes | Dataverse environment URL \(e.g., https://myorg.crm.dynamics.com\) |
|
||||
| `entitySetName` | string | Yes | Source entity set name \(e.g., accounts\) |
|
||||
| `recordId` | string | Yes | Source record GUID |
|
||||
| `navigationProperty` | string | Yes | Navigation property name \(e.g., contact_customer_accounts for collection-valued, or parentcustomerid_account for single-valued\) |
|
||||
| `targetEntitySetName` | string | Yes | Target entity set name \(e.g., contacts\) |
|
||||
| `targetRecordId` | string | Yes | Target record GUID to associate |
|
||||
| `navigationType` | string | No | Type of navigation property: "collection" \(default, uses POST\) or "single" \(uses PUT for lookup fields\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the association was created successfully |
|
||||
| `entitySetName` | string | Source entity set name used in the association |
|
||||
| `recordId` | string | Source record GUID that was associated |
|
||||
| `navigationProperty` | string | Navigation property used for the association |
|
||||
| `targetEntitySetName` | string | Target entity set name used in the association |
|
||||
| `targetRecordId` | string | Target record GUID that was associated |
|
||||
|
||||
### `microsoft_dataverse_create_record`
|
||||
|
||||
Create a new record in a Microsoft Dataverse table. Requires the entity set name (plural table name) and record data as a JSON object.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `environmentUrl` | string | Yes | Dataverse environment URL \(e.g., https://myorg.crm.dynamics.com\) |
|
||||
| `entitySetName` | string | Yes | Entity set name \(plural table name, e.g., accounts, contacts\) |
|
||||
| `data` | object | Yes | Record data as a JSON object with column names as keys |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `recordId` | string | The ID of the created record |
|
||||
| `record` | object | Dataverse record object. Contains dynamic columns based on the queried table, plus OData metadata fields. |
|
||||
| `success` | boolean | Whether the record was created successfully |
|
||||
|
||||
### `microsoft_dataverse_delete_record`
|
||||
|
||||
Delete a record from a Microsoft Dataverse table by its ID.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `environmentUrl` | string | Yes | Dataverse environment URL \(e.g., https://myorg.crm.dynamics.com\) |
|
||||
| `entitySetName` | string | Yes | Entity set name \(plural table name, e.g., accounts, contacts\) |
|
||||
| `recordId` | string | Yes | The unique identifier \(GUID\) of the record to delete |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `recordId` | string | The ID of the deleted record |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `microsoft_dataverse_disassociate`
|
||||
|
||||
Remove an association between two records in Microsoft Dataverse. For collection-valued navigation properties, provide the target record ID. For single-valued navigation properties, only the navigation property name is needed.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `environmentUrl` | string | Yes | Dataverse environment URL \(e.g., https://myorg.crm.dynamics.com\) |
|
||||
| `entitySetName` | string | Yes | Source entity set name \(e.g., accounts\) |
|
||||
| `recordId` | string | Yes | Source record GUID |
|
||||
| `navigationProperty` | string | Yes | Navigation property name \(e.g., contact_customer_accounts for collection-valued, or parentcustomerid_account for single-valued\) |
|
||||
| `targetRecordId` | string | No | Target record GUID \(required for collection-valued navigation properties, omit for single-valued\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the disassociation was completed successfully |
|
||||
| `entitySetName` | string | Source entity set name used in the disassociation |
|
||||
| `recordId` | string | Source record GUID that was disassociated |
|
||||
| `navigationProperty` | string | Navigation property used for the disassociation |
|
||||
| `targetRecordId` | string | Target record GUID that was disassociated |
|
||||
|
||||
### `microsoft_dataverse_get_record`
|
||||
|
||||
Retrieve a single record from a Microsoft Dataverse table by its ID. Supports $select and $expand OData query options.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `environmentUrl` | string | Yes | Dataverse environment URL \(e.g., https://myorg.crm.dynamics.com\) |
|
||||
| `entitySetName` | string | Yes | Entity set name \(plural table name, e.g., accounts, contacts\) |
|
||||
| `recordId` | string | Yes | The unique identifier \(GUID\) of the record to retrieve |
|
||||
| `select` | string | No | Comma-separated list of columns to return \(OData $select\) |
|
||||
| `expand` | string | No | Navigation properties to expand \(OData $expand\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `record` | object | Dataverse record object. Contains dynamic columns based on the queried table, plus OData metadata fields. |
|
||||
| `recordId` | string | The record primary key ID \(auto-detected from response\) |
|
||||
| `success` | boolean | Whether the record was retrieved successfully |
|
||||
|
||||
### `microsoft_dataverse_list_records`
|
||||
|
||||
Query and list records from a Microsoft Dataverse table. Supports OData query options for filtering, selecting columns, ordering, and pagination.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `environmentUrl` | string | Yes | Dataverse environment URL \(e.g., https://myorg.crm.dynamics.com\) |
|
||||
| `entitySetName` | string | Yes | Entity set name \(plural table name, e.g., accounts, contacts\) |
|
||||
| `select` | string | No | Comma-separated list of columns to return \(OData $select\) |
|
||||
| `filter` | string | No | OData $filter expression \(e.g., statecode eq 0\) |
|
||||
| `orderBy` | string | No | OData $orderby expression \(e.g., name asc, createdon desc\) |
|
||||
| `top` | number | No | Maximum number of records to return \(OData $top\) |
|
||||
| `expand` | string | No | Navigation properties to expand \(OData $expand\) |
|
||||
| `count` | string | No | Set to "true" to include total record count in response \(OData $count\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `records` | array | Array of Dataverse records. Each record has dynamic columns based on the table schema. |
|
||||
| `count` | number | Number of records returned in the current page |
|
||||
| `totalCount` | number | Total number of matching records server-side \(requires $count=true\) |
|
||||
| `nextLink` | string | URL for the next page of results |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `microsoft_dataverse_update_record`
|
||||
|
||||
Update an existing record in a Microsoft Dataverse table. Only send the columns you want to change.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `environmentUrl` | string | Yes | Dataverse environment URL \(e.g., https://myorg.crm.dynamics.com\) |
|
||||
| `entitySetName` | string | Yes | Entity set name \(plural table name, e.g., accounts, contacts\) |
|
||||
| `recordId` | string | Yes | The unique identifier \(GUID\) of the record to update |
|
||||
| `data` | object | Yes | Record data to update as a JSON object with column names as keys |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `recordId` | string | The ID of the updated record |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `microsoft_dataverse_upsert_record`
|
||||
|
||||
Create or update a record in a Microsoft Dataverse table. If a record with the given ID exists, it is updated; otherwise, a new record is created.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `environmentUrl` | string | Yes | Dataverse environment URL \(e.g., https://myorg.crm.dynamics.com\) |
|
||||
| `entitySetName` | string | Yes | Entity set name \(plural table name, e.g., accounts, contacts\) |
|
||||
| `recordId` | string | Yes | The unique identifier \(GUID\) of the record to upsert |
|
||||
| `data` | object | Yes | Record data as a JSON object with column names as keys |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `recordId` | string | The ID of the upserted record |
|
||||
| `created` | boolean | True if the record was created, false if updated |
|
||||
| `record` | object | Dataverse record object. Contains dynamic columns based on the queried table, plus OData metadata fields. |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `microsoft_dataverse_whoami`
|
||||
|
||||
Retrieve the current authenticated user information from Microsoft Dataverse. Useful for testing connectivity and getting the user ID, business unit ID, and organization ID.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `environmentUrl` | string | Yes | Dataverse environment URL \(e.g., https://myorg.crm.dynamics.com\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `userId` | string | The authenticated user ID |
|
||||
| `businessUnitId` | string | The business unit ID |
|
||||
| `organizationId` | string | The organization ID |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
|
||||
812
apps/sim/blocks/blocks/cloudflare.ts
Normal file
812
apps/sim/blocks/blocks/cloudflare.ts
Normal file
@@ -0,0 +1,812 @@
|
||||
import { CloudflareIcon } from '@/components/icons'
|
||||
import { AuthMode, type BlockConfig } from '@/blocks/types'
|
||||
import type { CloudflareResponse } from '@/tools/cloudflare/types'
|
||||
|
||||
export const CloudflareBlock: BlockConfig<CloudflareResponse> = {
|
||||
type: 'cloudflare',
|
||||
name: 'Cloudflare',
|
||||
description: 'Manage DNS, domains, certificates, and cache',
|
||||
authMode: AuthMode.ApiKey,
|
||||
longDescription:
|
||||
'Integrate Cloudflare into the workflow. Manage zones (domains), DNS records, SSL/TLS certificates, zone settings, DNS analytics, and cache purging via the Cloudflare API.',
|
||||
docsLink: 'https://docs.sim.ai/tools/cloudflare',
|
||||
category: 'tools',
|
||||
bgColor: '#F5F6FA',
|
||||
icon: CloudflareIcon,
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'List Zones', id: 'list_zones' },
|
||||
{ label: 'Get Zone Details', id: 'get_zone' },
|
||||
{ label: 'Create Zone', id: 'create_zone' },
|
||||
{ label: 'Delete Zone', id: 'delete_zone' },
|
||||
{ label: 'List DNS Records', id: 'list_dns_records' },
|
||||
{ label: 'Create DNS Record', id: 'create_dns_record' },
|
||||
{ label: 'Update DNS Record', id: 'update_dns_record' },
|
||||
{ label: 'Delete DNS Record', id: 'delete_dns_record' },
|
||||
{ label: 'List Certificates', id: 'list_certificates' },
|
||||
{ label: 'Get Zone Settings', id: 'get_zone_settings' },
|
||||
{ label: 'Update Zone Setting', id: 'update_zone_setting' },
|
||||
{ label: 'DNS Analytics', id: 'dns_analytics' },
|
||||
{ label: 'Purge Cache', id: 'purge_cache' },
|
||||
],
|
||||
value: () => 'list_zones',
|
||||
},
|
||||
|
||||
// List Zones inputs
|
||||
{
|
||||
id: 'name',
|
||||
title: 'Domain Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Filter by domain (e.g., example.com)',
|
||||
condition: { field: 'operation', value: 'list_zones' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'status',
|
||||
title: 'Status Filter',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'All', id: '' },
|
||||
{ label: 'Active', id: 'active' },
|
||||
{ label: 'Pending', id: 'pending' },
|
||||
{ label: 'Initializing', id: 'initializing' },
|
||||
{ label: 'Moved', id: 'moved' },
|
||||
{ label: 'Deleted', id: 'deleted' },
|
||||
{ label: 'Deactivated', id: 'deactivated' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'list_zones' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
// Create Zone inputs
|
||||
{
|
||||
id: 'name',
|
||||
title: 'Domain Name',
|
||||
type: 'short-input',
|
||||
required: true,
|
||||
placeholder: 'e.g., example.com',
|
||||
condition: { field: 'operation', value: 'create_zone' },
|
||||
},
|
||||
{
|
||||
id: 'accountId',
|
||||
title: 'Account ID',
|
||||
type: 'short-input',
|
||||
required: true,
|
||||
placeholder: 'Enter Cloudflare account ID',
|
||||
condition: { field: 'operation', value: 'create_zone' },
|
||||
},
|
||||
{
|
||||
id: 'zoneType',
|
||||
title: 'Zone Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Full (Cloudflare DNS)', id: 'full' },
|
||||
{ label: 'Partial (CNAME Setup)', id: 'partial' },
|
||||
],
|
||||
value: () => 'full',
|
||||
condition: { field: 'operation', value: 'create_zone' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
// Get Zone inputs
|
||||
{
|
||||
id: 'zoneId',
|
||||
title: 'Zone ID',
|
||||
type: 'short-input',
|
||||
required: true,
|
||||
placeholder: 'Enter zone ID',
|
||||
condition: { field: 'operation', value: 'get_zone' },
|
||||
},
|
||||
|
||||
// Delete Zone inputs
|
||||
{
|
||||
id: 'zoneId',
|
||||
title: 'Zone ID',
|
||||
type: 'short-input',
|
||||
required: true,
|
||||
placeholder: 'Enter zone ID to delete',
|
||||
condition: { field: 'operation', value: 'delete_zone' },
|
||||
},
|
||||
|
||||
// List DNS Records inputs
|
||||
{
|
||||
id: 'zoneId',
|
||||
title: 'Zone ID',
|
||||
type: 'short-input',
|
||||
required: true,
|
||||
placeholder: 'Enter zone ID',
|
||||
condition: { field: 'operation', value: 'list_dns_records' },
|
||||
},
|
||||
{
|
||||
id: 'type',
|
||||
title: 'Record Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'All', id: '' },
|
||||
{ label: 'A', id: 'A' },
|
||||
{ label: 'AAAA', id: 'AAAA' },
|
||||
{ label: 'CNAME', id: 'CNAME' },
|
||||
{ label: 'MX', id: 'MX' },
|
||||
{ label: 'TXT', id: 'TXT' },
|
||||
{ label: 'NS', id: 'NS' },
|
||||
{ label: 'SRV', id: 'SRV' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'list_dns_records' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'name',
|
||||
title: 'Name Filter',
|
||||
type: 'short-input',
|
||||
placeholder: 'Filter by record name (substring match)',
|
||||
condition: { field: 'operation', value: 'list_dns_records' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'content',
|
||||
title: 'Content Filter',
|
||||
type: 'short-input',
|
||||
placeholder: 'Filter by record content (substring match)',
|
||||
condition: { field: 'operation', value: 'list_dns_records' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
// Create DNS Record inputs
|
||||
{
|
||||
id: 'zoneId',
|
||||
title: 'Zone ID',
|
||||
type: 'short-input',
|
||||
required: true,
|
||||
placeholder: 'Enter zone ID',
|
||||
condition: { field: 'operation', value: 'create_dns_record' },
|
||||
},
|
||||
{
|
||||
id: 'type',
|
||||
title: 'Record Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'A', id: 'A' },
|
||||
{ label: 'AAAA', id: 'AAAA' },
|
||||
{ label: 'CNAME', id: 'CNAME' },
|
||||
{ label: 'MX', id: 'MX' },
|
||||
{ label: 'TXT', id: 'TXT' },
|
||||
{ label: 'NS', id: 'NS' },
|
||||
{ label: 'SRV', id: 'SRV' },
|
||||
],
|
||||
value: () => 'A',
|
||||
condition: { field: 'operation', value: 'create_dns_record' },
|
||||
},
|
||||
{
|
||||
id: 'name',
|
||||
title: 'Record Name',
|
||||
type: 'short-input',
|
||||
required: true,
|
||||
placeholder: 'e.g., example.com or sub.example.com',
|
||||
condition: { field: 'operation', value: 'create_dns_record' },
|
||||
},
|
||||
{
|
||||
id: 'content',
|
||||
title: 'Record Content',
|
||||
type: 'short-input',
|
||||
required: true,
|
||||
placeholder: 'e.g., 192.0.2.1 or target.example.com',
|
||||
condition: { field: 'operation', value: 'create_dns_record' },
|
||||
},
|
||||
{
|
||||
id: 'ttl',
|
||||
title: 'TTL (seconds)',
|
||||
type: 'short-input',
|
||||
placeholder: '1 (automatic)',
|
||||
condition: { field: 'operation', value: 'create_dns_record' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'proxied',
|
||||
title: 'Proxied',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'No', id: 'false' },
|
||||
{ label: 'Yes', id: 'true' },
|
||||
],
|
||||
value: () => 'false',
|
||||
condition: { field: 'operation', value: 'create_dns_record' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'priority',
|
||||
title: 'Priority',
|
||||
type: 'short-input',
|
||||
placeholder: 'MX/SRV priority (e.g., 10)',
|
||||
condition: { field: 'operation', value: 'create_dns_record' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'comment',
|
||||
title: 'Comment',
|
||||
type: 'short-input',
|
||||
placeholder: 'Optional comment',
|
||||
condition: { field: 'operation', value: 'create_dns_record' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
// Update DNS Record inputs
|
||||
{
|
||||
id: 'zoneId',
|
||||
title: 'Zone ID',
|
||||
type: 'short-input',
|
||||
required: true,
|
||||
placeholder: 'Enter zone ID',
|
||||
condition: { field: 'operation', value: 'update_dns_record' },
|
||||
},
|
||||
{
|
||||
id: 'recordId',
|
||||
title: 'Record ID',
|
||||
type: 'short-input',
|
||||
required: true,
|
||||
placeholder: 'Enter DNS record ID',
|
||||
condition: { field: 'operation', value: 'update_dns_record' },
|
||||
},
|
||||
{
|
||||
id: 'type',
|
||||
title: 'Record Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'No Change', id: '' },
|
||||
{ label: 'A', id: 'A' },
|
||||
{ label: 'AAAA', id: 'AAAA' },
|
||||
{ label: 'CNAME', id: 'CNAME' },
|
||||
{ label: 'MX', id: 'MX' },
|
||||
{ label: 'TXT', id: 'TXT' },
|
||||
{ label: 'NS', id: 'NS' },
|
||||
{ label: 'SRV', id: 'SRV' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'update_dns_record' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'name',
|
||||
title: 'Record Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., example.com or sub.example.com',
|
||||
condition: { field: 'operation', value: 'update_dns_record' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'content',
|
||||
title: 'New Content',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., 192.0.2.1',
|
||||
condition: { field: 'operation', value: 'update_dns_record' },
|
||||
},
|
||||
{
|
||||
id: 'ttl',
|
||||
title: 'TTL (seconds)',
|
||||
type: 'short-input',
|
||||
placeholder: '1 (automatic)',
|
||||
condition: { field: 'operation', value: 'update_dns_record' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'proxied',
|
||||
title: 'Proxied',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'No Change', id: '' },
|
||||
{ label: 'No', id: 'false' },
|
||||
{ label: 'Yes', id: 'true' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'update_dns_record' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'priority',
|
||||
title: 'Priority',
|
||||
type: 'short-input',
|
||||
placeholder: 'MX/SRV priority (e.g., 10)',
|
||||
condition: { field: 'operation', value: 'update_dns_record' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'comment',
|
||||
title: 'Comment',
|
||||
type: 'short-input',
|
||||
placeholder: 'Optional comment',
|
||||
condition: { field: 'operation', value: 'update_dns_record' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
// Delete DNS Record inputs
|
||||
{
|
||||
id: 'zoneId',
|
||||
title: 'Zone ID',
|
||||
type: 'short-input',
|
||||
required: true,
|
||||
placeholder: 'Enter zone ID',
|
||||
condition: { field: 'operation', value: 'delete_dns_record' },
|
||||
},
|
||||
{
|
||||
id: 'recordId',
|
||||
title: 'Record ID',
|
||||
type: 'short-input',
|
||||
required: true,
|
||||
placeholder: 'Enter DNS record ID to delete',
|
||||
condition: { field: 'operation', value: 'delete_dns_record' },
|
||||
},
|
||||
|
||||
// List Certificates inputs
|
||||
{
|
||||
id: 'zoneId',
|
||||
title: 'Zone ID',
|
||||
type: 'short-input',
|
||||
required: true,
|
||||
placeholder: 'Enter zone ID',
|
||||
condition: { field: 'operation', value: 'list_certificates' },
|
||||
},
|
||||
{
|
||||
id: 'status',
|
||||
title: 'Status Filter',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'All', id: 'all' },
|
||||
{ label: 'Active', id: 'active' },
|
||||
{ label: 'Pending', id: 'pending' },
|
||||
],
|
||||
value: () => 'all',
|
||||
condition: { field: 'operation', value: 'list_certificates' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
// Get Zone Settings inputs
|
||||
{
|
||||
id: 'zoneId',
|
||||
title: 'Zone ID',
|
||||
type: 'short-input',
|
||||
required: true,
|
||||
placeholder: 'Enter zone ID',
|
||||
condition: { field: 'operation', value: 'get_zone_settings' },
|
||||
},
|
||||
|
||||
// Update Zone Setting inputs
|
||||
{
|
||||
id: 'zoneId',
|
||||
title: 'Zone ID',
|
||||
type: 'short-input',
|
||||
required: true,
|
||||
placeholder: 'Enter zone ID',
|
||||
condition: { field: 'operation', value: 'update_zone_setting' },
|
||||
},
|
||||
{
|
||||
id: 'settingId',
|
||||
title: 'Setting',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'SSL Mode', id: 'ssl' },
|
||||
{ label: 'Always Use HTTPS', id: 'always_use_https' },
|
||||
{ label: 'Security Level', id: 'security_level' },
|
||||
{ label: 'Cache Level', id: 'cache_level' },
|
||||
{ label: 'Browser Cache TTL', id: 'browser_cache_ttl' },
|
||||
{ label: 'Minification', id: 'minify' },
|
||||
{ label: 'Auto Minify', id: 'auto_minify' },
|
||||
{ label: 'Rocket Loader', id: 'rocket_loader' },
|
||||
{ label: 'Email Obfuscation', id: 'email_obfuscation' },
|
||||
{ label: 'Hotlink Protection', id: 'hotlink_protection' },
|
||||
{ label: 'IP Geolocation', id: 'ip_geolocation' },
|
||||
{ label: 'HTTP/2', id: 'http2' },
|
||||
{ label: 'HTTP/3', id: 'http3' },
|
||||
{ label: 'WebSockets', id: 'websockets' },
|
||||
{ label: 'TLS 1.3', id: 'tls_1_3' },
|
||||
{ label: 'Minimum TLS Version', id: 'min_tls_version' },
|
||||
],
|
||||
value: () => 'ssl',
|
||||
condition: { field: 'operation', value: 'update_zone_setting' },
|
||||
},
|
||||
{
|
||||
id: 'value',
|
||||
title: 'Value',
|
||||
type: 'short-input',
|
||||
required: true,
|
||||
placeholder: 'e.g., full, strict, on, off, medium',
|
||||
condition: { field: 'operation', value: 'update_zone_setting' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate the correct value for a Cloudflare zone setting based on the user's description.
|
||||
|
||||
Common settings and their valid values:
|
||||
- ssl: "off", "flexible", "full", "strict"
|
||||
- always_use_https: "on", "off"
|
||||
- security_level: "off", "essentially_off", "low", "medium", "high", "under_attack"
|
||||
- cache_level: "aggressive", "basic", "simplified"
|
||||
- browser_cache_ttl: number in seconds (e.g., 14400 for 4 hours, 86400 for 1 day)
|
||||
- minify: JSON object {"css":"on","html":"off","js":"on"}
|
||||
- rocket_loader: "on", "off"
|
||||
- email_obfuscation: "on", "off"
|
||||
- hotlink_protection: "on", "off"
|
||||
- ip_geolocation: "on", "off"
|
||||
- http2: "on", "off"
|
||||
- http3: "on", "off"
|
||||
- websockets: "on", "off"
|
||||
- tls_1_3: "on", "off", "zrt"
|
||||
- min_tls_version: "1.0", "1.1", "1.2", "1.3"
|
||||
|
||||
For simple string/boolean settings, return the plain value (e.g., "full", "on").
|
||||
For complex settings like minify, return the JSON string (e.g., {"css":"on","html":"on","js":"on"}).
|
||||
For numeric settings like browser_cache_ttl, return the number (e.g., 14400).
|
||||
|
||||
Return ONLY the value - no explanations, no extra text.`,
|
||||
placeholder:
|
||||
'Describe the setting value (e.g., "enable strict SSL", "minify CSS and JS")...',
|
||||
},
|
||||
},
|
||||
|
||||
// DNS Analytics inputs
|
||||
{
|
||||
id: 'zoneId',
|
||||
title: 'Zone ID',
|
||||
type: 'short-input',
|
||||
required: true,
|
||||
placeholder: 'Enter zone ID',
|
||||
condition: { field: 'operation', value: 'dns_analytics' },
|
||||
},
|
||||
{
|
||||
id: 'since',
|
||||
title: 'Start Date',
|
||||
type: 'short-input',
|
||||
placeholder: 'ISO 8601 or relative (e.g., 2024-01-01T00:00:00Z or -6h)',
|
||||
condition: { field: 'operation', value: 'dns_analytics' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a timestamp or relative time expression for the Cloudflare DNS Analytics API based on the user's description.
|
||||
Cloudflare accepts either ISO 8601 timestamps (e.g., 2024-01-01T00:00:00Z) or relative expressions (e.g., -6h, -7d, -30d).
|
||||
Examples:
|
||||
- "last 6 hours" -> -6h
|
||||
- "last 24 hours" -> -24h
|
||||
- "last 7 days" -> -7d
|
||||
- "last 30 days" -> -30d
|
||||
- "since January 1st 2024" -> 2024-01-01T00:00:00Z
|
||||
- "beginning of this month" -> First day of current month at 00:00:00Z
|
||||
- "1 hour ago" -> -1h
|
||||
|
||||
Return ONLY the timestamp or relative expression - no explanations, no quotes, no extra text.`,
|
||||
placeholder: 'Describe the start time (e.g., "last 7 days", "since January 1st")...',
|
||||
generationType: 'timestamp',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'until',
|
||||
title: 'End Date',
|
||||
type: 'short-input',
|
||||
placeholder: 'ISO 8601 or relative (e.g., 2024-01-31T23:59:59Z or now)',
|
||||
condition: { field: 'operation', value: 'dns_analytics' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a timestamp or relative time expression for the Cloudflare DNS Analytics API based on the user's description.
|
||||
Cloudflare accepts either ISO 8601 timestamps (e.g., 2024-01-31T23:59:59Z) or relative expressions (e.g., now).
|
||||
Examples:
|
||||
- "now" -> now
|
||||
- "today" -> Today's date at 23:59:59Z
|
||||
- "end of yesterday" -> Yesterday's date at 23:59:59Z
|
||||
- "end of last month" -> Last day of previous month at 23:59:59Z
|
||||
|
||||
Return ONLY the timestamp or relative expression - no explanations, no quotes, no extra text.`,
|
||||
placeholder: 'Describe the end time (e.g., "now", "end of yesterday")...',
|
||||
generationType: 'timestamp',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'metrics',
|
||||
title: 'Metrics',
|
||||
type: 'short-input',
|
||||
placeholder: 'Comma-separated (e.g., queryCount,uncachedCount,responseTimeAvg)',
|
||||
condition: { field: 'operation', value: 'dns_analytics' },
|
||||
mode: 'advanced',
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a comma-separated list of Cloudflare DNS Analytics metrics based on the user's description.
|
||||
|
||||
Available metrics:
|
||||
- queryCount: Total number of DNS queries
|
||||
- uncachedCount: Number of DNS queries not served from cache
|
||||
- staleCount: Number of stale DNS responses served
|
||||
- responseTimeAvg: Average response time in milliseconds
|
||||
- responseTimeMedian: Median response time in milliseconds
|
||||
- responseTime90th: 90th percentile response time
|
||||
- responseTime99th: 99th percentile response time
|
||||
|
||||
Examples:
|
||||
- "query counts" -> queryCount
|
||||
- "all query metrics" -> queryCount,uncachedCount,staleCount
|
||||
- "response times" -> responseTimeAvg,responseTimeMedian,responseTime90th,responseTime99th
|
||||
- "everything" -> queryCount,uncachedCount,staleCount,responseTimeAvg,responseTimeMedian,responseTime90th,responseTime99th
|
||||
|
||||
Return ONLY the comma-separated metric names - no explanations, no quotes, no extra text.`,
|
||||
placeholder: 'Describe what to measure (e.g., "query counts and response times")...',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'dimensions',
|
||||
title: 'Dimensions',
|
||||
type: 'short-input',
|
||||
placeholder: 'Comma-separated (e.g., queryName,queryType,responseCode)',
|
||||
condition: { field: 'operation', value: 'dns_analytics' },
|
||||
mode: 'advanced',
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a comma-separated list of Cloudflare DNS Analytics dimensions based on the user's description.
|
||||
|
||||
Available dimensions:
|
||||
- queryName: DNS record name being queried
|
||||
- queryType: DNS query type (A, AAAA, CNAME, MX, etc.)
|
||||
- responseCode: DNS response code (NOERROR, NXDOMAIN, SERVFAIL, etc.)
|
||||
- responseCached: Whether the response was cached
|
||||
- coloName: Cloudflare data center handling the query
|
||||
- origin: Origin server
|
||||
- dayOfWeek: Day of the week
|
||||
- tcp: Whether the query used TCP
|
||||
- ipVersion: IP version (4 or 6)
|
||||
|
||||
Examples:
|
||||
- "by record type" -> queryType
|
||||
- "by record name and type" -> queryName,queryType
|
||||
- "by data center" -> coloName
|
||||
- "by response code and cache status" -> responseCode,responseCached
|
||||
|
||||
Return ONLY the comma-separated dimension names - no explanations, no quotes, no extra text.`,
|
||||
placeholder: 'Describe how to group results (e.g., "by record type and name")...',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'filters',
|
||||
title: 'Filters',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., queryType==A',
|
||||
condition: { field: 'operation', value: 'dns_analytics' },
|
||||
mode: 'advanced',
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a Cloudflare DNS Analytics filter expression based on the user's description.
|
||||
|
||||
Filter syntax: field==value or field!=value
|
||||
Multiple filters can be combined with semicolons: field1==value1;field2==value2
|
||||
|
||||
Available filter fields:
|
||||
- queryType: DNS record type (A, AAAA, CNAME, MX, TXT, NS, SRV, etc.)
|
||||
- queryName: DNS record name
|
||||
- responseCode: DNS response code (NOERROR, NXDOMAIN, SERVFAIL, REFUSED)
|
||||
- responseCached: Whether cached (0 or 1)
|
||||
- coloName: Data center name
|
||||
- origin: Origin server
|
||||
|
||||
Examples:
|
||||
- "only A records" -> queryType==A
|
||||
- "only CNAME records" -> queryType==CNAME
|
||||
- "failed queries" -> responseCode==SERVFAIL
|
||||
- "non-existent domains" -> responseCode==NXDOMAIN
|
||||
- "A records that weren't cached" -> queryType==A;responseCached==0
|
||||
- "queries for example.com" -> queryName==example.com
|
||||
|
||||
Return ONLY the filter expression - no explanations, no quotes, no extra text.`,
|
||||
placeholder: 'Describe what to filter (e.g., "only A records", "failed queries")...',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'sort',
|
||||
title: 'Sort',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., +queryCount or -responseTimeAvg',
|
||||
condition: { field: 'operation', value: 'dns_analytics' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'limit',
|
||||
title: 'Limit',
|
||||
type: 'short-input',
|
||||
placeholder: 'Max results (e.g., 100)',
|
||||
condition: { field: 'operation', value: 'dns_analytics' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
// Purge Cache inputs
|
||||
{
|
||||
id: 'zoneId',
|
||||
title: 'Zone ID',
|
||||
type: 'short-input',
|
||||
required: true,
|
||||
placeholder: 'Enter zone ID',
|
||||
condition: { field: 'operation', value: 'purge_cache' },
|
||||
},
|
||||
{
|
||||
id: 'purge_everything',
|
||||
title: 'Purge Everything',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Yes - Purge All', id: 'true' },
|
||||
{ label: 'No - Purge Specific', id: 'false' },
|
||||
],
|
||||
value: () => 'true',
|
||||
condition: { field: 'operation', value: 'purge_cache' },
|
||||
},
|
||||
{
|
||||
id: 'files',
|
||||
title: 'Files to Purge',
|
||||
type: 'long-input',
|
||||
placeholder:
|
||||
'Comma-separated URLs (e.g., https://example.com/style.css, https://example.com/app.js)',
|
||||
condition: { field: 'operation', value: 'purge_cache' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a comma-separated list of URLs to purge from Cloudflare's cache based on the user's description.
|
||||
|
||||
Each URL should be a full URL including the protocol (https://).
|
||||
Examples:
|
||||
- "the homepage and about page of example.com" -> https://example.com/, https://example.com/about
|
||||
- "all CSS and JS files" -> https://example.com/style.css, https://example.com/app.js
|
||||
- "the API endpoint" -> https://example.com/api/v1/data
|
||||
|
||||
Return ONLY the comma-separated URLs - no explanations, no extra text.`,
|
||||
placeholder: 'Describe what to purge (e.g., "homepage and CSS files")...',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'tags',
|
||||
title: 'Cache Tags',
|
||||
type: 'short-input',
|
||||
placeholder: 'Comma-separated cache tags (Enterprise only)',
|
||||
condition: { field: 'operation', value: 'purge_cache' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'hosts',
|
||||
title: 'Hostnames',
|
||||
type: 'short-input',
|
||||
placeholder: 'Comma-separated hostnames (Enterprise only)',
|
||||
condition: { field: 'operation', value: 'purge_cache' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'prefixes',
|
||||
title: 'URL Prefixes',
|
||||
type: 'short-input',
|
||||
placeholder: 'Comma-separated URL prefixes (Enterprise only)',
|
||||
condition: { field: 'operation', value: 'purge_cache' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
// API Key (common)
|
||||
{
|
||||
id: 'apiKey',
|
||||
title: 'API Token',
|
||||
type: 'short-input',
|
||||
required: true,
|
||||
placeholder: 'Enter your Cloudflare API token',
|
||||
password: true,
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: [
|
||||
'cloudflare_list_zones',
|
||||
'cloudflare_get_zone',
|
||||
'cloudflare_create_zone',
|
||||
'cloudflare_delete_zone',
|
||||
'cloudflare_list_dns_records',
|
||||
'cloudflare_create_dns_record',
|
||||
'cloudflare_update_dns_record',
|
||||
'cloudflare_delete_dns_record',
|
||||
'cloudflare_list_certificates',
|
||||
'cloudflare_get_zone_settings',
|
||||
'cloudflare_update_zone_setting',
|
||||
'cloudflare_dns_analytics',
|
||||
'cloudflare_purge_cache',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => {
|
||||
// Convert numeric string params
|
||||
if (params.ttl) params.ttl = Number(params.ttl)
|
||||
if (params.priority) params.priority = Number(params.priority)
|
||||
|
||||
// Convert boolean string params
|
||||
if (params.proxied === 'true') params.proxied = true
|
||||
else if (params.proxied === 'false') params.proxied = false
|
||||
else if (params.proxied === '') params.proxied = undefined
|
||||
|
||||
if (params.purge_everything === 'true') params.purge_everything = true
|
||||
else if (params.purge_everything === 'false') params.purge_everything = false
|
||||
|
||||
// Convert limit to number for dns_analytics
|
||||
if (params.limit) params.limit = Number(params.limit)
|
||||
|
||||
// Clear empty string dropdown values
|
||||
if (params.type === '' && params.operation !== 'create_dns_record') {
|
||||
params.type = undefined
|
||||
}
|
||||
if (params.status === '') params.status = undefined
|
||||
|
||||
// Map zoneType to type for create_zone
|
||||
if (params.operation === 'create_zone' && params.zoneType) {
|
||||
params.type = params.zoneType
|
||||
}
|
||||
|
||||
return `cloudflare_${params.operation}`
|
||||
},
|
||||
},
|
||||
},
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'Operation to perform' },
|
||||
apiKey: { type: 'string', description: 'Cloudflare API token' },
|
||||
zoneId: { type: 'string', description: 'Zone ID' },
|
||||
accountId: { type: 'string', description: 'Cloudflare account ID' },
|
||||
zoneType: { type: 'string', description: 'Zone type (full or partial)' },
|
||||
recordId: { type: 'string', description: 'DNS record ID' },
|
||||
name: { type: 'string', description: 'Domain or record name' },
|
||||
type: { type: 'string', description: 'DNS record type' },
|
||||
content: { type: 'string', description: 'DNS record content' },
|
||||
ttl: { type: 'number', description: 'Time to live in seconds' },
|
||||
proxied: { type: 'boolean', description: 'Whether Cloudflare proxy is enabled' },
|
||||
priority: { type: 'number', description: 'Record priority (MX/SRV)' },
|
||||
comment: { type: 'string', description: 'Record comment' },
|
||||
settingId: { type: 'string', description: 'Zone setting ID' },
|
||||
value: { type: 'string', description: 'Setting value' },
|
||||
since: { type: 'string', description: 'Start date for analytics' },
|
||||
until: { type: 'string', description: 'End date for analytics' },
|
||||
metrics: { type: 'string', description: 'Comma-separated metrics to retrieve' },
|
||||
dimensions: { type: 'string', description: 'Comma-separated dimensions to group by' },
|
||||
filters: { type: 'string', description: 'Filters to apply (e.g., queryType==A)' },
|
||||
sort: { type: 'string', description: 'Sort order for results' },
|
||||
limit: { type: 'number', description: 'Maximum number of results' },
|
||||
status: { type: 'string', description: 'Status filter for zones or certificates' },
|
||||
page: { type: 'number', description: 'Page number for pagination' },
|
||||
per_page: { type: 'number', description: 'Number of results per page' },
|
||||
purge_everything: { type: 'boolean', description: 'Purge all cached content' },
|
||||
files: { type: 'string', description: 'Comma-separated URLs to purge' },
|
||||
tags: { type: 'string', description: 'Comma-separated cache tags to purge (Enterprise only)' },
|
||||
hosts: { type: 'string', description: 'Comma-separated hostnames to purge (Enterprise only)' },
|
||||
prefixes: {
|
||||
type: 'string',
|
||||
description: 'Comma-separated URL prefixes to purge (Enterprise only)',
|
||||
},
|
||||
},
|
||||
outputs: {
|
||||
zones: { type: 'json', description: 'List of zones/domains' },
|
||||
records: { type: 'json', description: 'List of DNS records' },
|
||||
certificates: { type: 'json', description: 'List of SSL/TLS certificate packs' },
|
||||
settings: { type: 'json', description: 'List of zone settings' },
|
||||
totals: { type: 'json', description: 'Aggregate DNS analytics totals' },
|
||||
timeseries: { type: 'json', description: 'DNS analytics time series data' },
|
||||
data_lag: {
|
||||
type: 'number',
|
||||
description: 'Processing lag in seconds before analytics data becomes available',
|
||||
},
|
||||
rows: { type: 'number', description: 'Total number of rows in the DNS analytics result set' },
|
||||
id: { type: 'string', description: 'Resource ID' },
|
||||
zone_id: { type: 'string', description: 'Zone ID the record belongs to' },
|
||||
zone_name: { type: 'string', description: 'Zone domain name' },
|
||||
name: { type: 'string', description: 'Resource name' },
|
||||
status: { type: 'string', description: 'Resource status' },
|
||||
paused: { type: 'boolean', description: 'Whether the zone is paused' },
|
||||
type: { type: 'string', description: 'Zone or record type' },
|
||||
name_servers: { type: 'json', description: 'Assigned Cloudflare name servers' },
|
||||
original_name_servers: { type: 'json', description: 'Original registrar name servers' },
|
||||
plan: { type: 'json', description: 'Zone plan information' },
|
||||
content: { type: 'string', description: 'DNS record value (e.g., IP address)' },
|
||||
proxiable: { type: 'boolean', description: 'Whether the record can be proxied' },
|
||||
proxied: { type: 'boolean', description: 'Whether Cloudflare proxy is enabled' },
|
||||
ttl: { type: 'number', description: 'TTL in seconds (1 = automatic)' },
|
||||
locked: { type: 'boolean', description: 'Whether the record is locked' },
|
||||
priority: { type: 'number', description: 'Priority for MX and SRV records' },
|
||||
comment: { type: 'string', description: 'Record comment' },
|
||||
tags: { type: 'json', description: 'Tags associated with the record' },
|
||||
created_on: { type: 'string', description: 'Creation date (ISO 8601)' },
|
||||
modified_on: { type: 'string', description: 'Last modified date (ISO 8601)' },
|
||||
value: { type: 'json', description: 'Setting value' },
|
||||
editable: { type: 'boolean', description: 'Whether the setting can be modified' },
|
||||
time_remaining: { type: 'number', description: 'Seconds until setting can be modified again' },
|
||||
total_count: { type: 'number', description: 'Total count of results' },
|
||||
},
|
||||
}
|
||||
337
apps/sim/blocks/blocks/microsoft_dataverse.ts
Normal file
337
apps/sim/blocks/blocks/microsoft_dataverse.ts
Normal file
@@ -0,0 +1,337 @@
|
||||
import { MicrosoftDataverseIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import type { DataverseResponse } from '@/tools/microsoft_dataverse/types'
|
||||
|
||||
export const MicrosoftDataverseBlock: BlockConfig<DataverseResponse> = {
|
||||
type: 'microsoft_dataverse',
|
||||
name: 'Microsoft Dataverse',
|
||||
description: 'Manage records in Microsoft Dataverse tables',
|
||||
authMode: AuthMode.OAuth,
|
||||
longDescription:
|
||||
'Integrate Microsoft Dataverse into your workflow. Create, read, update, delete, upsert, associate, and query records in Dataverse tables using the Web API. Works with Dynamics 365, Power Platform, and custom Dataverse environments.',
|
||||
docsLink: 'https://docs.sim.ai/tools/microsoft_dataverse',
|
||||
category: 'tools',
|
||||
bgColor: '#E0E0E0',
|
||||
icon: MicrosoftDataverseIcon,
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'List Records', id: 'list_records' },
|
||||
{ label: 'Get Record', id: 'get_record' },
|
||||
{ label: 'Create Record', id: 'create_record' },
|
||||
{ label: 'Update Record', id: 'update_record' },
|
||||
{ label: 'Upsert Record', id: 'upsert_record' },
|
||||
{ label: 'Delete Record', id: 'delete_record' },
|
||||
{ label: 'Associate Records', id: 'associate' },
|
||||
{ label: 'Disassociate Records', id: 'disassociate' },
|
||||
{ label: 'WhoAmI', id: 'whoami' },
|
||||
],
|
||||
value: () => 'list_records',
|
||||
},
|
||||
{
|
||||
id: 'credential',
|
||||
title: 'Microsoft Account',
|
||||
type: 'oauth-input',
|
||||
serviceId: 'microsoft-dataverse',
|
||||
requiredScopes: [
|
||||
'openid',
|
||||
'profile',
|
||||
'email',
|
||||
'https://dynamics.microsoft.com/user_impersonation',
|
||||
'offline_access',
|
||||
],
|
||||
placeholder: 'Select Microsoft account',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'environmentUrl',
|
||||
title: 'Environment URL',
|
||||
type: 'short-input',
|
||||
placeholder: 'https://myorg.crm.dynamics.com',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'entitySetName',
|
||||
title: 'Entity Set Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Plural table name (e.g., accounts, contacts)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'whoami',
|
||||
not: true,
|
||||
},
|
||||
required: {
|
||||
field: 'operation',
|
||||
value: 'whoami',
|
||||
not: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'recordId',
|
||||
title: 'Record ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Record GUID (e.g., 00000000-0000-0000-0000-000000000000)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'get_record',
|
||||
'update_record',
|
||||
'upsert_record',
|
||||
'delete_record',
|
||||
'associate',
|
||||
'disassociate',
|
||||
],
|
||||
},
|
||||
required: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'get_record',
|
||||
'update_record',
|
||||
'upsert_record',
|
||||
'delete_record',
|
||||
'associate',
|
||||
'disassociate',
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'data',
|
||||
title: 'Record Data',
|
||||
type: 'long-input',
|
||||
placeholder:
|
||||
'JSON object with column values (e.g., {"name": "Contoso", "telephone1": "555-0100"})',
|
||||
condition: { field: 'operation', value: ['create_record', 'update_record', 'upsert_record'] },
|
||||
required: { field: 'operation', value: ['create_record', 'update_record', 'upsert_record'] },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a Dataverse record JSON object based on the user's description.
|
||||
The JSON should contain column logical names as keys and appropriate values.
|
||||
Common Dataverse column naming conventions:
|
||||
- Text: "name", "description", "emailaddress1", "telephone1"
|
||||
- Lookup: "_primarycontactid_value" (read-only), use "primarycontactid@odata.bind": "/contacts(guid)" for setting
|
||||
- Choice/OptionSet: integer values (e.g., "statecode": 0, "statuscode": 1)
|
||||
- Date: ISO 8601 format (e.g., "createdon": "2024-01-15T00:00:00Z")
|
||||
- Currency: decimal numbers (e.g., "revenue": 1000000.00)
|
||||
|
||||
Return ONLY valid JSON - no explanations, no markdown code blocks.`,
|
||||
placeholder: 'Describe the record data you want to create or update...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'select',
|
||||
title: 'Select Columns',
|
||||
type: 'short-input',
|
||||
placeholder: 'Comma-separated columns (e.g., name,telephone1,emailaddress1)',
|
||||
condition: { field: 'operation', value: ['list_records', 'get_record'] },
|
||||
mode: 'advanced',
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a comma-separated list of Dataverse column logical names based on the user's description.
|
||||
Use lowercase logical names without spaces.
|
||||
Common columns by table:
|
||||
- Accounts: name, accountnumber, telephone1, emailaddress1, address1_city, revenue, industrycode
|
||||
- Contacts: firstname, lastname, fullname, emailaddress1, telephone1, jobtitle, birthdate
|
||||
- General: statecode, statuscode, createdon, modifiedon, ownerid, createdby
|
||||
|
||||
Return ONLY the comma-separated column names - no explanations.`,
|
||||
placeholder: 'Describe which columns you want to retrieve...',
|
||||
generationType: 'odata-expression',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'filter',
|
||||
title: 'Filter',
|
||||
type: 'short-input',
|
||||
placeholder: "OData filter (e.g., statecode eq 0 and contains(name,'Contoso'))",
|
||||
condition: { field: 'operation', value: 'list_records' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate an OData $filter expression for the Dataverse Web API based on the user's description.
|
||||
OData filter syntax:
|
||||
- Comparison: eq, ne, gt, ge, lt, le (e.g., "revenue gt 1000000")
|
||||
- Logical: and, or, not (e.g., "statecode eq 0 and revenue gt 1000000")
|
||||
- String functions: contains(name,'value'), startswith(name,'value'), endswith(name,'value')
|
||||
- Date functions: year(createdon) eq 2024, month(createdon) eq 1
|
||||
- Null check: fieldname eq null, fieldname ne null
|
||||
- Status: statecode eq 0 (active), statecode eq 1 (inactive)
|
||||
|
||||
Return ONLY the filter expression - no $filter= prefix, no explanations.`,
|
||||
placeholder: 'Describe which records you want to filter for...',
|
||||
generationType: 'odata-expression',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'orderBy',
|
||||
title: 'Order By',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., name asc, createdon desc',
|
||||
condition: { field: 'operation', value: 'list_records' },
|
||||
mode: 'advanced',
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate an OData $orderby expression for sorting Dataverse records based on the user's description.
|
||||
Format: column_name asc|desc, separated by commas for multi-column sort.
|
||||
Examples:
|
||||
- "name asc" - Sort by name alphabetically
|
||||
- "createdon desc" - Sort by creation date, newest first
|
||||
- "name asc, createdon desc" - Sort by name, then by date
|
||||
|
||||
Return ONLY the orderby expression - no $orderby= prefix, no explanations.`,
|
||||
placeholder: 'Describe how you want to sort the results...',
|
||||
generationType: 'odata-expression',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'top',
|
||||
title: 'Max Results',
|
||||
type: 'short-input',
|
||||
placeholder: 'Maximum number of records (default: 5000)',
|
||||
condition: { field: 'operation', value: 'list_records' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'expand',
|
||||
title: 'Expand',
|
||||
type: 'short-input',
|
||||
placeholder: 'Navigation properties to expand (e.g., primarycontactid)',
|
||||
condition: { field: 'operation', value: ['list_records', 'get_record'] },
|
||||
mode: 'advanced',
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate an OData $expand expression for the Dataverse Web API based on the user's description.
|
||||
$expand retrieves related records through navigation properties.
|
||||
Examples:
|
||||
- "primarycontactid" - Expand the primary contact lookup
|
||||
- "contact_customer_accounts" - Expand related contacts for an account
|
||||
- "primarycontactid($select=fullname,emailaddress1)" - Expand with selected columns
|
||||
- "contact_customer_accounts($select=fullname;$top=5;$orderby=fullname asc)" - Expand with query options
|
||||
|
||||
Return ONLY the expand expression - no $expand= prefix, no explanations.`,
|
||||
placeholder: 'Describe which related records you want to include...',
|
||||
generationType: 'odata-expression',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'navigationProperty',
|
||||
title: 'Navigation Property',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., contact_customer_accounts',
|
||||
condition: { field: 'operation', value: ['associate', 'disassociate'] },
|
||||
required: { field: 'operation', value: ['associate', 'disassociate'] },
|
||||
},
|
||||
{
|
||||
id: 'navigationType',
|
||||
title: 'Navigation Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Collection-valued (default)', id: 'collection' },
|
||||
{ label: 'Single-valued (lookup)', id: 'single' },
|
||||
],
|
||||
value: () => 'collection',
|
||||
condition: { field: 'operation', value: 'associate' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'targetEntitySetName',
|
||||
title: 'Target Entity Set',
|
||||
type: 'short-input',
|
||||
placeholder: 'Target table name (e.g., contacts)',
|
||||
condition: { field: 'operation', value: 'associate' },
|
||||
required: { field: 'operation', value: 'associate' },
|
||||
},
|
||||
{
|
||||
id: 'targetRecordId',
|
||||
title: 'Target Record ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Target record GUID',
|
||||
condition: { field: 'operation', value: ['associate', 'disassociate'] },
|
||||
required: { field: 'operation', value: 'associate' },
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: [
|
||||
'microsoft_dataverse_associate',
|
||||
'microsoft_dataverse_create_record',
|
||||
'microsoft_dataverse_delete_record',
|
||||
'microsoft_dataverse_disassociate',
|
||||
'microsoft_dataverse_get_record',
|
||||
'microsoft_dataverse_list_records',
|
||||
'microsoft_dataverse_update_record',
|
||||
'microsoft_dataverse_upsert_record',
|
||||
'microsoft_dataverse_whoami',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => `microsoft_dataverse_${params.operation}`,
|
||||
params: (params) => {
|
||||
const { credential, operation, ...rest } = params
|
||||
|
||||
const cleanParams: Record<string, unknown> = {
|
||||
credential,
|
||||
}
|
||||
|
||||
Object.entries(rest).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && value !== '') {
|
||||
cleanParams[key] = value
|
||||
}
|
||||
})
|
||||
|
||||
return cleanParams
|
||||
},
|
||||
},
|
||||
},
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'Operation to perform' },
|
||||
credential: { type: 'string', description: 'Microsoft Dataverse OAuth credential' },
|
||||
environmentUrl: { type: 'string', description: 'Dataverse environment URL' },
|
||||
entitySetName: { type: 'string', description: 'Entity set name (plural table name)' },
|
||||
recordId: { type: 'string', description: 'Record GUID' },
|
||||
data: { type: 'json', description: 'Record data as JSON object' },
|
||||
select: { type: 'string', description: 'Columns to return (comma-separated)' },
|
||||
filter: { type: 'string', description: 'OData $filter expression' },
|
||||
orderBy: { type: 'string', description: 'OData $orderby expression' },
|
||||
top: { type: 'string', description: 'Maximum number of records' },
|
||||
expand: { type: 'string', description: 'Navigation properties to expand' },
|
||||
navigationProperty: {
|
||||
type: 'string',
|
||||
description: 'Navigation property name for associations',
|
||||
},
|
||||
navigationType: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Navigation property type: "collection" (default) or "single" (for lookup fields)',
|
||||
},
|
||||
targetEntitySetName: { type: 'string', description: 'Target entity set for association' },
|
||||
targetRecordId: { type: 'string', description: 'Target record GUID for association' },
|
||||
},
|
||||
outputs: {
|
||||
records: { type: 'json', description: 'Array of records (list operation)' },
|
||||
record: { type: 'json', description: 'Single record data' },
|
||||
recordId: { type: 'string', description: 'Record ID' },
|
||||
count: { type: 'number', description: 'Number of records returned in the current page' },
|
||||
totalCount: {
|
||||
type: 'number',
|
||||
description: 'Total matching records server-side (requires $count=true)',
|
||||
},
|
||||
nextLink: { type: 'string', description: 'URL for next page of results' },
|
||||
created: { type: 'boolean', description: 'Whether a new record was created (upsert)' },
|
||||
userId: { type: 'string', description: 'Authenticated user ID (WhoAmI)' },
|
||||
businessUnitId: { type: 'string', description: 'Business unit ID (WhoAmI)' },
|
||||
organizationId: { type: 'string', description: 'Organization ID (WhoAmI)' },
|
||||
entitySetName: {
|
||||
type: 'string',
|
||||
description: 'Source entity set name (associate/disassociate)',
|
||||
},
|
||||
navigationProperty: {
|
||||
type: 'string',
|
||||
description: 'Navigation property used (associate/disassociate)',
|
||||
},
|
||||
targetEntitySetName: { type: 'string', description: 'Target entity set name (associate)' },
|
||||
targetRecordId: { type: 'string', description: 'Target record GUID (associate/disassociate)' },
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import { ChatTriggerBlock } from '@/blocks/blocks/chat_trigger'
|
||||
import { CirclebackBlock } from '@/blocks/blocks/circleback'
|
||||
import { ClayBlock } from '@/blocks/blocks/clay'
|
||||
import { ClerkBlock } from '@/blocks/blocks/clerk'
|
||||
import { CloudflareBlock } from '@/blocks/blocks/cloudflare'
|
||||
import { ConditionBlock } from '@/blocks/blocks/condition'
|
||||
import { ConfluenceBlock, ConfluenceV2Block } from '@/blocks/blocks/confluence'
|
||||
import { CursorBlock, CursorV2Block } from '@/blocks/blocks/cursor'
|
||||
@@ -78,6 +79,7 @@ import { ManualTriggerBlock } from '@/blocks/blocks/manual_trigger'
|
||||
import { McpBlock } from '@/blocks/blocks/mcp'
|
||||
import { Mem0Block } from '@/blocks/blocks/mem0'
|
||||
import { MemoryBlock } from '@/blocks/blocks/memory'
|
||||
import { MicrosoftDataverseBlock } from '@/blocks/blocks/microsoft_dataverse'
|
||||
import { MicrosoftExcelBlock, MicrosoftExcelV2Block } from '@/blocks/blocks/microsoft_excel'
|
||||
import { MicrosoftPlannerBlock } from '@/blocks/blocks/microsoft_planner'
|
||||
import { MicrosoftTeamsBlock } from '@/blocks/blocks/microsoft_teams'
|
||||
@@ -183,6 +185,7 @@ export const registry: Record<string, BlockConfig> = {
|
||||
calendly: CalendlyBlock,
|
||||
chat_trigger: ChatTriggerBlock,
|
||||
circleback: CirclebackBlock,
|
||||
cloudflare: CloudflareBlock,
|
||||
clay: ClayBlock,
|
||||
clerk: ClerkBlock,
|
||||
condition: ConditionBlock,
|
||||
@@ -191,6 +194,7 @@ export const registry: Record<string, BlockConfig> = {
|
||||
cursor: CursorBlock,
|
||||
cursor_v2: CursorV2Block,
|
||||
datadog: DatadogBlock,
|
||||
microsoft_dataverse: MicrosoftDataverseBlock,
|
||||
discord: DiscordBlock,
|
||||
dropbox: DropboxBlock,
|
||||
dspy: DSPyBlock,
|
||||
|
||||
@@ -41,6 +41,7 @@ export type GenerationType =
|
||||
| 'timestamp'
|
||||
| 'timezone'
|
||||
| 'cron-expression'
|
||||
| 'odata-expression'
|
||||
|
||||
export type SubBlockType =
|
||||
| 'short-input' // Single line input
|
||||
|
||||
@@ -4407,6 +4407,161 @@ export function DatadogIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function MicrosoftDataverseIcon(props: SVGProps<SVGSVGElement>) {
|
||||
const id = useId()
|
||||
const clip0 = `dataverse_clip0_${id}`
|
||||
const clip1 = `dataverse_clip1_${id}`
|
||||
const clip2 = `dataverse_clip2_${id}`
|
||||
const paint0 = `dataverse_paint0_${id}`
|
||||
const paint1 = `dataverse_paint1_${id}`
|
||||
const paint2 = `dataverse_paint2_${id}`
|
||||
const paint3 = `dataverse_paint3_${id}`
|
||||
const paint4 = `dataverse_paint4_${id}`
|
||||
const paint5 = `dataverse_paint5_${id}`
|
||||
const paint6 = `dataverse_paint6_${id}`
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
width='96'
|
||||
height='96'
|
||||
viewBox='0 0 96 96'
|
||||
fill='none'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
>
|
||||
<g clipPath={`url(#${clip0})`}>
|
||||
<g clipPath={`url(#${clip1})`}>
|
||||
<g clipPath={`url(#${clip2})`}>
|
||||
<path
|
||||
d='M13.8776 21.8242C29.1033 8.13791 49.7501 8.1861 62.955 18.9134C74.9816 28.6836 77.4697 44.3159 70.851 55.7801C64.2321 67.2443 52.5277 70.1455 39.5011 62.6247L31.7286 76.087L31.7234 76.0862C27.4181 83.5324 17.8937 86.0828 10.4437 81.7817C7.45394 80.0556 5.25322 77.4879 3.96665 74.551L3.96096 74.5511C-4.07832 55.7804 0.200745 34.1184 13.8776 21.8242Z'
|
||||
fill={`url(#${paint0})`}
|
||||
/>
|
||||
<path
|
||||
d='M13.8776 21.8242C29.1033 8.13791 49.7501 8.1861 62.955 18.9134C74.9816 28.6836 77.4697 44.3159 70.851 55.7801C64.2321 67.2443 52.5277 70.1455 39.5011 62.6247L31.7286 76.087L31.7234 76.0862C27.4181 83.5324 17.8937 86.0828 10.4437 81.7817C7.45394 80.0556 5.25322 77.4879 3.96665 74.551L3.96096 74.5511C-4.07832 55.7804 0.200745 34.1184 13.8776 21.8242Z'
|
||||
fill={`url(#${paint1})`}
|
||||
fillOpacity='0.8'
|
||||
/>
|
||||
<path
|
||||
d='M85.4327 14.2231C88.4528 15.9668 90.6686 18.569 91.9494 21.5433L91.9533 21.5444C99.9406 40.2943 95.6533 61.9068 81.9983 74.1814C66.7726 87.8677 46.1257 87.8196 32.9209 77.0923C20.8945 67.3221 18.4062 51.6897 25.0249 40.2256C31.6438 28.7614 43.3482 25.8601 56.3748 33.381L64.1434 19.9255L64.1482 19.9249C68.4516 12.4736 77.9805 9.92084 85.4327 14.2231Z'
|
||||
fill={`url(#${paint2})`}
|
||||
/>
|
||||
<path
|
||||
d='M85.4327 14.2231C88.4528 15.9668 90.6686 18.569 91.9494 21.5433L91.9533 21.5444C99.9406 40.2943 95.6533 61.9068 81.9983 74.1814C66.7726 87.8677 46.1257 87.8196 32.9209 77.0923C20.8945 67.3221 18.4062 51.6897 25.0249 40.2256C31.6438 28.7614 43.3482 25.8601 56.3748 33.381L64.1434 19.9255L64.1482 19.9249C68.4516 12.4736 77.9805 9.92084 85.4327 14.2231Z'
|
||||
fill={`url(#${paint3})`}
|
||||
fillOpacity='0.9'
|
||||
/>
|
||||
<path
|
||||
d='M39.5041 62.6261C52.5307 70.1469 64.2352 67.2456 70.8541 55.7814C77.2488 44.7055 75.1426 29.7389 64.147 19.9271L56.3791 33.3814L39.5041 62.6261Z'
|
||||
fill={`url(#${paint4})`}
|
||||
/>
|
||||
<path
|
||||
d='M56.3794 33.3815C43.3528 25.8607 31.6482 28.762 25.0294 40.2262C18.6347 51.3021 20.7409 66.2687 31.7364 76.0806L39.5043 62.6262L56.3794 33.3815Z'
|
||||
fill={`url(#${paint5})`}
|
||||
/>
|
||||
<path
|
||||
d='M33.3215 56.4453C37.9837 64.5204 48.3094 67.2872 56.3846 62.625C64.4598 57.9628 67.2266 47.6371 62.5643 39.5619C57.9021 31.4867 47.5764 28.72 39.5013 33.3822C31.4261 38.0444 28.6593 48.3701 33.3215 56.4453Z'
|
||||
fill={`url(#${paint6})`}
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<radialGradient
|
||||
id={paint0}
|
||||
cx='0'
|
||||
cy='0'
|
||||
r='1'
|
||||
gradientUnits='userSpaceOnUse'
|
||||
gradientTransform='translate(46.0001 49.4996) rotate(-148.717) scale(46.2195 47.5359)'
|
||||
>
|
||||
<stop offset='0.465088' stopColor='#09442A' />
|
||||
<stop offset='0.70088' stopColor='#136C6C' />
|
||||
<stop offset='1' stopColor='#22918B' />
|
||||
</radialGradient>
|
||||
<radialGradient
|
||||
id={paint1}
|
||||
cx='0'
|
||||
cy='0'
|
||||
r='1'
|
||||
gradientUnits='userSpaceOnUse'
|
||||
gradientTransform='translate(50.0001 32.4996) rotate(123.57) scale(66.0095 46.5498)'
|
||||
>
|
||||
<stop offset='0.718705' stopColor='#1A7F7C' stopOpacity='0' />
|
||||
<stop offset='1' stopColor='#16BBDA' />
|
||||
</radialGradient>
|
||||
<radialGradient
|
||||
id={paint2}
|
||||
cx='0'
|
||||
cy='0'
|
||||
r='1'
|
||||
gradientUnits='userSpaceOnUse'
|
||||
gradientTransform='translate(50.4999 44.5001) rotate(30.75) scale(45.9618 44.5095)'
|
||||
>
|
||||
<stop offset='0.358097' stopColor='#136C6C' />
|
||||
<stop offset='0.789474' stopColor='#42B870' />
|
||||
<stop offset='1' stopColor='#76D45E' />
|
||||
</radialGradient>
|
||||
<radialGradient
|
||||
id={paint3}
|
||||
cx='0'
|
||||
cy='0'
|
||||
r='1'
|
||||
gradientTransform='matrix(42.5 -36.0002 31.1824 36.8127 49.4998 55.5001)'
|
||||
gradientUnits='userSpaceOnUse'
|
||||
>
|
||||
<stop offset='0.583166' stopColor='#76D45E' stopOpacity='0' />
|
||||
<stop offset='1' stopColor='#C8F5B7' />
|
||||
</radialGradient>
|
||||
<radialGradient
|
||||
id={paint4}
|
||||
cx='0'
|
||||
cy='0'
|
||||
r='1'
|
||||
gradientUnits='userSpaceOnUse'
|
||||
gradientTransform='translate(47.5 48) rotate(-58.9042) scale(32.6898)'
|
||||
>
|
||||
<stop offset='0.486266' stopColor='#22918B' />
|
||||
<stop offset='0.729599' stopColor='#42B870' />
|
||||
<stop offset='1' stopColor='#43E5CA' />
|
||||
</radialGradient>
|
||||
<radialGradient
|
||||
id={paint5}
|
||||
cx='0'
|
||||
cy='0'
|
||||
r='1'
|
||||
gradientUnits='userSpaceOnUse'
|
||||
gradientTransform='translate(47.3833 49.0077) rotate(119.859) scale(31.1328 29.4032)'
|
||||
>
|
||||
<stop offset='0.459553' stopColor='#08494E' />
|
||||
<stop offset='0.742242' stopColor='#1A7F7C' />
|
||||
<stop offset='1' stopColor='#309C61' />
|
||||
</radialGradient>
|
||||
<radialGradient
|
||||
id={paint6}
|
||||
cx='0'
|
||||
cy='0'
|
||||
r='1'
|
||||
gradientUnits='userSpaceOnUse'
|
||||
gradientTransform='translate(52.5 40) rotate(120.784) scale(27.3542)'
|
||||
>
|
||||
<stop stopColor='#C8F5B7' />
|
||||
<stop offset='0.24583' stopColor='#98F0B0' />
|
||||
<stop offset='0.643961' stopColor='#52D17C' />
|
||||
<stop offset='1' stopColor='#119FC5' />
|
||||
</radialGradient>
|
||||
<clipPath id={clip0}>
|
||||
<rect width='96' height='96' fill='white' />
|
||||
</clipPath>
|
||||
<clipPath id={clip1}>
|
||||
<rect width='96' height='96' fill='white' />
|
||||
</clipPath>
|
||||
<clipPath id={clip2}>
|
||||
<rect width='95.9998' height='96' fill='white' />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function KalshiIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 78 20' fill='currentColor' xmlns='http://www.w3.org/2000/svg'>
|
||||
@@ -5547,3 +5702,18 @@ export function VercelIcon(props: SVGProps<SVGSVGElement>) {
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function CloudflareIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'>
|
||||
<path
|
||||
fill='#f38020'
|
||||
d='M331 326c11-26-4-38-19-38l-148-2c-4 0-4-6 1-7l150-2c17-1 37-15 43-33 0 0 10-21 9-24a97 97 0 0 0-187-11c-38-25-78 9-69 46-48 3-65 46-60 72 0 1 1 2 3 2h274c1 0 3-1 3-3z'
|
||||
/>
|
||||
<path
|
||||
fill='#faae40'
|
||||
d='M381 224c-4 0-6-1-7 1l-5 21c-5 16 3 30 20 31l32 2c4 0 4 6-1 7l-33 1c-36 4-46 39-46 39 0 2 0 3 2 3h113l3-2a81 81 0 0 0-78-103'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ export const MICROSOFT_REFRESH_TOKEN_LIFETIME_DAYS = 90
|
||||
export const PROACTIVE_REFRESH_THRESHOLD_DAYS = 7
|
||||
|
||||
export const MICROSOFT_PROVIDERS = new Set([
|
||||
'microsoft-dataverse',
|
||||
'microsoft-excel',
|
||||
'microsoft-planner',
|
||||
'microsoft-teams',
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
JiraIcon,
|
||||
LinearIcon,
|
||||
LinkedInIcon,
|
||||
MicrosoftDataverseIcon,
|
||||
MicrosoftExcelIcon,
|
||||
MicrosoftIcon,
|
||||
MicrosoftOneDriveIcon,
|
||||
@@ -154,6 +155,20 @@ export const OAUTH_PROVIDERS: Record<string, OAuthProviderConfig> = {
|
||||
name: 'Microsoft',
|
||||
icon: MicrosoftIcon,
|
||||
services: {
|
||||
'microsoft-dataverse': {
|
||||
name: 'Microsoft Dataverse',
|
||||
description: 'Connect to Microsoft Dataverse and manage records.',
|
||||
providerId: 'microsoft-dataverse',
|
||||
icon: MicrosoftDataverseIcon,
|
||||
baseProviderIcon: MicrosoftIcon,
|
||||
scopes: [
|
||||
'openid',
|
||||
'profile',
|
||||
'email',
|
||||
'https://dynamics.microsoft.com/user_impersonation',
|
||||
'offline_access',
|
||||
],
|
||||
},
|
||||
'microsoft-excel': {
|
||||
name: 'Microsoft Excel',
|
||||
description: 'Connect to Microsoft Excel and manage spreadsheets.',
|
||||
|
||||
@@ -20,6 +20,7 @@ export type OAuthProvider =
|
||||
| 'jira'
|
||||
| 'dropbox'
|
||||
| 'microsoft'
|
||||
| 'microsoft-dataverse'
|
||||
| 'microsoft-excel'
|
||||
| 'microsoft-planner'
|
||||
| 'microsoft-teams'
|
||||
@@ -61,6 +62,7 @@ export type OAuthService =
|
||||
| 'notion'
|
||||
| 'jira'
|
||||
| 'dropbox'
|
||||
| 'microsoft-dataverse'
|
||||
| 'microsoft-excel'
|
||||
| 'microsoft-teams'
|
||||
| 'microsoft-planner'
|
||||
|
||||
174
apps/sim/tools/cloudflare/create_dns_record.ts
Normal file
174
apps/sim/tools/cloudflare/create_dns_record.ts
Normal file
@@ -0,0 +1,174 @@
|
||||
import type {
|
||||
CloudflareCreateDnsRecordParams,
|
||||
CloudflareCreateDnsRecordResponse,
|
||||
} from '@/tools/cloudflare/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const createDnsRecordTool: ToolConfig<
|
||||
CloudflareCreateDnsRecordParams,
|
||||
CloudflareCreateDnsRecordResponse
|
||||
> = {
|
||||
id: 'cloudflare_create_dns_record',
|
||||
name: 'Cloudflare Create DNS Record',
|
||||
description: 'Creates a new DNS record for a zone.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
zoneId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The zone ID to create the DNS record in',
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'DNS record type (e.g., "A", "AAAA", "CNAME", "MX", "TXT", "NS", "SRV")',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'DNS record name (e.g., "example.com" or "subdomain.example.com")',
|
||||
},
|
||||
content: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'DNS record content (e.g., IP address for A records, target for CNAME)',
|
||||
},
|
||||
ttl: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Time to live in seconds (1 = automatic, default: 1)',
|
||||
},
|
||||
proxied: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to enable Cloudflare proxy (default: false)',
|
||||
},
|
||||
priority: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Priority for MX and SRV records',
|
||||
},
|
||||
comment: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comment for the DNS record',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Cloudflare API Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.cloudflare.com/client/v4/zones/${params.zoneId}/dns_records`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, any> = {
|
||||
type: params.type,
|
||||
name: params.name,
|
||||
content: params.content,
|
||||
}
|
||||
if (params.ttl !== undefined) body.ttl = Number(params.ttl)
|
||||
if (params.proxied !== undefined) body.proxied = params.proxied
|
||||
if (params.priority !== undefined) body.priority = Number(params.priority)
|
||||
if (params.comment) body.comment = params.comment
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.success) {
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
id: '',
|
||||
zone_id: '',
|
||||
zone_name: '',
|
||||
type: '',
|
||||
name: '',
|
||||
content: '',
|
||||
proxiable: false,
|
||||
proxied: false,
|
||||
ttl: 0,
|
||||
locked: false,
|
||||
priority: undefined,
|
||||
comment: null,
|
||||
tags: [],
|
||||
created_on: '',
|
||||
modified_on: '',
|
||||
},
|
||||
error: data.errors?.[0]?.message ?? 'Failed to create DNS record',
|
||||
}
|
||||
}
|
||||
|
||||
const record = data.result
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: record?.id ?? '',
|
||||
zone_id: record?.zone_id ?? '',
|
||||
zone_name: record?.zone_name ?? '',
|
||||
type: record?.type ?? '',
|
||||
name: record?.name ?? '',
|
||||
content: record?.content ?? '',
|
||||
proxiable: record?.proxiable ?? false,
|
||||
proxied: record?.proxied ?? false,
|
||||
ttl: record?.ttl ?? 0,
|
||||
locked: record?.locked ?? false,
|
||||
priority: record?.priority ?? null,
|
||||
comment: record?.comment ?? null,
|
||||
tags: record?.tags ?? [],
|
||||
created_on: record?.created_on ?? '',
|
||||
modified_on: record?.modified_on ?? '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Unique identifier for the created DNS record' },
|
||||
zone_id: { type: 'string', description: 'The ID of the zone the record belongs to' },
|
||||
zone_name: { type: 'string', description: 'The name of the zone' },
|
||||
type: { type: 'string', description: 'DNS record type (A, AAAA, CNAME, MX, TXT, etc.)' },
|
||||
name: { type: 'string', description: 'DNS record hostname' },
|
||||
content: {
|
||||
type: 'string',
|
||||
description: 'DNS record value (e.g., IP address, target hostname)',
|
||||
},
|
||||
proxiable: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the record can be proxied through Cloudflare',
|
||||
},
|
||||
proxied: { type: 'boolean', description: 'Whether Cloudflare proxy is enabled' },
|
||||
ttl: { type: 'number', description: 'Time to live in seconds (1 = automatic)' },
|
||||
locked: { type: 'boolean', description: 'Whether the record is locked' },
|
||||
priority: { type: 'number', description: 'Priority for MX and SRV records', optional: true },
|
||||
comment: { type: 'string', description: 'Comment associated with the record', optional: true },
|
||||
tags: {
|
||||
type: 'array',
|
||||
description: 'Tags associated with the record',
|
||||
items: { type: 'string', description: 'Tag value' },
|
||||
},
|
||||
created_on: { type: 'string', description: 'ISO 8601 timestamp when the record was created' },
|
||||
modified_on: {
|
||||
type: 'string',
|
||||
description: 'ISO 8601 timestamp when the record was last modified',
|
||||
},
|
||||
},
|
||||
}
|
||||
135
apps/sim/tools/cloudflare/create_zone.ts
Normal file
135
apps/sim/tools/cloudflare/create_zone.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import type {
|
||||
CloudflareCreateZoneParams,
|
||||
CloudflareCreateZoneResponse,
|
||||
} from '@/tools/cloudflare/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const createZoneTool: ToolConfig<CloudflareCreateZoneParams, CloudflareCreateZoneResponse> =
|
||||
{
|
||||
id: 'cloudflare_create_zone',
|
||||
name: 'Cloudflare Create Zone',
|
||||
description: 'Adds a new zone (domain) to the Cloudflare account.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The domain name to add (e.g., "example.com")',
|
||||
},
|
||||
accountId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The Cloudflare account ID',
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Zone type: "full" (Cloudflare manages DNS) or "partial" (CNAME setup)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Cloudflare API Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.cloudflare.com/client/v4/zones',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, any> = {
|
||||
name: params.name,
|
||||
account: { id: params.accountId },
|
||||
}
|
||||
if (params.type) body.type = params.type
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.success) {
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
id: '',
|
||||
name: '',
|
||||
status: '',
|
||||
paused: false,
|
||||
type: '',
|
||||
name_servers: [],
|
||||
original_name_servers: [],
|
||||
created_on: '',
|
||||
modified_on: '',
|
||||
plan: { id: '', name: '' },
|
||||
},
|
||||
error: data.errors?.[0]?.message ?? 'Failed to create zone',
|
||||
}
|
||||
}
|
||||
|
||||
const zone = data.result
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: zone?.id ?? '',
|
||||
name: zone?.name ?? '',
|
||||
status: zone?.status ?? '',
|
||||
paused: zone?.paused ?? false,
|
||||
type: zone?.type ?? '',
|
||||
name_servers: zone?.name_servers ?? [],
|
||||
original_name_servers: zone?.original_name_servers ?? [],
|
||||
created_on: zone?.created_on ?? '',
|
||||
modified_on: zone?.modified_on ?? '',
|
||||
plan: {
|
||||
id: zone?.plan?.id ?? '',
|
||||
name: zone?.plan?.name ?? '',
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Created zone ID' },
|
||||
name: { type: 'string', description: 'Domain name' },
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Zone status (active, pending, initializing, moved, deleted, deactivated)',
|
||||
},
|
||||
paused: { type: 'boolean', description: 'Whether the zone is paused' },
|
||||
type: { type: 'string', description: 'Zone type (full or partial)' },
|
||||
name_servers: {
|
||||
type: 'array',
|
||||
description: 'Assigned Cloudflare name servers',
|
||||
items: { type: 'string', description: 'Name server hostname' },
|
||||
},
|
||||
original_name_servers: {
|
||||
type: 'array',
|
||||
description: 'Original name servers before moving to Cloudflare',
|
||||
items: { type: 'string', description: 'Name server hostname' },
|
||||
optional: true,
|
||||
},
|
||||
created_on: { type: 'string', description: 'ISO 8601 date when the zone was created' },
|
||||
modified_on: {
|
||||
type: 'string',
|
||||
description: 'ISO 8601 date when the zone was last modified',
|
||||
},
|
||||
plan: {
|
||||
type: 'object',
|
||||
description: 'Zone plan information',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Plan identifier' },
|
||||
name: { type: 'string', description: 'Plan name' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
69
apps/sim/tools/cloudflare/delete_dns_record.ts
Normal file
69
apps/sim/tools/cloudflare/delete_dns_record.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import type {
|
||||
CloudflareDeleteDnsRecordParams,
|
||||
CloudflareDeleteDnsRecordResponse,
|
||||
} from '@/tools/cloudflare/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const deleteDnsRecordTool: ToolConfig<
|
||||
CloudflareDeleteDnsRecordParams,
|
||||
CloudflareDeleteDnsRecordResponse
|
||||
> = {
|
||||
id: 'cloudflare_delete_dns_record',
|
||||
name: 'Cloudflare Delete DNS Record',
|
||||
description: 'Deletes a DNS record from a zone.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
zoneId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The zone ID containing the DNS record',
|
||||
},
|
||||
recordId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The DNS record ID to delete',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Cloudflare API Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.cloudflare.com/client/v4/zones/${params.zoneId}/dns_records/${params.recordId}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.success) {
|
||||
return {
|
||||
success: false,
|
||||
output: { id: '' },
|
||||
error: data.errors?.[0]?.message ?? 'Failed to delete DNS record',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.result?.id ?? '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Deleted record ID' },
|
||||
},
|
||||
}
|
||||
60
apps/sim/tools/cloudflare/delete_zone.ts
Normal file
60
apps/sim/tools/cloudflare/delete_zone.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import type {
|
||||
CloudflareDeleteZoneParams,
|
||||
CloudflareDeleteZoneResponse,
|
||||
} from '@/tools/cloudflare/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const deleteZoneTool: ToolConfig<CloudflareDeleteZoneParams, CloudflareDeleteZoneResponse> =
|
||||
{
|
||||
id: 'cloudflare_delete_zone',
|
||||
name: 'Cloudflare Delete Zone',
|
||||
description: 'Deletes a zone (domain) from the Cloudflare account.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
zoneId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The zone ID to delete',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Cloudflare API Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.cloudflare.com/client/v4/zones/${params.zoneId}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.success) {
|
||||
return {
|
||||
success: false,
|
||||
output: { id: '' },
|
||||
error: data.errors?.[0]?.message ?? 'Failed to delete zone',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.result?.id ?? '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Deleted zone ID' },
|
||||
},
|
||||
}
|
||||
206
apps/sim/tools/cloudflare/dns_analytics.ts
Normal file
206
apps/sim/tools/cloudflare/dns_analytics.ts
Normal file
@@ -0,0 +1,206 @@
|
||||
import type {
|
||||
CloudflareDnsAnalyticsParams,
|
||||
CloudflareDnsAnalyticsResponse,
|
||||
} from '@/tools/cloudflare/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const dnsAnalyticsTool: ToolConfig<
|
||||
CloudflareDnsAnalyticsParams,
|
||||
CloudflareDnsAnalyticsResponse
|
||||
> = {
|
||||
id: 'cloudflare_dns_analytics',
|
||||
name: 'Cloudflare DNS Analytics',
|
||||
description: 'Gets DNS analytics report for a zone including query counts and trends.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
zoneId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The zone ID to get DNS analytics for',
|
||||
},
|
||||
since: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Start date for analytics (ISO 8601, e.g., "2024-01-01T00:00:00Z") or relative (e.g., "-6h")',
|
||||
},
|
||||
until: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'End date for analytics (ISO 8601, e.g., "2024-01-31T23:59:59Z") or relative (e.g., "now")',
|
||||
},
|
||||
metrics: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated metrics to retrieve (e.g., "queryCount,uncachedCount,staleCount,responseTimeAvg,responseTimeMedian,responseTime90th,responseTime99th")',
|
||||
},
|
||||
dimensions: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated dimensions to group by (e.g., "queryName,queryType,responseCode,responseCached,coloName,origin,dayOfWeek,tcp,ipVersion")',
|
||||
},
|
||||
filters: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filters to apply to the data (e.g., "queryType==A")',
|
||||
},
|
||||
sort: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Sort order for the result set. Fields must be included in metrics or dimensions (e.g., "+queryCount" or "-responseTimeAvg")',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of results to return',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Cloudflare API Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(
|
||||
`https://api.cloudflare.com/client/v4/zones/${params.zoneId}/dns_analytics/report`
|
||||
)
|
||||
if (params.since) url.searchParams.append('since', params.since)
|
||||
if (params.until) url.searchParams.append('until', params.until)
|
||||
if (params.metrics) url.searchParams.append('metrics', params.metrics)
|
||||
if (params.dimensions) url.searchParams.append('dimensions', params.dimensions)
|
||||
if (params.filters) url.searchParams.append('filters', params.filters)
|
||||
if (params.sort) url.searchParams.append('sort', params.sort)
|
||||
if (params.limit) url.searchParams.append('limit', String(params.limit))
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.success) {
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
totals: {
|
||||
queryCount: 0,
|
||||
uncachedCount: 0,
|
||||
staleCount: 0,
|
||||
responseTimeAvg: 0,
|
||||
responseTimeMedian: 0,
|
||||
responseTime90th: 0,
|
||||
responseTime99th: 0,
|
||||
},
|
||||
data: [],
|
||||
data_lag: 0,
|
||||
rows: 0,
|
||||
},
|
||||
error: data.errors?.[0]?.message ?? 'Failed to get DNS analytics',
|
||||
}
|
||||
}
|
||||
|
||||
const result = data.result
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
totals: {
|
||||
queryCount: result?.totals?.queryCount ?? 0,
|
||||
uncachedCount: result?.totals?.uncachedCount ?? 0,
|
||||
staleCount: result?.totals?.staleCount ?? 0,
|
||||
responseTimeAvg: result?.totals?.responseTimeAvg ?? 0,
|
||||
responseTimeMedian: result?.totals?.responseTimeMedian ?? 0,
|
||||
responseTime90th: result?.totals?.responseTime90th ?? 0,
|
||||
responseTime99th: result?.totals?.responseTime99th ?? 0,
|
||||
},
|
||||
data:
|
||||
result?.data?.map((entry: any) => ({
|
||||
dimensions: entry.dimensions ?? [],
|
||||
metrics: entry.metrics ?? [],
|
||||
})) ?? [],
|
||||
data_lag: result?.data_lag ?? 0,
|
||||
rows: result?.rows ?? 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
totals: {
|
||||
type: 'object',
|
||||
description: 'Aggregate DNS analytics totals for the entire queried period',
|
||||
properties: {
|
||||
queryCount: { type: 'number', description: 'Total number of DNS queries' },
|
||||
uncachedCount: { type: 'number', description: 'Number of uncached DNS queries' },
|
||||
staleCount: { type: 'number', description: 'Number of stale DNS queries' },
|
||||
responseTimeAvg: {
|
||||
type: 'number',
|
||||
description: 'Average response time in milliseconds',
|
||||
optional: true,
|
||||
},
|
||||
responseTimeMedian: {
|
||||
type: 'number',
|
||||
description: 'Median response time in milliseconds',
|
||||
optional: true,
|
||||
},
|
||||
responseTime90th: {
|
||||
type: 'number',
|
||||
description: '90th percentile response time in milliseconds',
|
||||
optional: true,
|
||||
},
|
||||
responseTime99th: {
|
||||
type: 'number',
|
||||
description: '99th percentile response time in milliseconds',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
data: {
|
||||
type: 'array',
|
||||
description: 'Raw analytics data rows returned by the Cloudflare DNS analytics report',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
dimensions: {
|
||||
type: 'array',
|
||||
description:
|
||||
'Dimension values for this data row, parallel to the requested dimensions list',
|
||||
items: { type: 'string', description: 'Dimension value' },
|
||||
},
|
||||
metrics: {
|
||||
type: 'array',
|
||||
description: 'Metric values for this data row, parallel to the requested metrics list',
|
||||
items: { type: 'number', description: 'Metric value' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
data_lag: {
|
||||
type: 'number',
|
||||
description: 'Processing lag in seconds before analytics data becomes available',
|
||||
},
|
||||
rows: {
|
||||
type: 'number',
|
||||
description: 'Total number of rows in the result set',
|
||||
},
|
||||
},
|
||||
}
|
||||
108
apps/sim/tools/cloudflare/get_zone.ts
Normal file
108
apps/sim/tools/cloudflare/get_zone.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import type { CloudflareGetZoneParams, CloudflareGetZoneResponse } from '@/tools/cloudflare/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const getZoneTool: ToolConfig<CloudflareGetZoneParams, CloudflareGetZoneResponse> = {
|
||||
id: 'cloudflare_get_zone',
|
||||
name: 'Cloudflare Get Zone',
|
||||
description: 'Gets details for a specific zone (domain) by its ID.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
zoneId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The zone ID to retrieve details for',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Cloudflare API Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.cloudflare.com/client/v4/zones/${params.zoneId}`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.success) {
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
id: '',
|
||||
name: '',
|
||||
status: '',
|
||||
paused: false,
|
||||
type: '',
|
||||
name_servers: [],
|
||||
original_name_servers: [],
|
||||
created_on: '',
|
||||
modified_on: '',
|
||||
plan: { id: '', name: '' },
|
||||
},
|
||||
error: data.errors?.[0]?.message ?? 'Failed to get zone',
|
||||
}
|
||||
}
|
||||
|
||||
const zone = data.result
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: zone?.id ?? '',
|
||||
name: zone?.name ?? '',
|
||||
status: zone?.status ?? '',
|
||||
paused: zone?.paused ?? false,
|
||||
type: zone?.type ?? '',
|
||||
name_servers: zone?.name_servers ?? [],
|
||||
original_name_servers: zone?.original_name_servers ?? [],
|
||||
created_on: zone?.created_on ?? '',
|
||||
modified_on: zone?.modified_on ?? '',
|
||||
plan: {
|
||||
id: zone?.plan?.id ?? '',
|
||||
name: zone?.plan?.name ?? '',
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Zone ID' },
|
||||
name: { type: 'string', description: 'Domain name' },
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Zone status (active, pending, initializing, moved, deleted, deactivated)',
|
||||
},
|
||||
paused: { type: 'boolean', description: 'Whether the zone is paused' },
|
||||
type: { type: 'string', description: 'Zone type (full or partial)' },
|
||||
name_servers: {
|
||||
type: 'array',
|
||||
description: 'Assigned Cloudflare name servers',
|
||||
items: { type: 'string', description: 'Name server hostname' },
|
||||
},
|
||||
original_name_servers: {
|
||||
type: 'array',
|
||||
description: 'Original name servers before moving to Cloudflare',
|
||||
items: { type: 'string', description: 'Name server hostname' },
|
||||
optional: true,
|
||||
},
|
||||
created_on: { type: 'string', description: 'ISO 8601 date when the zone was created' },
|
||||
modified_on: { type: 'string', description: 'ISO 8601 date when the zone was last modified' },
|
||||
plan: {
|
||||
type: 'object',
|
||||
description: 'Zone plan information',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Plan identifier' },
|
||||
name: { type: 'string', description: 'Plan name' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
104
apps/sim/tools/cloudflare/get_zone_settings.ts
Normal file
104
apps/sim/tools/cloudflare/get_zone_settings.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import type {
|
||||
CloudflareGetZoneSettingsParams,
|
||||
CloudflareGetZoneSettingsResponse,
|
||||
} from '@/tools/cloudflare/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const getZoneSettingsTool: ToolConfig<
|
||||
CloudflareGetZoneSettingsParams,
|
||||
CloudflareGetZoneSettingsResponse
|
||||
> = {
|
||||
id: 'cloudflare_get_zone_settings',
|
||||
name: 'Cloudflare Get Zone Settings',
|
||||
description:
|
||||
'Gets all settings for a zone including SSL mode, minification, caching level, and security settings.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
zoneId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The zone ID to get settings for',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Cloudflare API Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.cloudflare.com/client/v4/zones/${params.zoneId}/settings`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.success) {
|
||||
return {
|
||||
success: false,
|
||||
output: { settings: [] },
|
||||
error: data.errors?.[0]?.message ?? 'Failed to get zone settings',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
settings:
|
||||
data.result?.map((setting: Record<string, unknown>) => ({
|
||||
id: (setting.id as string) ?? '',
|
||||
value: setting.value ?? null,
|
||||
editable: (setting.editable as boolean) ?? false,
|
||||
modified_on: (setting.modified_on as string) ?? '',
|
||||
...(setting.time_remaining != null
|
||||
? { time_remaining: setting.time_remaining as number }
|
||||
: {}),
|
||||
})) ?? [],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
settings: {
|
||||
type: 'array',
|
||||
description: 'List of zone settings',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Setting identifier (e.g., ssl, minify, cache_level, security_level, always_use_https)',
|
||||
},
|
||||
value: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Setting value - may be a string, number, boolean, or object depending on the setting',
|
||||
},
|
||||
editable: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the setting can be modified for the current zone plan',
|
||||
},
|
||||
modified_on: {
|
||||
type: 'string',
|
||||
description: 'ISO 8601 timestamp when the setting was last modified',
|
||||
},
|
||||
time_remaining: {
|
||||
type: 'number',
|
||||
description:
|
||||
'Seconds remaining until the setting can be modified again (only present for rate-limited settings)',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
27
apps/sim/tools/cloudflare/index.ts
Normal file
27
apps/sim/tools/cloudflare/index.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { createDnsRecordTool } from '@/tools/cloudflare/create_dns_record'
|
||||
import { createZoneTool } from '@/tools/cloudflare/create_zone'
|
||||
import { deleteDnsRecordTool } from '@/tools/cloudflare/delete_dns_record'
|
||||
import { deleteZoneTool } from '@/tools/cloudflare/delete_zone'
|
||||
import { dnsAnalyticsTool } from '@/tools/cloudflare/dns_analytics'
|
||||
import { getZoneTool } from '@/tools/cloudflare/get_zone'
|
||||
import { getZoneSettingsTool } from '@/tools/cloudflare/get_zone_settings'
|
||||
import { listCertificatesTool } from '@/tools/cloudflare/list_certificates'
|
||||
import { listDnsRecordsTool } from '@/tools/cloudflare/list_dns_records'
|
||||
import { listZonesTool } from '@/tools/cloudflare/list_zones'
|
||||
import { purgeCacheTool } from '@/tools/cloudflare/purge_cache'
|
||||
import { updateDnsRecordTool } from '@/tools/cloudflare/update_dns_record'
|
||||
import { updateZoneSettingTool } from '@/tools/cloudflare/update_zone_setting'
|
||||
|
||||
export const cloudflareCreateDnsRecordTool = createDnsRecordTool
|
||||
export const cloudflareCreateZoneTool = createZoneTool
|
||||
export const cloudflareDeleteDnsRecordTool = deleteDnsRecordTool
|
||||
export const cloudflareDeleteZoneTool = deleteZoneTool
|
||||
export const cloudflareDnsAnalyticsTool = dnsAnalyticsTool
|
||||
export const cloudflareGetZoneTool = getZoneTool
|
||||
export const cloudflareGetZoneSettingsTool = getZoneSettingsTool
|
||||
export const cloudflareListCertificatesTool = listCertificatesTool
|
||||
export const cloudflareListDnsRecordsTool = listDnsRecordsTool
|
||||
export const cloudflareListZonesTool = listZonesTool
|
||||
export const cloudflarePurgeCacheTool = purgeCacheTool
|
||||
export const cloudflareUpdateDnsRecordTool = updateDnsRecordTool
|
||||
export const cloudflareUpdateZoneSettingTool = updateZoneSettingTool
|
||||
199
apps/sim/tools/cloudflare/list_certificates.ts
Normal file
199
apps/sim/tools/cloudflare/list_certificates.ts
Normal file
@@ -0,0 +1,199 @@
|
||||
import type {
|
||||
CloudflareListCertificatesParams,
|
||||
CloudflareListCertificatesResponse,
|
||||
} from '@/tools/cloudflare/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listCertificatesTool: ToolConfig<
|
||||
CloudflareListCertificatesParams,
|
||||
CloudflareListCertificatesResponse
|
||||
> = {
|
||||
id: 'cloudflare_list_certificates',
|
||||
name: 'Cloudflare List Certificates',
|
||||
description: 'Lists SSL/TLS certificate packs for a zone.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
zoneId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The zone ID to list certificates for',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter certificate packs by status (e.g., "all", "active", "pending")',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Cloudflare API Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(
|
||||
`https://api.cloudflare.com/client/v4/zones/${params.zoneId}/ssl/certificate_packs`
|
||||
)
|
||||
if (params.status) url.searchParams.append('status', params.status)
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.success) {
|
||||
return {
|
||||
success: false,
|
||||
output: { certificates: [], total_count: 0 },
|
||||
error: data.errors?.[0]?.message ?? 'Failed to list certificates',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
certificates:
|
||||
data.result?.map((cert: any) => ({
|
||||
id: cert.id ?? '',
|
||||
type: cert.type ?? '',
|
||||
hosts: cert.hosts ?? [],
|
||||
primary_certificate: cert.primary_certificate ?? '',
|
||||
status: cert.status ?? '',
|
||||
certificates:
|
||||
cert.certificates?.map((c: any) => ({
|
||||
id: c.id ?? '',
|
||||
hosts: c.hosts ?? [],
|
||||
issuer: c.issuer ?? '',
|
||||
signature: c.signature ?? '',
|
||||
status: c.status ?? '',
|
||||
bundle_method: c.bundle_method ?? '',
|
||||
zone_id: c.zone_id ?? '',
|
||||
uploaded_on: c.uploaded_on ?? '',
|
||||
modified_on: c.modified_on ?? '',
|
||||
expires_on: c.expires_on ?? '',
|
||||
priority: c.priority ?? 0,
|
||||
geo_restrictions: c.geo_restrictions ?? undefined,
|
||||
})) ?? [],
|
||||
cloudflare_branding: cert.cloudflare_branding ?? false,
|
||||
validation_method: cert.validation_method ?? '',
|
||||
validity_days: cert.validity_days ?? 0,
|
||||
certificate_authority: cert.certificate_authority ?? '',
|
||||
created_on: cert.created_on ?? '',
|
||||
})) ?? [],
|
||||
total_count: data.result_info?.total_count ?? data.result?.length ?? 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
certificates: {
|
||||
type: 'array',
|
||||
description: 'List of SSL/TLS certificate packs',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Certificate pack ID' },
|
||||
type: { type: 'string', description: 'Certificate type (e.g., "universal", "advanced")' },
|
||||
hosts: {
|
||||
type: 'array',
|
||||
description: 'Hostnames covered by this certificate pack',
|
||||
items: {
|
||||
type: 'string',
|
||||
description: 'Hostname',
|
||||
},
|
||||
},
|
||||
primary_certificate: {
|
||||
type: 'string',
|
||||
description: 'ID of the primary certificate in the pack',
|
||||
optional: true,
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Certificate pack status (e.g., "active", "pending")',
|
||||
},
|
||||
certificates: {
|
||||
type: 'array',
|
||||
description: 'Individual certificates within the pack',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Certificate ID' },
|
||||
hosts: {
|
||||
type: 'array',
|
||||
description: 'Hostnames covered by this certificate',
|
||||
items: { type: 'string', description: 'Hostname' },
|
||||
},
|
||||
issuer: { type: 'string', description: 'Certificate issuer' },
|
||||
signature: {
|
||||
type: 'string',
|
||||
description: 'Signature algorithm (e.g., "ECDSAWithSHA256")',
|
||||
},
|
||||
status: { type: 'string', description: 'Certificate status' },
|
||||
bundle_method: {
|
||||
type: 'string',
|
||||
description: 'Bundle method (e.g., "ubiquitous")',
|
||||
},
|
||||
zone_id: { type: 'string', description: 'Zone ID the certificate belongs to' },
|
||||
uploaded_on: { type: 'string', description: 'Upload date (ISO 8601)' },
|
||||
modified_on: { type: 'string', description: 'Last modified date (ISO 8601)' },
|
||||
expires_on: { type: 'string', description: 'Expiration date (ISO 8601)' },
|
||||
priority: {
|
||||
type: 'number',
|
||||
description: 'Certificate priority order',
|
||||
optional: true,
|
||||
},
|
||||
geo_restrictions: {
|
||||
type: 'object',
|
||||
description: 'Geographic restrictions for the certificate',
|
||||
optional: true,
|
||||
properties: {
|
||||
label: {
|
||||
type: 'string',
|
||||
description: 'Geographic restriction label',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
cloudflare_branding: {
|
||||
type: 'boolean',
|
||||
description: 'Whether Cloudflare branding is enabled on the certificate',
|
||||
optional: true,
|
||||
},
|
||||
validation_method: {
|
||||
type: 'string',
|
||||
description: 'Validation method (e.g., "txt", "http", "cname")',
|
||||
optional: true,
|
||||
},
|
||||
validity_days: {
|
||||
type: 'number',
|
||||
description: 'Validity period in days',
|
||||
optional: true,
|
||||
},
|
||||
certificate_authority: {
|
||||
type: 'string',
|
||||
description: 'Certificate authority (e.g., "lets_encrypt", "google")',
|
||||
optional: true,
|
||||
},
|
||||
created_on: { type: 'string', description: 'Creation date (ISO 8601)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
total_count: {
|
||||
type: 'number',
|
||||
description: 'Total number of certificate packs',
|
||||
},
|
||||
},
|
||||
}
|
||||
159
apps/sim/tools/cloudflare/list_dns_records.ts
Normal file
159
apps/sim/tools/cloudflare/list_dns_records.ts
Normal file
@@ -0,0 +1,159 @@
|
||||
import type {
|
||||
CloudflareListDnsRecordsParams,
|
||||
CloudflareListDnsRecordsResponse,
|
||||
} from '@/tools/cloudflare/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listDnsRecordsTool: ToolConfig<
|
||||
CloudflareListDnsRecordsParams,
|
||||
CloudflareListDnsRecordsResponse
|
||||
> = {
|
||||
id: 'cloudflare_list_dns_records',
|
||||
name: 'Cloudflare List DNS Records',
|
||||
description: 'Lists DNS records for a specific zone.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
zoneId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The zone ID to list DNS records for',
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by record type (e.g., "A", "AAAA", "CNAME", "MX", "TXT")',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by record name (substring match)',
|
||||
},
|
||||
content: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by record content (substring match)',
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number for pagination (default: 1)',
|
||||
},
|
||||
per_page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of records per page (default: 100, max: 5000000)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Cloudflare API Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`https://api.cloudflare.com/client/v4/zones/${params.zoneId}/dns_records`)
|
||||
if (params.type) url.searchParams.append('type', params.type)
|
||||
if (params.name) url.searchParams.append('name', params.name)
|
||||
if (params.content) url.searchParams.append('content', params.content)
|
||||
if (params.page) url.searchParams.append('page', String(params.page))
|
||||
if (params.per_page) url.searchParams.append('per_page', String(params.per_page))
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.success) {
|
||||
return {
|
||||
success: false,
|
||||
output: { records: [], total_count: 0 },
|
||||
error: data.errors?.[0]?.message ?? 'Failed to list DNS records',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
records:
|
||||
data.result?.map((record: any) => ({
|
||||
id: record.id ?? '',
|
||||
zone_id: record.zone_id ?? '',
|
||||
zone_name: record.zone_name ?? '',
|
||||
type: record.type ?? '',
|
||||
name: record.name ?? '',
|
||||
content: record.content ?? '',
|
||||
proxiable: record.proxiable ?? false,
|
||||
proxied: record.proxied ?? false,
|
||||
ttl: record.ttl ?? 0,
|
||||
locked: record.locked ?? false,
|
||||
priority: record.priority ?? null,
|
||||
comment: record.comment ?? null,
|
||||
tags: record.tags ?? [],
|
||||
created_on: record.created_on ?? '',
|
||||
modified_on: record.modified_on ?? '',
|
||||
})) ?? [],
|
||||
total_count: data.result_info?.total_count ?? data.result?.length ?? 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
records: {
|
||||
type: 'array',
|
||||
description: 'List of DNS records',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique identifier for the DNS record' },
|
||||
zone_id: { type: 'string', description: 'The ID of the zone the record belongs to' },
|
||||
zone_name: { type: 'string', description: 'The name of the zone' },
|
||||
type: { type: 'string', description: 'Record type (A, AAAA, CNAME, MX, TXT, etc.)' },
|
||||
name: { type: 'string', description: 'Record name (e.g., example.com)' },
|
||||
content: { type: 'string', description: 'Record content (e.g., IP address)' },
|
||||
proxiable: { type: 'boolean', description: 'Whether the record can be proxied' },
|
||||
proxied: { type: 'boolean', description: 'Whether Cloudflare proxy is enabled' },
|
||||
ttl: { type: 'number', description: 'TTL in seconds (1 = automatic)' },
|
||||
locked: { type: 'boolean', description: 'Whether the record is locked' },
|
||||
priority: { type: 'number', description: 'MX/SRV record priority', optional: true },
|
||||
comment: {
|
||||
type: 'string',
|
||||
description: 'Comment associated with the record',
|
||||
optional: true,
|
||||
},
|
||||
tags: {
|
||||
type: 'array',
|
||||
description: 'Tags associated with the record',
|
||||
items: { type: 'string', description: 'Tag value' },
|
||||
},
|
||||
created_on: {
|
||||
type: 'string',
|
||||
description: 'ISO 8601 timestamp when the record was created',
|
||||
},
|
||||
modified_on: {
|
||||
type: 'string',
|
||||
description: 'ISO 8601 timestamp when the record was last modified',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
total_count: {
|
||||
type: 'number',
|
||||
description: 'Total number of DNS records matching the query',
|
||||
},
|
||||
},
|
||||
}
|
||||
144
apps/sim/tools/cloudflare/list_zones.ts
Normal file
144
apps/sim/tools/cloudflare/list_zones.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
import type {
|
||||
CloudflareListZonesParams,
|
||||
CloudflareListZonesResponse,
|
||||
} from '@/tools/cloudflare/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listZonesTool: ToolConfig<CloudflareListZonesParams, CloudflareListZonesResponse> = {
|
||||
id: 'cloudflare_list_zones',
|
||||
name: 'Cloudflare List Zones',
|
||||
description: 'Lists all zones (domains) in the Cloudflare account.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter zones by domain name (e.g., "example.com")',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by zone status (e.g., "active", "pending", "initializing")',
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number for pagination (default: 1)',
|
||||
},
|
||||
per_page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of zones per page (default: 20, max: 50)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Cloudflare API Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://api.cloudflare.com/client/v4/zones')
|
||||
if (params.name) url.searchParams.append('name', params.name)
|
||||
if (params.status) url.searchParams.append('status', params.status)
|
||||
if (params.page) url.searchParams.append('page', String(params.page))
|
||||
if (params.per_page) url.searchParams.append('per_page', String(params.per_page))
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.success) {
|
||||
return {
|
||||
success: false,
|
||||
output: { zones: [], total_count: 0 },
|
||||
error: data.errors?.[0]?.message ?? 'Failed to list zones',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
zones:
|
||||
data.result?.map((zone: any) => ({
|
||||
id: zone.id ?? '',
|
||||
name: zone.name ?? '',
|
||||
status: zone.status ?? '',
|
||||
paused: zone.paused ?? false,
|
||||
type: zone.type ?? '',
|
||||
name_servers: zone.name_servers ?? [],
|
||||
original_name_servers: zone.original_name_servers ?? [],
|
||||
created_on: zone.created_on ?? '',
|
||||
modified_on: zone.modified_on ?? '',
|
||||
plan: {
|
||||
id: zone.plan?.id ?? '',
|
||||
name: zone.plan?.name ?? '',
|
||||
},
|
||||
})) ?? [],
|
||||
total_count: data.result_info?.total_count ?? data.result?.length ?? 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
zones: {
|
||||
type: 'array',
|
||||
description: 'List of zones/domains',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Zone ID' },
|
||||
name: { type: 'string', description: 'Domain name' },
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Zone status (active, pending, initializing, moved, deleted, deactivated)',
|
||||
},
|
||||
paused: { type: 'boolean', description: 'Whether the zone is paused' },
|
||||
type: { type: 'string', description: 'Zone type (full or partial)' },
|
||||
name_servers: {
|
||||
type: 'array',
|
||||
description: 'Assigned Cloudflare name servers',
|
||||
items: { type: 'string', description: 'Name server hostname' },
|
||||
},
|
||||
original_name_servers: {
|
||||
type: 'array',
|
||||
description: 'Original name servers before moving to Cloudflare',
|
||||
items: { type: 'string', description: 'Name server hostname' },
|
||||
optional: true,
|
||||
},
|
||||
created_on: { type: 'string', description: 'ISO 8601 date when the zone was created' },
|
||||
modified_on: {
|
||||
type: 'string',
|
||||
description: 'ISO 8601 date when the zone was last modified',
|
||||
},
|
||||
plan: {
|
||||
type: 'object',
|
||||
description: 'Zone plan information',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Plan identifier' },
|
||||
name: { type: 'string', description: 'Plan name' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
total_count: {
|
||||
type: 'number',
|
||||
description: 'Total number of zones matching the query',
|
||||
},
|
||||
},
|
||||
}
|
||||
126
apps/sim/tools/cloudflare/purge_cache.ts
Normal file
126
apps/sim/tools/cloudflare/purge_cache.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
import type {
|
||||
CloudflarePurgeCacheParams,
|
||||
CloudflarePurgeCacheResponse,
|
||||
} from '@/tools/cloudflare/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const purgeCacheTool: ToolConfig<CloudflarePurgeCacheParams, CloudflarePurgeCacheResponse> =
|
||||
{
|
||||
id: 'cloudflare_purge_cache',
|
||||
name: 'Cloudflare Purge Cache',
|
||||
description:
|
||||
'Purges cached content for a zone. Can purge everything or specific files/tags/hosts/prefixes.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
zoneId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The zone ID to purge cache for',
|
||||
},
|
||||
purge_everything: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Set to true to purge all cached content. Mutually exclusive with files, tags, hosts, and prefixes',
|
||||
},
|
||||
files: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of URLs to purge from cache',
|
||||
},
|
||||
tags: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of cache tags to purge (Enterprise only)',
|
||||
},
|
||||
hosts: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of hostnames to purge (Enterprise only)',
|
||||
},
|
||||
prefixes: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of URL prefixes to purge (Enterprise only)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Cloudflare API Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.cloudflare.com/client/v4/zones/${params.zoneId}/purge_cache`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
if (params.purge_everything) {
|
||||
return { purge_everything: true }
|
||||
}
|
||||
if (params.files) {
|
||||
const fileList = String(params.files)
|
||||
.split(',')
|
||||
.map((f) => f.trim())
|
||||
.filter(Boolean)
|
||||
return { files: fileList }
|
||||
}
|
||||
if (params.tags) {
|
||||
const tagList = String(params.tags)
|
||||
.split(',')
|
||||
.map((t) => t.trim())
|
||||
.filter(Boolean)
|
||||
return { tags: tagList }
|
||||
}
|
||||
if (params.hosts) {
|
||||
const hostList = String(params.hosts)
|
||||
.split(',')
|
||||
.map((h) => h.trim())
|
||||
.filter(Boolean)
|
||||
return { hosts: hostList }
|
||||
}
|
||||
if (params.prefixes) {
|
||||
const prefixList = String(params.prefixes)
|
||||
.split(',')
|
||||
.map((p) => p.trim())
|
||||
.filter(Boolean)
|
||||
return { prefixes: prefixList }
|
||||
}
|
||||
return { purge_everything: true }
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.success) {
|
||||
return {
|
||||
success: false,
|
||||
output: { id: '' },
|
||||
error: data.errors?.[0]?.message ?? 'Failed to purge cache',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.result?.id ?? '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Purge request identifier returned by Cloudflare' },
|
||||
},
|
||||
}
|
||||
339
apps/sim/tools/cloudflare/types.ts
Normal file
339
apps/sim/tools/cloudflare/types.ts
Normal file
@@ -0,0 +1,339 @@
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface CloudflareBaseParams {
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
export interface CloudflareListZonesParams extends CloudflareBaseParams {
|
||||
name?: string
|
||||
status?: string
|
||||
page?: number
|
||||
per_page?: number
|
||||
}
|
||||
|
||||
export interface CloudflareZone {
|
||||
id: string
|
||||
name: string
|
||||
status: string
|
||||
paused: boolean
|
||||
type: string
|
||||
name_servers: string[]
|
||||
original_name_servers: string[]
|
||||
created_on: string
|
||||
modified_on: string
|
||||
plan: {
|
||||
id: string
|
||||
name: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface CloudflareListZonesResponse extends ToolResponse {
|
||||
output: {
|
||||
zones: CloudflareZone[]
|
||||
total_count: number
|
||||
}
|
||||
}
|
||||
|
||||
export interface CloudflareGetZoneParams extends CloudflareBaseParams {
|
||||
zoneId: string
|
||||
}
|
||||
|
||||
export interface CloudflareGetZoneResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
name: string
|
||||
status: string
|
||||
paused: boolean
|
||||
type: string
|
||||
name_servers: string[]
|
||||
original_name_servers: string[]
|
||||
created_on: string
|
||||
modified_on: string
|
||||
plan: {
|
||||
id: string
|
||||
name: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface CloudflareCreateZoneParams extends CloudflareBaseParams {
|
||||
name: string
|
||||
accountId: string
|
||||
type?: string
|
||||
}
|
||||
|
||||
export interface CloudflareCreateZoneResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
name: string
|
||||
status: string
|
||||
paused: boolean
|
||||
type: string
|
||||
name_servers: string[]
|
||||
original_name_servers: string[]
|
||||
created_on: string
|
||||
modified_on: string
|
||||
plan: {
|
||||
id: string
|
||||
name: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface CloudflareDeleteZoneParams extends CloudflareBaseParams {
|
||||
zoneId: string
|
||||
}
|
||||
|
||||
export interface CloudflareDeleteZoneResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface CloudflareListDnsRecordsParams extends CloudflareBaseParams {
|
||||
zoneId: string
|
||||
type?: string
|
||||
name?: string
|
||||
content?: string
|
||||
page?: number
|
||||
per_page?: number
|
||||
}
|
||||
|
||||
export interface CloudflareDnsRecord {
|
||||
id: string
|
||||
zone_id: string
|
||||
zone_name: string
|
||||
type: string
|
||||
name: string
|
||||
content: string
|
||||
proxiable: boolean
|
||||
proxied: boolean
|
||||
ttl: number
|
||||
locked: boolean
|
||||
priority?: number
|
||||
comment?: string | null
|
||||
tags: string[]
|
||||
created_on: string
|
||||
modified_on: string
|
||||
}
|
||||
|
||||
export interface CloudflareListDnsRecordsResponse extends ToolResponse {
|
||||
output: {
|
||||
records: CloudflareDnsRecord[]
|
||||
total_count: number
|
||||
}
|
||||
}
|
||||
|
||||
export interface CloudflareCreateDnsRecordParams extends CloudflareBaseParams {
|
||||
zoneId: string
|
||||
type: string
|
||||
name: string
|
||||
content: string
|
||||
ttl?: number
|
||||
proxied?: boolean
|
||||
priority?: number
|
||||
comment?: string
|
||||
}
|
||||
|
||||
export interface CloudflareCreateDnsRecordResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
zone_id: string
|
||||
zone_name: string
|
||||
type: string
|
||||
name: string
|
||||
content: string
|
||||
proxiable: boolean
|
||||
proxied: boolean
|
||||
ttl: number
|
||||
locked: boolean
|
||||
priority?: number
|
||||
comment?: string | null
|
||||
tags: string[]
|
||||
created_on: string
|
||||
modified_on: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface CloudflareUpdateDnsRecordParams extends CloudflareBaseParams {
|
||||
zoneId: string
|
||||
recordId: string
|
||||
type?: string
|
||||
name?: string
|
||||
content?: string
|
||||
ttl?: number
|
||||
proxied?: boolean
|
||||
priority?: number
|
||||
comment?: string
|
||||
}
|
||||
|
||||
export interface CloudflareUpdateDnsRecordResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
zone_id: string
|
||||
zone_name: string
|
||||
type: string
|
||||
name: string
|
||||
content: string
|
||||
proxiable: boolean
|
||||
proxied: boolean
|
||||
ttl: number
|
||||
locked: boolean
|
||||
priority?: number
|
||||
comment?: string | null
|
||||
tags: string[]
|
||||
created_on: string
|
||||
modified_on: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface CloudflareDeleteDnsRecordParams extends CloudflareBaseParams {
|
||||
zoneId: string
|
||||
recordId: string
|
||||
}
|
||||
|
||||
export interface CloudflareDeleteDnsRecordResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface CloudflareListCertificatesParams extends CloudflareBaseParams {
|
||||
zoneId: string
|
||||
status?: string
|
||||
}
|
||||
|
||||
export interface CloudflareCertificateGeoRestrictions {
|
||||
label: string
|
||||
}
|
||||
|
||||
export interface CloudflareCertificate {
|
||||
id: string
|
||||
hosts: string[]
|
||||
issuer: string
|
||||
signature: string
|
||||
status: string
|
||||
bundle_method: string
|
||||
zone_id: string
|
||||
uploaded_on: string
|
||||
modified_on: string
|
||||
expires_on: string
|
||||
priority?: number
|
||||
geo_restrictions?: CloudflareCertificateGeoRestrictions
|
||||
}
|
||||
|
||||
export interface CloudflareCertificatePack {
|
||||
id: string
|
||||
type: string
|
||||
hosts: string[]
|
||||
primary_certificate: string
|
||||
status: string
|
||||
certificates: CloudflareCertificate[]
|
||||
cloudflare_branding?: boolean
|
||||
validation_method?: string
|
||||
validity_days?: number
|
||||
certificate_authority?: string
|
||||
created_on: string
|
||||
}
|
||||
|
||||
export interface CloudflareListCertificatesResponse extends ToolResponse {
|
||||
output: {
|
||||
certificates: CloudflareCertificatePack[]
|
||||
total_count: number
|
||||
}
|
||||
}
|
||||
|
||||
export interface CloudflarePurgeCacheParams extends CloudflareBaseParams {
|
||||
zoneId: string
|
||||
purge_everything?: boolean
|
||||
files?: string[]
|
||||
tags?: string[]
|
||||
hosts?: string[]
|
||||
prefixes?: string[]
|
||||
}
|
||||
|
||||
export interface CloudflarePurgeCacheResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface CloudflareDnsAnalyticsParams extends CloudflareBaseParams {
|
||||
zoneId: string
|
||||
since?: string
|
||||
until?: string
|
||||
metrics?: string
|
||||
dimensions?: string
|
||||
filters?: string
|
||||
sort?: string
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface CloudflareDnsAnalyticsResponse extends ToolResponse {
|
||||
output: {
|
||||
totals: {
|
||||
queryCount: number
|
||||
uncachedCount: number
|
||||
staleCount: number
|
||||
responseTimeAvg?: number
|
||||
responseTimeMedian?: number
|
||||
responseTime90th?: number
|
||||
responseTime99th?: number
|
||||
}
|
||||
data: Array<{
|
||||
dimensions: string[]
|
||||
metrics: number[]
|
||||
}>
|
||||
data_lag: number
|
||||
rows: number
|
||||
}
|
||||
}
|
||||
|
||||
export interface CloudflareGetZoneSettingsParams extends CloudflareBaseParams {
|
||||
zoneId: string
|
||||
}
|
||||
|
||||
export interface CloudflareZoneSetting {
|
||||
id: string
|
||||
value: unknown
|
||||
editable: boolean
|
||||
modified_on: string
|
||||
time_remaining?: number
|
||||
}
|
||||
|
||||
export interface CloudflareGetZoneSettingsResponse extends ToolResponse {
|
||||
output: {
|
||||
settings: CloudflareZoneSetting[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface CloudflareUpdateZoneSettingParams extends CloudflareBaseParams {
|
||||
zoneId: string
|
||||
settingId: string
|
||||
value: string
|
||||
}
|
||||
|
||||
export interface CloudflareUpdateZoneSettingResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
value: unknown
|
||||
editable: boolean
|
||||
modified_on: string
|
||||
time_remaining?: number
|
||||
}
|
||||
}
|
||||
|
||||
export type CloudflareResponse =
|
||||
| CloudflareListZonesResponse
|
||||
| CloudflareGetZoneResponse
|
||||
| CloudflareCreateZoneResponse
|
||||
| CloudflareDeleteZoneResponse
|
||||
| CloudflareListDnsRecordsResponse
|
||||
| CloudflareCreateDnsRecordResponse
|
||||
| CloudflareUpdateDnsRecordResponse
|
||||
| CloudflareDeleteDnsRecordResponse
|
||||
| CloudflareListCertificatesResponse
|
||||
| CloudflarePurgeCacheResponse
|
||||
| CloudflareDnsAnalyticsResponse
|
||||
| CloudflareGetZoneSettingsResponse
|
||||
| CloudflareUpdateZoneSettingResponse
|
||||
180
apps/sim/tools/cloudflare/update_dns_record.ts
Normal file
180
apps/sim/tools/cloudflare/update_dns_record.ts
Normal file
@@ -0,0 +1,180 @@
|
||||
import type {
|
||||
CloudflareUpdateDnsRecordParams,
|
||||
CloudflareUpdateDnsRecordResponse,
|
||||
} from '@/tools/cloudflare/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const updateDnsRecordTool: ToolConfig<
|
||||
CloudflareUpdateDnsRecordParams,
|
||||
CloudflareUpdateDnsRecordResponse
|
||||
> = {
|
||||
id: 'cloudflare_update_dns_record',
|
||||
name: 'Cloudflare Update DNS Record',
|
||||
description: 'Updates an existing DNS record for a zone.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
zoneId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The zone ID containing the DNS record',
|
||||
},
|
||||
recordId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The DNS record ID to update',
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'DNS record type (e.g., "A", "AAAA", "CNAME", "MX", "TXT")',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'DNS record name',
|
||||
},
|
||||
content: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'DNS record content (e.g., IP address)',
|
||||
},
|
||||
ttl: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Time to live in seconds (1 = automatic)',
|
||||
},
|
||||
proxied: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to enable Cloudflare proxy',
|
||||
},
|
||||
priority: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Priority for MX and SRV records',
|
||||
},
|
||||
comment: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comment for the DNS record',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Cloudflare API Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.cloudflare.com/client/v4/zones/${params.zoneId}/dns_records/${params.recordId}`,
|
||||
method: 'PATCH',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, any> = {}
|
||||
if (params.type !== undefined) body.type = params.type
|
||||
if (params.name !== undefined) body.name = params.name
|
||||
if (params.content !== undefined) body.content = params.content
|
||||
if (params.ttl !== undefined) body.ttl = Number(params.ttl)
|
||||
if (params.proxied !== undefined) body.proxied = params.proxied
|
||||
if (params.priority !== undefined) body.priority = Number(params.priority)
|
||||
if (params.comment !== undefined) body.comment = params.comment
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.success) {
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
id: '',
|
||||
zone_id: '',
|
||||
zone_name: '',
|
||||
type: '',
|
||||
name: '',
|
||||
content: '',
|
||||
proxiable: false,
|
||||
proxied: false,
|
||||
ttl: 0,
|
||||
locked: false,
|
||||
priority: undefined,
|
||||
comment: null,
|
||||
tags: [],
|
||||
created_on: '',
|
||||
modified_on: '',
|
||||
},
|
||||
error: data.errors?.[0]?.message ?? 'Failed to update DNS record',
|
||||
}
|
||||
}
|
||||
|
||||
const record = data.result
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: record?.id ?? '',
|
||||
zone_id: record?.zone_id ?? '',
|
||||
zone_name: record?.zone_name ?? '',
|
||||
type: record?.type ?? '',
|
||||
name: record?.name ?? '',
|
||||
content: record?.content ?? '',
|
||||
proxiable: record?.proxiable ?? false,
|
||||
proxied: record?.proxied ?? false,
|
||||
ttl: record?.ttl ?? 0,
|
||||
locked: record?.locked ?? false,
|
||||
priority: record?.priority ?? null,
|
||||
comment: record?.comment ?? null,
|
||||
tags: record?.tags ?? [],
|
||||
created_on: record?.created_on ?? '',
|
||||
modified_on: record?.modified_on ?? '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Unique identifier for the updated DNS record' },
|
||||
zone_id: { type: 'string', description: 'The ID of the zone the record belongs to' },
|
||||
zone_name: { type: 'string', description: 'The name of the zone' },
|
||||
type: { type: 'string', description: 'DNS record type (A, AAAA, CNAME, MX, TXT, etc.)' },
|
||||
name: { type: 'string', description: 'DNS record hostname' },
|
||||
content: {
|
||||
type: 'string',
|
||||
description: 'DNS record value (e.g., IP address, target hostname)',
|
||||
},
|
||||
proxiable: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the record can be proxied through Cloudflare',
|
||||
},
|
||||
proxied: { type: 'boolean', description: 'Whether Cloudflare proxy is enabled' },
|
||||
ttl: { type: 'number', description: 'Time to live in seconds (1 = automatic)' },
|
||||
locked: { type: 'boolean', description: 'Whether the record is locked' },
|
||||
priority: { type: 'number', description: 'Priority for MX and SRV records', optional: true },
|
||||
comment: { type: 'string', description: 'Comment associated with the record', optional: true },
|
||||
tags: {
|
||||
type: 'array',
|
||||
description: 'Tags associated with the record',
|
||||
items: { type: 'string', description: 'Tag value' },
|
||||
},
|
||||
created_on: { type: 'string', description: 'ISO 8601 timestamp when the record was created' },
|
||||
modified_on: {
|
||||
type: 'string',
|
||||
description: 'ISO 8601 timestamp when the record was last modified',
|
||||
},
|
||||
},
|
||||
}
|
||||
114
apps/sim/tools/cloudflare/update_zone_setting.ts
Normal file
114
apps/sim/tools/cloudflare/update_zone_setting.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import type {
|
||||
CloudflareUpdateZoneSettingParams,
|
||||
CloudflareUpdateZoneSettingResponse,
|
||||
} from '@/tools/cloudflare/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const updateZoneSettingTool: ToolConfig<
|
||||
CloudflareUpdateZoneSettingParams,
|
||||
CloudflareUpdateZoneSettingResponse
|
||||
> = {
|
||||
id: 'cloudflare_update_zone_setting',
|
||||
name: 'Cloudflare Update Zone Setting',
|
||||
description:
|
||||
'Updates a specific zone setting such as SSL mode, security level, cache level, minification, or other configuration.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
zoneId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The zone ID to update settings for',
|
||||
},
|
||||
settingId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Setting to update (e.g., "ssl", "security_level", "cache_level", "minify", "always_use_https", "browser_cache_ttl", "http3", "min_tls_version", "ciphers")',
|
||||
},
|
||||
value: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'New value for the setting as a string or JSON string for complex values (e.g., "full" for SSL, "medium" for security_level, "aggressive" for cache_level, \'{"css":"on","html":"on","js":"on"}\' for minify, \'["ECDHE-RSA-AES128-GCM-SHA256"]\' for ciphers)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Cloudflare API Token',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`https://api.cloudflare.com/client/v4/zones/${params.zoneId}/settings/${params.settingId}`,
|
||||
method: 'PATCH',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
try {
|
||||
return { value: JSON.parse(params.value) }
|
||||
} catch {
|
||||
return { value: params.value }
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.success) {
|
||||
return {
|
||||
success: false,
|
||||
output: { id: '', value: null, editable: false, modified_on: '' },
|
||||
error: data.errors?.[0]?.message ?? 'Failed to update zone setting',
|
||||
}
|
||||
}
|
||||
|
||||
const setting = data.result
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: setting?.id ?? '',
|
||||
value: setting?.value ?? null,
|
||||
editable: setting?.editable ?? false,
|
||||
modified_on: setting?.modified_on ?? '',
|
||||
...(setting?.time_remaining != null
|
||||
? { time_remaining: setting.time_remaining as number }
|
||||
: {}),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: 'Setting identifier (e.g., ssl, minify, cache_level)',
|
||||
},
|
||||
value: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Updated setting value - may be a string, number, boolean, or object depending on the setting',
|
||||
},
|
||||
editable: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the setting can be modified for the current zone plan',
|
||||
},
|
||||
modified_on: {
|
||||
type: 'string',
|
||||
description: 'ISO 8601 timestamp when the setting was last modified',
|
||||
},
|
||||
time_remaining: {
|
||||
type: 'number',
|
||||
description:
|
||||
'Seconds remaining until the setting can be modified again (only present for rate-limited settings)',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
137
apps/sim/tools/microsoft_dataverse/associate.ts
Normal file
137
apps/sim/tools/microsoft_dataverse/associate.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type {
|
||||
DataverseAssociateParams,
|
||||
DataverseAssociateResponse,
|
||||
} from '@/tools/microsoft_dataverse/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('DataverseAssociate')
|
||||
|
||||
export const dataverseAssociateTool: ToolConfig<
|
||||
DataverseAssociateParams,
|
||||
DataverseAssociateResponse
|
||||
> = {
|
||||
id: 'microsoft_dataverse_associate',
|
||||
name: 'Associate Microsoft Dataverse Records',
|
||||
description:
|
||||
'Associate two records in Microsoft Dataverse via a navigation property. Creates a relationship between a source record and a target record. Supports both collection-valued (POST) and single-valued (PUT) navigation properties.',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: { required: true, provider: 'microsoft-dataverse' },
|
||||
errorExtractor: 'nested-error-object',
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Microsoft Dataverse API',
|
||||
},
|
||||
environmentUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Dataverse environment URL (e.g., https://myorg.crm.dynamics.com)',
|
||||
},
|
||||
entitySetName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Source entity set name (e.g., accounts)',
|
||||
},
|
||||
recordId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Source record GUID',
|
||||
},
|
||||
navigationProperty: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Navigation property name (e.g., contact_customer_accounts for collection-valued, or parentcustomerid_account for single-valued)',
|
||||
},
|
||||
targetEntitySetName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Target entity set name (e.g., contacts)',
|
||||
},
|
||||
targetRecordId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Target record GUID to associate',
|
||||
},
|
||||
navigationType: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Type of navigation property: "collection" (default, uses POST) or "single" (uses PUT for lookup fields)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = params.environmentUrl.replace(/\/$/, '')
|
||||
return `${baseUrl}/api/data/v9.2/${params.entitySetName}(${params.recordId})/${params.navigationProperty}/$ref`
|
||||
},
|
||||
method: (params) => (params.navigationType === 'single' ? 'PUT' : 'POST'),
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
'OData-MaxVersion': '4.0',
|
||||
'OData-Version': '4.0',
|
||||
Accept: 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const baseUrl = params.environmentUrl.replace(/\/$/, '')
|
||||
return {
|
||||
'@odata.id': `${baseUrl}/api/data/v9.2/${params.targetEntitySetName}(${params.targetRecordId})`,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: DataverseAssociateParams) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
const errorMessage =
|
||||
errorData?.error?.message ??
|
||||
`Dataverse API error: ${response.status} ${response.statusText}`
|
||||
logger.error('Dataverse associate failed', { errorData, status: response.status })
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
success: true,
|
||||
entitySetName: params?.entitySetName ?? '',
|
||||
recordId: params?.recordId ?? '',
|
||||
navigationProperty: params?.navigationProperty ?? '',
|
||||
targetEntitySetName: params?.targetEntitySetName ?? '',
|
||||
targetRecordId: params?.targetRecordId ?? '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the association was created successfully' },
|
||||
entitySetName: {
|
||||
type: 'string',
|
||||
description: 'Source entity set name used in the association',
|
||||
},
|
||||
recordId: { type: 'string', description: 'Source record GUID that was associated' },
|
||||
navigationProperty: {
|
||||
type: 'string',
|
||||
description: 'Navigation property used for the association',
|
||||
},
|
||||
targetEntitySetName: {
|
||||
type: 'string',
|
||||
description: 'Target entity set name used in the association',
|
||||
},
|
||||
targetRecordId: { type: 'string', description: 'Target record GUID that was associated' },
|
||||
},
|
||||
}
|
||||
121
apps/sim/tools/microsoft_dataverse/create_record.ts
Normal file
121
apps/sim/tools/microsoft_dataverse/create_record.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type {
|
||||
DataverseCreateRecordParams,
|
||||
DataverseCreateRecordResponse,
|
||||
} from '@/tools/microsoft_dataverse/types'
|
||||
import { DATAVERSE_RECORD_OUTPUT } from '@/tools/microsoft_dataverse/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('DataverseCreateRecord')
|
||||
|
||||
export const dataverseCreateRecordTool: ToolConfig<
|
||||
DataverseCreateRecordParams,
|
||||
DataverseCreateRecordResponse
|
||||
> = {
|
||||
id: 'microsoft_dataverse_create_record',
|
||||
name: 'Create Microsoft Dataverse Record',
|
||||
description:
|
||||
'Create a new record in a Microsoft Dataverse table. Requires the entity set name (plural table name) and record data as a JSON object.',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: { required: true, provider: 'microsoft-dataverse' },
|
||||
errorExtractor: 'nested-error-object',
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Microsoft Dataverse API',
|
||||
},
|
||||
environmentUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Dataverse environment URL (e.g., https://myorg.crm.dynamics.com)',
|
||||
},
|
||||
entitySetName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Entity set name (plural table name, e.g., accounts, contacts)',
|
||||
},
|
||||
data: {
|
||||
type: 'object',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Record data as a JSON object with column names as keys',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = params.environmentUrl.replace(/\/$/, '')
|
||||
return `${baseUrl}/api/data/v9.2/${params.entitySetName}`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
'OData-MaxVersion': '4.0',
|
||||
'OData-Version': '4.0',
|
||||
Accept: 'application/json',
|
||||
Prefer: 'return=representation',
|
||||
}),
|
||||
body: (params) => {
|
||||
let data = params.data
|
||||
if (typeof data === 'string') {
|
||||
try {
|
||||
data = JSON.parse(data)
|
||||
} catch {
|
||||
throw new Error('Invalid JSON format for record data')
|
||||
}
|
||||
}
|
||||
return data
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
const errorMessage =
|
||||
errorData?.error?.message ??
|
||||
`Dataverse API error: ${response.status} ${response.statusText}`
|
||||
logger.error('Dataverse create record failed', { errorData, status: response.status })
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
|
||||
const data = await response.json().catch(() => null)
|
||||
|
||||
let recordId = ''
|
||||
if (data) {
|
||||
const idKey = Object.keys(data).find((k) => k.endsWith('id') && !k.startsWith('@'))
|
||||
recordId = idKey ? String(data[idKey]) : ''
|
||||
}
|
||||
|
||||
if (!recordId) {
|
||||
const entityIdHeader = response.headers.get('OData-EntityId')
|
||||
if (entityIdHeader) {
|
||||
const match = entityIdHeader.match(/\(([^)]+)\)/)
|
||||
if (match) {
|
||||
recordId = match[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
recordId,
|
||||
record: data ?? {},
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
recordId: { type: 'string', description: 'The ID of the created record', optional: true },
|
||||
record: { ...DATAVERSE_RECORD_OUTPUT, optional: true },
|
||||
success: { type: 'boolean', description: 'Whether the record was created successfully' },
|
||||
},
|
||||
}
|
||||
86
apps/sim/tools/microsoft_dataverse/delete_record.ts
Normal file
86
apps/sim/tools/microsoft_dataverse/delete_record.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type {
|
||||
DataverseDeleteRecordParams,
|
||||
DataverseDeleteRecordResponse,
|
||||
} from '@/tools/microsoft_dataverse/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('DataverseDeleteRecord')
|
||||
|
||||
export const dataverseDeleteRecordTool: ToolConfig<
|
||||
DataverseDeleteRecordParams,
|
||||
DataverseDeleteRecordResponse
|
||||
> = {
|
||||
id: 'microsoft_dataverse_delete_record',
|
||||
name: 'Delete Microsoft Dataverse Record',
|
||||
description: 'Delete a record from a Microsoft Dataverse table by its ID.',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: { required: true, provider: 'microsoft-dataverse' },
|
||||
errorExtractor: 'nested-error-object',
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Microsoft Dataverse API',
|
||||
},
|
||||
environmentUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Dataverse environment URL (e.g., https://myorg.crm.dynamics.com)',
|
||||
},
|
||||
entitySetName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Entity set name (plural table name, e.g., accounts, contacts)',
|
||||
},
|
||||
recordId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The unique identifier (GUID) of the record to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = params.environmentUrl.replace(/\/$/, '')
|
||||
return `${baseUrl}/api/data/v9.2/${params.entitySetName}(${params.recordId})`
|
||||
},
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'OData-MaxVersion': '4.0',
|
||||
'OData-Version': '4.0',
|
||||
Accept: 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: DataverseDeleteRecordParams) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
const errorMessage =
|
||||
errorData?.error?.message ??
|
||||
`Dataverse API error: ${response.status} ${response.statusText}`
|
||||
logger.error('Dataverse delete record failed', { errorData, status: response.status })
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
recordId: params?.recordId ?? '',
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
recordId: { type: 'string', description: 'The ID of the deleted record' },
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
123
apps/sim/tools/microsoft_dataverse/disassociate.ts
Normal file
123
apps/sim/tools/microsoft_dataverse/disassociate.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type {
|
||||
DataverseDisassociateParams,
|
||||
DataverseDisassociateResponse,
|
||||
} from '@/tools/microsoft_dataverse/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('DataverseDisassociate')
|
||||
|
||||
export const dataverseDisassociateTool: ToolConfig<
|
||||
DataverseDisassociateParams,
|
||||
DataverseDisassociateResponse
|
||||
> = {
|
||||
id: 'microsoft_dataverse_disassociate',
|
||||
name: 'Disassociate Microsoft Dataverse Records',
|
||||
description:
|
||||
'Remove an association between two records in Microsoft Dataverse. For collection-valued navigation properties, provide the target record ID. For single-valued navigation properties, only the navigation property name is needed.',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: { required: true, provider: 'microsoft-dataverse' },
|
||||
errorExtractor: 'nested-error-object',
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Microsoft Dataverse API',
|
||||
},
|
||||
environmentUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Dataverse environment URL (e.g., https://myorg.crm.dynamics.com)',
|
||||
},
|
||||
entitySetName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Source entity set name (e.g., accounts)',
|
||||
},
|
||||
recordId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Source record GUID',
|
||||
},
|
||||
navigationProperty: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Navigation property name (e.g., contact_customer_accounts for collection-valued, or parentcustomerid_account for single-valued)',
|
||||
},
|
||||
targetRecordId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Target record GUID (required for collection-valued navigation properties, omit for single-valued)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = params.environmentUrl.replace(/\/$/, '')
|
||||
if (params.targetRecordId) {
|
||||
return `${baseUrl}/api/data/v9.2/${params.entitySetName}(${params.recordId})/${params.navigationProperty}(${params.targetRecordId})/$ref`
|
||||
}
|
||||
return `${baseUrl}/api/data/v9.2/${params.entitySetName}(${params.recordId})/${params.navigationProperty}/$ref`
|
||||
},
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'OData-MaxVersion': '4.0',
|
||||
'OData-Version': '4.0',
|
||||
Accept: 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: DataverseDisassociateParams) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
const errorMessage =
|
||||
errorData?.error?.message ??
|
||||
`Dataverse API error: ${response.status} ${response.statusText}`
|
||||
logger.error('Dataverse disassociate failed', { errorData, status: response.status })
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
success: true,
|
||||
entitySetName: params?.entitySetName ?? '',
|
||||
recordId: params?.recordId ?? '',
|
||||
navigationProperty: params?.navigationProperty ?? '',
|
||||
...(params?.targetRecordId ? { targetRecordId: params.targetRecordId } : {}),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the disassociation was completed successfully',
|
||||
},
|
||||
entitySetName: {
|
||||
type: 'string',
|
||||
description: 'Source entity set name used in the disassociation',
|
||||
},
|
||||
recordId: { type: 'string', description: 'Source record GUID that was disassociated' },
|
||||
navigationProperty: {
|
||||
type: 'string',
|
||||
description: 'Navigation property used for the disassociation',
|
||||
},
|
||||
targetRecordId: {
|
||||
type: 'string',
|
||||
description: 'Target record GUID that was disassociated',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
115
apps/sim/tools/microsoft_dataverse/get_record.ts
Normal file
115
apps/sim/tools/microsoft_dataverse/get_record.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type {
|
||||
DataverseGetRecordParams,
|
||||
DataverseGetRecordResponse,
|
||||
} from '@/tools/microsoft_dataverse/types'
|
||||
import { DATAVERSE_RECORD_OUTPUT } from '@/tools/microsoft_dataverse/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('DataverseGetRecord')
|
||||
|
||||
export const dataverseGetRecordTool: ToolConfig<
|
||||
DataverseGetRecordParams,
|
||||
DataverseGetRecordResponse
|
||||
> = {
|
||||
id: 'microsoft_dataverse_get_record',
|
||||
name: 'Get Microsoft Dataverse Record',
|
||||
description:
|
||||
'Retrieve a single record from a Microsoft Dataverse table by its ID. Supports $select and $expand OData query options.',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: { required: true, provider: 'microsoft-dataverse' },
|
||||
errorExtractor: 'nested-error-object',
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Microsoft Dataverse API',
|
||||
},
|
||||
environmentUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Dataverse environment URL (e.g., https://myorg.crm.dynamics.com)',
|
||||
},
|
||||
entitySetName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Entity set name (plural table name, e.g., accounts, contacts)',
|
||||
},
|
||||
recordId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The unique identifier (GUID) of the record to retrieve',
|
||||
},
|
||||
select: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of columns to return (OData $select)',
|
||||
},
|
||||
expand: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Navigation properties to expand (OData $expand)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = params.environmentUrl.replace(/\/$/, '')
|
||||
const queryParts: string[] = []
|
||||
if (params.select) queryParts.push(`$select=${params.select}`)
|
||||
if (params.expand) queryParts.push(`$expand=${params.expand}`)
|
||||
const query = queryParts.length > 0 ? `?${queryParts.join('&')}` : ''
|
||||
return `${baseUrl}/api/data/v9.2/${params.entitySetName}(${params.recordId})${query}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'OData-MaxVersion': '4.0',
|
||||
'OData-Version': '4.0',
|
||||
Accept: 'application/json',
|
||||
Prefer: 'odata.include-annotations="*"',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
const errorMessage =
|
||||
errorData?.error?.message ??
|
||||
`Dataverse API error: ${response.status} ${response.statusText}`
|
||||
logger.error('Dataverse get record failed', { errorData, status: response.status })
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const idKey = Object.keys(data).find((k) => k.endsWith('id') && !k.startsWith('@'))
|
||||
const recordId = idKey ? String(data[idKey]) : ''
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
record: data,
|
||||
recordId,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
record: DATAVERSE_RECORD_OUTPUT,
|
||||
recordId: {
|
||||
type: 'string',
|
||||
description: 'The record primary key ID (auto-detected from response)',
|
||||
optional: true,
|
||||
},
|
||||
success: { type: 'boolean', description: 'Whether the record was retrieved successfully' },
|
||||
},
|
||||
}
|
||||
9
apps/sim/tools/microsoft_dataverse/index.ts
Normal file
9
apps/sim/tools/microsoft_dataverse/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export { dataverseAssociateTool } from './associate'
|
||||
export { dataverseCreateRecordTool } from './create_record'
|
||||
export { dataverseDeleteRecordTool } from './delete_record'
|
||||
export { dataverseDisassociateTool } from './disassociate'
|
||||
export { dataverseGetRecordTool } from './get_record'
|
||||
export { dataverseListRecordsTool } from './list_records'
|
||||
export { dataverseUpdateRecordTool } from './update_record'
|
||||
export { dataverseUpsertRecordTool } from './upsert_record'
|
||||
export { dataverseWhoAmITool } from './whoami'
|
||||
146
apps/sim/tools/microsoft_dataverse/list_records.ts
Normal file
146
apps/sim/tools/microsoft_dataverse/list_records.ts
Normal file
@@ -0,0 +1,146 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type {
|
||||
DataverseListRecordsParams,
|
||||
DataverseListRecordsResponse,
|
||||
} from '@/tools/microsoft_dataverse/types'
|
||||
import { DATAVERSE_RECORDS_ARRAY_OUTPUT } from '@/tools/microsoft_dataverse/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('DataverseListRecords')
|
||||
|
||||
export const dataverseListRecordsTool: ToolConfig<
|
||||
DataverseListRecordsParams,
|
||||
DataverseListRecordsResponse
|
||||
> = {
|
||||
id: 'microsoft_dataverse_list_records',
|
||||
name: 'List Microsoft Dataverse Records',
|
||||
description:
|
||||
'Query and list records from a Microsoft Dataverse table. Supports OData query options for filtering, selecting columns, ordering, and pagination.',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: { required: true, provider: 'microsoft-dataverse' },
|
||||
errorExtractor: 'nested-error-object',
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Microsoft Dataverse API',
|
||||
},
|
||||
environmentUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Dataverse environment URL (e.g., https://myorg.crm.dynamics.com)',
|
||||
},
|
||||
entitySetName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Entity set name (plural table name, e.g., accounts, contacts)',
|
||||
},
|
||||
select: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of columns to return (OData $select)',
|
||||
},
|
||||
filter: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'OData $filter expression (e.g., statecode eq 0)',
|
||||
},
|
||||
orderBy: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'OData $orderby expression (e.g., name asc, createdon desc)',
|
||||
},
|
||||
top: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of records to return (OData $top)',
|
||||
},
|
||||
expand: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Navigation properties to expand (OData $expand)',
|
||||
},
|
||||
count: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Set to "true" to include total record count in response (OData $count)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = params.environmentUrl.replace(/\/$/, '')
|
||||
const queryParts: string[] = []
|
||||
if (params.select) queryParts.push(`$select=${params.select}`)
|
||||
if (params.filter) queryParts.push(`$filter=${params.filter}`)
|
||||
if (params.orderBy) queryParts.push(`$orderby=${params.orderBy}`)
|
||||
if (params.top) queryParts.push(`$top=${params.top}`)
|
||||
if (params.expand) queryParts.push(`$expand=${params.expand}`)
|
||||
if (params.count) queryParts.push(`$count=${params.count}`)
|
||||
const query = queryParts.length > 0 ? `?${queryParts.join('&')}` : ''
|
||||
return `${baseUrl}/api/data/v9.2/${params.entitySetName}${query}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'OData-MaxVersion': '4.0',
|
||||
'OData-Version': '4.0',
|
||||
Accept: 'application/json',
|
||||
Prefer: 'odata.include-annotations="*",odata.maxpagesize=100',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
const errorMessage =
|
||||
errorData?.error?.message ??
|
||||
`Dataverse API error: ${response.status} ${response.statusText}`
|
||||
logger.error('Dataverse list records failed', { errorData, status: response.status })
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const records = data.value ?? []
|
||||
const nextLink = data['@odata.nextLink'] ?? null
|
||||
const totalCount = data['@odata.count'] ?? null
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
records,
|
||||
count: records.length,
|
||||
totalCount,
|
||||
nextLink,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
records: DATAVERSE_RECORDS_ARRAY_OUTPUT,
|
||||
count: { type: 'number', description: 'Number of records returned in the current page' },
|
||||
totalCount: {
|
||||
type: 'number',
|
||||
description: 'Total number of matching records server-side (requires $count=true)',
|
||||
optional: true,
|
||||
},
|
||||
nextLink: {
|
||||
type: 'string',
|
||||
description: 'URL for the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
217
apps/sim/tools/microsoft_dataverse/types.ts
Normal file
217
apps/sim/tools/microsoft_dataverse/types.ts
Normal file
@@ -0,0 +1,217 @@
|
||||
import type { OutputProperty, ToolResponse } from '@/tools/types'
|
||||
|
||||
/**
|
||||
* Microsoft Dataverse Web API types.
|
||||
* @see https://learn.microsoft.com/en-us/power-apps/developer/data-platform/webapi/overview
|
||||
*/
|
||||
|
||||
/**
|
||||
* Dataverse record output definition.
|
||||
* Dataverse records are dynamic (user-defined tables), so columns vary by table.
|
||||
* Every record includes OData metadata fields such as `@odata.etag`.
|
||||
* @see https://learn.microsoft.com/en-us/power-apps/developer/data-platform/webapi/retrieve-entity-using-web-api
|
||||
*/
|
||||
export const DATAVERSE_RECORD_OUTPUT: OutputProperty = {
|
||||
type: 'object',
|
||||
description:
|
||||
'Dataverse record object. Contains dynamic columns based on the queried table, plus OData metadata fields.',
|
||||
properties: {
|
||||
'@odata.context': {
|
||||
type: 'string',
|
||||
description: 'OData context URL describing the entity type and properties returned',
|
||||
optional: true,
|
||||
},
|
||||
'@odata.etag': {
|
||||
type: 'string',
|
||||
description: 'OData entity tag for concurrency control (e.g., W/"12345")',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Array of Dataverse records output definition for list endpoints.
|
||||
* Each item mirrors `DATAVERSE_RECORD_OUTPUT`.
|
||||
* @see https://learn.microsoft.com/en-us/power-apps/developer/data-platform/webapi/query-data-web-api
|
||||
*/
|
||||
export const DATAVERSE_RECORDS_ARRAY_OUTPUT: OutputProperty = {
|
||||
type: 'array',
|
||||
description:
|
||||
'Array of Dataverse records. Each record has dynamic columns based on the table schema.',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
'@odata.etag': {
|
||||
type: 'string',
|
||||
description: 'OData entity tag for concurrency control (e.g., W/"12345")',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export interface DataverseCreateRecordParams {
|
||||
accessToken: string
|
||||
environmentUrl: string
|
||||
entitySetName: string
|
||||
data: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface DataverseGetRecordParams {
|
||||
accessToken: string
|
||||
environmentUrl: string
|
||||
entitySetName: string
|
||||
recordId: string
|
||||
select?: string
|
||||
expand?: string
|
||||
}
|
||||
|
||||
export interface DataverseUpdateRecordParams {
|
||||
accessToken: string
|
||||
environmentUrl: string
|
||||
entitySetName: string
|
||||
recordId: string
|
||||
data: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface DataverseDeleteRecordParams {
|
||||
accessToken: string
|
||||
environmentUrl: string
|
||||
entitySetName: string
|
||||
recordId: string
|
||||
}
|
||||
|
||||
export interface DataverseListRecordsParams {
|
||||
accessToken: string
|
||||
environmentUrl: string
|
||||
entitySetName: string
|
||||
select?: string
|
||||
filter?: string
|
||||
orderBy?: string
|
||||
top?: number
|
||||
expand?: string
|
||||
count?: string
|
||||
}
|
||||
|
||||
export interface DataverseCreateRecordResponse extends ToolResponse {
|
||||
output: {
|
||||
recordId: string
|
||||
record: Record<string, unknown>
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface DataverseGetRecordResponse extends ToolResponse {
|
||||
output: {
|
||||
record: Record<string, unknown>
|
||||
recordId: string
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface DataverseUpdateRecordResponse extends ToolResponse {
|
||||
output: {
|
||||
recordId: string
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface DataverseDeleteRecordResponse extends ToolResponse {
|
||||
output: {
|
||||
recordId: string
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface DataverseListRecordsResponse extends ToolResponse {
|
||||
output: {
|
||||
records: Record<string, unknown>[]
|
||||
count: number
|
||||
totalCount: number | null
|
||||
nextLink: string | null
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface DataverseUpsertRecordParams {
|
||||
accessToken: string
|
||||
environmentUrl: string
|
||||
entitySetName: string
|
||||
recordId: string
|
||||
data: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface DataverseUpsertRecordResponse extends ToolResponse {
|
||||
output: {
|
||||
recordId: string
|
||||
created: boolean
|
||||
record: Record<string, unknown> | null
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface DataverseWhoAmIParams {
|
||||
accessToken: string
|
||||
environmentUrl: string
|
||||
}
|
||||
|
||||
export interface DataverseWhoAmIResponse extends ToolResponse {
|
||||
output: {
|
||||
userId: string
|
||||
businessUnitId: string
|
||||
organizationId: string
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface DataverseAssociateParams {
|
||||
accessToken: string
|
||||
environmentUrl: string
|
||||
entitySetName: string
|
||||
recordId: string
|
||||
navigationProperty: string
|
||||
targetEntitySetName: string
|
||||
targetRecordId: string
|
||||
navigationType?: 'collection' | 'single'
|
||||
}
|
||||
|
||||
export interface DataverseAssociateResponse extends ToolResponse {
|
||||
output: {
|
||||
success: boolean
|
||||
entitySetName: string
|
||||
recordId: string
|
||||
navigationProperty: string
|
||||
targetEntitySetName: string
|
||||
targetRecordId: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface DataverseDisassociateParams {
|
||||
accessToken: string
|
||||
environmentUrl: string
|
||||
entitySetName: string
|
||||
recordId: string
|
||||
navigationProperty: string
|
||||
targetRecordId?: string
|
||||
}
|
||||
|
||||
export interface DataverseDisassociateResponse extends ToolResponse {
|
||||
output: {
|
||||
success: boolean
|
||||
entitySetName: string
|
||||
recordId: string
|
||||
navigationProperty: string
|
||||
targetRecordId?: string
|
||||
}
|
||||
}
|
||||
|
||||
export type DataverseResponse =
|
||||
| DataverseCreateRecordResponse
|
||||
| DataverseGetRecordResponse
|
||||
| DataverseUpdateRecordResponse
|
||||
| DataverseDeleteRecordResponse
|
||||
| DataverseListRecordsResponse
|
||||
| DataverseUpsertRecordResponse
|
||||
| DataverseWhoAmIResponse
|
||||
| DataverseAssociateResponse
|
||||
| DataverseDisassociateResponse
|
||||
106
apps/sim/tools/microsoft_dataverse/update_record.ts
Normal file
106
apps/sim/tools/microsoft_dataverse/update_record.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type {
|
||||
DataverseUpdateRecordParams,
|
||||
DataverseUpdateRecordResponse,
|
||||
} from '@/tools/microsoft_dataverse/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('DataverseUpdateRecord')
|
||||
|
||||
export const dataverseUpdateRecordTool: ToolConfig<
|
||||
DataverseUpdateRecordParams,
|
||||
DataverseUpdateRecordResponse
|
||||
> = {
|
||||
id: 'microsoft_dataverse_update_record',
|
||||
name: 'Update Microsoft Dataverse Record',
|
||||
description:
|
||||
'Update an existing record in a Microsoft Dataverse table. Only send the columns you want to change.',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: { required: true, provider: 'microsoft-dataverse' },
|
||||
errorExtractor: 'nested-error-object',
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Microsoft Dataverse API',
|
||||
},
|
||||
environmentUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Dataverse environment URL (e.g., https://myorg.crm.dynamics.com)',
|
||||
},
|
||||
entitySetName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Entity set name (plural table name, e.g., accounts, contacts)',
|
||||
},
|
||||
recordId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The unique identifier (GUID) of the record to update',
|
||||
},
|
||||
data: {
|
||||
type: 'object',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Record data to update as a JSON object with column names as keys',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = params.environmentUrl.replace(/\/$/, '')
|
||||
return `${baseUrl}/api/data/v9.2/${params.entitySetName}(${params.recordId})`
|
||||
},
|
||||
method: 'PATCH',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
'OData-MaxVersion': '4.0',
|
||||
'OData-Version': '4.0',
|
||||
Accept: 'application/json',
|
||||
'If-Match': '*',
|
||||
}),
|
||||
body: (params) => {
|
||||
let data = params.data
|
||||
if (typeof data === 'string') {
|
||||
try {
|
||||
data = JSON.parse(data)
|
||||
} catch {
|
||||
throw new Error('Invalid JSON format for record data')
|
||||
}
|
||||
}
|
||||
return data
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: DataverseUpdateRecordParams) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
const errorMessage =
|
||||
errorData?.error?.message ??
|
||||
`Dataverse API error: ${response.status} ${response.statusText}`
|
||||
logger.error('Dataverse update record failed', { errorData, status: response.status })
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
recordId: params?.recordId ?? '',
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
recordId: { type: 'string', description: 'The ID of the updated record' },
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
114
apps/sim/tools/microsoft_dataverse/upsert_record.ts
Normal file
114
apps/sim/tools/microsoft_dataverse/upsert_record.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type {
|
||||
DataverseUpsertRecordParams,
|
||||
DataverseUpsertRecordResponse,
|
||||
} from '@/tools/microsoft_dataverse/types'
|
||||
import { DATAVERSE_RECORD_OUTPUT } from '@/tools/microsoft_dataverse/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('DataverseUpsertRecord')
|
||||
|
||||
export const dataverseUpsertRecordTool: ToolConfig<
|
||||
DataverseUpsertRecordParams,
|
||||
DataverseUpsertRecordResponse
|
||||
> = {
|
||||
id: 'microsoft_dataverse_upsert_record',
|
||||
name: 'Upsert Microsoft Dataverse Record',
|
||||
description:
|
||||
'Create or update a record in a Microsoft Dataverse table. If a record with the given ID exists, it is updated; otherwise, a new record is created.',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: { required: true, provider: 'microsoft-dataverse' },
|
||||
errorExtractor: 'nested-error-object',
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Microsoft Dataverse API',
|
||||
},
|
||||
environmentUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Dataverse environment URL (e.g., https://myorg.crm.dynamics.com)',
|
||||
},
|
||||
entitySetName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Entity set name (plural table name, e.g., accounts, contacts)',
|
||||
},
|
||||
recordId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The unique identifier (GUID) of the record to upsert',
|
||||
},
|
||||
data: {
|
||||
type: 'object',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Record data as a JSON object with column names as keys',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = params.environmentUrl.replace(/\/$/, '')
|
||||
return `${baseUrl}/api/data/v9.2/${params.entitySetName}(${params.recordId})`
|
||||
},
|
||||
method: 'PATCH',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
'OData-MaxVersion': '4.0',
|
||||
'OData-Version': '4.0',
|
||||
Accept: 'application/json',
|
||||
Prefer: 'return=representation',
|
||||
}),
|
||||
body: (params) => {
|
||||
let data = params.data
|
||||
if (typeof data === 'string') {
|
||||
try {
|
||||
data = JSON.parse(data)
|
||||
} catch {
|
||||
throw new Error('Invalid JSON format for record data')
|
||||
}
|
||||
}
|
||||
return data
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: DataverseUpsertRecordParams) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
const errorMessage =
|
||||
errorData?.error?.message ??
|
||||
`Dataverse API error: ${response.status} ${response.statusText}`
|
||||
logger.error('Dataverse upsert record failed', { errorData, status: response.status })
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
|
||||
const created = response.status === 201
|
||||
const data = await response.json().catch(() => null)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
recordId: params?.recordId ?? '',
|
||||
created,
|
||||
record: data,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
recordId: { type: 'string', description: 'The ID of the upserted record' },
|
||||
created: { type: 'boolean', description: 'True if the record was created, false if updated' },
|
||||
record: { ...DATAVERSE_RECORD_OUTPUT, optional: true },
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
78
apps/sim/tools/microsoft_dataverse/whoami.ts
Normal file
78
apps/sim/tools/microsoft_dataverse/whoami.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type {
|
||||
DataverseWhoAmIParams,
|
||||
DataverseWhoAmIResponse,
|
||||
} from '@/tools/microsoft_dataverse/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('DataverseWhoAmI')
|
||||
|
||||
export const dataverseWhoAmITool: ToolConfig<DataverseWhoAmIParams, DataverseWhoAmIResponse> = {
|
||||
id: 'microsoft_dataverse_whoami',
|
||||
name: 'Microsoft Dataverse WhoAmI',
|
||||
description:
|
||||
'Retrieve the current authenticated user information from Microsoft Dataverse. Useful for testing connectivity and getting the user ID, business unit ID, and organization ID.',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: { required: true, provider: 'microsoft-dataverse' },
|
||||
errorExtractor: 'nested-error-object',
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Microsoft Dataverse API',
|
||||
},
|
||||
environmentUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Dataverse environment URL (e.g., https://myorg.crm.dynamics.com)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = params.environmentUrl.replace(/\/$/, '')
|
||||
return `${baseUrl}/api/data/v9.2/WhoAmI()`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'OData-MaxVersion': '4.0',
|
||||
'OData-Version': '4.0',
|
||||
Accept: 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
const errorMessage =
|
||||
errorData?.error?.message ??
|
||||
`Dataverse API error: ${response.status} ${response.statusText}`
|
||||
logger.error('Dataverse WhoAmI failed', { errorData, status: response.status })
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
userId: data.UserId ?? '',
|
||||
businessUnitId: data.BusinessUnitId ?? '',
|
||||
organizationId: data.OrganizationId ?? '',
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
userId: { type: 'string', description: 'The authenticated user ID' },
|
||||
businessUnitId: { type: 'string', description: 'The business unit ID' },
|
||||
organizationId: { type: 'string', description: 'The organization ID' },
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
@@ -110,6 +110,21 @@ import {
|
||||
clerkRevokeSessionTool,
|
||||
clerkUpdateUserTool,
|
||||
} from '@/tools/clerk'
|
||||
import {
|
||||
cloudflareCreateDnsRecordTool,
|
||||
cloudflareCreateZoneTool,
|
||||
cloudflareDeleteDnsRecordTool,
|
||||
cloudflareDeleteZoneTool,
|
||||
cloudflareDnsAnalyticsTool,
|
||||
cloudflareGetZoneSettingsTool,
|
||||
cloudflareGetZoneTool,
|
||||
cloudflareListCertificatesTool,
|
||||
cloudflareListDnsRecordsTool,
|
||||
cloudflareListZonesTool,
|
||||
cloudflarePurgeCacheTool,
|
||||
cloudflareUpdateDnsRecordTool,
|
||||
cloudflareUpdateZoneSettingTool,
|
||||
} from '@/tools/cloudflare'
|
||||
import {
|
||||
confluenceAddLabelTool,
|
||||
confluenceCreateBlogPostTool,
|
||||
@@ -1074,6 +1089,17 @@ import {
|
||||
} from '@/tools/mailgun'
|
||||
import { mem0AddMemoriesTool, mem0GetMemoriesTool, mem0SearchMemoriesTool } from '@/tools/mem0'
|
||||
import { memoryAddTool, memoryDeleteTool, memoryGetAllTool, memoryGetTool } from '@/tools/memory'
|
||||
import {
|
||||
dataverseAssociateTool,
|
||||
dataverseCreateRecordTool,
|
||||
dataverseDeleteRecordTool,
|
||||
dataverseDisassociateTool,
|
||||
dataverseGetRecordTool,
|
||||
dataverseListRecordsTool,
|
||||
dataverseUpdateRecordTool,
|
||||
dataverseUpsertRecordTool,
|
||||
dataverseWhoAmITool,
|
||||
} from '@/tools/microsoft_dataverse'
|
||||
import {
|
||||
microsoftExcelReadTool,
|
||||
microsoftExcelReadV2Tool,
|
||||
@@ -2934,6 +2960,19 @@ export const tools: Record<string, ToolConfig> = {
|
||||
clerk_list_sessions: clerkListSessionsTool,
|
||||
clerk_get_session: clerkGetSessionTool,
|
||||
clerk_revoke_session: clerkRevokeSessionTool,
|
||||
cloudflare_list_zones: cloudflareListZonesTool,
|
||||
cloudflare_get_zone: cloudflareGetZoneTool,
|
||||
cloudflare_create_zone: cloudflareCreateZoneTool,
|
||||
cloudflare_delete_zone: cloudflareDeleteZoneTool,
|
||||
cloudflare_list_dns_records: cloudflareListDnsRecordsTool,
|
||||
cloudflare_create_dns_record: cloudflareCreateDnsRecordTool,
|
||||
cloudflare_update_dns_record: cloudflareUpdateDnsRecordTool,
|
||||
cloudflare_delete_dns_record: cloudflareDeleteDnsRecordTool,
|
||||
cloudflare_list_certificates: cloudflareListCertificatesTool,
|
||||
cloudflare_get_zone_settings: cloudflareGetZoneSettingsTool,
|
||||
cloudflare_update_zone_setting: cloudflareUpdateZoneSettingTool,
|
||||
cloudflare_dns_analytics: cloudflareDnsAnalyticsTool,
|
||||
cloudflare_purge_cache: cloudflarePurgeCacheTool,
|
||||
discord_send_message: discordSendMessageTool,
|
||||
discord_get_messages: discordGetMessagesTool,
|
||||
discord_get_server: discordGetServerTool,
|
||||
@@ -2981,6 +3020,15 @@ export const tools: Record<string, ToolConfig> = {
|
||||
datadog_create_downtime: datadogCreateDowntimeTool,
|
||||
datadog_list_downtimes: datadogListDowntimesTool,
|
||||
datadog_cancel_downtime: datadogCancelDowntimeTool,
|
||||
microsoft_dataverse_associate: dataverseAssociateTool,
|
||||
microsoft_dataverse_create_record: dataverseCreateRecordTool,
|
||||
microsoft_dataverse_delete_record: dataverseDeleteRecordTool,
|
||||
microsoft_dataverse_disassociate: dataverseDisassociateTool,
|
||||
microsoft_dataverse_get_record: dataverseGetRecordTool,
|
||||
microsoft_dataverse_list_records: dataverseListRecordsTool,
|
||||
microsoft_dataverse_update_record: dataverseUpdateRecordTool,
|
||||
microsoft_dataverse_upsert_record: dataverseUpsertRecordTool,
|
||||
microsoft_dataverse_whoami: dataverseWhoAmITool,
|
||||
openai_image: openAIImageTool,
|
||||
microsoft_teams_read_chat: microsoftTeamsReadChatTool,
|
||||
microsoft_teams_write_chat: microsoftTeamsWriteChatTool,
|
||||
|
||||
Reference in New Issue
Block a user