mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-08 14:43:54 -05:00
feat(tools): added zoom, elasticsearch, dropbox, kalshi, polymarket, datadog, ahrefs, gitlab, shopify, ssh, wordpress (#2175)
* feat(tools): added zoom, elasticsearch, dropbox, box, datadog, ahrefs, gitlab, shopify, ssh, wordpress * added polymarket & kalshi, fixed ssh * fix search modal bg instead of bgColor, added polymarket and kalshi new endpoints * split up grafana * update docs script & docs * added more zoom endpoints * remove unused box creds * finished wordpress, shopify, kalshi * cleanup * revert envvar dropdown changes * updated grafana endpoints
This commit is contained in:
@@ -473,6 +473,30 @@ export function GithubIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function GitLabIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} width='24' height='24' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
|
||||
<path
|
||||
d='M22.65 14.39L12 22.13 1.35 14.39a.84.84 0 0 1-.3-.94l1.22-3.78 2.44-7.51A.42.42 0 0 1 4.82 2a.43.43 0 0 1 .58 0 .42.42 0 0 1 .11.18l2.44 7.49h8.1l2.44-7.51A.42.42 0 0 1 18.6 2a.43.43 0 0 1 .58 0 .42.42 0 0 1 .11.18l2.44 7.51L23 13.45a.84.84 0 0 1-.35.94z'
|
||||
fill='#FC6D26'
|
||||
/>
|
||||
<path d='M12 22.13L16.05 9.67H7.95L12 22.13z' fill='#E24329' />
|
||||
<path d='M12 22.13L7.95 9.67H1.69L12 22.13z' fill='#FC6D26' />
|
||||
<path d='M1.69 9.67L.47 13.45a.84.84 0 0 0 .3.94L12 22.13 1.69 9.67z' fill='#FCA326' />
|
||||
<path
|
||||
d='M1.69 9.67H7.95L5.51 2.16A.42.42 0 0 0 4.93 2a.42.42 0 0 0-.11.18L1.69 9.67z'
|
||||
fill='#E24329'
|
||||
/>
|
||||
<path d='M12 22.13L16.05 9.67H22.31L12 22.13z' fill='#FC6D26' />
|
||||
<path d='M22.31 9.67L23.53 13.45a.84.84 0 0 1-.3.94L12 22.13 22.31 9.67z' fill='#FCA326' />
|
||||
<path
|
||||
d='M22.31 9.67H16.05l2.44-7.51A.42.42 0 0 1 19.07 2a.42.42 0 0 1 .11.18l3.13 7.49z'
|
||||
fill='#E24329'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function SerperIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg viewBox='0 0 654 600' xmlns='http://www.w3.org/2000/svg' {...props}>
|
||||
@@ -649,6 +673,37 @@ export function GmailIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function GrafanaIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
width='800px'
|
||||
height='800px'
|
||||
viewBox='0 0 16 16'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
fill='none'
|
||||
>
|
||||
<path
|
||||
fill='url(#grafana-color-16__paint0_linear_2372_364)'
|
||||
d='M13.985 7.175a4.408 4.408 0 00-.138-.802 5.035 5.035 0 00-1.054-1.998 2.96 2.96 0 00-.366-.393c.198-.787-.245-1.468-.245-1.468-.764-.046-1.237.227-1.42.363-.031-.015-.062-.03-.092-.03-.122-.046-.26-.106-.397-.137-.138-.045-.275-.075-.413-.12-.137-.031-.29-.061-.443-.092-.03 0-.046 0-.076-.015C9.005 1.44 8.058 1 8.058 1 7.004 1.666 6.79 2.604 6.79 2.604s0 .015-.016.06l-.183.046c-.076.03-.168.06-.244.076-.077.03-.168.06-.245.09-.153.076-.32.152-.473.228-.153.09-.306.181-.443.272-.016-.015-.03-.015-.03-.015-1.467-.545-2.766.136-2.766.136-.122 1.544.58 2.528.733 2.71-.03.09-.06.196-.091.287a8.104 8.104 0 00-.245 1.09c0 .06-.015.106-.015.166C1.397 8.386 1 9.748 1 9.748c1.13 1.287 2.46 1.377 2.46 1.377.167.303.366.575.58.848.092.106.183.212.29.318a3.014 3.014 0 00.061 2.149c1.268.045 2.093-.545 2.261-.681.122.045.26.076.382.106.382.106.78.151 1.176.181h.49c.595.848 1.634.954 1.634.954.748-.772.779-1.544.779-1.71v-.015-.03-.03c.153-.107.305-.228.443-.35a5.37 5.37 0 00.779-.892c.015-.03.046-.06.061-.09.84.045 1.436-.515 1.436-.515-.138-.863-.642-1.287-.749-1.378l-.015-.015h-.015s-.015 0-.015-.015c0-.045.015-.106.015-.151 0-.091.015-.182.015-.288V9.4v-.166-.076-.152l-.015-.075c-.015-.091-.03-.197-.061-.288a3.506 3.506 0 00-.428-1.044 3.856 3.856 0 00-.718-.848 3.784 3.784 0 00-.901-.575 3.347 3.347 0 00-.993-.272c-.168-.015-.336-.03-.504-.03H9.37 9.204c-.092.015-.169.015-.26.03-.336.06-.642.181-.932.348-.275.166-.52.363-.718.605a2.579 2.579 0 00-.459.757 2.63 2.63 0 00-.183.817v.393c.015.137.046.273.077.394.076.258.183.485.336.666.137.197.32.348.504.485.183.12.382.212.58.272.199.06.382.076.565.076h.244c.031 0 .047 0 .062-.015.015 0 .046-.015.061-.015.046-.016.076-.016.122-.03l.23-.092a.869.869 0 00.198-.12c.015-.016.03-.03.046-.03a.129.129 0 00.015-.198c-.046-.06-.122-.075-.183-.03-.015.015-.03.015-.046.03-.046.03-.107.046-.168.06l-.183.046c-.03 0-.061.015-.092.015H8.73a1.519 1.519 0 01-.825-.378 1.452 1.452 0 01-.306-.378 1.655 1.655 0 01-.168-.485c-.015-.09-.015-.166-.015-.257v-.106-.03c0-.046.015-.091.015-.136.061-.364.26-.727.55-1 .077-.075.153-.136.23-.181.076-.06.167-.106.259-.151.092-.046.183-.076.29-.106a.993.993 0 01.306-.046h.321c.107.015.229.03.336.046.214.045.427.12.626.242.397.212.733.56.947.969.107.211.183.423.214.65.015.06.015.121.015.167v.363c0 .06-.015.121-.015.182 0 .06-.015.12-.03.181l-.046.182c-.03.121-.077.242-.123.363a3.183 3.183 0 01-.366.666 3.002 3.002 0 01-1.91 1.18c-.122.016-.26.03-.382.046h-.198c-.061 0-.138 0-.199-.015a3.637 3.637 0 01-.81-.151 4.068 4.068 0 01-.748-.303 4.098 4.098 0 01-1.696-1.695 4.398 4.398 0 01-.29-.742c-.076-.257-.107-.514-.137-.772v-.302-.091c0-.136.015-.258.03-.394s.046-.272.061-.393c.03-.137.061-.258.092-.394a5.33 5.33 0 01.275-.741c.214-.47.504-.893.855-1.226.092-.091.184-.167.275-.243.092-.075.184-.136.29-.211a5.39 5.39 0 01.306-.182c.046-.03.107-.045.153-.076a.26.26 0 01.076-.03.26.26 0 01.077-.03c.107-.046.229-.091.336-.121.03-.015.06-.015.091-.03.03-.016.061-.016.092-.03.061-.016.122-.031.168-.046.03-.015.061-.015.092-.015.03 0 .06-.016.091-.016.03 0 .061-.015.092-.015l.046-.015h.046c.03 0 .06-.015.091-.015.03 0 .061-.015.107-.015.03 0 .077-.015.107-.015h.764c.23.015.443.03.657.075.428.076.84.212 1.207.394.366.182.702.393.977.636l.046.045.046.045c.03.03.061.061.107.091l.092.091.091.09c.123.122.23.258.336.394.199.258.367.515.49.772.014.015.014.03.03.046.015.015.015.03.015.045l.046.09.046.092.045.09c.046.122.092.228.123.333.06.167.107.318.137.455.015.045.061.09.122.075a.104.104 0 00.107-.106c.092-.227.092-.393.077-.575z'
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id='grafana-color-16__paint0_linear_2372_364'
|
||||
x1='7.502'
|
||||
x2='7.502'
|
||||
y1='18.142'
|
||||
y2='5.356'
|
||||
gradientUnits='userSpaceOnUse'
|
||||
>
|
||||
<stop stop-color='#FFF200' />
|
||||
<stop offset='1' stop-color='#F15A29' />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function GoogleDriveIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
@@ -1259,7 +1314,7 @@ export function ConfluenceIcon(props: SVGProps<SVGSVGElement>) {
|
||||
|
||||
export function TwilioIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'>
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='-8 -8 272 272'>
|
||||
<path
|
||||
fill='currentColor'
|
||||
d='M128 0c70.656 0 128 57.344 128 128s-57.344 128-128 128S0 198.656 0 128 57.344 0 128 0zm0 33.792c-52.224 0-94.208 41.984-94.208 94.208S75.776 222.208 128 222.208s94.208-41.984 94.208-94.208S180.224 33.792 128 33.792zm31.744 99.328c14.704 0 26.624 11.92 26.624 26.624 0 14.704-11.92 26.624-26.624 26.624-14.704 0-26.624-11.92-26.624-26.624 0-14.704 11.92-26.624 26.624-26.624zm-63.488 0c14.704 0 26.624 11.92 26.624 26.624 0 14.704-11.92 26.624-26.624 26.624-14.704 0-26.624-11.92-26.624-26.624 0-14.704 11.92-26.624 26.624-26.624zm63.488-63.488c14.704 0 26.624 11.92 26.624 26.624 0 14.704-11.92 26.624-26.624 26.624-14.704 0-26.624-11.92-26.624-26.624 0-14.704 11.92-26.624 26.624-26.624zm-63.488 0c14.704 0 26.624 11.92 26.624 26.624 0 14.704-11.92 26.624-26.624 26.624-14.704 0-26.624-11.92-26.624-26.624 0-14.704 11.92-26.624 26.624-26.624z'
|
||||
@@ -1666,21 +1721,13 @@ export function ElevenLabsIcon(props: SVGProps<SVGSVGElement>) {
|
||||
|
||||
export function LinkupIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
viewBox='0 0 24 24'
|
||||
width='24'
|
||||
height='24'
|
||||
fill='none'
|
||||
>
|
||||
<path
|
||||
d='M20.2 14.1c-.4-.3-1.6-.4-2.9-.2.5-1.4 1.3-3.9.1-5-.6-.5-1.5-.7-2.6-.5-.3 0-.6.1-1 .2-1.1-1.6-2.4-2.5-3.8-2.5-1.6 0-3.1 1-4.1 2.9-1.2 2.1-1.9 5.1-1.9 8.8v.03l.4.3c3-.9 7.5-2.3 10.7-2.9 0 .9.1 1.9.1 2.8v.03l.4.3c.1 0 5.4-1.7 5.3-3.3 0-.2-.1-.5-.3-.7zM19.9 14.7c.03.4-1.7 1.4-4 2.3.5-.7 1-1.6 1.3-2.5 1.4-.1 2.4-.1 2.7.2zM16.4 14.6c-.3.7-.7 1.4-1.2 2-.02-.6-.1-1.2-.2-1.8.4-.1.9-.1 1.4-.2zM16.5 9.4c.8.7.9 2.4.1 5.1-.5.1-1 .1-1.5.2-.3-2-.9-3.8-1.7-5.3.3-.1.6-.2.8-.2.9-.1 1.7.05 2.3.2zM9.5 6.8c1.2 0 2.3.7 3.2 2.1-2.8 1.1-5.9 3.4-8.4 7.8.2-5.1 1.9-9.9 5.2-9.9zM4.7 17c3.4-4.9 6.4-6.8 8.4-7.8.7 1.3 1.2 2.9 1.5 4.8-3.2.6-7.3 1.8-9.9 3z'
|
||||
fill='currentColor'
|
||||
stroke='currentColor'
|
||||
strokeWidth='0.5'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'>
|
||||
<g transform='translate(12, 12) scale(1.3) translate(-12, -12)'>
|
||||
<path
|
||||
d='M20.2 14.1c-.4-.3-1.6-.4-2.9-.2.5-1.4 1.3-3.9.1-5-.6-.5-1.5-.7-2.6-.5-.3 0-.6.1-1 .2-1.1-1.6-2.4-2.5-3.8-2.5-1.6 0-3.1 1-4.1 2.9-1.2 2.1-1.9 5.1-1.9 8.8v.03l.4.3c3-.9 7.5-2.3 10.7-2.9 0 .9.1 1.9.1 2.8v.03l.4.3c.1 0 5.4-1.7 5.3-3.3 0-.2-.1-.5-.3-.7zM19.9 14.7c.03.4-1.7 1.4-4 2.3.5-.7 1-1.6 1.3-2.5 1.4-.1 2.4-.1 2.7.2zM16.4 14.6c-.3.7-.7 1.4-1.2 2-.02-.6-.1-1.2-.2-1.8.4-.1.9-.1 1.4-.2zM16.5 9.4c.8.7.9 2.4.1 5.1-.5.1-1 .1-1.5.2-.3-2-.9-3.8-1.7-5.3.3-.1.6-.2.8-.2.9-.1 1.7.05 2.3.2zM9.5 6.8c1.2 0 2.3.7 3.2 2.1-2.8 1.1-5.9 3.4-8.4 7.8.2-5.1 1.9-9.9 5.2-9.9zM4.7 17c3.4-4.9 6.4-6.8 8.4-7.8.7 1.3 1.2 2.9 1.5 4.8-3.2.6-7.3 1.8-9.9 3z'
|
||||
fill='#000000'
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -3694,6 +3741,22 @@ export function ZendeskIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function ZoomIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
fill='currentColor'
|
||||
width='800px'
|
||||
height='800px'
|
||||
viewBox='0 0 32 32'
|
||||
version='1.1'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
>
|
||||
<path d='M19.283 17.4c-0.367 0.374-0.879 0.606-1.444 0.606-1.117 0-2.023-0.906-2.023-2.023s0.906-2.023 2.023-2.023c0.929 0 1.712 0.626 1.949 1.479l0.003 0.014c0.045 0.159 0.071 0.341 0.071 0.53 0 0.552-0.221 1.052-0.579 1.417l0-0zM15.471 13.586c-0.648 0.615-1.052 1.483-1.052 2.446 0 1.861 1.509 3.37 3.37 3.37s3.37-1.509 3.37-3.37c0-1.54-1.033-2.838-2.444-3.241l-0.024-0.006c-0.27-0.078-0.581-0.123-0.902-0.123-0.899 0-1.716 0.352-2.32 0.925l0.002-0.001zM28.296 12.601c-0.802 0.001-1.522 0.352-2.016 0.909l-0.002 0.003c-0.496-0.562-1.219-0.915-2.023-0.915-0.563 0-1.086 0.173-1.519 0.468l0.009-0.006c-0.316-0.278-0.73-0.451-1.184-0.462l-0.002-0v6.742l0.337-0.016c0.544-0.014 0.981-0.451 0.995-0.993l0-0.001 0.016-0.337v-2.361l0.017-0.337c0-0.001 0-0.002 0-0.003 0-0.245 0.061-0.477 0.169-0.679l-0.004 0.008c0.238-0.405 0.671-0.672 1.166-0.672s0.928 0.267 1.162 0.664l0.003 0.006c0.103 0.196 0.164 0.428 0.165 0.675v0l0.017 0.339v2.361l0.016 0.336c0.022 0.54 0.454 0.972 0.991 0.995l0.002 0 0.337 0.016v-3.708l0.015-0.337c0-0.001 0-0.002 0-0.003 0-0.247 0.062-0.48 0.171-0.683l-0.004 0.008c0.238-0.403 0.67-0.669 1.165-0.669 0.496 0 0.929 0.268 1.164 0.666l0.003 0.006c0.102 0.195 0.162 0.427 0.162 0.673 0 0.001 0 0.001 0 0.002v-0l0.019 0.337v2.361l0.016 0.336c0.020 0.541 0.454 0.975 0.993 0.995l0.002 0 0.337 0.016v-4.045c-0.001-1.488-1.208-2.694-2.697-2.694-0.001 0-0.002 0-0.003 0h0zM12.206 17.4c-0.37 0.393-0.894 0.638-1.475 0.638-1.117 0-2.023-0.906-2.023-2.023s0.906-2.023 2.023-2.023c0.924 0 1.703 0.619 1.945 1.465l0.004 0.014c0.047 0.163 0.075 0.351 0.075 0.544 0 0.536-0.209 1.024-0.549 1.386l0.001-0.001zM10.78 12.6h-0.005c-1.86 0.001-3.367 1.509-3.367 3.368s1.508 3.368 3.368 3.368 3.368-1.508 3.368-3.368c0-1.86-1.507-3.367-3.366-3.368h-0zM6.734 18.008l-0.337-0.015h-3.035l4.044-4.045-0.016-0.337c-0.013-0.544-0.451-0.982-0.994-0.995l-0.001-0-0.337-0.016h-5.052l0.018 0.337c0.026 0.538 0.455 0.967 0.99 0.995l0.002 0 0.337 0.016h3.037l-4.049 4.045 0.017 0.336c0.019 0.541 0.453 0.975 0.992 0.995l0.002 0 0.337 0.016h5.056l-0.018-0.337c-0.024-0.539-0.455-0.969-0.991-0.993l-0.002-0z' />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function PylonIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
@@ -3801,6 +3864,30 @@ export function SmtpIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function SshIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
width='24'
|
||||
height='24'
|
||||
viewBox='0 0 24 24'
|
||||
fill='none'
|
||||
stroke='currentColor'
|
||||
strokeWidth='2'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
>
|
||||
<rect x='2' y='4' width='20' height='16' rx='2' />
|
||||
<path d='M6 8h.01' />
|
||||
<path d='M10 8h.01' />
|
||||
<path d='M14 8h8' />
|
||||
<path d='M6 12l3 3-3 3' />
|
||||
<path d='M12 18h6' />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function ApifyIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
@@ -3887,3 +3974,181 @@ export function DynamoDBIcon(props: SVGProps<SVGSVGElement>) {
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function WordpressIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 25.925 25.925'>
|
||||
<g fill='currentColor'>
|
||||
<path d='M1.843,12.962c0,4.401,2.557,8.205,6.267,10.008L2.805,8.437C2.189,9.819,1.843,11.35,1.843,12.962z M20.469,12.4c0-1.374-0.493-2.326-0.917-3.066c-0.563-0.917-1.092-1.691-1.092-2.608c0-1.021,0.775-1.973,1.867-1.973c0.049,0,0.096,0.006,0.145,0.008c-1.979-1.813-4.615-2.919-7.509-2.919c-3.885,0-7.303,1.993-9.291,5.013c0.261,0.008,0.507,0.013,0.716,0.013c1.163,0,2.963-0.142,2.963-0.142c0.599-0.035,0.67,0.846,0.071,0.917c0,0-0.603,0.07-1.272,0.105l4.049,12.045l2.434-7.298l-1.732-4.747c-0.599-0.035-1.167-0.105-1.167-0.105c-0.6-0.036-0.529-0.953,0.07-0.917c0,0,1.836,0.142,2.928,0.142c1.163,0,2.964-0.142,2.964-0.142c0.6-0.035,0.67,0.846,0.071,0.917c0,0-0.604,0.07-1.272,0.105l4.018,11.953l1.11-3.706C20.187,14.55,20.469,13.353,20.469,12.4z M13.158,13.935l-3.337,9.694c0.997,0.293,2.05,0.453,3.142,0.453c1.294,0,2.537-0.224,3.693-0.63c-0.029-0.048-0.057-0.099-0.08-0.153L13.158,13.935z M22.72,7.627c0.049,0.354,0.075,0.734,0.075,1.144c0,1.128-0.212,2.396-0.846,3.982l-3.396,9.82c3.306-1.928,5.529-5.509,5.529-9.611C24.082,11.028,23.588,9.21,22.72,7.627z' />
|
||||
<path d='M0,12.962c0,7.147,5.815,12.963,12.962,12.963c7.149,0,12.963-5.816,12.963-12.963S20.111,0,12.963,0S0,5.814,0,12.962z M0.594,12.962c0-6.819,5.548-12.368,12.368-12.368s12.369,5.549,12.369,12.368S19.782,25.33,12.963,25.33S0.594,19.781,0.594,12.962z' />
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function AhrefsIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1065 1300'>
|
||||
<path
|
||||
fillRule='evenodd'
|
||||
fill='#ff8800'
|
||||
d='m111.3 0.7h953.4v1264.4h-256.9v-137.8h-21.2c-127 129.9-230.4 172.4-410.4 172.4-227.8 0-376.1-127.3-376.1-339.5v-58.3c0-288.9 121.8-392.4 511-421.5l241-18.6v-180.2h-640.8zm640.8 707.7l-193.4 18.6c-203.9 18.6-248.8 47.8-251.5 169.7-2.7 87.5 42.4 127.2 148.3 127.2 98 0 217.1-53 296.6-132.5z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function ShopifyIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
viewBox='0 0 48 48'
|
||||
width='48px'
|
||||
height='48px'
|
||||
>
|
||||
<path
|
||||
fill='#7cb342'
|
||||
d='M37.216,11.78c-0.023-0.211-0.211-0.305-0.351-0.305s-3.21-0.234-3.21-0.234s-2.132-2.132-2.39-2.343 c-0.234-0.234-0.68-0.164-0.867-0.117c-0.023,0-0.469,0.141-1.195,0.375c-0.726-2.086-1.968-3.984-4.194-3.984h-0.211 C24.187,4.375,23.391,4,22.735,4c-5.155,0-7.639,6.444-8.412,9.725c-2.015,0.633-3.445,1.054-3.609,1.125 c-1.125,0.351-1.148,0.375-1.289,1.429c-0.117,0.797-3.046,23.456-3.046,23.456L29.179,44l12.373-2.671 C41.575,41.282,37.24,11.991,37.216,11.78z M27.937,9.483c-0.562,0.164-1.242,0.375-1.921,0.609V9.671 c0-1.265-0.164-2.296-0.469-3.117C26.718,6.695,27.445,7.984,27.937,9.483L27.937,9.483z M24.117,6.812 c0.305,0.797,0.516,1.922,0.516,3.468v0.234c-1.265,0.398-2.601,0.797-3.984,1.242C21.422,8.804,22.899,7.351,24.117,6.812 L24.117,6.812z M22.617,5.359c0.234,0,0.469,0.094,0.656,0.234c-1.664,0.773-3.421,2.718-4.148,6.655 c-1.101,0.351-2.156,0.656-3.163,0.984C16.806,10.233,18.915,5.359,22.617,5.359z'
|
||||
/>
|
||||
<path
|
||||
fill='#558b2f'
|
||||
d='M36.865,11.428c-0.141,0-3.21-0.234-3.21-0.234s-2.132-2.132-2.39-2.343 C31.17,8.757,31.053,8.71,30.96,8.71L29.249,44l12.373-2.671c0,0-4.335-29.338-4.359-29.549 C37.169,11.569,37.005,11.475,36.865,11.428z'
|
||||
/>
|
||||
<path
|
||||
fill='#fff'
|
||||
d='M24.792,18.593l-1.475,4.449c0,0-1.337-0.715-2.927-0.715c-2.374,0-2.489,1.498-2.489,1.867 c0,2.028,5.301,2.812,5.301,7.583c0,3.757-2.374,6.177-5.578,6.177c-3.872,0-5.808-2.397-5.808-2.397l1.037-3.411 c0,0,2.028,1.752,3.734,1.752c1.129,0,1.59-0.876,1.59-1.521c0-2.651-4.333-2.766-4.333-7.145c0-3.665,2.628-7.214,7.952-7.214 C23.777,17.994,24.792,18.593,24.792,18.593z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function BoxCompanyIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 41 22'>
|
||||
<path
|
||||
d='M39.7 19.2c.5.7.4 1.6-.2 2.1-.7.5-1.7.4-2.2-.2l-3.5-4.5-3.4 4.4c-.5.7-1.5.7-2.2.2-.7-.5-.8-1.4-.3-2.1l4-5.2-4-5.2c-.5-.7-.3-1.7.3-2.2.7-.5 1.7-.3 2.2.3l3.4 4.5L37.3 7c.5-.7 1.4-.8 2.2-.3.7.5.7 1.5.2 2.2L35.8 14l3.9 5.2zm-18.2-.6c-2.6 0-4.7-2-4.7-4.6 0-2.5 2.1-4.6 4.7-4.6s4.7 2.1 4.7 4.6c-.1 2.6-2.2 4.6-4.7 4.6zm-13.8 0c-2.6 0-4.7-2-4.7-4.6 0-2.5 2.1-4.6 4.7-4.6s4.7 2.1 4.7 4.6c0 2.6-2.1 4.6-4.7 4.6zM21.5 6.4c-2.9 0-5.5 1.6-6.8 4-1.3-2.4-3.9-4-6.9-4-1.8 0-3.4.6-4.7 1.5V1.5C3.1.7 2.4 0 1.6 0 .7 0 0 .7 0 1.5v12.6c.1 4.2 3.5 7.5 7.7 7.5 3 0 5.6-1.7 6.9-4.1 1.3 2.4 3.9 4.1 6.8 4.1 4.3 0 7.8-3.4 7.8-7.7.1-4.1-3.4-7.5-7.7-7.5z'
|
||||
fill='currentColor'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function DropboxIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 43 40'>
|
||||
<path
|
||||
d='m12.5 0l-12.5 8.1 8.7 7 12.5-7.8-8.7-7.3zm-12.5 21.9l12.5 8.2 8.7-7.3-12.5-7.7-8.7 6.8zm21.2 0.9l8.8 7.3 12.4-8.1-8.6-6.9-12.6 7.7zm21.2-14.7l-12.4-8.1-8.8 7.3 12.6 7.8 8.6-7zm-21.1 16.3l-8.8 7.3-3.7-2.5v2.8l12.5 7.5 12.5-7.5v-2.8l-3.8 2.5-8.7-7.3z'
|
||||
fill='currentColor'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function ElasticsearchIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 128 128'>
|
||||
<path
|
||||
fill='#343741'
|
||||
d='M4 64c0 5.535.777 10.879 2.098 16H84c8.836 0 16-7.164 16-16s-7.164-16-16-16H6.098A63.738 63.738 0 0 0 4 64'
|
||||
/>
|
||||
<path
|
||||
fill='#fec514'
|
||||
d='M111.695 30.648A61.485 61.485 0 0 0 117.922 24C106.188 9.379 88.199 0 68 0 42.715 0 20.957 14.71 10.574 36H98.04a20.123 20.123 0 0 0 13.652-5.352'
|
||||
/>
|
||||
<path
|
||||
fill='#00bfb3'
|
||||
d='M98.04 92H10.577C20.961 113.29 42.715 128 68 128c20.2 0 38.188-9.383 49.922-24a61.1 61.1 0 0 0-6.227-6.648A20.133 20.133 0 0 0 98.04 92'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function GitlabIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 380 380'>
|
||||
<path
|
||||
fill='#e24329'
|
||||
d='M265.26416,174.37243l-.2134-.55822-21.19899-55.30908c-.4236-1.08359-1.18542-1.99642-2.17699-2.62689-.98837-.63373-2.14749-.93253-3.32305-.87014-1.1689.06239-2.29195.48925-3.20809,1.21821-.90957.73554-1.56629,1.73047-1.87493,2.85346l-14.31327,43.80662h-57.90965l-14.31327-43.80662c-.30864-1.12299-.96536-2.11791-1.87493-2.85346-.91614-.72895-2.03911-1.15582-3.20809-1.21821-1.17548-.06239-2.33468.23641-3.32297.87014-.99166.63047-1.75348,1.5433-2.17707,2.62689l-21.19891,55.31237-.21348.55493c-6.28158,16.38521-.92929,34.90803,13.05891,45.48782.02621.01641.04922.03611.07552.05582l.18719.14119,32.29094,24.17392,15.97151,12.09024,9.71951,7.34871c2.34117,1.77316,5.57877,1.77316,7.92002,0l9.71943-7.34871,15.96822-12.09024,32.48142-24.31511c.02958-.02299.05588-.04269.08538-.06568,13.97834-10.57977,19.32735-29.09604,13.04905-45.47796Z'
|
||||
/>
|
||||
<path
|
||||
fill='#fc6d26'
|
||||
d='M265.26416,174.37243l-.2134-.55822c-10.5174,2.16062-20.20405,6.6099-28.49844,12.81593-.1346.0985-25.20497,19.05805-46.55171,35.19699,15.84998,11.98517,29.6477,22.40405,29.6477,22.40405l32.48142-24.31511c.02958-.02299.05588-.04269.08538-.06568,13.97834-10.57977,19.32735-29.09604,13.04905-45.47796Z'
|
||||
/>
|
||||
<path
|
||||
fill='#fca326'
|
||||
d='M160.34962,244.23117l15.97151,12.09024,9.71951,7.34871c2.34117,1.77316,5.57877,1.77316,7.92002,0l9.71943-7.34871,15.96822-12.09024s-13.79772-10.41888-29.6477-22.40405c-15.85327,11.98517-29.65099,22.40405-29.65099,22.40405Z'
|
||||
/>
|
||||
<path
|
||||
fill='#fc6d26'
|
||||
d='M143.44561,186.63014c-8.29111-6.20274-17.97446-10.65531-28.49507-12.81264l-.21348.55493c-6.28158,16.38521-.92929,34.90803,13.05891,45.48782.02621.01641.04922.03611.07552.05582l.18719.14119,32.29094,24.17392s13.79772-10.41888,29.65099-22.40405c-21.34673-16.13894-46.42031-35.09848-46.55499-35.19699Z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function SSHIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
viewBox='0 0 48 48'
|
||||
width='48px'
|
||||
height='48px'
|
||||
>
|
||||
<path
|
||||
fill='#7cb342'
|
||||
d='M37.216,11.78c-0.023-0.211-0.211-0.305-0.351-0.305s-3.21-0.234-3.21-0.234s-2.132-2.132-2.39-2.343 c-0.234-0.234-0.68-0.164-0.867-0.117c-0.023,0-0.469,0.141-1.195,0.375c-0.726-2.086-1.968-3.984-4.194-3.984h-0.211 C24.187,4.375,23.391,4,22.735,4c-5.155,0-7.639,6.444-8.412,9.725c-2.015,0.633-3.445,1.054-3.609,1.125 c-1.125,0.351-1.148,0.375-1.289,1.429c-0.117,0.797-3.046,23.456-3.046,23.456L29.179,44l12.373-2.671 C41.575,41.282,37.24,11.991,37.216,11.78z M27.937,9.483c-0.562,0.164-1.242,0.375-1.921,0.609V9.671 c0-1.265-0.164-2.296-0.469-3.117C26.718,6.695,27.445,7.984,27.937,9.483L27.937,9.483z M24.117,6.812 c0.305,0.797,0.516,1.922,0.516,3.468v0.234c-1.265,0.398-2.601,0.797-3.984,1.242C21.422,8.804,22.899,7.351,24.117,6.812 L24.117,6.812z M22.617,5.359c0.234,0,0.469,0.094,0.656,0.234c-1.664,0.773-3.421,2.718-4.148,6.655 c-1.101,0.351-2.156,0.656-3.163,0.984C16.806,10.233,18.915,5.359,22.617,5.359z'
|
||||
/>
|
||||
<path
|
||||
fill='#558b2f'
|
||||
d='M36.865,11.428c-0.141,0-3.21-0.234-3.21-0.234s-2.132-2.132-2.39-2.343 C31.17,8.757,31.053,8.71,30.96,8.71L29.249,44l12.373-2.671c0,0-4.335-29.338-4.359-29.549 C37.169,11.569,37.005,11.475,36.865,11.428z'
|
||||
/>
|
||||
<path
|
||||
fill='#fff'
|
||||
d='M24.792,18.593l-1.475,4.449c0,0-1.337-0.715-2.927-0.715c-2.374,0-2.489,1.498-2.489,1.867 c0,2.028,5.301,2.812,5.301,7.583c0,3.757-2.374,6.177-5.578,6.177c-3.872,0-5.808-2.397-5.808-2.397l1.037-3.411 c0,0,2.028,1.752,3.734,1.752c1.129,0,1.59-0.876,1.59-1.521c0-2.651-4.333-2.766-4.333-7.145c0-3.665,2.628-7.214,7.952-7.214 C23.777,17.994,24.792,18.593,24.792,18.593z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function DatadogIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'>
|
||||
<g fill='currentColor'>
|
||||
<path d='M57.705 33.717l1.575 20.1L31.4 58.82 29 38.72l3.983-.556c.648.278 1.112.37 1.853.556 1.204.278 2.594.648 4.63-.463.463-.278 1.482-1.112 1.853-1.667zm-43.073 23.9c-.463-.74-1.297-1.575-2.594-2.594-1.853-1.482-1.204-4.076-.093-5.65 1.4-2.686 8.707-6.206 8.337-10.56-.185-1.575-.37-3.613-1.853-5.095-.093.556 0 1.112 0 1.112s-.648-.74-.926-1.853c-.278-.37-.556-.556-.834-1.112-.185.648-.185 1.297-.185 1.297s-.463-1.204-.556-2.13c-.278.463-.37 1.297-.37 1.297s-.648-1.853-.463-2.87c-.278-.834-1.204-2.594-.926-6.484 1.575 1.112 5.095.834 6.484-1.204.463-.648.74-2.5-.185-6.114-.648-2.316-2.223-5.743-2.87-7.04l-.093.093c.37 1.02 1.02 3.242 1.297 4.354.74 3.242 1.02 4.354.648 5.836-.278 1.297-1.02 2.13-2.964 3.057-1.853.926-4.446-1.4-4.54-1.482-1.853-1.482-3.242-3.9-3.427-5.002-.185-1.297.74-2.038 1.204-3.057-.648.185-1.4.556-1.4.556s.834-.926 1.945-1.667a9.43 9.43 0 0 0 1.204-.834H10.28s1.112-.648 2.316-1.02H10.93l4.446-1.945c1.4-.556 2.686-.37 3.427.648 1.02 1.4 2.038 2.13 4.168 2.686 1.297-.556 1.76-.926 3.427-1.4 1.482-1.667 2.686-1.853 2.686-1.853s-.834.74-.926 1.575c.834-.648 1.76-1.204 1.76-1.204s-.37.463-.648 1.112l.093.093c1.02-.556 2.13-1.02 2.13-1.02s-.37.37-.74.926c.74 0 2.223 0 2.78.093 3.427.093 4.168-3.613 5.465-4.168 1.667-.556 2.408-.926 5.187 1.853 2.408 2.408 4.26 6.577 3.335 7.503-.74.74-2.316-.278-4.076-2.408-.926-1.112-1.575-2.5-1.945-4.168-.185-1.4-1.204-2.223-1.204-2.223s.648 1.4.648 2.594c0 .648.093 3.057 1.112 4.446-.093.185-.185 1.02-.278 1.112-1.204-1.482-3.9-2.5-4.26-2.87 1.482 1.204 4.817 3.9 6.114 6.484 1.204 2.5.463 4.724 1.112 5.28.185.185 2.594 3.15 3.057 4.724.834 2.686.093 5.465-1.02 7.132l-2.964.463a3.92 3.92 0 0 1-1.112-.371c.185-.37.648-1.297.648-1.482l-.185-.278c-.926 1.297-2.5 2.594-3.798 3.335-1.667.926-3.613.834-4.9.37-3.613-1.112-7.04-3.52-7.78-4.168 0 0 0 .463.093.648a28.07 28.07 0 0 0 5.002 4.168l-4.26.463 2.038 15.747c-.926.093-1.02.185-2.038.37-.834-3.057-2.5-5.002-4.354-6.206-1.575-1.02-3.798-1.297-5.928-.834l-.093.185c1.482-.185 3.242.093 5.002 1.204s3.15 3.983 3.705 5.65c.648 2.223 1.112 4.54-.648 7.04-1.297 1.76-5.002 2.78-7.966.648.834 1.297 1.853 2.316 3.335 2.5 2.13.278 4.168-.093 5.65-1.482 1.204-1.204 1.853-3.798 1.667-6.577l1.945-.278.648 4.9L62.06 56.7l-2.78-25.195-1.575.278L54.556 0 1.942 6.114l6.484 52.428z' />
|
||||
<path d='M39.665 30.06c1.4 1.02 2.594 1.667 3.798 1.575.74-.093 1.482-1.297 1.945-2.408.37-.74.37-1.575-.185-1.853-.278-.093-1.4-.093-2.223 0-1.575.185-3.15.74-3.52 1.02-.556.37-.278 1.297.185 1.667m.37-10.84v.093l.093.185c.37.74.74 1.482 1.482 1.853.185 0 .37-.093.556-.093.648 0 1.02.093 1.297.185v-.556c-.093-.926.185-2.594-1.667-3.52-.74-.37-1.667-.185-2.038.185h.185c.463.185.185.37.093.556-.093.37-.185.463 0 1.112' />
|
||||
<path d='M31.698 19.407c.463-.37-2.13-.926-4.168.37-1.482 1.02-1.482 3.15-.093 4.354a1.27 1.27 0 0 1 .37.278c.37-.185.926-.37 1.4-.556.926-.278 1.667-.463 2.316-.556.278-.37.648-.926.556-1.945-.093-1.4-1.204-1.112-.37-1.945m15.098 22.5l-4.446 7.4-5.187-1.575-4.54 6.947.185 2.223 24.825-4.54-1.482-15.47-4.076 8.522z' />
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function KalshiIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 78 20' fill='currentColor' xmlns='http://www.w3.org/2000/svg'>
|
||||
<path d='M40.1043 0H36.0332V19.9986H40.1043V0Z' />
|
||||
<path d='M0.416887 0.0221237H4.73849V8.99348L12.818 0.0221237H18.0582L10.6468 8.24586L18.5384 20H13.3608L7.59868 11.5686L4.73849 14.7459V20H0.416887V0.0221237Z' />
|
||||
<path
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
d='M34.4675 19.8117H32.4007C30.5426 19.8117 29.624 19.0017 29.6658 17.4027C29.1229 18.2334 28.4549 18.8771 27.6824 19.3132C26.8891 19.7494 25.9496 19.9778 24.8222 19.9778C23.1729 19.9778 21.8368 19.604 20.8138 18.8564C19.8117 18.088 19.3106 17.0289 19.3106 15.6582C19.3106 14.1007 19.8952 12.8962 21.0434 12.0656C22.2126 11.2141 23.9036 10.778 26.1166 10.778H29.0603V10.0719C29.0603 9.40737 28.8098 8.8882 28.3087 8.49362C27.8077 8.09905 27.1396 7.89138 26.2836 7.89138C25.532 7.89138 24.9266 8.05752 24.4464 8.36902C23.9662 8.70129 23.674 9.1374 23.5905 9.67734H19.6446C19.7699 8.18212 20.4589 7.01916 21.6697 6.18848C22.8806 5.3578 24.4882 4.92169 26.4924 4.92169C28.5801 4.92169 30.2086 5.37857 31.3359 6.29232C32.4842 7.20607 33.0688 8.53516 33.0688 10.2588V15.4298C33.0688 15.7828 33.1523 16.0321 33.2984 16.1774C33.4445 16.302 33.6951 16.3851 34.0291 16.3851H34.4675V19.8117ZM26.0749 13.4569C25.2398 13.4569 24.5717 13.6231 24.0915 13.9761C23.6322 14.3084 23.4026 14.7653 23.4026 15.3675C23.4026 15.8867 23.5905 16.2813 23.9871 16.5928C24.3838 16.9043 24.9266 17.0496 25.5947 17.0496C26.6594 17.0496 27.4945 16.7589 28.1 16.1567C28.7054 15.5544 29.0394 14.7445 29.0603 13.7269V13.4569H26.0749Z'
|
||||
/>
|
||||
<path d='M45.5115 14.9314C45.5741 15.5752 45.8873 16.0944 46.4718 16.5097C47.0564 16.9043 47.7871 17.112 48.6848 17.112C49.5408 17.112 50.2297 16.9874 50.7308 16.7174C51.2318 16.4266 51.4824 16.0321 51.4824 15.5129C51.4824 15.1391 51.3571 14.8483 51.1275 14.6614C50.8978 14.4745 50.5638 14.3292 50.1462 14.2669C49.7287 14.163 49.0397 14.0592 48.0794 13.9554C46.7641 13.7892 45.6785 13.5608 44.8225 13.2908C43.9665 13.0208 43.2567 12.6055 42.7557 12.024C42.2337 11.4426 41.9832 10.6949 41.9832 9.73966C41.9832 8.78438 42.2337 7.9537 42.7557 7.22685C43.2985 6.47924 44.0501 5.91853 45.0104 5.50319C45.9708 5.10861 47.0773 4.90094 48.3299 4.90094C50.355 4.92171 51.9625 5.35782 53.1943 6.1885C54.4469 7.01918 55.115 8.18213 55.2194 9.67736H51.3571C51.2945 9.11665 51.0022 8.68054 50.4594 8.3275C49.9374 7.97446 49.2694 7.78756 48.4343 7.78756C47.6618 7.78756 47.0355 7.93293 46.5553 8.22367C46.096 8.5144 45.8664 8.88821 45.8664 9.36585C45.8664 9.71889 45.9916 9.9681 46.2422 10.1342C46.4927 10.3004 46.8267 10.425 47.2234 10.508C47.6201 10.5911 48.309 10.6742 49.2485 10.7572C51.2527 10.9857 52.7768 11.4218 53.8206 12.0448C54.9062 12.647 55.4282 13.7062 55.4282 15.2222C55.4282 16.1774 55.1359 17.0081 54.5722 17.735C54.0085 18.4618 53.2361 19.0225 52.2131 19.4171C51.211 19.7909 50.0418 19.9986 48.7266 19.9986C46.6806 19.9986 44.9895 19.5417 43.716 18.6487C42.4216 17.735 41.7535 16.4889 41.67 14.9314H45.5115Z' />
|
||||
<path d='M69.7503 6.72852C68.623 5.6694 67.2033 5.12946 65.4496 5.12946C63.6333 5.12946 62.1719 5.794 61.0654 7.12309V0H56.9943V19.9986H61.0654V12.4602C61.0654 11.1934 61.3368 10.2174 61.9213 9.5113C62.5059 8.80522 63.3201 8.45218 64.364 8.45218C65.3661 8.45218 66.1177 8.78445 66.6187 9.42823C67.1198 10.0512 67.3703 10.965 67.3703 12.1902V19.9986H71.4414V12.0241C71.4414 9.55283 70.8777 7.78763 69.7503 6.72852Z' />
|
||||
<path d='M73.0068 5.29551H77.0779V19.9778H73.0068V5.29551Z' />
|
||||
<path d='M76.473 0.581477C76.0972 0.20767 75.617 0 75.0324 0C74.4688 0 73.9677 0.20767 73.571 0.581477C73.1952 0.955283 72.9865 1.41216 72.9865 1.97287C72.9865 2.53358 73.1952 3.01122 73.571 3.38503C73.9677 3.75883 74.4688 3.9665 75.0324 3.9665C75.5961 3.9665 76.0972 3.7796 76.473 3.38503C76.8488 2.99045 77.0575 2.53358 77.0575 1.97287C77.0575 1.41216 76.8488 0.934516 76.473 0.581477Z' />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function PolymarketIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='51 209 123 155'>
|
||||
<path
|
||||
fill='currentColor'
|
||||
d='M173.2,363.2L51.1,328.3v-83.7l122.1-34.9V363.2z M161.4,296.2l-89.8,25.6l89.8,25.6L161.4,296.2z M62.9,260.8v51.3l89.8-25.6L62.9,260.8z M161.4,225.3L71.6,251l89.8,25.6L161.4,225.3z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import type { ComponentType, SVGProps } from 'react'
|
||||
import {
|
||||
AhrefsIcon,
|
||||
AirtableIcon,
|
||||
ApifyIcon,
|
||||
ApolloIcon,
|
||||
@@ -14,14 +15,18 @@ import {
|
||||
CalendlyIcon,
|
||||
ClayIcon,
|
||||
ConfluenceIcon,
|
||||
DatadogIcon,
|
||||
DiscordIcon,
|
||||
DocumentIcon,
|
||||
DropboxIcon,
|
||||
DynamoDBIcon,
|
||||
ElasticsearchIcon,
|
||||
ElevenLabsIcon,
|
||||
ExaAIIcon,
|
||||
EyeIcon,
|
||||
FirecrawlIcon,
|
||||
GithubIcon,
|
||||
GitLabIcon,
|
||||
GmailIcon,
|
||||
GoogleCalendarIcon,
|
||||
GoogleDocsIcon,
|
||||
@@ -30,6 +35,7 @@ import {
|
||||
GoogleIcon,
|
||||
GoogleSheetsIcon,
|
||||
GoogleVaultIcon,
|
||||
GrafanaIcon,
|
||||
HubspotIcon,
|
||||
HuggingFaceIcon,
|
||||
HunterIOIcon,
|
||||
@@ -38,6 +44,7 @@ import {
|
||||
IntercomIcon,
|
||||
JinaAIIcon,
|
||||
JiraIcon,
|
||||
KalshiIcon,
|
||||
LinearIcon,
|
||||
LinkedInIcon,
|
||||
LinkupIcon,
|
||||
@@ -61,6 +68,7 @@ import {
|
||||
PerplexityIcon,
|
||||
PineconeIcon,
|
||||
PipedriveIcon,
|
||||
PolymarketIcon,
|
||||
PostgresIcon,
|
||||
PosthogIcon,
|
||||
PylonIcon,
|
||||
@@ -74,8 +82,10 @@ import {
|
||||
SendgridIcon,
|
||||
SentryIcon,
|
||||
SerperIcon,
|
||||
ShopifyIcon,
|
||||
SlackIcon,
|
||||
SmtpIcon,
|
||||
SshIcon,
|
||||
STTIcon,
|
||||
StagehandIcon,
|
||||
StripeIcon,
|
||||
@@ -92,19 +102,23 @@ import {
|
||||
WebflowIcon,
|
||||
WhatsAppIcon,
|
||||
WikipediaIcon,
|
||||
WordpressIcon,
|
||||
xIcon,
|
||||
YouTubeIcon,
|
||||
ZendeskIcon,
|
||||
ZepIcon,
|
||||
ZoomIcon,
|
||||
} from '@/components/icons'
|
||||
|
||||
type IconComponent = ComponentType<SVGProps<SVGSVGElement>>
|
||||
|
||||
export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
zoom: ZoomIcon,
|
||||
zep: ZepIcon,
|
||||
zendesk: ZendeskIcon,
|
||||
youtube: YouTubeIcon,
|
||||
x: xIcon,
|
||||
wordpress: WordpressIcon,
|
||||
wikipedia: WikipediaIcon,
|
||||
whatsapp: WhatsAppIcon,
|
||||
webflow: WebflowIcon,
|
||||
@@ -125,8 +139,10 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
stripe: StripeIcon,
|
||||
stagehand_agent: StagehandIcon,
|
||||
stagehand: StagehandIcon,
|
||||
ssh: SshIcon,
|
||||
smtp: SmtpIcon,
|
||||
slack: SlackIcon,
|
||||
shopify: ShopifyIcon,
|
||||
sharepoint: MicrosoftSharepointIcon,
|
||||
serper: SerperIcon,
|
||||
sentry: SentryIcon,
|
||||
@@ -141,6 +157,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
pylon: PylonIcon,
|
||||
posthog: PosthogIcon,
|
||||
postgresql: PostgresIcon,
|
||||
polymarket: PolymarketIcon,
|
||||
pipedrive: PipedriveIcon,
|
||||
pinecone: PineconeIcon,
|
||||
perplexity: PerplexityIcon,
|
||||
@@ -164,6 +181,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
linkedin: LinkedInIcon,
|
||||
linear: LinearIcon,
|
||||
knowledge: PackageSearchIcon,
|
||||
kalshi: KalshiIcon,
|
||||
jira: JiraIcon,
|
||||
jina: JinaAIIcon,
|
||||
intercom: IntercomIcon,
|
||||
@@ -172,6 +190,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
hunter: HunterIOIcon,
|
||||
huggingface: HuggingFaceIcon,
|
||||
hubspot: HubspotIcon,
|
||||
grafana: GrafanaIcon,
|
||||
google_vault: GoogleVaultIcon,
|
||||
google_sheets: GoogleSheetsIcon,
|
||||
google_forms: GoogleFormsIcon,
|
||||
@@ -180,13 +199,17 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
google_calendar: GoogleCalendarIcon,
|
||||
google_search: GoogleIcon,
|
||||
gmail: GmailIcon,
|
||||
gitlab: GitLabIcon,
|
||||
github: GithubIcon,
|
||||
firecrawl: FirecrawlIcon,
|
||||
file: DocumentIcon,
|
||||
exa: ExaAIIcon,
|
||||
elevenlabs: ElevenLabsIcon,
|
||||
elasticsearch: ElasticsearchIcon,
|
||||
dynamodb: DynamoDBIcon,
|
||||
dropbox: DropboxIcon,
|
||||
discord: DiscordIcon,
|
||||
datadog: DatadogIcon,
|
||||
confluence: ConfluenceIcon,
|
||||
clay: ClayIcon,
|
||||
calendly: CalendlyIcon,
|
||||
@@ -196,4 +219,5 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
apollo: ApolloIcon,
|
||||
apify: ApifyIcon,
|
||||
airtable: AirtableIcon,
|
||||
ahrefs: AhrefsIcon,
|
||||
}
|
||||
|
||||
204
apps/docs/content/docs/en/tools/ahrefs.mdx
Normal file
204
apps/docs/content/docs/en/tools/ahrefs.mdx
Normal file
@@ -0,0 +1,204 @@
|
||||
---
|
||||
title: Ahrefs
|
||||
description: SEO analysis with Ahrefs
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="ahrefs"
|
||||
color="#E0E0E0"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[Ahrefs](https://ahrefs.com/) is a leading SEO toolset for analyzing websites, tracking rankings, monitoring backlinks, and researching keywords. It provides detailed insights into your own website as well as your competitors, helping you make data-driven decisions to improve your search visibility.
|
||||
|
||||
With the Ahrefs integration in Sim, you can:
|
||||
|
||||
- **Analyze Domain Rating & Authority**: Instantly check the Domain Rating (DR) and Ahrefs Rank of any website to gauge its authority.
|
||||
- **Fetch Backlinks**: Retrieve a list of backlinks pointing to a site or specific URL, with details like anchor text, referring page DR, and more.
|
||||
- **Get Backlink Statistics**: Access metrics on backlink types (dofollow, nofollow, text, image, redirect, etc.) for a domain or URL.
|
||||
- **Explore Organic Keywords** *(planned)*: View keywords a domain ranks for and their positions in Google search results.
|
||||
- **Discover Top Pages** *(planned)*: Identify the highest-performing pages by organic traffic and links.
|
||||
|
||||
These tools let your agents automate SEO research, monitor competitors, and generate reports—all as part of your workflow automations. To use the Ahrefs integration, you’ll need an Ahrefs Enterprise subscription with API access.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Ahrefs SEO tools into your workflow. Analyze domain ratings, backlinks, organic keywords, top pages, and more. Requires an Ahrefs Enterprise plan with API access.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `ahrefs_domain_rating`
|
||||
|
||||
Get the Domain Rating (DR) and Ahrefs Rank for a target domain. Domain Rating shows the strength of a website
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `target` | string | Yes | The target domain to analyze \(e.g., example.com\) |
|
||||
| `date` | string | No | Date for historical data in YYYY-MM-DD format \(defaults to today\) |
|
||||
| `apiKey` | string | Yes | Ahrefs API Key |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `domainRating` | number | Domain Rating score \(0-100\) |
|
||||
| `ahrefsRank` | number | Ahrefs Rank - global ranking based on backlink profile strength |
|
||||
|
||||
### `ahrefs_backlinks`
|
||||
|
||||
Get a list of backlinks pointing to a target domain or URL. Returns details about each backlink including source URL, anchor text, and domain rating.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `target` | string | Yes | The target domain or URL to analyze |
|
||||
| `mode` | string | No | Analysis mode: domain \(entire domain\), prefix \(URL prefix\), subdomains \(include all subdomains\), exact \(exact URL match\) |
|
||||
| `date` | string | No | Date for historical data in YYYY-MM-DD format \(defaults to today\) |
|
||||
| `limit` | number | No | Maximum number of results to return \(default: 100\) |
|
||||
| `offset` | number | No | Number of results to skip for pagination |
|
||||
| `apiKey` | string | Yes | Ahrefs API Key |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `backlinks` | array | List of backlinks pointing to the target |
|
||||
|
||||
### `ahrefs_backlinks_stats`
|
||||
|
||||
Get backlink statistics for a target domain or URL. Returns totals for different backlink types including dofollow, nofollow, text, image, and redirect links.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `target` | string | Yes | The target domain or URL to analyze |
|
||||
| `mode` | string | No | Analysis mode: domain \(entire domain\), prefix \(URL prefix\), subdomains \(include all subdomains\), exact \(exact URL match\) |
|
||||
| `date` | string | No | Date for historical data in YYYY-MM-DD format \(defaults to today\) |
|
||||
| `apiKey` | string | Yes | Ahrefs API Key |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `stats` | object | Backlink statistics summary |
|
||||
|
||||
### `ahrefs_referring_domains`
|
||||
|
||||
Get a list of domains that link to a target domain or URL. Returns unique referring domains with their domain rating, backlink counts, and discovery dates.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `target` | string | Yes | The target domain or URL to analyze |
|
||||
| `mode` | string | No | Analysis mode: domain \(entire domain\), prefix \(URL prefix\), subdomains \(include all subdomains\), exact \(exact URL match\) |
|
||||
| `date` | string | No | Date for historical data in YYYY-MM-DD format \(defaults to today\) |
|
||||
| `limit` | number | No | Maximum number of results to return \(default: 100\) |
|
||||
| `offset` | number | No | Number of results to skip for pagination |
|
||||
| `apiKey` | string | Yes | Ahrefs API Key |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `referringDomains` | array | List of domains linking to the target |
|
||||
|
||||
### `ahrefs_organic_keywords`
|
||||
|
||||
Get organic keywords that a target domain or URL ranks for in Google search results. Returns keyword details including search volume, ranking position, and estimated traffic.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `target` | string | Yes | The target domain or URL to analyze |
|
||||
| `country` | string | No | Country code for search results \(e.g., us, gb, de\). Default: us |
|
||||
| `mode` | string | No | Analysis mode: domain \(entire domain\), prefix \(URL prefix\), subdomains \(include all subdomains\), exact \(exact URL match\) |
|
||||
| `date` | string | No | Date for historical data in YYYY-MM-DD format \(defaults to today\) |
|
||||
| `limit` | number | No | Maximum number of results to return \(default: 100\) |
|
||||
| `offset` | number | No | Number of results to skip for pagination |
|
||||
| `apiKey` | string | Yes | Ahrefs API Key |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `keywords` | array | List of organic keywords the target ranks for |
|
||||
|
||||
### `ahrefs_top_pages`
|
||||
|
||||
Get the top pages of a target domain sorted by organic traffic. Returns page URLs with their traffic, keyword counts, and estimated traffic value.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `target` | string | Yes | The target domain to analyze |
|
||||
| `country` | string | No | Country code for traffic data \(e.g., us, gb, de\). Default: us |
|
||||
| `mode` | string | No | Analysis mode: domain \(entire domain\), prefix \(URL prefix\), subdomains \(include all subdomains\) |
|
||||
| `date` | string | No | Date for historical data in YYYY-MM-DD format \(defaults to today\) |
|
||||
| `limit` | number | No | Maximum number of results to return \(default: 100\) |
|
||||
| `offset` | number | No | Number of results to skip for pagination |
|
||||
| `apiKey` | string | Yes | Ahrefs API Key |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `pages` | array | List of top pages by organic traffic |
|
||||
|
||||
### `ahrefs_keyword_overview`
|
||||
|
||||
Get detailed metrics for a keyword including search volume, keyword difficulty, CPC, clicks, and traffic potential.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `keyword` | string | Yes | The keyword to analyze |
|
||||
| `country` | string | No | Country code for keyword data \(e.g., us, gb, de\). Default: us |
|
||||
| `apiKey` | string | Yes | Ahrefs API Key |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `overview` | object | Keyword metrics overview |
|
||||
|
||||
### `ahrefs_broken_backlinks`
|
||||
|
||||
Get a list of broken backlinks pointing to a target domain or URL. Useful for identifying link reclamation opportunities.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `target` | string | Yes | The target domain or URL to analyze |
|
||||
| `mode` | string | No | Analysis mode: domain \(entire domain\), prefix \(URL prefix\), subdomains \(include all subdomains\), exact \(exact URL match\) |
|
||||
| `date` | string | No | Date for historical data in YYYY-MM-DD format \(defaults to today\) |
|
||||
| `limit` | number | No | Maximum number of results to return \(default: 100\) |
|
||||
| `offset` | number | No | Number of results to skip for pagination |
|
||||
| `apiKey` | string | Yes | Ahrefs API Key |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `brokenBacklinks` | array | List of broken backlinks |
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- Category: `tools`
|
||||
- Type: `ahrefs`
|
||||
307
apps/docs/content/docs/en/tools/datadog.mdx
Normal file
307
apps/docs/content/docs/en/tools/datadog.mdx
Normal file
@@ -0,0 +1,307 @@
|
||||
---
|
||||
title: Datadog
|
||||
description: Monitor infrastructure, applications, and logs with Datadog
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="datadog"
|
||||
color="#632CA6"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[Datadog](https://datadoghq.com/) is a comprehensive monitoring and analytics platform for infrastructure, applications, logs, and more. It enables organizations to gain real-time visibility into the health and performance of systems, detect anomalies, and automate incident response.
|
||||
|
||||
With Datadog, you can:
|
||||
|
||||
- **Monitor metrics**: Collect, visualize, and analyze metrics from servers, cloud services, and custom applications.
|
||||
- **Query time series data**: Run advanced queries on performance metrics for trend analysis and reporting.
|
||||
- **Manage monitors and events**: Set up monitors to detect issues, trigger alerts, and create events for observability.
|
||||
- **Handle downtimes**: Schedule and programmatically manage planned downtimes to suppress alerts during maintenance.
|
||||
- **Analyze logs and traces** *(with additional setup in Datadog)*: Centralize and inspect logs or distributed traces for deeper troubleshooting.
|
||||
|
||||
Sim’s Datadog integration lets your agents automate these operations and interact with your Datadog account programmatically. Use it to submit custom metrics, query timeseries data, manage monitors, create events, and streamline your monitoring workflows directly within Sim automations.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Datadog monitoring into workflows. Submit metrics, manage monitors, query logs, create events, handle downtimes, and more.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `datadog_submit_metrics`
|
||||
|
||||
Submit custom metrics to Datadog. Use for tracking application performance, business metrics, or custom monitoring data.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `series` | string | Yes | JSON array of metric series to submit. Each series should include metric name, type \(gauge/rate/count\), points \(timestamp/value pairs\), and optional tags. |
|
||||
| `apiKey` | string | Yes | Datadog API key |
|
||||
| `site` | string | No | Datadog site/region \(default: datadoghq.com\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the metrics were submitted successfully |
|
||||
| `errors` | array | Any errors that occurred during submission |
|
||||
|
||||
### `datadog_query_timeseries`
|
||||
|
||||
Query metric timeseries data from Datadog. Use for analyzing trends, creating reports, or retrieving metric values.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `query` | string | Yes | Datadog metrics query \(e.g., "avg:system.cpu.user\{*\}"\) |
|
||||
| `from` | number | Yes | Start time as Unix timestamp in seconds |
|
||||
| `to` | number | Yes | End time as Unix timestamp in seconds |
|
||||
| `apiKey` | string | Yes | Datadog API key |
|
||||
| `applicationKey` | string | Yes | Datadog Application key |
|
||||
| `site` | string | No | Datadog site/region \(default: datadoghq.com\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `series` | array | Array of timeseries data with metric name, tags, and data points |
|
||||
| `status` | string | Query status |
|
||||
|
||||
### `datadog_create_event`
|
||||
|
||||
Post an event to the Datadog event stream. Use for deployment notifications, alerts, or any significant occurrences.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `title` | string | Yes | Event title |
|
||||
| `text` | string | Yes | Event body/description. Supports markdown. |
|
||||
| `alertType` | string | No | Alert type: error, warning, info, success, user_update, recommendation, or snapshot |
|
||||
| `priority` | string | No | Event priority: normal or low |
|
||||
| `host` | string | No | Host name to associate with this event |
|
||||
| `tags` | string | No | Comma-separated list of tags \(e.g., "env:production,service:api"\) |
|
||||
| `aggregationKey` | string | No | Key to aggregate events together |
|
||||
| `sourceTypeName` | string | No | Source type name for the event |
|
||||
| `dateHappened` | number | No | Unix timestamp when the event occurred \(defaults to now\) |
|
||||
| `apiKey` | string | Yes | Datadog API key |
|
||||
| `site` | string | No | Datadog site/region \(default: datadoghq.com\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `event` | object | The created event details |
|
||||
|
||||
### `datadog_create_monitor`
|
||||
|
||||
Create a new monitor/alert in Datadog. Monitors can track metrics, service checks, events, and more.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `name` | string | Yes | Monitor name |
|
||||
| `type` | string | Yes | Monitor type: metric alert, service check, event alert, process alert, log alert, query alert, composite, synthetics alert, slo alert |
|
||||
| `query` | string | Yes | Monitor query \(e.g., "avg\(last_5m\):avg:system.cpu.idle\{*\} < 20"\) |
|
||||
| `message` | string | No | Message to include with notifications. Can include @-mentions and markdown. |
|
||||
| `tags` | string | No | Comma-separated list of tags |
|
||||
| `priority` | number | No | Monitor priority \(1-5, where 1 is highest\) |
|
||||
| `options` | string | No | JSON string of monitor options \(thresholds, notify_no_data, renotify_interval, etc.\) |
|
||||
| `apiKey` | string | Yes | Datadog API key |
|
||||
| `applicationKey` | string | Yes | Datadog Application key |
|
||||
| `site` | string | No | Datadog site/region \(default: datadoghq.com\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `monitor` | object | The created monitor details |
|
||||
|
||||
### `datadog_get_monitor`
|
||||
|
||||
Retrieve details of a specific monitor by ID.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `monitorId` | string | Yes | The ID of the monitor to retrieve |
|
||||
| `groupStates` | string | No | Comma-separated group states to include: alert, warn, no data, ok |
|
||||
| `withDowntimes` | boolean | No | Include downtime data with the monitor |
|
||||
| `apiKey` | string | Yes | Datadog API key |
|
||||
| `applicationKey` | string | Yes | Datadog Application key |
|
||||
| `site` | string | No | Datadog site/region \(default: datadoghq.com\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `monitor` | object | The monitor details |
|
||||
|
||||
### `datadog_list_monitors`
|
||||
|
||||
List all monitors in Datadog with optional filtering by name, tags, or state.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `groupStates` | string | No | Comma-separated group states to filter by: alert, warn, no data, ok |
|
||||
| `name` | string | No | Filter monitors by name \(partial match\) |
|
||||
| `tags` | string | No | Comma-separated list of tags to filter by |
|
||||
| `monitorTags` | string | No | Comma-separated list of monitor tags to filter by |
|
||||
| `withDowntimes` | boolean | No | Include downtime data with monitors |
|
||||
| `page` | number | No | Page number for pagination \(0-indexed\) |
|
||||
| `pageSize` | number | No | Number of monitors per page \(max 1000\) |
|
||||
| `apiKey` | string | Yes | Datadog API key |
|
||||
| `applicationKey` | string | Yes | Datadog Application key |
|
||||
| `site` | string | No | Datadog site/region \(default: datadoghq.com\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `monitors` | array | List of monitors |
|
||||
|
||||
### `datadog_mute_monitor`
|
||||
|
||||
Mute a monitor to temporarily suppress notifications.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `monitorId` | string | Yes | The ID of the monitor to mute |
|
||||
| `scope` | string | No | Scope to mute \(e.g., "host:myhost"\). If not specified, mutes all scopes. |
|
||||
| `end` | number | No | Unix timestamp when the mute should end. If not specified, mutes indefinitely. |
|
||||
| `apiKey` | string | Yes | Datadog API key |
|
||||
| `applicationKey` | string | Yes | Datadog Application key |
|
||||
| `site` | string | No | Datadog site/region \(default: datadoghq.com\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the monitor was successfully muted |
|
||||
|
||||
### `datadog_query_logs`
|
||||
|
||||
Search and retrieve logs from Datadog. Use for troubleshooting, analysis, or monitoring.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `query` | string | Yes | Log search query \(e.g., "service:web-app status:error"\) |
|
||||
| `from` | string | Yes | Start time in ISO-8601 format or relative \(e.g., "now-1h"\) |
|
||||
| `to` | string | Yes | End time in ISO-8601 format or relative \(e.g., "now"\) |
|
||||
| `limit` | number | No | Maximum number of logs to return \(default: 50, max: 1000\) |
|
||||
| `sort` | string | No | Sort order: timestamp \(oldest first\) or -timestamp \(newest first\) |
|
||||
| `indexes` | string | No | Comma-separated list of log indexes to search |
|
||||
| `apiKey` | string | Yes | Datadog API key |
|
||||
| `applicationKey` | string | Yes | Datadog Application key |
|
||||
| `site` | string | No | Datadog site/region \(default: datadoghq.com\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `logs` | array | List of log entries |
|
||||
|
||||
### `datadog_send_logs`
|
||||
|
||||
Send log entries to Datadog for centralized logging and analysis.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `logs` | string | Yes | JSON array of log entries. Each entry should have message and optionally ddsource, ddtags, hostname, service. |
|
||||
| `apiKey` | string | Yes | Datadog API key |
|
||||
| `site` | string | No | Datadog site/region \(default: datadoghq.com\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the logs were sent successfully |
|
||||
|
||||
### `datadog_create_downtime`
|
||||
|
||||
Schedule a downtime to suppress monitor notifications during maintenance windows.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `scope` | string | Yes | Scope to apply downtime to \(e.g., "host:myhost", "env:production", or "*" for all\) |
|
||||
| `message` | string | No | Message to display during downtime |
|
||||
| `start` | number | No | Unix timestamp for downtime start \(defaults to now\) |
|
||||
| `end` | number | No | Unix timestamp for downtime end |
|
||||
| `timezone` | string | No | Timezone for the downtime \(e.g., "America/New_York"\) |
|
||||
| `monitorId` | string | No | Specific monitor ID to mute |
|
||||
| `monitorTags` | string | No | Comma-separated monitor tags to match \(e.g., "team:backend,priority:high"\) |
|
||||
| `muteFirstRecoveryNotification` | boolean | No | Mute the first recovery notification |
|
||||
| `apiKey` | string | Yes | Datadog API key |
|
||||
| `applicationKey` | string | Yes | Datadog Application key |
|
||||
| `site` | string | No | Datadog site/region \(default: datadoghq.com\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `downtime` | object | The created downtime details |
|
||||
|
||||
### `datadog_list_downtimes`
|
||||
|
||||
List all scheduled downtimes in Datadog.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `currentOnly` | boolean | No | Only return currently active downtimes |
|
||||
| `monitorId` | string | No | Filter by monitor ID |
|
||||
| `apiKey` | string | Yes | Datadog API key |
|
||||
| `applicationKey` | string | Yes | Datadog Application key |
|
||||
| `site` | string | No | Datadog site/region \(default: datadoghq.com\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `downtimes` | array | List of downtimes |
|
||||
|
||||
### `datadog_cancel_downtime`
|
||||
|
||||
Cancel a scheduled downtime.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `downtimeId` | string | Yes | The ID of the downtime to cancel |
|
||||
| `apiKey` | string | Yes | Datadog API key |
|
||||
| `applicationKey` | string | Yes | Datadog Application key |
|
||||
| `site` | string | No | Datadog site/region \(default: datadoghq.com\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the downtime was successfully canceled |
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- Category: `tools`
|
||||
- Type: `datadog`
|
||||
224
apps/docs/content/docs/en/tools/dropbox.mdx
Normal file
224
apps/docs/content/docs/en/tools/dropbox.mdx
Normal file
@@ -0,0 +1,224 @@
|
||||
---
|
||||
title: Dropbox
|
||||
description: Upload, download, share, and manage files in Dropbox
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="dropbox"
|
||||
color="#0061FF"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[Dropbox](https://dropbox.com/) is a popular cloud storage and collaboration platform that enables individuals and teams to securely store, access, and share files from anywhere. Dropbox is designed for easy file management, syncing, and powerful collaboration, whether you're working solo or with a group.
|
||||
|
||||
With Dropbox in Sim, you can:
|
||||
|
||||
- **Upload and download files**: Seamlessly upload any file to your Dropbox or retrieve content on demand
|
||||
- **List folder contents**: Browse the files and folders within any Dropbox directory
|
||||
- **Create new folders**: Organize your files by programmatically creating new folders in your Dropbox
|
||||
- **Search files and folders**: Locate documents, images, or other items by name or content
|
||||
- **Generate shared links**: Quickly create shareable public or private links for files and folders
|
||||
- **Manage files**: Move, delete, or rename files and folders as part of automated workflows
|
||||
|
||||
These capabilities allow your Sim agents to automate Dropbox operations directly within your workflows — from backing up important files to distributing content and maintaining organized folders. Use Dropbox as both a source and destination for files, enabling seamless cloud storage management as part of your business processes.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Dropbox into your workflow for file management, sharing, and collaboration. Upload files, download content, create folders, manage shared links, and more.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `dropbox_upload`
|
||||
|
||||
Upload a file to Dropbox
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `path` | string | Yes | The path in Dropbox where the file should be saved \(e.g., /folder/document.pdf\) |
|
||||
| `fileContent` | string | Yes | The base64 encoded content of the file to upload |
|
||||
| `fileName` | string | No | Optional filename \(used if path is a folder\) |
|
||||
| `mode` | string | No | Write mode: add \(default\) or overwrite |
|
||||
| `autorename` | boolean | No | If true, rename the file if there is a conflict |
|
||||
| `mute` | boolean | No | If true, don't notify the user about this upload |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `file` | object | The uploaded file metadata |
|
||||
|
||||
### `dropbox_download`
|
||||
|
||||
Download a file from Dropbox and get a temporary link
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `path` | string | Yes | The path of the file to download \(e.g., /folder/document.pdf\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `file` | object | The file metadata |
|
||||
|
||||
### `dropbox_list_folder`
|
||||
|
||||
List the contents of a folder in Dropbox
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `path` | string | Yes | The path of the folder to list \(use "" for root\) |
|
||||
| `recursive` | boolean | No | If true, list contents recursively |
|
||||
| `includeDeleted` | boolean | No | If true, include deleted files/folders |
|
||||
| `includeMediaInfo` | boolean | No | If true, include media info for photos/videos |
|
||||
| `limit` | number | No | Maximum number of results to return \(default: 500\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `entries` | array | List of files and folders in the directory |
|
||||
|
||||
### `dropbox_create_folder`
|
||||
|
||||
Create a new folder in Dropbox
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `path` | string | Yes | The path where the folder should be created \(e.g., /new-folder\) |
|
||||
| `autorename` | boolean | No | If true, rename the folder if there is a conflict |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `folder` | object | The created folder metadata |
|
||||
|
||||
### `dropbox_delete`
|
||||
|
||||
Delete a file or folder in Dropbox (moves to trash)
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `path` | string | Yes | The path of the file or folder to delete |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `metadata` | object | Metadata of the deleted item |
|
||||
|
||||
### `dropbox_copy`
|
||||
|
||||
Copy a file or folder in Dropbox
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `fromPath` | string | Yes | The source path of the file or folder to copy |
|
||||
| `toPath` | string | Yes | The destination path for the copied file or folder |
|
||||
| `autorename` | boolean | No | If true, rename the file if there is a conflict at destination |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `metadata` | object | Metadata of the copied item |
|
||||
|
||||
### `dropbox_move`
|
||||
|
||||
Move or rename a file or folder in Dropbox
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `fromPath` | string | Yes | The source path of the file or folder to move |
|
||||
| `toPath` | string | Yes | The destination path for the moved file or folder |
|
||||
| `autorename` | boolean | No | If true, rename the file if there is a conflict at destination |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `metadata` | object | Metadata of the moved item |
|
||||
|
||||
### `dropbox_get_metadata`
|
||||
|
||||
Get metadata for a file or folder in Dropbox
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `path` | string | Yes | The path of the file or folder to get metadata for |
|
||||
| `includeMediaInfo` | boolean | No | If true, include media info for photos/videos |
|
||||
| `includeDeleted` | boolean | No | If true, include deleted files in results |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `metadata` | object | Metadata for the file or folder |
|
||||
|
||||
### `dropbox_create_shared_link`
|
||||
|
||||
Create a shareable link for a file or folder in Dropbox
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `path` | string | Yes | The path of the file or folder to share |
|
||||
| `requestedVisibility` | string | No | Visibility: public, team_only, or password |
|
||||
| `linkPassword` | string | No | Password for the shared link \(only if visibility is password\) |
|
||||
| `expires` | string | No | Expiration date in ISO 8601 format \(e.g., 2025-12-31T23:59:59Z\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `sharedLink` | object | The created shared link |
|
||||
|
||||
### `dropbox_search`
|
||||
|
||||
Search for files and folders in Dropbox
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `query` | string | Yes | The search query |
|
||||
| `path` | string | No | Limit search to a specific folder path |
|
||||
| `fileExtensions` | string | No | Comma-separated list of file extensions to filter by \(e.g., pdf,xlsx\) |
|
||||
| `maxResults` | number | No | Maximum number of results to return \(default: 100\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `matches` | array | Search results |
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- Category: `tools`
|
||||
- Type: `dropbox`
|
||||
370
apps/docs/content/docs/en/tools/elasticsearch.mdx
Normal file
370
apps/docs/content/docs/en/tools/elasticsearch.mdx
Normal file
@@ -0,0 +1,370 @@
|
||||
---
|
||||
title: Elasticsearch
|
||||
description: Search, index, and manage data in Elasticsearch
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="elasticsearch"
|
||||
color="#E0E0E0"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[Elasticsearch](https://www.elastic.co/elasticsearch/) is a powerful distributed search and analytics engine that enables you to index, search, and analyze large volumes of data in real time. It’s widely used for powering search features, log and event data analytics, observability, and more.
|
||||
|
||||
With Elasticsearch in Sim, you gain programmatic access to core Elasticsearch capabilities, including:
|
||||
|
||||
- **Search documents**: Perform advanced searches on structured or unstructured text using Query DSL, with support for sorting, pagination, and field selection.
|
||||
- **Index documents**: Add new documents or update existing ones in any Elasticsearch index for immediate retrieval and analysis.
|
||||
- **Get, update, or delete documents**: Retrieve, modify, or remove specific documents by ID.
|
||||
- **Bulk operations**: Execute multiple indexing or update actions in a single request for high-throughput data processing.
|
||||
- **Manage indexes**: Create, delete, or get details about indexes as part of your workflow automation.
|
||||
- **Cluster monitoring**: Check the health and stats of your Elasticsearch deployment.
|
||||
|
||||
Sim's Elasticsearch tools work with both self-hosted and Elastic Cloud environments. Integrate Elasticsearch into your agent workflows to automate data ingestion, search across vast datasets, run reporting, or build custom search-powered applications – all without manual intervention.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Elasticsearch into workflows for powerful search, indexing, and data management. Supports document CRUD operations, advanced search queries, bulk operations, index management, and cluster monitoring. Works with both self-hosted and Elastic Cloud deployments.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `elasticsearch_search`
|
||||
|
||||
Search documents in Elasticsearch using Query DSL. Returns matching documents with scores and metadata.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `deploymentType` | string | Yes | Deployment type: self_hosted or cloud |
|
||||
| `host` | string | No | Elasticsearch host URL \(for self-hosted\) |
|
||||
| `cloudId` | string | No | Elastic Cloud ID \(for cloud deployments\) |
|
||||
| `authMethod` | string | Yes | Authentication method: api_key or basic_auth |
|
||||
| `apiKey` | string | No | Elasticsearch API key |
|
||||
| `username` | string | No | Username for basic auth |
|
||||
| `password` | string | No | Password for basic auth |
|
||||
| `index` | string | Yes | Index name to search |
|
||||
| `query` | string | No | Query DSL as JSON string |
|
||||
| `from` | number | No | Starting offset for pagination \(default: 0\) |
|
||||
| `size` | number | No | Number of results to return \(default: 10\) |
|
||||
| `sort` | string | No | Sort specification as JSON string |
|
||||
| `sourceIncludes` | string | No | Comma-separated list of fields to include in _source |
|
||||
| `sourceExcludes` | string | No | Comma-separated list of fields to exclude from _source |
|
||||
| `trackTotalHits` | boolean | No | Track accurate total hit count \(default: true\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `took` | number | Time in milliseconds the search took |
|
||||
| `timed_out` | boolean | Whether the search timed out |
|
||||
| `hits` | object | Search results with total count and matching documents |
|
||||
| `aggregations` | json | Aggregation results if any |
|
||||
|
||||
### `elasticsearch_index_document`
|
||||
|
||||
Index (create or update) a document in Elasticsearch.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `deploymentType` | string | Yes | Deployment type: self_hosted or cloud |
|
||||
| `host` | string | No | Elasticsearch host URL \(for self-hosted\) |
|
||||
| `cloudId` | string | No | Elastic Cloud ID \(for cloud deployments\) |
|
||||
| `authMethod` | string | Yes | Authentication method: api_key or basic_auth |
|
||||
| `apiKey` | string | No | Elasticsearch API key |
|
||||
| `username` | string | No | Username for basic auth |
|
||||
| `password` | string | No | Password for basic auth |
|
||||
| `index` | string | Yes | Target index name |
|
||||
| `documentId` | string | No | Document ID \(auto-generated if not provided\) |
|
||||
| `document` | string | Yes | Document body as JSON string |
|
||||
| `refresh` | string | No | Refresh policy: true, false, or wait_for |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `_index` | string | Index where the document was stored |
|
||||
| `_id` | string | Document ID |
|
||||
| `_version` | number | Document version |
|
||||
| `result` | string | Operation result \(created or updated\) |
|
||||
|
||||
### `elasticsearch_get_document`
|
||||
|
||||
Retrieve a document by ID from Elasticsearch.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `deploymentType` | string | Yes | Deployment type: self_hosted or cloud |
|
||||
| `host` | string | No | Elasticsearch host URL \(for self-hosted\) |
|
||||
| `cloudId` | string | No | Elastic Cloud ID \(for cloud deployments\) |
|
||||
| `authMethod` | string | Yes | Authentication method: api_key or basic_auth |
|
||||
| `apiKey` | string | No | Elasticsearch API key |
|
||||
| `username` | string | No | Username for basic auth |
|
||||
| `password` | string | No | Password for basic auth |
|
||||
| `index` | string | Yes | Index name |
|
||||
| `documentId` | string | Yes | Document ID to retrieve |
|
||||
| `sourceIncludes` | string | No | Comma-separated list of fields to include |
|
||||
| `sourceExcludes` | string | No | Comma-separated list of fields to exclude |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `_index` | string | Index name |
|
||||
| `_id` | string | Document ID |
|
||||
| `_version` | number | Document version |
|
||||
| `found` | boolean | Whether the document was found |
|
||||
| `_source` | json | Document content |
|
||||
|
||||
### `elasticsearch_update_document`
|
||||
|
||||
Partially update a document in Elasticsearch using doc merge.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `deploymentType` | string | Yes | Deployment type: self_hosted or cloud |
|
||||
| `host` | string | No | Elasticsearch host URL \(for self-hosted\) |
|
||||
| `cloudId` | string | No | Elastic Cloud ID \(for cloud deployments\) |
|
||||
| `authMethod` | string | Yes | Authentication method: api_key or basic_auth |
|
||||
| `apiKey` | string | No | Elasticsearch API key |
|
||||
| `username` | string | No | Username for basic auth |
|
||||
| `password` | string | No | Password for basic auth |
|
||||
| `index` | string | Yes | Index name |
|
||||
| `documentId` | string | Yes | Document ID to update |
|
||||
| `document` | string | Yes | Partial document to merge as JSON string |
|
||||
| `retryOnConflict` | number | No | Number of retries on version conflict |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `_index` | string | Index name |
|
||||
| `_id` | string | Document ID |
|
||||
| `_version` | number | New document version |
|
||||
| `result` | string | Operation result \(updated or noop\) |
|
||||
|
||||
### `elasticsearch_delete_document`
|
||||
|
||||
Delete a document from Elasticsearch by ID.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `deploymentType` | string | Yes | Deployment type: self_hosted or cloud |
|
||||
| `host` | string | No | Elasticsearch host URL \(for self-hosted\) |
|
||||
| `cloudId` | string | No | Elastic Cloud ID \(for cloud deployments\) |
|
||||
| `authMethod` | string | Yes | Authentication method: api_key or basic_auth |
|
||||
| `apiKey` | string | No | Elasticsearch API key |
|
||||
| `username` | string | No | Username for basic auth |
|
||||
| `password` | string | No | Password for basic auth |
|
||||
| `index` | string | Yes | Index name |
|
||||
| `documentId` | string | Yes | Document ID to delete |
|
||||
| `refresh` | string | No | Refresh policy: true, false, or wait_for |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `_index` | string | Index name |
|
||||
| `_id` | string | Document ID |
|
||||
| `_version` | number | Document version |
|
||||
| `result` | string | Operation result \(deleted or not_found\) |
|
||||
|
||||
### `elasticsearch_bulk`
|
||||
|
||||
Perform multiple index, create, delete, or update operations in a single request for high performance.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `deploymentType` | string | Yes | Deployment type: self_hosted or cloud |
|
||||
| `host` | string | No | Elasticsearch host URL \(for self-hosted\) |
|
||||
| `cloudId` | string | No | Elastic Cloud ID \(for cloud deployments\) |
|
||||
| `authMethod` | string | Yes | Authentication method: api_key or basic_auth |
|
||||
| `apiKey` | string | No | Elasticsearch API key |
|
||||
| `username` | string | No | Username for basic auth |
|
||||
| `password` | string | No | Password for basic auth |
|
||||
| `index` | string | No | Default index for operations that do not specify one |
|
||||
| `operations` | string | Yes | Bulk operations as NDJSON string \(newline-delimited JSON\) |
|
||||
| `refresh` | string | No | Refresh policy: true, false, or wait_for |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `took` | number | Time in milliseconds the bulk operation took |
|
||||
| `errors` | boolean | Whether any operation had an error |
|
||||
| `items` | array | Results for each operation |
|
||||
|
||||
### `elasticsearch_count`
|
||||
|
||||
Count documents matching a query in Elasticsearch.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `deploymentType` | string | Yes | Deployment type: self_hosted or cloud |
|
||||
| `host` | string | No | Elasticsearch host URL \(for self-hosted\) |
|
||||
| `cloudId` | string | No | Elastic Cloud ID \(for cloud deployments\) |
|
||||
| `authMethod` | string | Yes | Authentication method: api_key or basic_auth |
|
||||
| `apiKey` | string | No | Elasticsearch API key |
|
||||
| `username` | string | No | Username for basic auth |
|
||||
| `password` | string | No | Password for basic auth |
|
||||
| `index` | string | Yes | Index name to count documents in |
|
||||
| `query` | string | No | Optional query to filter documents \(JSON string\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `count` | number | Number of documents matching the query |
|
||||
| `_shards` | object | Shard statistics |
|
||||
|
||||
### `elasticsearch_create_index`
|
||||
|
||||
Create a new index with optional settings and mappings.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `deploymentType` | string | Yes | Deployment type: self_hosted or cloud |
|
||||
| `host` | string | No | Elasticsearch host URL \(for self-hosted\) |
|
||||
| `cloudId` | string | No | Elastic Cloud ID \(for cloud deployments\) |
|
||||
| `authMethod` | string | Yes | Authentication method: api_key or basic_auth |
|
||||
| `apiKey` | string | No | Elasticsearch API key |
|
||||
| `username` | string | No | Username for basic auth |
|
||||
| `password` | string | No | Password for basic auth |
|
||||
| `index` | string | Yes | Index name to create |
|
||||
| `settings` | string | No | Index settings as JSON string |
|
||||
| `mappings` | string | No | Index mappings as JSON string |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `acknowledged` | boolean | Whether the request was acknowledged |
|
||||
| `shards_acknowledged` | boolean | Whether the shards were acknowledged |
|
||||
| `index` | string | Created index name |
|
||||
|
||||
### `elasticsearch_delete_index`
|
||||
|
||||
Delete an index and all its documents. This operation is irreversible.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `deploymentType` | string | Yes | Deployment type: self_hosted or cloud |
|
||||
| `host` | string | No | Elasticsearch host URL \(for self-hosted\) |
|
||||
| `cloudId` | string | No | Elastic Cloud ID \(for cloud deployments\) |
|
||||
| `authMethod` | string | Yes | Authentication method: api_key or basic_auth |
|
||||
| `apiKey` | string | No | Elasticsearch API key |
|
||||
| `username` | string | No | Username for basic auth |
|
||||
| `password` | string | No | Password for basic auth |
|
||||
| `index` | string | Yes | Index name to delete |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `acknowledged` | boolean | Whether the deletion was acknowledged |
|
||||
|
||||
### `elasticsearch_get_index`
|
||||
|
||||
Retrieve index information including settings, mappings, and aliases.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `deploymentType` | string | Yes | Deployment type: self_hosted or cloud |
|
||||
| `host` | string | No | Elasticsearch host URL \(for self-hosted\) |
|
||||
| `cloudId` | string | No | Elastic Cloud ID \(for cloud deployments\) |
|
||||
| `authMethod` | string | Yes | Authentication method: api_key or basic_auth |
|
||||
| `apiKey` | string | No | Elasticsearch API key |
|
||||
| `username` | string | No | Username for basic auth |
|
||||
| `password` | string | No | Password for basic auth |
|
||||
| `index` | string | Yes | Index name to retrieve info for |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `index` | json | Index information including aliases, mappings, and settings |
|
||||
|
||||
### `elasticsearch_cluster_health`
|
||||
|
||||
Get the health status of the Elasticsearch cluster.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `deploymentType` | string | Yes | Deployment type: self_hosted or cloud |
|
||||
| `host` | string | No | Elasticsearch host URL \(for self-hosted\) |
|
||||
| `cloudId` | string | No | Elastic Cloud ID \(for cloud deployments\) |
|
||||
| `authMethod` | string | Yes | Authentication method: api_key or basic_auth |
|
||||
| `apiKey` | string | No | Elasticsearch API key |
|
||||
| `username` | string | No | Username for basic auth |
|
||||
| `password` | string | No | Password for basic auth |
|
||||
| `waitForStatus` | string | No | Wait until cluster reaches this status: green, yellow, or red |
|
||||
| `timeout` | string | No | Timeout for the wait operation \(e.g., 30s, 1m\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `cluster_name` | string | Name of the cluster |
|
||||
| `status` | string | Cluster health status: green, yellow, or red |
|
||||
| `number_of_nodes` | number | Total number of nodes in the cluster |
|
||||
| `number_of_data_nodes` | number | Number of data nodes |
|
||||
| `active_shards` | number | Number of active shards |
|
||||
| `unassigned_shards` | number | Number of unassigned shards |
|
||||
|
||||
### `elasticsearch_cluster_stats`
|
||||
|
||||
Get comprehensive statistics about the Elasticsearch cluster.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `deploymentType` | string | Yes | Deployment type: self_hosted or cloud |
|
||||
| `host` | string | No | Elasticsearch host URL \(for self-hosted\) |
|
||||
| `cloudId` | string | No | Elastic Cloud ID \(for cloud deployments\) |
|
||||
| `authMethod` | string | Yes | Authentication method: api_key or basic_auth |
|
||||
| `apiKey` | string | No | Elasticsearch API key |
|
||||
| `username` | string | No | Username for basic auth |
|
||||
| `password` | string | No | Password for basic auth |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `cluster_name` | string | Name of the cluster |
|
||||
| `status` | string | Cluster health status |
|
||||
| `nodes` | object | Node statistics including count and versions |
|
||||
| `indices` | object | Index statistics including document count and store size |
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- Category: `tools`
|
||||
- Type: `elasticsearch`
|
||||
434
apps/docs/content/docs/en/tools/gitlab.mdx
Normal file
434
apps/docs/content/docs/en/tools/gitlab.mdx
Normal file
@@ -0,0 +1,434 @@
|
||||
---
|
||||
title: GitLab
|
||||
description: Interact with GitLab projects, issues, merge requests, and pipelines
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="gitlab"
|
||||
color="#E0E0E0"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[GitLab](https://gitlab.com/) is a comprehensive DevOps platform that allows teams to manage, collaborate on, and automate their software development lifecycle. With GitLab, you can effortlessly handle source code management, CI/CD, reviews, and collaboration in a single application.
|
||||
|
||||
With GitLab in Sim, you can:
|
||||
|
||||
- **Manage projects and repositories**: List and retrieve your GitLab projects, access details, and organize your repositories
|
||||
- **Work with issues**: List, create, and comment on issues to track work and collaborate effectively
|
||||
- **Handle merge requests**: Review, create, and manage merge requests for code changes and peer reviews
|
||||
- **Automate CI/CD pipelines**: Trigger, monitor, and interact with GitLab pipelines as part of your automation flows
|
||||
- **Collaborate with comments**: Add comments to issues or merge requests for efficient communication within your team
|
||||
|
||||
Using Sim’s GitLab integration, your agents can programmatically interact with your GitLab projects. Automate project management, issue tracking, code reviews, and pipeline operations seamlessly in your workflows, optimizing your software development process and enhancing collaboration across your team.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate GitLab into the workflow. Can manage projects, issues, merge requests, pipelines, and add comments. Supports all core GitLab DevOps operations.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `gitlab_list_projects`
|
||||
|
||||
List GitLab projects accessible to the authenticated user
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `owned` | boolean | No | Limit to projects owned by the current user |
|
||||
| `membership` | boolean | No | Limit to projects the current user is a member of |
|
||||
| `search` | string | No | Search projects by name |
|
||||
| `visibility` | string | No | Filter by visibility \(public, internal, private\) |
|
||||
| `orderBy` | string | No | Order by field \(id, name, path, created_at, updated_at, last_activity_at\) |
|
||||
| `sort` | string | No | Sort direction \(asc, desc\) |
|
||||
| `perPage` | number | No | Number of results per page \(default 20, max 100\) |
|
||||
| `page` | number | No | Page number for pagination |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `projects` | array | List of GitLab projects |
|
||||
| `total` | number | Total number of projects |
|
||||
|
||||
### `gitlab_get_project`
|
||||
|
||||
Get details of a specific GitLab project
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Project ID or URL-encoded path \(e.g., "namespace/project"\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `project` | object | The GitLab project details |
|
||||
|
||||
### `gitlab_list_issues`
|
||||
|
||||
List issues in a GitLab project
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Project ID or URL-encoded path |
|
||||
| `state` | string | No | Filter by state \(opened, closed, all\) |
|
||||
| `labels` | string | No | Comma-separated list of label names |
|
||||
| `assigneeId` | number | No | Filter by assignee user ID |
|
||||
| `milestoneTitle` | string | No | Filter by milestone title |
|
||||
| `search` | string | No | Search issues by title and description |
|
||||
| `orderBy` | string | No | Order by field \(created_at, updated_at\) |
|
||||
| `sort` | string | No | Sort direction \(asc, desc\) |
|
||||
| `perPage` | number | No | Number of results per page \(default 20, max 100\) |
|
||||
| `page` | number | No | Page number for pagination |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `issues` | array | List of GitLab issues |
|
||||
| `total` | number | Total number of issues |
|
||||
|
||||
### `gitlab_get_issue`
|
||||
|
||||
Get details of a specific GitLab issue
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Project ID or URL-encoded path |
|
||||
| `issueIid` | number | Yes | Issue number within the project \(the # shown in GitLab UI\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `issue` | object | The GitLab issue details |
|
||||
|
||||
### `gitlab_create_issue`
|
||||
|
||||
Create a new issue in a GitLab project
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Project ID or URL-encoded path |
|
||||
| `title` | string | Yes | Issue title |
|
||||
| `description` | string | No | Issue description \(Markdown supported\) |
|
||||
| `labels` | string | No | Comma-separated list of label names |
|
||||
| `assigneeIds` | array | No | Array of user IDs to assign |
|
||||
| `milestoneId` | number | No | Milestone ID to assign |
|
||||
| `dueDate` | string | No | Due date in YYYY-MM-DD format |
|
||||
| `confidential` | boolean | No | Whether the issue is confidential |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `issue` | object | The created GitLab issue |
|
||||
|
||||
### `gitlab_update_issue`
|
||||
|
||||
Update an existing issue in a GitLab project
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Project ID or URL-encoded path |
|
||||
| `issueIid` | number | Yes | Issue internal ID \(IID\) |
|
||||
| `title` | string | No | New issue title |
|
||||
| `description` | string | No | New issue description \(Markdown supported\) |
|
||||
| `stateEvent` | string | No | State event \(close or reopen\) |
|
||||
| `labels` | string | No | Comma-separated list of label names |
|
||||
| `assigneeIds` | array | No | Array of user IDs to assign |
|
||||
| `milestoneId` | number | No | Milestone ID to assign |
|
||||
| `dueDate` | string | No | Due date in YYYY-MM-DD format |
|
||||
| `confidential` | boolean | No | Whether the issue is confidential |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `issue` | object | The updated GitLab issue |
|
||||
|
||||
### `gitlab_delete_issue`
|
||||
|
||||
Delete an issue from a GitLab project
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Project ID or URL-encoded path |
|
||||
| `issueIid` | number | Yes | Issue internal ID \(IID\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the issue was deleted successfully |
|
||||
|
||||
### `gitlab_create_issue_note`
|
||||
|
||||
Add a comment to a GitLab issue
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Project ID or URL-encoded path |
|
||||
| `issueIid` | number | Yes | Issue internal ID \(IID\) |
|
||||
| `body` | string | Yes | Comment body \(Markdown supported\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `note` | object | The created comment |
|
||||
|
||||
### `gitlab_list_merge_requests`
|
||||
|
||||
List merge requests in a GitLab project
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Project ID or URL-encoded path |
|
||||
| `state` | string | No | Filter by state \(opened, closed, merged, all\) |
|
||||
| `labels` | string | No | Comma-separated list of label names |
|
||||
| `sourceBranch` | string | No | Filter by source branch |
|
||||
| `targetBranch` | string | No | Filter by target branch |
|
||||
| `orderBy` | string | No | Order by field \(created_at, updated_at\) |
|
||||
| `sort` | string | No | Sort direction \(asc, desc\) |
|
||||
| `perPage` | number | No | Number of results per page \(default 20, max 100\) |
|
||||
| `page` | number | No | Page number for pagination |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `mergeRequests` | array | List of GitLab merge requests |
|
||||
| `total` | number | Total number of merge requests |
|
||||
|
||||
### `gitlab_get_merge_request`
|
||||
|
||||
Get details of a specific GitLab merge request
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Project ID or URL-encoded path |
|
||||
| `mergeRequestIid` | number | Yes | Merge request internal ID \(IID\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `mergeRequest` | object | The GitLab merge request details |
|
||||
|
||||
### `gitlab_create_merge_request`
|
||||
|
||||
Create a new merge request in a GitLab project
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Project ID or URL-encoded path |
|
||||
| `sourceBranch` | string | Yes | Source branch name |
|
||||
| `targetBranch` | string | Yes | Target branch name |
|
||||
| `title` | string | Yes | Merge request title |
|
||||
| `description` | string | No | Merge request description \(Markdown supported\) |
|
||||
| `labels` | string | No | Comma-separated list of label names |
|
||||
| `assigneeIds` | array | No | Array of user IDs to assign |
|
||||
| `milestoneId` | number | No | Milestone ID to assign |
|
||||
| `removeSourceBranch` | boolean | No | Delete source branch after merge |
|
||||
| `squash` | boolean | No | Squash commits on merge |
|
||||
| `draft` | boolean | No | Mark as draft \(work in progress\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `mergeRequest` | object | The created GitLab merge request |
|
||||
|
||||
### `gitlab_update_merge_request`
|
||||
|
||||
Update an existing merge request in a GitLab project
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Project ID or URL-encoded path |
|
||||
| `mergeRequestIid` | number | Yes | Merge request internal ID \(IID\) |
|
||||
| `title` | string | No | New merge request title |
|
||||
| `description` | string | No | New merge request description |
|
||||
| `stateEvent` | string | No | State event \(close or reopen\) |
|
||||
| `labels` | string | No | Comma-separated list of label names |
|
||||
| `assigneeIds` | array | No | Array of user IDs to assign |
|
||||
| `milestoneId` | number | No | Milestone ID to assign |
|
||||
| `targetBranch` | string | No | New target branch |
|
||||
| `removeSourceBranch` | boolean | No | Delete source branch after merge |
|
||||
| `squash` | boolean | No | Squash commits on merge |
|
||||
| `draft` | boolean | No | Mark as draft \(work in progress\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `mergeRequest` | object | The updated GitLab merge request |
|
||||
|
||||
### `gitlab_merge_merge_request`
|
||||
|
||||
Merge a merge request in a GitLab project
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Project ID or URL-encoded path |
|
||||
| `mergeRequestIid` | number | Yes | Merge request internal ID \(IID\) |
|
||||
| `mergeCommitMessage` | string | No | Custom merge commit message |
|
||||
| `squashCommitMessage` | string | No | Custom squash commit message |
|
||||
| `squash` | boolean | No | Squash commits before merging |
|
||||
| `shouldRemoveSourceBranch` | boolean | No | Delete source branch after merge |
|
||||
| `mergeWhenPipelineSucceeds` | boolean | No | Merge when pipeline succeeds |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `mergeRequest` | object | The merged GitLab merge request |
|
||||
|
||||
### `gitlab_create_merge_request_note`
|
||||
|
||||
Add a comment to a GitLab merge request
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Project ID or URL-encoded path |
|
||||
| `mergeRequestIid` | number | Yes | Merge request internal ID \(IID\) |
|
||||
| `body` | string | Yes | Comment body \(Markdown supported\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `note` | object | The created comment |
|
||||
|
||||
### `gitlab_list_pipelines`
|
||||
|
||||
List pipelines in a GitLab project
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Project ID or URL-encoded path |
|
||||
| `ref` | string | No | Filter by ref \(branch or tag\) |
|
||||
| `status` | string | No | Filter by status \(created, waiting_for_resource, preparing, pending, running, success, failed, canceled, skipped, manual, scheduled\) |
|
||||
| `orderBy` | string | No | Order by field \(id, status, ref, updated_at, user_id\) |
|
||||
| `sort` | string | No | Sort direction \(asc, desc\) |
|
||||
| `perPage` | number | No | Number of results per page \(default 20, max 100\) |
|
||||
| `page` | number | No | Page number for pagination |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `pipelines` | array | List of GitLab pipelines |
|
||||
| `total` | number | Total number of pipelines |
|
||||
|
||||
### `gitlab_get_pipeline`
|
||||
|
||||
Get details of a specific GitLab pipeline
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Project ID or URL-encoded path |
|
||||
| `pipelineId` | number | Yes | Pipeline ID |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `pipeline` | object | The GitLab pipeline details |
|
||||
|
||||
### `gitlab_create_pipeline`
|
||||
|
||||
Trigger a new pipeline in a GitLab project
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Project ID or URL-encoded path |
|
||||
| `ref` | string | Yes | Branch or tag to run the pipeline on |
|
||||
| `variables` | array | No | Array of variables for the pipeline \(each with key, value, and optional variable_type\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `pipeline` | object | The created GitLab pipeline |
|
||||
|
||||
### `gitlab_retry_pipeline`
|
||||
|
||||
Retry a failed GitLab pipeline
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Project ID or URL-encoded path |
|
||||
| `pipelineId` | number | Yes | Pipeline ID |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `pipeline` | object | The retried GitLab pipeline |
|
||||
|
||||
### `gitlab_cancel_pipeline`
|
||||
|
||||
Cancel a running GitLab pipeline
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `projectId` | string | Yes | Project ID or URL-encoded path |
|
||||
| `pipelineId` | number | Yes | Pipeline ID |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `pipeline` | object | The cancelled GitLab pipeline |
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- Category: `tools`
|
||||
- Type: `gitlab`
|
||||
499
apps/docs/content/docs/en/tools/grafana.mdx
Normal file
499
apps/docs/content/docs/en/tools/grafana.mdx
Normal file
@@ -0,0 +1,499 @@
|
||||
---
|
||||
title: Grafana
|
||||
description: Interact with Grafana dashboards, alerts, and annotations
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="grafana"
|
||||
color="#E0E0E0"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[Grafana](https://grafana.com/) is a leading open-source platform for monitoring, observability, and visualization. It allows users to query, visualize, alert on, and analyze data from a variety of sources, making it an essential tool for infrastructure and application monitoring.
|
||||
|
||||
With Grafana, you can:
|
||||
|
||||
- **Visualize data**: Build and customize dashboards to display metrics, logs, and traces in real time
|
||||
- **Monitor health and status**: Check the health of your Grafana instance and connected data sources
|
||||
- **Manage alerts and annotations**: Set up alert rules, manage notifications, and annotate dashboards with important events
|
||||
- **Organize content**: Organize dashboards and data sources into folders for better access management
|
||||
|
||||
In Sim, the Grafana integration empowers your agents to interact directly with your Grafana instance via API, enabling actions such as:
|
||||
|
||||
- Checking the Grafana server, database, and data source health status
|
||||
- Retrieving, listing, and managing dashboards, alert rules, annotations, data sources, and folders
|
||||
- Automating the monitoring of your infrastructure by integrating Grafana data and alerts into your workflow automations
|
||||
|
||||
These capabilities enable Sim agents to monitor systems, proactively respond to alerts, and help ensure the reliability and visibility of your services — all as part of your automated workflows.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Grafana into workflows. Manage dashboards, alerts, annotations, data sources, folders, and monitor health status.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `grafana_get_dashboard`
|
||||
|
||||
Get a dashboard by its UID
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Grafana Service Account Token |
|
||||
| `baseUrl` | string | Yes | Grafana instance URL \(e.g., https://your-grafana.com\) |
|
||||
| `organizationId` | string | No | Organization ID for multi-org Grafana instances |
|
||||
| `dashboardUid` | string | Yes | The UID of the dashboard to retrieve |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `dashboard` | json | The full dashboard JSON object |
|
||||
| `meta` | json | Dashboard metadata \(version, permissions, etc.\) |
|
||||
|
||||
### `grafana_list_dashboards`
|
||||
|
||||
Search and list all dashboards
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Grafana Service Account Token |
|
||||
| `baseUrl` | string | Yes | Grafana instance URL \(e.g., https://your-grafana.com\) |
|
||||
| `organizationId` | string | No | Organization ID for multi-org Grafana instances |
|
||||
| `query` | string | No | Search query to filter dashboards by title |
|
||||
| `tag` | string | No | Filter by tag \(comma-separated for multiple tags\) |
|
||||
| `folderIds` | string | No | Filter by folder IDs \(comma-separated\) |
|
||||
| `starred` | boolean | No | Only return starred dashboards |
|
||||
| `limit` | number | No | Maximum number of dashboards to return |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `dashboards` | array | List of dashboard search results |
|
||||
|
||||
### `grafana_create_dashboard`
|
||||
|
||||
Create a new dashboard
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Grafana Service Account Token |
|
||||
| `baseUrl` | string | Yes | Grafana instance URL \(e.g., https://your-grafana.com\) |
|
||||
| `organizationId` | string | No | Organization ID for multi-org Grafana instances |
|
||||
| `title` | string | Yes | The title of the new dashboard |
|
||||
| `folderUid` | string | No | The UID of the folder to create the dashboard in |
|
||||
| `tags` | string | No | Comma-separated list of tags |
|
||||
| `timezone` | string | No | Dashboard timezone \(e.g., browser, utc\) |
|
||||
| `refresh` | string | No | Auto-refresh interval \(e.g., 5s, 1m, 5m\) |
|
||||
| `panels` | string | No | JSON array of panel configurations |
|
||||
| `overwrite` | boolean | No | Overwrite existing dashboard with same title |
|
||||
| `message` | string | No | Commit message for the dashboard version |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | number | The numeric ID of the created dashboard |
|
||||
| `uid` | string | The UID of the created dashboard |
|
||||
| `url` | string | The URL path to the dashboard |
|
||||
| `status` | string | Status of the operation \(success\) |
|
||||
| `version` | number | The version number of the dashboard |
|
||||
| `slug` | string | URL-friendly slug of the dashboard |
|
||||
|
||||
### `grafana_update_dashboard`
|
||||
|
||||
Update an existing dashboard. Fetches the current dashboard and merges your changes.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Grafana Service Account Token |
|
||||
| `baseUrl` | string | Yes | Grafana instance URL \(e.g., https://your-grafana.com\) |
|
||||
| `organizationId` | string | No | Organization ID for multi-org Grafana instances |
|
||||
| `dashboardUid` | string | Yes | The UID of the dashboard to update |
|
||||
| `title` | string | No | New title for the dashboard |
|
||||
| `folderUid` | string | No | New folder UID to move the dashboard to |
|
||||
| `tags` | string | No | Comma-separated list of new tags |
|
||||
| `timezone` | string | No | Dashboard timezone \(e.g., browser, utc\) |
|
||||
| `refresh` | string | No | Auto-refresh interval \(e.g., 5s, 1m, 5m\) |
|
||||
| `panels` | string | No | JSON array of panel configurations |
|
||||
| `overwrite` | boolean | No | Overwrite even if there is a version conflict |
|
||||
| `message` | string | No | Commit message for this version |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | number | The numeric ID of the updated dashboard |
|
||||
| `uid` | string | The UID of the updated dashboard |
|
||||
| `url` | string | The URL path to the dashboard |
|
||||
| `status` | string | Status of the operation \(success\) |
|
||||
| `version` | number | The new version number of the dashboard |
|
||||
| `slug` | string | URL-friendly slug of the dashboard |
|
||||
|
||||
### `grafana_delete_dashboard`
|
||||
|
||||
Delete a dashboard by its UID
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Grafana Service Account Token |
|
||||
| `baseUrl` | string | Yes | Grafana instance URL \(e.g., https://your-grafana.com\) |
|
||||
| `organizationId` | string | No | Organization ID for multi-org Grafana instances |
|
||||
| `dashboardUid` | string | Yes | The UID of the dashboard to delete |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `title` | string | The title of the deleted dashboard |
|
||||
| `message` | string | Confirmation message |
|
||||
| `id` | number | The ID of the deleted dashboard |
|
||||
|
||||
### `grafana_list_alert_rules`
|
||||
|
||||
List all alert rules in the Grafana instance
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Grafana Service Account Token |
|
||||
| `baseUrl` | string | Yes | Grafana instance URL \(e.g., https://your-grafana.com\) |
|
||||
| `organizationId` | string | No | Organization ID for multi-org Grafana instances |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `rules` | array | List of alert rules |
|
||||
|
||||
### `grafana_get_alert_rule`
|
||||
|
||||
Get a specific alert rule by its UID
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Grafana Service Account Token |
|
||||
| `baseUrl` | string | Yes | Grafana instance URL \(e.g., https://your-grafana.com\) |
|
||||
| `organizationId` | string | No | Organization ID for multi-org Grafana instances |
|
||||
| `alertRuleUid` | string | Yes | The UID of the alert rule to retrieve |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `uid` | string | Alert rule UID |
|
||||
| `title` | string | Alert rule title |
|
||||
| `condition` | string | Alert condition |
|
||||
| `data` | json | Alert rule query data |
|
||||
| `folderUID` | string | Parent folder UID |
|
||||
| `ruleGroup` | string | Rule group name |
|
||||
| `noDataState` | string | State when no data is returned |
|
||||
| `execErrState` | string | State on execution error |
|
||||
| `annotations` | json | Alert annotations |
|
||||
| `labels` | json | Alert labels |
|
||||
|
||||
### `grafana_create_alert_rule`
|
||||
|
||||
Create a new alert rule
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Grafana Service Account Token |
|
||||
| `baseUrl` | string | Yes | Grafana instance URL \(e.g., https://your-grafana.com\) |
|
||||
| `organizationId` | string | No | Organization ID for multi-org Grafana instances |
|
||||
| `title` | string | Yes | The title of the alert rule |
|
||||
| `folderUid` | string | Yes | The UID of the folder to create the alert in |
|
||||
| `ruleGroup` | string | Yes | The name of the rule group |
|
||||
| `condition` | string | Yes | The refId of the query or expression to use as the alert condition |
|
||||
| `data` | string | Yes | JSON array of query/expression data objects |
|
||||
| `forDuration` | string | No | Duration to wait before firing \(e.g., 5m, 1h\) |
|
||||
| `noDataState` | string | No | State when no data is returned \(NoData, Alerting, OK\) |
|
||||
| `execErrState` | string | No | State on execution error \(Alerting, OK\) |
|
||||
| `annotations` | string | No | JSON object of annotations |
|
||||
| `labels` | string | No | JSON object of labels |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `uid` | string | The UID of the created alert rule |
|
||||
| `title` | string | Alert rule title |
|
||||
| `folderUID` | string | Parent folder UID |
|
||||
| `ruleGroup` | string | Rule group name |
|
||||
|
||||
### `grafana_update_alert_rule`
|
||||
|
||||
Update an existing alert rule. Fetches the current rule and merges your changes.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Grafana Service Account Token |
|
||||
| `baseUrl` | string | Yes | Grafana instance URL \(e.g., https://your-grafana.com\) |
|
||||
| `organizationId` | string | No | Organization ID for multi-org Grafana instances |
|
||||
| `alertRuleUid` | string | Yes | The UID of the alert rule to update |
|
||||
| `title` | string | No | New title for the alert rule |
|
||||
| `folderUid` | string | No | New folder UID to move the alert to |
|
||||
| `ruleGroup` | string | No | New rule group name |
|
||||
| `condition` | string | No | New condition refId |
|
||||
| `data` | string | No | New JSON array of query/expression data objects |
|
||||
| `forDuration` | string | No | Duration to wait before firing \(e.g., 5m, 1h\) |
|
||||
| `noDataState` | string | No | State when no data is returned \(NoData, Alerting, OK\) |
|
||||
| `execErrState` | string | No | State on execution error \(Alerting, OK\) |
|
||||
| `annotations` | string | No | JSON object of annotations |
|
||||
| `labels` | string | No | JSON object of labels |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `uid` | string | The UID of the updated alert rule |
|
||||
| `title` | string | Alert rule title |
|
||||
| `folderUID` | string | Parent folder UID |
|
||||
| `ruleGroup` | string | Rule group name |
|
||||
|
||||
### `grafana_delete_alert_rule`
|
||||
|
||||
Delete an alert rule by its UID
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Grafana Service Account Token |
|
||||
| `baseUrl` | string | Yes | Grafana instance URL \(e.g., https://your-grafana.com\) |
|
||||
| `organizationId` | string | No | Organization ID for multi-org Grafana instances |
|
||||
| `alertRuleUid` | string | Yes | The UID of the alert rule to delete |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Confirmation message |
|
||||
|
||||
### `grafana_list_contact_points`
|
||||
|
||||
List all alert notification contact points
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Grafana Service Account Token |
|
||||
| `baseUrl` | string | Yes | Grafana instance URL \(e.g., https://your-grafana.com\) |
|
||||
| `organizationId` | string | No | Organization ID for multi-org Grafana instances |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `contactPoints` | array | List of contact points |
|
||||
|
||||
### `grafana_create_annotation`
|
||||
|
||||
Create an annotation on a dashboard or as a global annotation
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Grafana Service Account Token |
|
||||
| `baseUrl` | string | Yes | Grafana instance URL \(e.g., https://your-grafana.com\) |
|
||||
| `organizationId` | string | No | Organization ID for multi-org Grafana instances |
|
||||
| `text` | string | Yes | The text content of the annotation |
|
||||
| `tags` | string | No | Comma-separated list of tags |
|
||||
| `dashboardUid` | string | No | UID of the dashboard to add the annotation to \(optional for global annotations\) |
|
||||
| `panelId` | number | No | ID of the panel to add the annotation to |
|
||||
| `time` | number | No | Start time in epoch milliseconds \(defaults to now\) |
|
||||
| `timeEnd` | number | No | End time in epoch milliseconds \(for range annotations\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | number | The ID of the created annotation |
|
||||
| `message` | string | Confirmation message |
|
||||
|
||||
### `grafana_list_annotations`
|
||||
|
||||
Query annotations by time range, dashboard, or tags
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Grafana Service Account Token |
|
||||
| `baseUrl` | string | Yes | Grafana instance URL \(e.g., https://your-grafana.com\) |
|
||||
| `organizationId` | string | No | Organization ID for multi-org Grafana instances |
|
||||
| `from` | number | No | Start time in epoch milliseconds |
|
||||
| `to` | number | No | End time in epoch milliseconds |
|
||||
| `dashboardUid` | string | No | Filter by dashboard UID |
|
||||
| `panelId` | number | No | Filter by panel ID |
|
||||
| `tags` | string | No | Comma-separated list of tags to filter by |
|
||||
| `type` | string | No | Filter by type \(alert or annotation\) |
|
||||
| `limit` | number | No | Maximum number of annotations to return |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `annotations` | array | List of annotations |
|
||||
|
||||
### `grafana_update_annotation`
|
||||
|
||||
Update an existing annotation
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Grafana Service Account Token |
|
||||
| `baseUrl` | string | Yes | Grafana instance URL \(e.g., https://your-grafana.com\) |
|
||||
| `organizationId` | string | No | Organization ID for multi-org Grafana instances |
|
||||
| `annotationId` | number | Yes | The ID of the annotation to update |
|
||||
| `text` | string | Yes | New text content for the annotation |
|
||||
| `tags` | string | No | Comma-separated list of new tags |
|
||||
| `time` | number | No | New start time in epoch milliseconds |
|
||||
| `timeEnd` | number | No | New end time in epoch milliseconds |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | number | The ID of the updated annotation |
|
||||
| `message` | string | Confirmation message |
|
||||
|
||||
### `grafana_delete_annotation`
|
||||
|
||||
Delete an annotation by its ID
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Grafana Service Account Token |
|
||||
| `baseUrl` | string | Yes | Grafana instance URL \(e.g., https://your-grafana.com\) |
|
||||
| `organizationId` | string | No | Organization ID for multi-org Grafana instances |
|
||||
| `annotationId` | number | Yes | The ID of the annotation to delete |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Confirmation message |
|
||||
|
||||
### `grafana_list_data_sources`
|
||||
|
||||
List all data sources configured in Grafana
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Grafana Service Account Token |
|
||||
| `baseUrl` | string | Yes | Grafana instance URL \(e.g., https://your-grafana.com\) |
|
||||
| `organizationId` | string | No | Organization ID for multi-org Grafana instances |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `dataSources` | array | List of data sources |
|
||||
|
||||
### `grafana_get_data_source`
|
||||
|
||||
Get a data source by its ID or UID
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Grafana Service Account Token |
|
||||
| `baseUrl` | string | Yes | Grafana instance URL \(e.g., https://your-grafana.com\) |
|
||||
| `organizationId` | string | No | Organization ID for multi-org Grafana instances |
|
||||
| `dataSourceId` | string | Yes | The ID or UID of the data source to retrieve |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | number | Data source ID |
|
||||
| `uid` | string | Data source UID |
|
||||
| `name` | string | Data source name |
|
||||
| `type` | string | Data source type |
|
||||
| `url` | string | Data source connection URL |
|
||||
| `database` | string | Database name \(if applicable\) |
|
||||
| `isDefault` | boolean | Whether this is the default data source |
|
||||
| `jsonData` | json | Additional data source configuration |
|
||||
|
||||
### `grafana_list_folders`
|
||||
|
||||
List all folders in Grafana
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Grafana Service Account Token |
|
||||
| `baseUrl` | string | Yes | Grafana instance URL \(e.g., https://your-grafana.com\) |
|
||||
| `organizationId` | string | No | Organization ID for multi-org Grafana instances |
|
||||
| `limit` | number | No | Maximum number of folders to return |
|
||||
| `page` | number | No | Page number for pagination |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `folders` | array | List of folders |
|
||||
|
||||
### `grafana_create_folder`
|
||||
|
||||
Create a new folder in Grafana
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Grafana Service Account Token |
|
||||
| `baseUrl` | string | Yes | Grafana instance URL \(e.g., https://your-grafana.com\) |
|
||||
| `organizationId` | string | No | Organization ID for multi-org Grafana instances |
|
||||
| `title` | string | Yes | The title of the new folder |
|
||||
| `uid` | string | No | Optional UID for the folder \(auto-generated if not provided\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | number | The numeric ID of the created folder |
|
||||
| `uid` | string | The UID of the created folder |
|
||||
| `title` | string | The title of the created folder |
|
||||
| `url` | string | The URL path to the folder |
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- Category: `tools`
|
||||
- Type: `grafana`
|
||||
300
apps/docs/content/docs/en/tools/kalshi.mdx
Normal file
300
apps/docs/content/docs/en/tools/kalshi.mdx
Normal file
@@ -0,0 +1,300 @@
|
||||
---
|
||||
title: Kalshi
|
||||
description: Access prediction markets data from Kalshi
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="kalshi"
|
||||
color="#09C285"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[Kalshi](https://kalshi.com) is a federally regulated exchange where users can trade directly on the outcomes of future events—prediction markets. Kalshi’s robust API and Sim integration enable agents and workflows to programmatically access all aspects of the platform, supporting everything from research and analytics to automated trading and monitoring.
|
||||
|
||||
With Kalshi’s integration in Sim, you can:
|
||||
|
||||
- **Market & Event Data:** Search, filter, and retrieve real-time and historical data for markets and events; fetch granular details on market status, series, event groupings, and more.
|
||||
- **Account & Balance Management:** Access account balances, available funds, and monitor real-time open positions.
|
||||
- **Order & Trade Management:** Place new orders, cancel existing ones, view open orders, retrieve a live orderbook, and access complete trade histories.
|
||||
- **Execution Analysis:** Fetch recent trades, historical fills, and candlestick data for backtesting or market structure research.
|
||||
- **Monitoring:** Check exchange-wide or series-level status, receive real-time updates about market changes or trading halts, and automate responses.
|
||||
- **Automation Ready:** Build end-to-end automated agents and dashboards that consume, analyze, and trade on real-world event probabilities.
|
||||
|
||||
By using these unified tools and endpoints, you can seamlessly incorporate Kalshi’s prediction markets, live trading capabilities, and deep event data into your AI-powered applications, dashboards, and workflows—enabling sophisticated, automated decision-making tied to real-world outcomes.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Kalshi prediction markets into the workflow. Can get markets, market, events, event, balance, positions, orders, orderbook, trades, candlesticks, fills, series, and exchange status.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `kalshi_get_markets`
|
||||
|
||||
Retrieve a list of prediction markets from Kalshi with optional filtering
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `status` | string | No | Filter by status \(unopened, open, closed, settled\) |
|
||||
| `seriesTicker` | string | No | Filter by series ticker |
|
||||
| `eventTicker` | string | No | Filter by event ticker |
|
||||
| `limit` | string | No | Number of results \(1-1000, default: 100\) |
|
||||
| `cursor` | string | No | Pagination cursor for next page |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Markets data and metadata |
|
||||
|
||||
### `kalshi_get_market`
|
||||
|
||||
Retrieve details of a specific prediction market by ticker
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `ticker` | string | Yes | The market ticker \(e.g., "KXBTC-24DEC31"\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Market data and metadata |
|
||||
|
||||
### `kalshi_get_events`
|
||||
|
||||
Retrieve a list of events from Kalshi with optional filtering
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `status` | string | No | Filter by status \(open, closed, settled\) |
|
||||
| `seriesTicker` | string | No | Filter by series ticker |
|
||||
| `withNestedMarkets` | string | No | Include nested markets in response \(true/false\) |
|
||||
| `limit` | string | No | Number of results \(1-200, default: 200\) |
|
||||
| `cursor` | string | No | Pagination cursor for next page |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Events data and metadata |
|
||||
|
||||
### `kalshi_get_event`
|
||||
|
||||
Retrieve details of a specific event by ticker
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `eventTicker` | string | Yes | The event ticker |
|
||||
| `withNestedMarkets` | string | No | Include nested markets in response \(true/false\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Event data and metadata |
|
||||
|
||||
### `kalshi_get_balance`
|
||||
|
||||
Retrieve your account balance and portfolio value from Kalshi
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `keyId` | string | Yes | Your Kalshi API Key ID |
|
||||
| `privateKey` | string | Yes | Your RSA Private Key \(PEM format\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Balance data and metadata |
|
||||
|
||||
### `kalshi_get_positions`
|
||||
|
||||
Retrieve your open positions from Kalshi
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `keyId` | string | Yes | Your Kalshi API Key ID |
|
||||
| `privateKey` | string | Yes | Your RSA Private Key \(PEM format\) |
|
||||
| `ticker` | string | No | Filter by market ticker |
|
||||
| `eventTicker` | string | No | Filter by event ticker \(max 10 comma-separated\) |
|
||||
| `settlementStatus` | string | No | Filter by settlement status \(all, unsettled, settled\). Default: unsettled |
|
||||
| `limit` | string | No | Number of results \(1-1000, default: 100\) |
|
||||
| `cursor` | string | No | Pagination cursor for next page |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Positions data and metadata |
|
||||
|
||||
### `kalshi_get_orders`
|
||||
|
||||
Retrieve your orders from Kalshi with optional filtering
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `keyId` | string | Yes | Your Kalshi API Key ID |
|
||||
| `privateKey` | string | Yes | Your RSA Private Key \(PEM format\) |
|
||||
| `ticker` | string | No | Filter by market ticker |
|
||||
| `eventTicker` | string | No | Filter by event ticker \(max 10 comma-separated\) |
|
||||
| `status` | string | No | Filter by status \(resting, canceled, executed\) |
|
||||
| `limit` | string | No | Number of results \(1-200, default: 100\) |
|
||||
| `cursor` | string | No | Pagination cursor for next page |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Orders data and metadata |
|
||||
|
||||
### `kalshi_get_orderbook`
|
||||
|
||||
Retrieve the orderbook (bids and asks) for a specific market
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `ticker` | string | Yes | Market ticker \(e.g., KXBTC-24DEC31\) |
|
||||
| `depth` | number | No | Number of price levels to return per side |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Orderbook data and metadata |
|
||||
|
||||
### `kalshi_get_trades`
|
||||
|
||||
Retrieve recent trades across all markets or for a specific market
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `ticker` | string | No | Filter by market ticker |
|
||||
| `minTs` | number | No | Minimum timestamp \(Unix milliseconds\) |
|
||||
| `maxTs` | number | No | Maximum timestamp \(Unix milliseconds\) |
|
||||
| `limit` | string | No | Number of results \(1-1000, default: 100\) |
|
||||
| `cursor` | string | No | Pagination cursor for next page |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Trades data and metadata |
|
||||
|
||||
### `kalshi_get_candlesticks`
|
||||
|
||||
Retrieve OHLC candlestick data for a specific market
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `seriesTicker` | string | Yes | Series ticker |
|
||||
| `ticker` | string | Yes | Market ticker \(e.g., KXBTC-24DEC31\) |
|
||||
| `startTs` | number | No | Start timestamp \(Unix milliseconds\) |
|
||||
| `endTs` | number | No | End timestamp \(Unix milliseconds\) |
|
||||
| `periodInterval` | number | No | Period interval: 1 \(1min\), 60 \(1hour\), or 1440 \(1day\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Candlestick data and metadata |
|
||||
|
||||
### `kalshi_get_fills`
|
||||
|
||||
Retrieve your portfolio
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `keyId` | string | Yes | Your Kalshi API Key ID |
|
||||
| `privateKey` | string | Yes | Your RSA Private Key \(PEM format\) |
|
||||
| `ticker` | string | No | Filter by market ticker |
|
||||
| `orderId` | string | No | Filter by order ID |
|
||||
| `minTs` | number | No | Minimum timestamp \(Unix milliseconds\) |
|
||||
| `maxTs` | number | No | Maximum timestamp \(Unix milliseconds\) |
|
||||
| `limit` | string | No | Number of results \(1-1000, default: 100\) |
|
||||
| `cursor` | string | No | Pagination cursor for next page |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Fills data and metadata |
|
||||
|
||||
### `kalshi_get_series_by_ticker`
|
||||
|
||||
Retrieve details of a specific market series by ticker
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `seriesTicker` | string | Yes | Series ticker |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Series data and metadata |
|
||||
|
||||
### `kalshi_get_exchange_status`
|
||||
|
||||
Retrieve the current status of the Kalshi exchange (trading and exchange activity)
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Exchange status data and metadata |
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- Category: `tools`
|
||||
- Type: `kalshi`
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"pages": [
|
||||
"index",
|
||||
"ahrefs",
|
||||
"airtable",
|
||||
"apify",
|
||||
"apollo",
|
||||
@@ -10,13 +11,17 @@
|
||||
"calendly",
|
||||
"clay",
|
||||
"confluence",
|
||||
"datadog",
|
||||
"discord",
|
||||
"dropbox",
|
||||
"dynamodb",
|
||||
"elasticsearch",
|
||||
"elevenlabs",
|
||||
"exa",
|
||||
"file",
|
||||
"firecrawl",
|
||||
"github",
|
||||
"gitlab",
|
||||
"gmail",
|
||||
"google_calendar",
|
||||
"google_docs",
|
||||
@@ -25,6 +30,7 @@
|
||||
"google_search",
|
||||
"google_sheets",
|
||||
"google_vault",
|
||||
"grafana",
|
||||
"hubspot",
|
||||
"huggingface",
|
||||
"hunter",
|
||||
@@ -33,6 +39,7 @@
|
||||
"intercom",
|
||||
"jina",
|
||||
"jira",
|
||||
"kalshi",
|
||||
"knowledge",
|
||||
"linear",
|
||||
"linkedin",
|
||||
@@ -56,6 +63,7 @@
|
||||
"perplexity",
|
||||
"pinecone",
|
||||
"pipedrive",
|
||||
"polymarket",
|
||||
"postgresql",
|
||||
"posthog",
|
||||
"pylon",
|
||||
@@ -70,8 +78,10 @@
|
||||
"sentry",
|
||||
"serper",
|
||||
"sharepoint",
|
||||
"shopify",
|
||||
"slack",
|
||||
"smtp",
|
||||
"ssh",
|
||||
"stagehand",
|
||||
"stagehand_agent",
|
||||
"stripe",
|
||||
@@ -92,9 +102,11 @@
|
||||
"webflow",
|
||||
"whatsapp",
|
||||
"wikipedia",
|
||||
"wordpress",
|
||||
"x",
|
||||
"youtube",
|
||||
"zendesk",
|
||||
"zep"
|
||||
"zep",
|
||||
"zoom"
|
||||
]
|
||||
}
|
||||
|
||||
357
apps/docs/content/docs/en/tools/polymarket.mdx
Normal file
357
apps/docs/content/docs/en/tools/polymarket.mdx
Normal file
@@ -0,0 +1,357 @@
|
||||
---
|
||||
title: Polymarket
|
||||
description: Access prediction markets data from Polymarket
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="polymarket"
|
||||
color="#4C82FB"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[Polymarket](https://polymarket.com) is a decentralized prediction markets platform where users can trade on the outcome of future events using blockchain technology. Polymarket provides a comprehensive API, enabling developers and agents to access live market data, event listings, price information, and orderbook statistics to power data-driven workflows and AI automations.
|
||||
|
||||
With Polymarket’s API and Sim integration, you can enable agents to programmatically retrieve prediction market information, explore open markets and associated events, analyze historical price data, and access orderbooks and market midpoints. This creates new possibilities for research, automated analysis, and developing intelligent agents that react to real-time event probabilities derived from market prices.
|
||||
|
||||
Key features of the Polymarket integration include:
|
||||
|
||||
- **Market Listing & Filtering:** List all current or historical prediction markets, filter by tag, sort, and paginate through results.
|
||||
- **Market Detail:** Retrieve details for a single market by market ID or slug, including its outcomes and status.
|
||||
- **Event Listings:** Access lists of Polymarket events and detailed event information.
|
||||
- **Orderbook & Price Data:** Analyze the orderbook, get the latest market prices, view the midpoint, or obtain historical price information for any market.
|
||||
- **Automation Ready:** Build agents or tools that react programmatically to market developments, changing odds, or specific event outcomes.
|
||||
|
||||
By using these documented API endpoints, you can seamlessly integrate Polymarket’s rich on-chain prediction market data into your own AI workflows, dashboards, research tools, and trading automations.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Polymarket prediction markets into the workflow. Can get markets, market, events, event, tags, series, orderbook, price, midpoint, price history, last trade price, spread, tick size, positions, trades, and search.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `polymarket_get_markets`
|
||||
|
||||
Retrieve a list of prediction markets from Polymarket with optional filtering
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `closed` | string | No | Filter by closed status \(true/false\). Use false for active markets only. |
|
||||
| `order` | string | No | Sort field \(e.g., id, volume, liquidity\) |
|
||||
| `ascending` | string | No | Sort direction \(true for ascending, false for descending\) |
|
||||
| `tagId` | string | No | Filter by tag ID |
|
||||
| `limit` | string | No | Number of results per page \(recommended: 25-50\) |
|
||||
| `offset` | string | No | Pagination offset \(skip this many results\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Markets data and metadata |
|
||||
|
||||
### `polymarket_get_market`
|
||||
|
||||
Retrieve details of a specific prediction market by ID or slug
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `marketId` | string | No | The market ID. Required if slug is not provided. |
|
||||
| `slug` | string | No | The market slug \(e.g., "will-trump-win"\). Required if marketId is not provided. |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Market data and metadata |
|
||||
|
||||
### `polymarket_get_events`
|
||||
|
||||
Retrieve a list of events from Polymarket with optional filtering
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `closed` | string | No | Filter by closed status \(true/false\). Use false for active events only. |
|
||||
| `order` | string | No | Sort field \(e.g., id, volume\) |
|
||||
| `ascending` | string | No | Sort direction \(true for ascending, false for descending\) |
|
||||
| `tagId` | string | No | Filter by tag ID |
|
||||
| `limit` | string | No | Number of results per page \(recommended: 25-50\) |
|
||||
| `offset` | string | No | Pagination offset \(skip this many results\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Events data and metadata |
|
||||
|
||||
### `polymarket_get_event`
|
||||
|
||||
Retrieve details of a specific event by ID or slug
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `eventId` | string | No | The event ID. Required if slug is not provided. |
|
||||
| `slug` | string | No | The event slug \(e.g., "2024-presidential-election"\). Required if eventId is not provided. |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Event data and metadata |
|
||||
|
||||
### `polymarket_get_tags`
|
||||
|
||||
Retrieve available tags for filtering markets from Polymarket
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `limit` | string | No | Number of results per page \(recommended: 25-50\) |
|
||||
| `offset` | string | No | Pagination offset \(skip this many results\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Tags data and metadata |
|
||||
|
||||
### `polymarket_search`
|
||||
|
||||
Search for markets, events, and profiles on Polymarket
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `query` | string | Yes | Search query term |
|
||||
| `limit` | string | No | Number of results per page \(recommended: 25-50\) |
|
||||
| `offset` | string | No | Pagination offset \(skip this many results\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Search results and metadata |
|
||||
|
||||
### `polymarket_get_series`
|
||||
|
||||
Retrieve series (related market groups) from Polymarket
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `limit` | string | No | Number of results per page \(recommended: 25-50\) |
|
||||
| `offset` | string | No | Pagination offset \(skip this many results\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Series data and metadata |
|
||||
|
||||
### `polymarket_get_series_by_id`
|
||||
|
||||
Retrieve a specific series (related market group) by ID from Polymarket
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `seriesId` | string | Yes | The series ID |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Series data and metadata |
|
||||
|
||||
### `polymarket_get_orderbook`
|
||||
|
||||
Retrieve the order book summary for a specific token
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `tokenId` | string | Yes | The CLOB token ID \(from market clobTokenIds\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Orderbook data and metadata |
|
||||
|
||||
### `polymarket_get_price`
|
||||
|
||||
Retrieve the market price for a specific token and side
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `tokenId` | string | Yes | The CLOB token ID \(from market clobTokenIds\) |
|
||||
| `side` | string | Yes | Order side: buy or sell |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Price data and metadata |
|
||||
|
||||
### `polymarket_get_midpoint`
|
||||
|
||||
Retrieve the midpoint price for a specific token
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `tokenId` | string | Yes | The CLOB token ID \(from market clobTokenIds\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Midpoint price data and metadata |
|
||||
|
||||
### `polymarket_get_price_history`
|
||||
|
||||
Retrieve historical price data for a specific market token
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `tokenId` | string | Yes | The CLOB token ID \(from market clobTokenIds\) |
|
||||
| `interval` | string | No | Duration ending at current time \(1m, 1h, 6h, 1d, 1w, max\). Mutually exclusive with startTs/endTs. |
|
||||
| `fidelity` | number | No | Data resolution in minutes \(e.g., 60 for hourly\) |
|
||||
| `startTs` | number | No | Start timestamp \(Unix seconds UTC\) |
|
||||
| `endTs` | number | No | End timestamp \(Unix seconds UTC\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Price history data and metadata |
|
||||
|
||||
### `polymarket_get_last_trade_price`
|
||||
|
||||
Retrieve the last trade price for a specific token
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `tokenId` | string | Yes | The CLOB token ID \(from market clobTokenIds\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Last trade price and metadata |
|
||||
|
||||
### `polymarket_get_spread`
|
||||
|
||||
Retrieve the bid-ask spread for a specific token
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `tokenId` | string | Yes | The CLOB token ID \(from market clobTokenIds\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Spread data and metadata |
|
||||
|
||||
### `polymarket_get_tick_size`
|
||||
|
||||
Retrieve the minimum tick size for a specific token
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `tokenId` | string | Yes | The CLOB token ID \(from market clobTokenIds\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Tick size and metadata |
|
||||
|
||||
### `polymarket_get_positions`
|
||||
|
||||
Retrieve user positions from Polymarket
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `user` | string | Yes | User wallet address |
|
||||
| `market` | string | No | Optional market ID to filter positions |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Positions data and metadata |
|
||||
|
||||
### `polymarket_get_trades`
|
||||
|
||||
Retrieve trade history from Polymarket
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `user` | string | No | User wallet address to filter trades |
|
||||
| `market` | string | No | Market ID to filter trades |
|
||||
| `limit` | string | No | Number of results per page \(recommended: 25-50\) |
|
||||
| `offset` | string | No | Pagination offset \(skip this many results\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Trades data and metadata |
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- Category: `tools`
|
||||
- Type: `polymarket`
|
||||
@@ -89,6 +89,13 @@ Create a new issue with specified properties
|
||||
| `bodyHtml` | string | Yes | Issue body in HTML format |
|
||||
| `accountId` | string | No | Account ID to associate with issue |
|
||||
| `assigneeId` | string | No | User ID to assign issue to |
|
||||
| `teamId` | string | No | Team ID to assign issue to |
|
||||
| `requesterId` | string | No | Requester user ID \(alternative to requester_email\) |
|
||||
| `requesterEmail` | string | No | Requester email address \(alternative to requester_id\) |
|
||||
| `priority` | string | No | Issue priority |
|
||||
| `tags` | string | No | Comma-separated tag IDs |
|
||||
| `customFields` | string | No | Custom fields as JSON object |
|
||||
| `attachmentUrls` | string | No | Comma-separated attachment URLs |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -130,6 +137,9 @@ Update an existing issue
|
||||
| `teamId` | string | No | Team ID to assign issue to |
|
||||
| `tags` | string | No | Comma-separated tag IDs |
|
||||
| `customFields` | string | No | Custom fields as JSON object |
|
||||
| `customerPortalVisible` | boolean | No | Whether issue is visible in customer portal |
|
||||
| `requesterId` | string | No | Requester user ID |
|
||||
| `accountId` | string | No | Account ID to associate with issue |
|
||||
|
||||
#### Output
|
||||
|
||||
|
||||
449
apps/docs/content/docs/en/tools/shopify.mdx
Normal file
449
apps/docs/content/docs/en/tools/shopify.mdx
Normal file
@@ -0,0 +1,449 @@
|
||||
---
|
||||
title: Shopify
|
||||
description: Manage products, orders, customers, and inventory in your Shopify store
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="shopify"
|
||||
color="#FFFFFF"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[Shopify](https://www.shopify.com/) is a leading e-commerce platform designed to help merchants build, run, and grow their online stores. Shopify makes it easy to manage every aspect of your store, from products and inventory to orders and customers.
|
||||
|
||||
With Shopify in Sim, your agents can:
|
||||
|
||||
- **Create and manage products**: Add new products, update product details, and remove products from your store.
|
||||
- **List and retrieve orders**: Get information about customer orders, including filtering and order management.
|
||||
- **Manage customers**: Access and update customer details, or add new customers to your store.
|
||||
- **Adjust inventory levels**: Programmatically change product stock levels to keep your inventory accurate.
|
||||
|
||||
Use Sim's Shopify integration to automate common store management workflows—such as syncing inventory, fulfilling orders, or managing listings—directly from your automations. Empower your agents to access, update, and organize all your store data using simple, programmatic tools.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Shopify into your workflow. Manage products, orders, customers, and inventory. Create, read, update, and delete products. List and manage orders. Handle customer data and adjust inventory levels.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `shopify_create_product`
|
||||
|
||||
Create a new product in your Shopify store
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `shopDomain` | string | Yes | Your Shopify store domain \(e.g., mystore.myshopify.com\) |
|
||||
| `title` | string | Yes | Product title |
|
||||
| `descriptionHtml` | string | No | Product description \(HTML\) |
|
||||
| `vendor` | string | No | Product vendor/brand |
|
||||
| `productType` | string | No | Product type/category |
|
||||
| `tags` | array | No | Product tags |
|
||||
| `status` | string | No | Product status \(ACTIVE, DRAFT, ARCHIVED\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `product` | object | The created product |
|
||||
|
||||
### `shopify_get_product`
|
||||
|
||||
Get a single product by ID from your Shopify store
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `shopDomain` | string | Yes | Your Shopify store domain \(e.g., mystore.myshopify.com\) |
|
||||
| `productId` | string | Yes | Product ID \(gid://shopify/Product/123456789\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `product` | object | The product details |
|
||||
|
||||
### `shopify_list_products`
|
||||
|
||||
List products from your Shopify store with optional filtering
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `shopDomain` | string | Yes | Your Shopify store domain \(e.g., mystore.myshopify.com\) |
|
||||
| `first` | number | No | Number of products to return \(default: 50, max: 250\) |
|
||||
| `query` | string | No | Search query to filter products \(e.g., "title:shirt" or "vendor:Nike" or "status:active"\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `products` | array | List of products |
|
||||
| `pageInfo` | object | Pagination information |
|
||||
|
||||
### `shopify_update_product`
|
||||
|
||||
Update an existing product in your Shopify store
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `shopDomain` | string | Yes | Your Shopify store domain \(e.g., mystore.myshopify.com\) |
|
||||
| `productId` | string | Yes | Product ID to update \(gid://shopify/Product/123456789\) |
|
||||
| `title` | string | No | New product title |
|
||||
| `descriptionHtml` | string | No | New product description \(HTML\) |
|
||||
| `vendor` | string | No | New product vendor/brand |
|
||||
| `productType` | string | No | New product type/category |
|
||||
| `tags` | array | No | New product tags |
|
||||
| `status` | string | No | New product status \(ACTIVE, DRAFT, ARCHIVED\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `product` | object | The updated product |
|
||||
|
||||
### `shopify_delete_product`
|
||||
|
||||
Delete a product from your Shopify store
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `shopDomain` | string | Yes | Your Shopify store domain \(e.g., mystore.myshopify.com\) |
|
||||
| `productId` | string | Yes | Product ID to delete \(gid://shopify/Product/123456789\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `deletedId` | string | The ID of the deleted product |
|
||||
|
||||
### `shopify_get_order`
|
||||
|
||||
Get a single order by ID from your Shopify store
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `shopDomain` | string | Yes | Your Shopify store domain \(e.g., mystore.myshopify.com\) |
|
||||
| `orderId` | string | Yes | Order ID \(gid://shopify/Order/123456789\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `order` | object | The order details |
|
||||
|
||||
### `shopify_list_orders`
|
||||
|
||||
List orders from your Shopify store with optional filtering
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `shopDomain` | string | Yes | Your Shopify store domain \(e.g., mystore.myshopify.com\) |
|
||||
| `first` | number | No | Number of orders to return \(default: 50, max: 250\) |
|
||||
| `status` | string | No | Filter by order status \(open, closed, cancelled, any\) |
|
||||
| `query` | string | No | Search query to filter orders \(e.g., "financial_status:paid" or "fulfillment_status:unfulfilled" or "email:customer@example.com"\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `orders` | array | List of orders |
|
||||
| `pageInfo` | object | Pagination information |
|
||||
|
||||
### `shopify_update_order`
|
||||
|
||||
Update an existing order in your Shopify store (note, tags, email)
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `shopDomain` | string | Yes | Your Shopify store domain \(e.g., mystore.myshopify.com\) |
|
||||
| `orderId` | string | Yes | Order ID to update \(gid://shopify/Order/123456789\) |
|
||||
| `note` | string | No | New order note |
|
||||
| `tags` | array | No | New order tags |
|
||||
| `email` | string | No | New customer email for the order |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `order` | object | The updated order |
|
||||
|
||||
### `shopify_cancel_order`
|
||||
|
||||
Cancel an order in your Shopify store
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `shopDomain` | string | Yes | Your Shopify store domain \(e.g., mystore.myshopify.com\) |
|
||||
| `orderId` | string | Yes | Order ID to cancel \(gid://shopify/Order/123456789\) |
|
||||
| `reason` | string | Yes | Cancellation reason \(CUSTOMER, DECLINED, FRAUD, INVENTORY, STAFF, OTHER\) |
|
||||
| `notifyCustomer` | boolean | No | Whether to notify the customer about the cancellation |
|
||||
| `refund` | boolean | No | Whether to refund the order |
|
||||
| `restock` | boolean | No | Whether to restock the inventory |
|
||||
| `staffNote` | string | No | A note about the cancellation for staff reference |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `order` | object | The cancellation result |
|
||||
|
||||
### `shopify_create_customer`
|
||||
|
||||
Create a new customer in your Shopify store
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `shopDomain` | string | Yes | Your Shopify store domain \(e.g., mystore.myshopify.com\) |
|
||||
| `email` | string | No | Customer email address |
|
||||
| `firstName` | string | No | Customer first name |
|
||||
| `lastName` | string | No | Customer last name |
|
||||
| `phone` | string | No | Customer phone number |
|
||||
| `note` | string | No | Note about the customer |
|
||||
| `tags` | array | No | Customer tags |
|
||||
| `addresses` | array | No | Customer addresses |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `customer` | object | The created customer |
|
||||
|
||||
### `shopify_get_customer`
|
||||
|
||||
Get a single customer by ID from your Shopify store
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `shopDomain` | string | Yes | Your Shopify store domain \(e.g., mystore.myshopify.com\) |
|
||||
| `customerId` | string | Yes | Customer ID \(gid://shopify/Customer/123456789\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `customer` | object | The customer details |
|
||||
|
||||
### `shopify_list_customers`
|
||||
|
||||
List customers from your Shopify store with optional filtering
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `shopDomain` | string | Yes | Your Shopify store domain \(e.g., mystore.myshopify.com\) |
|
||||
| `first` | number | No | Number of customers to return \(default: 50, max: 250\) |
|
||||
| `query` | string | No | Search query to filter customers \(e.g., "first_name:John" or "last_name:Smith" or "email:*@gmail.com" or "tag:vip"\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `customers` | array | List of customers |
|
||||
| `pageInfo` | object | Pagination information |
|
||||
|
||||
### `shopify_update_customer`
|
||||
|
||||
Update an existing customer in your Shopify store
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `shopDomain` | string | Yes | Your Shopify store domain \(e.g., mystore.myshopify.com\) |
|
||||
| `customerId` | string | Yes | Customer ID to update \(gid://shopify/Customer/123456789\) |
|
||||
| `email` | string | No | New customer email address |
|
||||
| `firstName` | string | No | New customer first name |
|
||||
| `lastName` | string | No | New customer last name |
|
||||
| `phone` | string | No | New customer phone number |
|
||||
| `note` | string | No | New note about the customer |
|
||||
| `tags` | array | No | New customer tags |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `customer` | object | The updated customer |
|
||||
|
||||
### `shopify_delete_customer`
|
||||
|
||||
Delete a customer from your Shopify store
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `shopDomain` | string | Yes | Your Shopify store domain \(e.g., mystore.myshopify.com\) |
|
||||
| `customerId` | string | Yes | Customer ID to delete \(gid://shopify/Customer/123456789\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `deletedId` | string | The ID of the deleted customer |
|
||||
|
||||
### `shopify_list_inventory_items`
|
||||
|
||||
List inventory items from your Shopify store. Use this to find inventory item IDs by SKU.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `shopDomain` | string | Yes | Your Shopify store domain \(e.g., mystore.myshopify.com\) |
|
||||
| `first` | number | No | Number of inventory items to return \(default: 50, max: 250\) |
|
||||
| `query` | string | No | Search query to filter inventory items \(e.g., "sku:ABC123"\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `inventoryItems` | array | List of inventory items with their IDs, SKUs, and stock levels |
|
||||
| `pageInfo` | object | Pagination information |
|
||||
|
||||
### `shopify_get_inventory_level`
|
||||
|
||||
Get inventory level for a product variant at a specific location
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `shopDomain` | string | Yes | Your Shopify store domain \(e.g., mystore.myshopify.com\) |
|
||||
| `inventoryItemId` | string | Yes | Inventory item ID \(gid://shopify/InventoryItem/123456789\) |
|
||||
| `locationId` | string | No | Location ID to filter by \(optional\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `inventoryLevel` | object | The inventory level details |
|
||||
|
||||
### `shopify_adjust_inventory`
|
||||
|
||||
Adjust inventory quantity for a product variant at a specific location
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `shopDomain` | string | Yes | Your Shopify store domain \(e.g., mystore.myshopify.com\) |
|
||||
| `inventoryItemId` | string | Yes | Inventory item ID \(gid://shopify/InventoryItem/123456789\) |
|
||||
| `locationId` | string | Yes | Location ID \(gid://shopify/Location/123456789\) |
|
||||
| `delta` | number | Yes | Amount to adjust \(positive to increase, negative to decrease\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `inventoryLevel` | object | The inventory adjustment result |
|
||||
|
||||
### `shopify_list_locations`
|
||||
|
||||
List inventory locations from your Shopify store. Use this to find location IDs needed for inventory operations.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `shopDomain` | string | Yes | Your Shopify store domain \(e.g., mystore.myshopify.com\) |
|
||||
| `first` | number | No | Number of locations to return \(default: 50, max: 250\) |
|
||||
| `includeInactive` | boolean | No | Whether to include deactivated locations \(default: false\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `locations` | array | List of locations with their IDs, names, and addresses |
|
||||
| `pageInfo` | object | Pagination information |
|
||||
|
||||
### `shopify_create_fulfillment`
|
||||
|
||||
Create a fulfillment to mark order items as shipped. Requires a fulfillment order ID (get this from the order details).
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `shopDomain` | string | Yes | Your Shopify store domain \(e.g., mystore.myshopify.com\) |
|
||||
| `fulfillmentOrderId` | string | Yes | The fulfillment order ID \(e.g., gid://shopify/FulfillmentOrder/123456789\) |
|
||||
| `trackingNumber` | string | No | Tracking number for the shipment |
|
||||
| `trackingCompany` | string | No | Shipping carrier name \(e.g., UPS, FedEx, USPS, DHL\) |
|
||||
| `trackingUrl` | string | No | URL to track the shipment |
|
||||
| `notifyCustomer` | boolean | No | Whether to send a shipping confirmation email to the customer \(default: true\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `fulfillment` | object | The created fulfillment with tracking info and fulfilled items |
|
||||
|
||||
### `shopify_list_collections`
|
||||
|
||||
List product collections from your Shopify store. Filter by title, type (custom/smart), or handle.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `shopDomain` | string | Yes | Your Shopify store domain \(e.g., mystore.myshopify.com\) |
|
||||
| `first` | number | No | Number of collections to return \(default: 50, max: 250\) |
|
||||
| `query` | string | No | Search query to filter collections \(e.g., "title:Summer" or "collection_type:smart"\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `collections` | array | List of collections with their IDs, titles, and product counts |
|
||||
| `pageInfo` | object | Pagination information |
|
||||
|
||||
### `shopify_get_collection`
|
||||
|
||||
Get a specific collection by ID, including its products. Use this to retrieve products within a collection.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `shopDomain` | string | Yes | Your Shopify store domain \(e.g., mystore.myshopify.com\) |
|
||||
| `collectionId` | string | Yes | The collection ID \(e.g., gid://shopify/Collection/123456789\) |
|
||||
| `productsFirst` | number | No | Number of products to return from this collection \(default: 50, max: 250\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `collection` | object | The collection details including its products |
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- Category: `tools`
|
||||
- Type: `shopify`
|
||||
399
apps/docs/content/docs/en/tools/ssh.mdx
Normal file
399
apps/docs/content/docs/en/tools/ssh.mdx
Normal file
@@ -0,0 +1,399 @@
|
||||
---
|
||||
title: SSH
|
||||
description: Connect to remote servers via SSH
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="ssh"
|
||||
color="#000000"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[SSH (Secure Shell)](https://en.wikipedia.org/wiki/Secure_Shell) is a widely-used protocol for securely connecting to remote servers, allowing you to execute commands, transfer files, and manage systems over encrypted channels.
|
||||
|
||||
With SSH support in Sim, your agents can:
|
||||
|
||||
- **Execute remote commands**: Run shell commands on any SSH-accessible server
|
||||
- **Upload and run scripts**: Easily transfer and execute multi-line scripts for advanced automation
|
||||
- **Transfer files securely**: Upload and download files as part of your workflows (coming soon or via command)
|
||||
- **Automate server management**: Perform updates, maintenance, monitoring, deployments, and configuration tasks programmatically
|
||||
- **Use flexible authentication**: Connect with password or private key authentication, including support for encrypted keys
|
||||
|
||||
The following Sim SSH tools enable your agents to interact with servers as part of larger automations:
|
||||
|
||||
- `ssh_execute_command`: Run any single shell command remotely and capture output, status, and errors.
|
||||
- `ssh_execute_script`: Upload and execute a full multi-line script on the remote system.
|
||||
- (Additional tools coming soon, such as file transfer.)
|
||||
|
||||
By integrating SSH into your agent workflows, you can automate secure access, remote operations, and server orchestration—streamlining DevOps, IT automation, and custom remote management, all from within Sim.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Execute commands, transfer files, and manage remote servers via SSH. Supports password and private key authentication for secure server access.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `ssh_execute_command`
|
||||
|
||||
Execute a shell command on a remote SSH server
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `host` | string | Yes | SSH server hostname or IP address |
|
||||
| `port` | number | Yes | SSH server port \(default: 22\) |
|
||||
| `username` | string | Yes | SSH username |
|
||||
| `password` | string | No | Password for authentication \(if not using private key\) |
|
||||
| `privateKey` | string | No | Private key for authentication \(OpenSSH format\) |
|
||||
| `passphrase` | string | No | Passphrase for encrypted private key |
|
||||
| `command` | string | Yes | Shell command to execute on the remote server |
|
||||
| `workingDirectory` | string | No | Working directory for command execution |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `stdout` | string | Standard output from command |
|
||||
| `stderr` | string | Standard error output |
|
||||
| `exitCode` | number | Command exit code |
|
||||
| `success` | boolean | Whether command succeeded \(exit code 0\) |
|
||||
| `message` | string | Operation status message |
|
||||
|
||||
### `ssh_execute_script`
|
||||
|
||||
Upload and execute a multi-line script on a remote SSH server
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `host` | string | Yes | SSH server hostname or IP address |
|
||||
| `port` | number | Yes | SSH server port \(default: 22\) |
|
||||
| `username` | string | Yes | SSH username |
|
||||
| `password` | string | No | Password for authentication \(if not using private key\) |
|
||||
| `privateKey` | string | No | Private key for authentication \(OpenSSH format\) |
|
||||
| `passphrase` | string | No | Passphrase for encrypted private key |
|
||||
| `script` | string | Yes | Script content to execute \(bash, python, etc.\) |
|
||||
| `interpreter` | string | No | Script interpreter \(default: /bin/bash\) |
|
||||
| `workingDirectory` | string | No | Working directory for script execution |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `stdout` | string | Standard output from script |
|
||||
| `stderr` | string | Standard error output |
|
||||
| `exitCode` | number | Script exit code |
|
||||
| `success` | boolean | Whether script succeeded \(exit code 0\) |
|
||||
| `scriptPath` | string | Temporary path where script was uploaded |
|
||||
| `message` | string | Operation status message |
|
||||
|
||||
### `ssh_check_command_exists`
|
||||
|
||||
Check if a command/program exists on the remote SSH server
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `host` | string | Yes | SSH server hostname or IP address |
|
||||
| `port` | number | Yes | SSH server port \(default: 22\) |
|
||||
| `username` | string | Yes | SSH username |
|
||||
| `password` | string | No | Password for authentication \(if not using private key\) |
|
||||
| `privateKey` | string | No | Private key for authentication \(OpenSSH format\) |
|
||||
| `passphrase` | string | No | Passphrase for encrypted private key |
|
||||
| `commandName` | string | Yes | Command name to check \(e.g., docker, git, python3\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `commandExists` | boolean | Whether the command exists |
|
||||
| `commandPath` | string | Full path to the command \(if found\) |
|
||||
| `version` | string | Command version output \(if applicable\) |
|
||||
| `message` | string | Operation status message |
|
||||
|
||||
### `ssh_upload_file`
|
||||
|
||||
Upload a file to a remote SSH server
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `host` | string | Yes | SSH server hostname or IP address |
|
||||
| `port` | number | Yes | SSH server port \(default: 22\) |
|
||||
| `username` | string | Yes | SSH username |
|
||||
| `password` | string | No | Password for authentication \(if not using private key\) |
|
||||
| `privateKey` | string | No | Private key for authentication \(OpenSSH format\) |
|
||||
| `passphrase` | string | No | Passphrase for encrypted private key |
|
||||
| `fileContent` | string | Yes | File content to upload \(base64 encoded for binary files\) |
|
||||
| `fileName` | string | Yes | Name of the file being uploaded |
|
||||
| `remotePath` | string | Yes | Destination path on the remote server |
|
||||
| `permissions` | string | No | File permissions \(e.g., 0644\) |
|
||||
| `overwrite` | boolean | No | Whether to overwrite existing files \(default: true\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `uploaded` | boolean | Whether the file was uploaded successfully |
|
||||
| `remotePath` | string | Final path on the remote server |
|
||||
| `size` | number | File size in bytes |
|
||||
| `message` | string | Operation status message |
|
||||
|
||||
### `ssh_download_file`
|
||||
|
||||
Download a file from a remote SSH server
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `host` | string | Yes | SSH server hostname or IP address |
|
||||
| `port` | number | Yes | SSH server port \(default: 22\) |
|
||||
| `username` | string | Yes | SSH username |
|
||||
| `password` | string | No | Password for authentication \(if not using private key\) |
|
||||
| `privateKey` | string | No | Private key for authentication \(OpenSSH format\) |
|
||||
| `passphrase` | string | No | Passphrase for encrypted private key |
|
||||
| `remotePath` | string | Yes | Path of the file on the remote server |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `downloaded` | boolean | Whether the file was downloaded successfully |
|
||||
| `fileContent` | string | File content \(base64 encoded for binary files\) |
|
||||
| `fileName` | string | Name of the downloaded file |
|
||||
| `remotePath` | string | Source path on the remote server |
|
||||
| `size` | number | File size in bytes |
|
||||
| `message` | string | Operation status message |
|
||||
|
||||
### `ssh_list_directory`
|
||||
|
||||
List files and directories in a remote directory
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `host` | string | Yes | SSH server hostname or IP address |
|
||||
| `port` | number | Yes | SSH server port \(default: 22\) |
|
||||
| `username` | string | Yes | SSH username |
|
||||
| `password` | string | No | Password for authentication \(if not using private key\) |
|
||||
| `privateKey` | string | No | Private key for authentication \(OpenSSH format\) |
|
||||
| `passphrase` | string | No | Passphrase for encrypted private key |
|
||||
| `path` | string | Yes | Remote directory path to list |
|
||||
| `detailed` | boolean | No | Include file details \(size, permissions, modified date\) |
|
||||
| `recursive` | boolean | No | List subdirectories recursively \(default: false\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `entries` | array | Array of file and directory entries |
|
||||
|
||||
### `ssh_check_file_exists`
|
||||
|
||||
Check if a file or directory exists on the remote SSH server
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `host` | string | Yes | SSH server hostname or IP address |
|
||||
| `port` | number | Yes | SSH server port \(default: 22\) |
|
||||
| `username` | string | Yes | SSH username |
|
||||
| `password` | string | No | Password for authentication \(if not using private key\) |
|
||||
| `privateKey` | string | No | Private key for authentication \(OpenSSH format\) |
|
||||
| `passphrase` | string | No | Passphrase for encrypted private key |
|
||||
| `path` | string | Yes | Remote file or directory path to check |
|
||||
| `type` | string | No | Expected type: file, directory, or any \(default: any\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `exists` | boolean | Whether the path exists |
|
||||
| `type` | string | Type of path \(file, directory, symlink, not_found\) |
|
||||
| `size` | number | File size if it is a file |
|
||||
| `permissions` | string | File permissions \(e.g., 0755\) |
|
||||
| `modified` | string | Last modified timestamp |
|
||||
| `message` | string | Operation status message |
|
||||
|
||||
### `ssh_create_directory`
|
||||
|
||||
Create a directory on the remote SSH server
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `host` | string | Yes | SSH server hostname or IP address |
|
||||
| `port` | number | Yes | SSH server port \(default: 22\) |
|
||||
| `username` | string | Yes | SSH username |
|
||||
| `password` | string | No | Password for authentication \(if not using private key\) |
|
||||
| `privateKey` | string | No | Private key for authentication \(OpenSSH format\) |
|
||||
| `passphrase` | string | No | Passphrase for encrypted private key |
|
||||
| `path` | string | Yes | Directory path to create |
|
||||
| `recursive` | boolean | No | Create parent directories if they do not exist \(default: true\) |
|
||||
| `permissions` | string | No | Directory permissions \(default: 0755\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `created` | boolean | Whether the directory was created successfully |
|
||||
| `remotePath` | string | Created directory path |
|
||||
| `alreadyExists` | boolean | Whether the directory already existed |
|
||||
| `message` | string | Operation status message |
|
||||
|
||||
### `ssh_delete_file`
|
||||
|
||||
Delete a file or directory from the remote SSH server
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `host` | string | Yes | SSH server hostname or IP address |
|
||||
| `port` | number | Yes | SSH server port \(default: 22\) |
|
||||
| `username` | string | Yes | SSH username |
|
||||
| `password` | string | No | Password for authentication \(if not using private key\) |
|
||||
| `privateKey` | string | No | Private key for authentication \(OpenSSH format\) |
|
||||
| `passphrase` | string | No | Passphrase for encrypted private key |
|
||||
| `path` | string | Yes | Path to delete |
|
||||
| `recursive` | boolean | No | Recursively delete directories \(default: false\) |
|
||||
| `force` | boolean | No | Force deletion without confirmation \(default: false\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `deleted` | boolean | Whether the path was deleted successfully |
|
||||
| `remotePath` | string | Deleted path |
|
||||
| `message` | string | Operation status message |
|
||||
|
||||
### `ssh_move_rename`
|
||||
|
||||
Move or rename a file or directory on the remote SSH server
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `host` | string | Yes | SSH server hostname or IP address |
|
||||
| `port` | number | Yes | SSH server port \(default: 22\) |
|
||||
| `username` | string | Yes | SSH username |
|
||||
| `password` | string | No | Password for authentication \(if not using private key\) |
|
||||
| `privateKey` | string | No | Private key for authentication \(OpenSSH format\) |
|
||||
| `passphrase` | string | No | Passphrase for encrypted private key |
|
||||
| `sourcePath` | string | Yes | Current path of the file or directory |
|
||||
| `destinationPath` | string | Yes | New path for the file or directory |
|
||||
| `overwrite` | boolean | No | Overwrite destination if it exists \(default: false\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `moved` | boolean | Whether the operation was successful |
|
||||
| `sourcePath` | string | Original path |
|
||||
| `destinationPath` | string | New path |
|
||||
| `message` | string | Operation status message |
|
||||
|
||||
### `ssh_get_system_info`
|
||||
|
||||
Retrieve system information from the remote SSH server
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `host` | string | Yes | SSH server hostname or IP address |
|
||||
| `port` | number | Yes | SSH server port \(default: 22\) |
|
||||
| `username` | string | Yes | SSH username |
|
||||
| `password` | string | No | Password for authentication \(if not using private key\) |
|
||||
| `privateKey` | string | No | Private key for authentication \(OpenSSH format\) |
|
||||
| `passphrase` | string | No | Passphrase for encrypted private key |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `hostname` | string | Server hostname |
|
||||
| `os` | string | Operating system \(e.g., Linux, Darwin\) |
|
||||
| `architecture` | string | CPU architecture \(e.g., x64, arm64\) |
|
||||
| `uptime` | number | System uptime in seconds |
|
||||
| `memory` | json | Memory information \(total, free, used\) |
|
||||
| `diskSpace` | json | Disk space information \(total, free, used\) |
|
||||
| `message` | string | Operation status message |
|
||||
|
||||
### `ssh_read_file_content`
|
||||
|
||||
Read the contents of a remote file
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `host` | string | Yes | SSH server hostname or IP address |
|
||||
| `port` | number | Yes | SSH server port \(default: 22\) |
|
||||
| `username` | string | Yes | SSH username |
|
||||
| `password` | string | No | Password for authentication \(if not using private key\) |
|
||||
| `privateKey` | string | No | Private key for authentication \(OpenSSH format\) |
|
||||
| `passphrase` | string | No | Passphrase for encrypted private key |
|
||||
| `path` | string | Yes | Remote file path to read |
|
||||
| `encoding` | string | No | File encoding \(default: utf-8\) |
|
||||
| `maxSize` | number | No | Maximum file size to read in MB \(default: 10\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `content` | string | File content as string |
|
||||
| `size` | number | File size in bytes |
|
||||
| `lines` | number | Number of lines in file |
|
||||
| `remotePath` | string | Remote file path |
|
||||
| `message` | string | Operation status message |
|
||||
|
||||
### `ssh_write_file_content`
|
||||
|
||||
Write or append content to a remote file
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `host` | string | Yes | SSH server hostname or IP address |
|
||||
| `port` | number | Yes | SSH server port \(default: 22\) |
|
||||
| `username` | string | Yes | SSH username |
|
||||
| `password` | string | No | Password for authentication \(if not using private key\) |
|
||||
| `privateKey` | string | No | Private key for authentication \(OpenSSH format\) |
|
||||
| `passphrase` | string | No | Passphrase for encrypted private key |
|
||||
| `path` | string | Yes | Remote file path to write to |
|
||||
| `content` | string | Yes | Content to write to the file |
|
||||
| `mode` | string | No | Write mode: overwrite, append, or create \(default: overwrite\) |
|
||||
| `permissions` | string | No | File permissions \(e.g., 0644\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `written` | boolean | Whether the file was written successfully |
|
||||
| `remotePath` | string | File path |
|
||||
| `size` | number | Final file size in bytes |
|
||||
| `message` | string | Operation status message |
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- Category: `tools`
|
||||
- Type: `ssh`
|
||||
571
apps/docs/content/docs/en/tools/wordpress.mdx
Normal file
571
apps/docs/content/docs/en/tools/wordpress.mdx
Normal file
@@ -0,0 +1,571 @@
|
||||
---
|
||||
title: WordPress
|
||||
description: Manage WordPress content
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="wordpress"
|
||||
color="#21759B"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[WordPress](https://wordpress.org/) is the world’s leading open-source content management system, making it easy to publish and manage websites, blogs, and all types of online content. With WordPress, you can create and update posts or pages, organize your content with categories and tags, manage media files, moderate comments, and handle user accounts—allowing you to run everything from personal blogs to complex business sites.
|
||||
|
||||
Sim’s integration with WordPress lets your agents automate essential website tasks. You can programmatically create new blog posts with specific titles, content, categories, tags, and featured images. Updating existing posts—such as changing their content, title, or publishing status—is straightforward. You can also publish or save content as drafts, manage static pages, work with media uploads, oversee comments, and assign content to relevant organizational taxonomies.
|
||||
|
||||
By connecting WordPress to your automations, Sim empowers your agents to streamline content publishing, editorial workflows, and everyday site management—helping you keep your website fresh, organized, and secure without manual effort.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate with WordPress to create, update, and manage posts, pages, media, comments, categories, tags, and users. Supports WordPress.com sites via OAuth and self-hosted WordPress sites using Application Passwords authentication.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `wordpress_create_post`
|
||||
|
||||
Create a new blog post in WordPress.com
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | WordPress.com site ID or domain \(e.g., 12345678 or mysite.wordpress.com\) |
|
||||
| `title` | string | Yes | Post title |
|
||||
| `content` | string | No | Post content \(HTML or plain text\) |
|
||||
| `status` | string | No | Post status: publish, draft, pending, private, or future |
|
||||
| `excerpt` | string | No | Post excerpt |
|
||||
| `categories` | string | No | Comma-separated category IDs |
|
||||
| `tags` | string | No | Comma-separated tag IDs |
|
||||
| `featuredMedia` | number | No | Featured image media ID |
|
||||
| `slug` | string | No | URL slug for the post |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `post` | object | The created post |
|
||||
|
||||
### `wordpress_update_post`
|
||||
|
||||
Update an existing blog post in WordPress.com
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | WordPress.com site ID or domain \(e.g., 12345678 or mysite.wordpress.com\) |
|
||||
| `postId` | number | Yes | The ID of the post to update |
|
||||
| `title` | string | No | Post title |
|
||||
| `content` | string | No | Post content \(HTML or plain text\) |
|
||||
| `status` | string | No | Post status: publish, draft, pending, private, or future |
|
||||
| `excerpt` | string | No | Post excerpt |
|
||||
| `categories` | string | No | Comma-separated category IDs |
|
||||
| `tags` | string | No | Comma-separated tag IDs |
|
||||
| `featuredMedia` | number | No | Featured image media ID |
|
||||
| `slug` | string | No | URL slug for the post |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `post` | object | The updated post |
|
||||
|
||||
### `wordpress_delete_post`
|
||||
|
||||
Delete a blog post from WordPress.com
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | WordPress.com site ID or domain \(e.g., 12345678 or mysite.wordpress.com\) |
|
||||
| `postId` | number | Yes | The ID of the post to delete |
|
||||
| `force` | boolean | No | Bypass trash and force delete permanently |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `deleted` | boolean | Whether the post was deleted |
|
||||
| `post` | object | The deleted post |
|
||||
|
||||
### `wordpress_get_post`
|
||||
|
||||
Get a single blog post from WordPress.com by ID
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | WordPress.com site ID or domain \(e.g., 12345678 or mysite.wordpress.com\) |
|
||||
| `postId` | number | Yes | The ID of the post to retrieve |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `post` | object | The retrieved post |
|
||||
|
||||
### `wordpress_list_posts`
|
||||
|
||||
List blog posts from WordPress.com with optional filters
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | WordPress.com site ID or domain \(e.g., 12345678 or mysite.wordpress.com\) |
|
||||
| `perPage` | number | No | Number of posts per page \(default: 10, max: 100\) |
|
||||
| `page` | number | No | Page number for pagination |
|
||||
| `status` | string | No | Post status filter: publish, draft, pending, private |
|
||||
| `author` | number | No | Filter by author ID |
|
||||
| `categories` | string | No | Comma-separated category IDs to filter by |
|
||||
| `tags` | string | No | Comma-separated tag IDs to filter by |
|
||||
| `search` | string | No | Search term to filter posts |
|
||||
| `orderBy` | string | No | Order by field: date, id, title, slug, modified |
|
||||
| `order` | string | No | Order direction: asc or desc |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `posts` | array | List of posts |
|
||||
|
||||
### `wordpress_create_page`
|
||||
|
||||
Create a new page in WordPress.com
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | WordPress.com site ID or domain \(e.g., 12345678 or mysite.wordpress.com\) |
|
||||
| `title` | string | Yes | Page title |
|
||||
| `content` | string | No | Page content \(HTML or plain text\) |
|
||||
| `status` | string | No | Page status: publish, draft, pending, private |
|
||||
| `excerpt` | string | No | Page excerpt |
|
||||
| `parent` | number | No | Parent page ID for hierarchical pages |
|
||||
| `menuOrder` | number | No | Order in page menu |
|
||||
| `featuredMedia` | number | No | Featured image media ID |
|
||||
| `slug` | string | No | URL slug for the page |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `page` | object | The created page |
|
||||
|
||||
### `wordpress_update_page`
|
||||
|
||||
Update an existing page in WordPress.com
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | WordPress.com site ID or domain \(e.g., 12345678 or mysite.wordpress.com\) |
|
||||
| `pageId` | number | Yes | The ID of the page to update |
|
||||
| `title` | string | No | Page title |
|
||||
| `content` | string | No | Page content \(HTML or plain text\) |
|
||||
| `status` | string | No | Page status: publish, draft, pending, private |
|
||||
| `excerpt` | string | No | Page excerpt |
|
||||
| `parent` | number | No | Parent page ID for hierarchical pages |
|
||||
| `menuOrder` | number | No | Order in page menu |
|
||||
| `featuredMedia` | number | No | Featured image media ID |
|
||||
| `slug` | string | No | URL slug for the page |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `page` | object | The updated page |
|
||||
|
||||
### `wordpress_delete_page`
|
||||
|
||||
Delete a page from WordPress.com
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | WordPress.com site ID or domain \(e.g., 12345678 or mysite.wordpress.com\) |
|
||||
| `pageId` | number | Yes | The ID of the page to delete |
|
||||
| `force` | boolean | No | Bypass trash and force delete permanently |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `deleted` | boolean | Whether the page was deleted |
|
||||
| `page` | object | The deleted page |
|
||||
|
||||
### `wordpress_get_page`
|
||||
|
||||
Get a single page from WordPress.com by ID
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | WordPress.com site ID or domain \(e.g., 12345678 or mysite.wordpress.com\) |
|
||||
| `pageId` | number | Yes | The ID of the page to retrieve |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `page` | object | The retrieved page |
|
||||
|
||||
### `wordpress_list_pages`
|
||||
|
||||
List pages from WordPress.com with optional filters
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | WordPress.com site ID or domain \(e.g., 12345678 or mysite.wordpress.com\) |
|
||||
| `perPage` | number | No | Number of pages per request \(default: 10, max: 100\) |
|
||||
| `page` | number | No | Page number for pagination |
|
||||
| `status` | string | No | Page status filter: publish, draft, pending, private |
|
||||
| `parent` | number | No | Filter by parent page ID |
|
||||
| `search` | string | No | Search term to filter pages |
|
||||
| `orderBy` | string | No | Order by field: date, id, title, slug, modified, menu_order |
|
||||
| `order` | string | No | Order direction: asc or desc |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `pages` | array | List of pages |
|
||||
|
||||
### `wordpress_upload_media`
|
||||
|
||||
Upload a media file (image, video, document) to WordPress.com
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | WordPress.com site ID or domain \(e.g., 12345678 or mysite.wordpress.com\) |
|
||||
| `file` | string | Yes | Base64 encoded file data or URL to fetch file from |
|
||||
| `filename` | string | Yes | Filename with extension \(e.g., image.jpg\) |
|
||||
| `title` | string | No | Media title |
|
||||
| `caption` | string | No | Media caption |
|
||||
| `altText` | string | No | Alternative text for accessibility |
|
||||
| `description` | string | No | Media description |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `media` | object | The uploaded media item |
|
||||
|
||||
### `wordpress_get_media`
|
||||
|
||||
Get a single media item from WordPress.com by ID
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | WordPress.com site ID or domain \(e.g., 12345678 or mysite.wordpress.com\) |
|
||||
| `mediaId` | number | Yes | The ID of the media item to retrieve |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `media` | object | The retrieved media item |
|
||||
|
||||
### `wordpress_list_media`
|
||||
|
||||
List media items from the WordPress.com media library
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | WordPress.com site ID or domain \(e.g., 12345678 or mysite.wordpress.com\) |
|
||||
| `perPage` | number | No | Number of media items per request \(default: 10, max: 100\) |
|
||||
| `page` | number | No | Page number for pagination |
|
||||
| `search` | string | No | Search term to filter media |
|
||||
| `mediaType` | string | No | Filter by media type: image, video, audio, application |
|
||||
| `mimeType` | string | No | Filter by specific MIME type \(e.g., image/jpeg\) |
|
||||
| `orderBy` | string | No | Order by field: date, id, title, slug |
|
||||
| `order` | string | No | Order direction: asc or desc |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `media` | array | List of media items |
|
||||
|
||||
### `wordpress_delete_media`
|
||||
|
||||
Delete a media item from WordPress.com
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | WordPress.com site ID or domain \(e.g., 12345678 or mysite.wordpress.com\) |
|
||||
| `mediaId` | number | Yes | The ID of the media item to delete |
|
||||
| `force` | boolean | No | Force delete \(media has no trash, so deletion is permanent\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `deleted` | boolean | Whether the media was deleted |
|
||||
| `media` | object | The deleted media item |
|
||||
|
||||
### `wordpress_create_comment`
|
||||
|
||||
Create a new comment on a WordPress.com post
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | WordPress.com site ID or domain \(e.g., 12345678 or mysite.wordpress.com\) |
|
||||
| `postId` | number | Yes | The ID of the post to comment on |
|
||||
| `content` | string | Yes | Comment content |
|
||||
| `parent` | number | No | Parent comment ID for replies |
|
||||
| `authorName` | string | No | Comment author display name |
|
||||
| `authorEmail` | string | No | Comment author email |
|
||||
| `authorUrl` | string | No | Comment author URL |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `comment` | object | The created comment |
|
||||
|
||||
### `wordpress_list_comments`
|
||||
|
||||
List comments from WordPress.com with optional filters
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | WordPress.com site ID or domain \(e.g., 12345678 or mysite.wordpress.com\) |
|
||||
| `perPage` | number | No | Number of comments per request \(default: 10, max: 100\) |
|
||||
| `page` | number | No | Page number for pagination |
|
||||
| `postId` | number | No | Filter by post ID |
|
||||
| `status` | string | No | Filter by comment status: approved, hold, spam, trash |
|
||||
| `search` | string | No | Search term to filter comments |
|
||||
| `orderBy` | string | No | Order by field: date, id, parent |
|
||||
| `order` | string | No | Order direction: asc or desc |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `comments` | array | List of comments |
|
||||
|
||||
### `wordpress_update_comment`
|
||||
|
||||
Update a comment in WordPress.com (content or status)
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | WordPress.com site ID or domain \(e.g., 12345678 or mysite.wordpress.com\) |
|
||||
| `commentId` | number | Yes | The ID of the comment to update |
|
||||
| `content` | string | No | Updated comment content |
|
||||
| `status` | string | No | Comment status: approved, hold, spam, trash |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `comment` | object | The updated comment |
|
||||
|
||||
### `wordpress_delete_comment`
|
||||
|
||||
Delete a comment from WordPress.com
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | WordPress.com site ID or domain \(e.g., 12345678 or mysite.wordpress.com\) |
|
||||
| `commentId` | number | Yes | The ID of the comment to delete |
|
||||
| `force` | boolean | No | Bypass trash and force delete permanently |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `deleted` | boolean | Whether the comment was deleted |
|
||||
| `comment` | object | The deleted comment |
|
||||
|
||||
### `wordpress_create_category`
|
||||
|
||||
Create a new category in WordPress.com
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | WordPress.com site ID or domain \(e.g., 12345678 or mysite.wordpress.com\) |
|
||||
| `name` | string | Yes | Category name |
|
||||
| `description` | string | No | Category description |
|
||||
| `parent` | number | No | Parent category ID for hierarchical categories |
|
||||
| `slug` | string | No | URL slug for the category |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `category` | object | The created category |
|
||||
|
||||
### `wordpress_list_categories`
|
||||
|
||||
List categories from WordPress.com
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | WordPress.com site ID or domain \(e.g., 12345678 or mysite.wordpress.com\) |
|
||||
| `perPage` | number | No | Number of categories per request \(default: 10, max: 100\) |
|
||||
| `page` | number | No | Page number for pagination |
|
||||
| `search` | string | No | Search term to filter categories |
|
||||
| `order` | string | No | Order direction: asc or desc |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `categories` | array | List of categories |
|
||||
|
||||
### `wordpress_create_tag`
|
||||
|
||||
Create a new tag in WordPress.com
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | WordPress.com site ID or domain \(e.g., 12345678 or mysite.wordpress.com\) |
|
||||
| `name` | string | Yes | Tag name |
|
||||
| `description` | string | No | Tag description |
|
||||
| `slug` | string | No | URL slug for the tag |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `tag` | object | The created tag |
|
||||
|
||||
### `wordpress_list_tags`
|
||||
|
||||
List tags from WordPress.com
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | WordPress.com site ID or domain \(e.g., 12345678 or mysite.wordpress.com\) |
|
||||
| `perPage` | number | No | Number of tags per request \(default: 10, max: 100\) |
|
||||
| `page` | number | No | Page number for pagination |
|
||||
| `search` | string | No | Search term to filter tags |
|
||||
| `order` | string | No | Order direction: asc or desc |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `tags` | array | List of tags |
|
||||
|
||||
### `wordpress_get_current_user`
|
||||
|
||||
Get information about the currently authenticated WordPress.com user
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | WordPress.com site ID or domain \(e.g., 12345678 or mysite.wordpress.com\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `user` | object | The current user |
|
||||
|
||||
### `wordpress_list_users`
|
||||
|
||||
List users from WordPress.com (requires admin privileges)
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | WordPress.com site ID or domain \(e.g., 12345678 or mysite.wordpress.com\) |
|
||||
| `perPage` | number | No | Number of users per request \(default: 10, max: 100\) |
|
||||
| `page` | number | No | Page number for pagination |
|
||||
| `search` | string | No | Search term to filter users |
|
||||
| `roles` | string | No | Comma-separated role names to filter by |
|
||||
| `order` | string | No | Order direction: asc or desc |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `users` | array | List of users |
|
||||
|
||||
### `wordpress_get_user`
|
||||
|
||||
Get a specific user from WordPress.com by ID
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | WordPress.com site ID or domain \(e.g., 12345678 or mysite.wordpress.com\) |
|
||||
| `userId` | number | Yes | The ID of the user to retrieve |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `user` | object | The retrieved user |
|
||||
|
||||
### `wordpress_search_content`
|
||||
|
||||
Search across all content types in WordPress.com (posts, pages, media)
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `siteId` | string | Yes | WordPress.com site ID or domain \(e.g., 12345678 or mysite.wordpress.com\) |
|
||||
| `query` | string | Yes | Search query |
|
||||
| `perPage` | number | No | Number of results per request \(default: 10, max: 100\) |
|
||||
| `page` | number | No | Page number for pagination |
|
||||
| `type` | string | No | Filter by content type: post, page, attachment |
|
||||
| `subtype` | string | No | Filter by post type slug \(e.g., post, page\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `results` | array | Search results |
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- Category: `tools`
|
||||
- Type: `wordpress`
|
||||
@@ -117,6 +117,9 @@ Create a new ticket in Zendesk with support for custom fields
|
||||
| `type` | string | No | Type \(problem, incident, question, task\) |
|
||||
| `tags` | string | No | Comma-separated tags |
|
||||
| `assigneeId` | string | No | Assignee user ID |
|
||||
| `groupId` | string | No | Group ID |
|
||||
| `requesterId` | string | No | Requester user ID |
|
||||
| `customFields` | string | No | Custom fields as JSON object \(e.g., \{"field_id": "value"\}\) |
|
||||
|
||||
#### Output
|
||||
|
||||
|
||||
255
apps/docs/content/docs/en/tools/zoom.mdx
Normal file
255
apps/docs/content/docs/en/tools/zoom.mdx
Normal file
@@ -0,0 +1,255 @@
|
||||
---
|
||||
title: Zoom
|
||||
description: Create and manage Zoom meetings and recordings
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="zoom"
|
||||
color="#2D8CFF"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[Zoom](https://zoom.us/) is a leading cloud-based communications platform for video meetings, webinars, and online collaboration. It allows users and organizations to easily schedule, host, and manage meetings, providing tools for screen sharing, chat, recordings, and more.
|
||||
|
||||
With Zoom, you can:
|
||||
|
||||
- **Schedule and manage meetings**: Create instant or scheduled meetings, including recurring events
|
||||
- **Configure meeting options**: Set meeting passwords, enable waiting rooms, and control participant video/audio
|
||||
- **Send invitations and share details**: Retrieve meeting invitations and information for easy sharing
|
||||
- **Get and update meeting data**: Access meeting details, modify existing meetings, and manage settings programmatically
|
||||
|
||||
In Sim, the Zoom integration empowers your agents to automate scheduling and meeting management. Use tool actions to:
|
||||
|
||||
- Programmatically create new meetings with custom settings
|
||||
- List all meetings for a specific user (or yourself)
|
||||
- Retrieve details or invitations for any meeting
|
||||
- Update or delete existing meetings directly from your automations
|
||||
|
||||
These capabilities let you streamline remote collaboration, automate recurring video sessions, and manage your organization's Zoom environment all as part of your workflows.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Zoom into workflows. Create, list, update, and delete Zoom meetings. Get meeting details, invitations, recordings, and participants. Manage cloud recordings programmatically.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `zoom_create_meeting`
|
||||
|
||||
Create a new Zoom meeting
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `userId` | string | Yes | The user ID or email address. Use "me" for the authenticated user. |
|
||||
| `topic` | string | Yes | Meeting topic |
|
||||
| `type` | number | No | Meeting type: 1=instant, 2=scheduled, 3=recurring no fixed time, 8=recurring fixed time |
|
||||
| `startTime` | string | No | Meeting start time in ISO 8601 format \(e.g., 2025-06-03T10:00:00Z\) |
|
||||
| `duration` | number | No | Meeting duration in minutes |
|
||||
| `timezone` | string | No | Timezone for the meeting \(e.g., America/Los_Angeles\) |
|
||||
| `password` | string | No | Meeting password |
|
||||
| `agenda` | string | No | Meeting agenda |
|
||||
| `hostVideo` | boolean | No | Start with host video on |
|
||||
| `participantVideo` | boolean | No | Start with participant video on |
|
||||
| `joinBeforeHost` | boolean | No | Allow participants to join before host |
|
||||
| `muteUponEntry` | boolean | No | Mute participants upon entry |
|
||||
| `waitingRoom` | boolean | No | Enable waiting room |
|
||||
| `autoRecording` | string | No | Auto recording setting: local, cloud, or none |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `meeting` | object | The created meeting with all its properties |
|
||||
|
||||
### `zoom_list_meetings`
|
||||
|
||||
List all meetings for a Zoom user
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `userId` | string | Yes | The user ID or email address. Use "me" for the authenticated user. |
|
||||
| `type` | string | No | Meeting type filter: scheduled, live, upcoming, upcoming_meetings, or previous_meetings |
|
||||
| `pageSize` | number | No | Number of records per page \(max 300\) |
|
||||
| `nextPageToken` | string | No | Token for pagination to get next page of results |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `meetings` | array | List of meetings |
|
||||
| `pageInfo` | object | Pagination information |
|
||||
|
||||
### `zoom_get_meeting`
|
||||
|
||||
Get details of a specific Zoom meeting
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `meetingId` | string | Yes | The meeting ID |
|
||||
| `occurrenceId` | string | No | Occurrence ID for recurring meetings |
|
||||
| `showPreviousOccurrences` | boolean | No | Show previous occurrences for recurring meetings |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `meeting` | object | The meeting details |
|
||||
|
||||
### `zoom_update_meeting`
|
||||
|
||||
Update an existing Zoom meeting
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `meetingId` | string | Yes | The meeting ID to update |
|
||||
| `topic` | string | No | Meeting topic |
|
||||
| `type` | number | No | Meeting type: 1=instant, 2=scheduled, 3=recurring no fixed time, 8=recurring fixed time |
|
||||
| `startTime` | string | No | Meeting start time in ISO 8601 format \(e.g., 2025-06-03T10:00:00Z\) |
|
||||
| `duration` | number | No | Meeting duration in minutes |
|
||||
| `timezone` | string | No | Timezone for the meeting \(e.g., America/Los_Angeles\) |
|
||||
| `password` | string | No | Meeting password |
|
||||
| `agenda` | string | No | Meeting agenda |
|
||||
| `hostVideo` | boolean | No | Start with host video on |
|
||||
| `participantVideo` | boolean | No | Start with participant video on |
|
||||
| `joinBeforeHost` | boolean | No | Allow participants to join before host |
|
||||
| `muteUponEntry` | boolean | No | Mute participants upon entry |
|
||||
| `waitingRoom` | boolean | No | Enable waiting room |
|
||||
| `autoRecording` | string | No | Auto recording setting: local, cloud, or none |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the meeting was updated successfully |
|
||||
|
||||
### `zoom_delete_meeting`
|
||||
|
||||
Delete or cancel a Zoom meeting
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `meetingId` | string | Yes | The meeting ID to delete |
|
||||
| `occurrenceId` | string | No | Occurrence ID for deleting a specific occurrence of a recurring meeting |
|
||||
| `scheduleForReminder` | boolean | No | Send cancellation reminder email to registrants |
|
||||
| `cancelMeetingReminder` | boolean | No | Send cancellation email to registrants and alternative hosts |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the meeting was deleted successfully |
|
||||
|
||||
### `zoom_get_meeting_invitation`
|
||||
|
||||
Get the meeting invitation text for a Zoom meeting
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `meetingId` | string | Yes | The meeting ID |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `invitation` | string | The meeting invitation text |
|
||||
|
||||
### `zoom_list_recordings`
|
||||
|
||||
List all cloud recordings for a Zoom user
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `userId` | string | Yes | The user ID or email address. Use "me" for the authenticated user. |
|
||||
| `from` | string | No | Start date in yyyy-mm-dd format \(within last 6 months\) |
|
||||
| `to` | string | No | End date in yyyy-mm-dd format |
|
||||
| `pageSize` | number | No | Number of records per page \(max 300\) |
|
||||
| `nextPageToken` | string | No | Token for pagination to get next page of results |
|
||||
| `trash` | boolean | No | Set to true to list recordings from trash |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `recordings` | array | List of recordings |
|
||||
| `pageInfo` | object | Pagination information |
|
||||
|
||||
### `zoom_get_meeting_recordings`
|
||||
|
||||
Get all recordings for a specific Zoom meeting
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `meetingId` | string | Yes | The meeting ID or meeting UUID |
|
||||
| `includeFolderItems` | boolean | No | Include items within a folder |
|
||||
| `ttl` | number | No | Time to live for download URLs in seconds \(max 604800\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `recording` | object | The meeting recording with all files |
|
||||
|
||||
### `zoom_delete_recording`
|
||||
|
||||
Delete cloud recordings for a Zoom meeting
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `meetingId` | string | Yes | The meeting ID or meeting UUID |
|
||||
| `recordingId` | string | No | Specific recording file ID to delete. If not provided, deletes all recordings. |
|
||||
| `action` | string | No | Delete action: "trash" \(move to trash\) or "delete" \(permanently delete\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the recording was deleted successfully |
|
||||
|
||||
### `zoom_list_past_participants`
|
||||
|
||||
List participants from a past Zoom meeting
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `meetingId` | string | Yes | The past meeting ID or UUID |
|
||||
| `pageSize` | number | No | Number of records per page \(max 300\) |
|
||||
| `nextPageToken` | string | No | Token for pagination to get next page of results |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `participants` | array | List of meeting participants |
|
||||
| `pageInfo` | object | Pagination information |
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- Category: `tools`
|
||||
- Type: `zoom`
|
||||
167
apps/sim/app/api/auth/oauth2/callback/shopify/route.ts
Normal file
167
apps/sim/app/api/auth/oauth2/callback/shopify/route.ts
Normal file
@@ -0,0 +1,167 @@
|
||||
import crypto from 'crypto'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import { getBaseUrl } from '@/lib/core/utils/urls'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
|
||||
const logger = createLogger('ShopifyCallback')
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
const SHOP_DOMAIN_REGEX = /^[a-zA-Z0-9][a-zA-Z0-9-]*\.myshopify\.com$/
|
||||
|
||||
/**
|
||||
* Validates the HMAC signature from Shopify to ensure the request is authentic
|
||||
* @see https://shopify.dev/docs/apps/build/authentication-authorization/access-tokens/offline-access-tokens
|
||||
*/
|
||||
function validateHmac(searchParams: URLSearchParams, clientSecret: string): boolean {
|
||||
const hmac = searchParams.get('hmac')
|
||||
if (!hmac) {
|
||||
return false
|
||||
}
|
||||
|
||||
const params: Record<string, string> = {}
|
||||
searchParams.forEach((value, key) => {
|
||||
if (key !== 'hmac') {
|
||||
params[key] = value
|
||||
}
|
||||
})
|
||||
|
||||
const message = Object.keys(params)
|
||||
.sort()
|
||||
.map((key) => `${key}=${params[key]}`)
|
||||
.join('&')
|
||||
|
||||
const generatedHmac = crypto.createHmac('sha256', clientSecret).update(message).digest('hex')
|
||||
|
||||
try {
|
||||
return crypto.timingSafeEqual(Buffer.from(hmac, 'hex'), Buffer.from(generatedHmac, 'hex'))
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const baseUrl = getBaseUrl()
|
||||
|
||||
try {
|
||||
const session = await getSession()
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.redirect(`${baseUrl}/workspace?error=unauthorized`)
|
||||
}
|
||||
|
||||
const { searchParams } = request.nextUrl
|
||||
const code = searchParams.get('code')
|
||||
const state = searchParams.get('state')
|
||||
const shop = searchParams.get('shop')
|
||||
|
||||
const storedState = request.cookies.get('shopify_oauth_state')?.value
|
||||
const storedShop = request.cookies.get('shopify_shop_domain')?.value
|
||||
|
||||
const clientId = env.SHOPIFY_CLIENT_ID
|
||||
const clientSecret = env.SHOPIFY_CLIENT_SECRET
|
||||
|
||||
if (!clientId || !clientSecret) {
|
||||
logger.error('Shopify credentials not configured')
|
||||
return NextResponse.redirect(`${baseUrl}/workspace?error=shopify_config_error`)
|
||||
}
|
||||
|
||||
if (!validateHmac(searchParams, clientSecret)) {
|
||||
logger.error('HMAC validation failed in Shopify OAuth callback')
|
||||
return NextResponse.redirect(`${baseUrl}/workspace?error=shopify_hmac_invalid`)
|
||||
}
|
||||
|
||||
if (!state || state !== storedState) {
|
||||
logger.error('State mismatch in Shopify OAuth callback')
|
||||
return NextResponse.redirect(`${baseUrl}/workspace?error=shopify_state_mismatch`)
|
||||
}
|
||||
|
||||
if (!code) {
|
||||
logger.error('No code received from Shopify')
|
||||
return NextResponse.redirect(`${baseUrl}/workspace?error=shopify_no_code`)
|
||||
}
|
||||
|
||||
const shopDomain = shop || storedShop
|
||||
if (!shopDomain) {
|
||||
logger.error('No shop domain available')
|
||||
return NextResponse.redirect(`${baseUrl}/workspace?error=shopify_no_shop`)
|
||||
}
|
||||
|
||||
if (!SHOP_DOMAIN_REGEX.test(shopDomain)) {
|
||||
logger.error('Invalid shop domain format:', { shopDomain })
|
||||
return NextResponse.redirect(`${baseUrl}/workspace?error=shopify_invalid_shop`)
|
||||
}
|
||||
|
||||
const tokenResponse = await fetch(`https://${shopDomain}/admin/oauth/access_token`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret,
|
||||
code: code,
|
||||
}),
|
||||
})
|
||||
|
||||
if (!tokenResponse.ok) {
|
||||
const errorText = await tokenResponse.text()
|
||||
logger.error('Failed to exchange code for token:', {
|
||||
status: tokenResponse.status,
|
||||
body: errorText,
|
||||
})
|
||||
return NextResponse.redirect(`${baseUrl}/workspace?error=shopify_token_error`)
|
||||
}
|
||||
|
||||
const tokenData = await tokenResponse.json()
|
||||
const accessToken = tokenData.access_token
|
||||
const scope = tokenData.scope
|
||||
|
||||
logger.info('Shopify token exchange successful:', {
|
||||
hasAccessToken: !!accessToken,
|
||||
scope: scope,
|
||||
})
|
||||
|
||||
if (!accessToken) {
|
||||
logger.error('No access token in response')
|
||||
return NextResponse.redirect(`${baseUrl}/workspace?error=shopify_no_token`)
|
||||
}
|
||||
|
||||
const storeUrl = new URL(`${baseUrl}/api/auth/oauth2/shopify/store`)
|
||||
|
||||
const response = NextResponse.redirect(storeUrl)
|
||||
|
||||
response.cookies.set('shopify_pending_token', accessToken, {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
sameSite: 'lax',
|
||||
maxAge: 60,
|
||||
path: '/',
|
||||
})
|
||||
|
||||
response.cookies.set('shopify_pending_shop', shopDomain, {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
sameSite: 'lax',
|
||||
maxAge: 60,
|
||||
path: '/',
|
||||
})
|
||||
|
||||
response.cookies.set('shopify_pending_scope', scope || '', {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
sameSite: 'lax',
|
||||
maxAge: 60,
|
||||
path: '/',
|
||||
})
|
||||
|
||||
response.cookies.delete('shopify_oauth_state')
|
||||
response.cookies.delete('shopify_shop_domain')
|
||||
|
||||
return response
|
||||
} catch (error) {
|
||||
logger.error('Error in Shopify OAuth callback:', error)
|
||||
return NextResponse.redirect(`${baseUrl}/workspace?error=shopify_callback_error`)
|
||||
}
|
||||
}
|
||||
96
apps/sim/app/api/auth/oauth2/shopify/store/route.ts
Normal file
96
apps/sim/app/api/auth/oauth2/shopify/store/route.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import { db } from '@sim/db'
|
||||
import { account } from '@sim/db/schema'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { getBaseUrl } from '@/lib/core/utils/urls'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
|
||||
const logger = createLogger('ShopifyStore')
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const baseUrl = getBaseUrl()
|
||||
|
||||
try {
|
||||
const session = await getSession()
|
||||
if (!session?.user?.id) {
|
||||
logger.warn('Unauthorized attempt to store Shopify token')
|
||||
return NextResponse.redirect(`${baseUrl}/workspace?error=unauthorized`)
|
||||
}
|
||||
|
||||
const accessToken = request.cookies.get('shopify_pending_token')?.value
|
||||
const shopDomain = request.cookies.get('shopify_pending_shop')?.value
|
||||
const scope = request.cookies.get('shopify_pending_scope')?.value
|
||||
|
||||
if (!accessToken || !shopDomain) {
|
||||
logger.error('Missing token or shop domain in cookies')
|
||||
return NextResponse.redirect(`${baseUrl}/workspace?error=shopify_missing_data`)
|
||||
}
|
||||
|
||||
const shopResponse = await fetch(`https://${shopDomain}/admin/api/2024-10/shop.json`, {
|
||||
headers: {
|
||||
'X-Shopify-Access-Token': accessToken,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
if (!shopResponse.ok) {
|
||||
const errorText = await shopResponse.text()
|
||||
logger.error('Invalid Shopify token', {
|
||||
status: shopResponse.status,
|
||||
error: errorText,
|
||||
})
|
||||
return NextResponse.redirect(`${baseUrl}/workspace?error=shopify_invalid_token`)
|
||||
}
|
||||
|
||||
const shopData = await shopResponse.json()
|
||||
const shopInfo = shopData.shop
|
||||
|
||||
const existing = await db.query.account.findFirst({
|
||||
where: and(eq(account.userId, session.user.id), eq(account.providerId, 'shopify')),
|
||||
})
|
||||
|
||||
const now = new Date()
|
||||
|
||||
const accountData = {
|
||||
accessToken: accessToken,
|
||||
accountId: shopInfo.id?.toString() || shopDomain,
|
||||
scope: scope || '',
|
||||
updatedAt: now,
|
||||
idToken: shopDomain,
|
||||
}
|
||||
|
||||
if (existing) {
|
||||
await db.update(account).set(accountData).where(eq(account.id, existing.id))
|
||||
logger.info('Updated existing Shopify account', { accountId: existing.id })
|
||||
} else {
|
||||
await db.insert(account).values({
|
||||
id: `shopify_${session.user.id}_${Date.now()}`,
|
||||
userId: session.user.id,
|
||||
providerId: 'shopify',
|
||||
...accountData,
|
||||
createdAt: now,
|
||||
})
|
||||
logger.info('Created new Shopify account for user', { userId: session.user.id })
|
||||
}
|
||||
|
||||
const returnUrl = request.cookies.get('shopify_return_url')?.value
|
||||
|
||||
const redirectUrl = returnUrl || `${baseUrl}/workspace`
|
||||
const finalUrl = new URL(redirectUrl)
|
||||
finalUrl.searchParams.set('shopify_connected', 'true')
|
||||
|
||||
const response = NextResponse.redirect(finalUrl.toString())
|
||||
response.cookies.delete('shopify_pending_token')
|
||||
response.cookies.delete('shopify_pending_shop')
|
||||
response.cookies.delete('shopify_pending_scope')
|
||||
response.cookies.delete('shopify_return_url')
|
||||
|
||||
return response
|
||||
} catch (error) {
|
||||
logger.error('Error storing Shopify token:', error)
|
||||
return NextResponse.redirect(`${baseUrl}/workspace?error=shopify_store_error`)
|
||||
}
|
||||
}
|
||||
215
apps/sim/app/api/auth/shopify/authorize/route.ts
Normal file
215
apps/sim/app/api/auth/shopify/authorize/route.ts
Normal file
@@ -0,0 +1,215 @@
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import { getBaseUrl } from '@/lib/core/utils/urls'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
|
||||
const logger = createLogger('ShopifyAuthorize')
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
const SHOPIFY_SCOPES = [
|
||||
'write_products',
|
||||
'write_orders',
|
||||
'write_customers',
|
||||
'write_inventory',
|
||||
'read_locations',
|
||||
'write_merchant_managed_fulfillment_orders',
|
||||
].join(',')
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const session = await getSession()
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
const clientId = env.SHOPIFY_CLIENT_ID
|
||||
|
||||
if (!clientId) {
|
||||
logger.error('SHOPIFY_CLIENT_ID not configured')
|
||||
return NextResponse.json({ error: 'Shopify client ID not configured' }, { status: 500 })
|
||||
}
|
||||
|
||||
const shopDomain = request.nextUrl.searchParams.get('shop')
|
||||
const returnUrl = request.nextUrl.searchParams.get('returnUrl')
|
||||
|
||||
if (!shopDomain) {
|
||||
const returnUrlParam = returnUrl ? encodeURIComponent(returnUrl) : ''
|
||||
return new NextResponse(
|
||||
`<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Connect Shopify Store</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
background: linear-gradient(135deg, #96BF48 0%, #5C8A23 100%);
|
||||
}
|
||||
.container {
|
||||
background: white;
|
||||
padding: 2rem;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 10px 40px rgba(0,0,0,0.1);
|
||||
text-align: center;
|
||||
max-width: 400px;
|
||||
width: 90%;
|
||||
}
|
||||
h2 {
|
||||
color: #111827;
|
||||
margin: 0 0 0.5rem 0;
|
||||
}
|
||||
p {
|
||||
color: #6b7280;
|
||||
margin: 0 0 1.5rem 0;
|
||||
}
|
||||
input {
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid #d1d5db;
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
input:focus {
|
||||
outline: none;
|
||||
border-color: #96BF48;
|
||||
box-shadow: 0 0 0 3px rgba(150, 191, 72, 0.2);
|
||||
}
|
||||
button {
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
background: #96BF48;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
}
|
||||
button:hover {
|
||||
background: #7FA93D;
|
||||
}
|
||||
.help {
|
||||
font-size: 0.875rem;
|
||||
color: #9ca3af;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h2>Connect Your Shopify Store</h2>
|
||||
<p>Enter your Shopify store domain to continue</p>
|
||||
<form onsubmit="handleSubmit(event)">
|
||||
<input
|
||||
type="text"
|
||||
id="shop"
|
||||
placeholder="mystore.myshopify.com"
|
||||
required
|
||||
pattern="[a-zA-Z0-9-]+\\.myshopify\\.com"
|
||||
/>
|
||||
<button type="submit">Connect Store</button>
|
||||
</form>
|
||||
<p class="help">Your store domain looks like: yourstore.myshopify.com</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const returnUrl = '${returnUrlParam}';
|
||||
function handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
let shop = document.getElementById('shop').value.trim().toLowerCase();
|
||||
|
||||
// Clean up the shop domain
|
||||
shop = shop.replace('https://', '').replace('http://', '');
|
||||
if (!shop.endsWith('.myshopify.com')) {
|
||||
shop = shop.replace('.myshopify.com', '') + '.myshopify.com';
|
||||
}
|
||||
|
||||
let url = window.location.pathname + '?shop=' + encodeURIComponent(shop);
|
||||
if (returnUrl) {
|
||||
url += '&returnUrl=' + returnUrl;
|
||||
}
|
||||
window.location.href = url;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>`,
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'text/html; charset=utf-8',
|
||||
'Cache-Control': 'no-store, no-cache, must-revalidate',
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
let cleanShop = shopDomain.toLowerCase().trim()
|
||||
cleanShop = cleanShop.replace('https://', '').replace('http://', '')
|
||||
if (!cleanShop.endsWith('.myshopify.com')) {
|
||||
cleanShop = `${cleanShop.replace('.myshopify.com', '')}.myshopify.com`
|
||||
}
|
||||
|
||||
const baseUrl = getBaseUrl()
|
||||
const redirectUri = `${baseUrl}/api/auth/oauth2/callback/shopify`
|
||||
|
||||
const state = crypto.randomUUID()
|
||||
|
||||
const oauthUrl =
|
||||
`https://${cleanShop}/admin/oauth/authorize?` +
|
||||
new URLSearchParams({
|
||||
client_id: clientId,
|
||||
scope: SHOPIFY_SCOPES,
|
||||
redirect_uri: redirectUri,
|
||||
state: state,
|
||||
}).toString()
|
||||
|
||||
logger.info('Initiating Shopify OAuth:', {
|
||||
shop: cleanShop,
|
||||
requestedScopes: SHOPIFY_SCOPES,
|
||||
redirectUri,
|
||||
returnUrl: returnUrl || 'not specified',
|
||||
})
|
||||
|
||||
const response = NextResponse.redirect(oauthUrl)
|
||||
|
||||
response.cookies.set('shopify_oauth_state', state, {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
sameSite: 'lax',
|
||||
maxAge: 60 * 10,
|
||||
path: '/',
|
||||
})
|
||||
|
||||
response.cookies.set('shopify_shop_domain', cleanShop, {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
sameSite: 'lax',
|
||||
maxAge: 60 * 10,
|
||||
path: '/',
|
||||
})
|
||||
|
||||
if (returnUrl) {
|
||||
response.cookies.set('shopify_return_url', returnUrl, {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
sameSite: 'lax',
|
||||
maxAge: 60 * 10,
|
||||
path: '/',
|
||||
})
|
||||
}
|
||||
|
||||
return response
|
||||
} catch (error) {
|
||||
logger.error('Error initiating Shopify authorization:', error)
|
||||
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
104
apps/sim/app/api/tools/ssh/check-command-exists/route.ts
Normal file
104
apps/sim/app/api/tools/ssh/check-command-exists/route.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import { randomUUID } from 'crypto'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { createSSHConnection, escapeShellArg, executeSSHCommand } from '@/app/api/tools/ssh/utils'
|
||||
|
||||
const logger = createLogger('SSHCheckCommandExistsAPI')
|
||||
|
||||
const CheckCommandExistsSchema = z.object({
|
||||
host: z.string().min(1, 'Host is required'),
|
||||
port: z.coerce.number().int().positive().default(22),
|
||||
username: z.string().min(1, 'Username is required'),
|
||||
password: z.string().nullish(),
|
||||
privateKey: z.string().nullish(),
|
||||
passphrase: z.string().nullish(),
|
||||
commandName: z.string().min(1, 'Command name is required'),
|
||||
})
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const requestId = randomUUID().slice(0, 8)
|
||||
|
||||
try {
|
||||
const body = await request.json()
|
||||
const params = CheckCommandExistsSchema.parse(body)
|
||||
|
||||
if (!params.password && !params.privateKey) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Either password or privateKey must be provided' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`[${requestId}] Checking if command '${params.commandName}' exists on ${params.host}:${params.port}`
|
||||
)
|
||||
|
||||
const client = await createSSHConnection({
|
||||
host: params.host,
|
||||
port: params.port,
|
||||
username: params.username,
|
||||
password: params.password,
|
||||
privateKey: params.privateKey,
|
||||
passphrase: params.passphrase,
|
||||
})
|
||||
|
||||
try {
|
||||
const escapedCommand = escapeShellArg(params.commandName)
|
||||
|
||||
const result = await executeSSHCommand(
|
||||
client,
|
||||
`command -v '${escapedCommand}' 2>/dev/null || which '${escapedCommand}' 2>/dev/null`
|
||||
)
|
||||
|
||||
const exists = result.exitCode === 0 && result.stdout.trim().length > 0
|
||||
const path = exists ? result.stdout.trim() : undefined
|
||||
|
||||
let version: string | undefined
|
||||
if (exists) {
|
||||
try {
|
||||
const versionResult = await executeSSHCommand(
|
||||
client,
|
||||
`'${escapedCommand}' --version 2>&1 | head -1 || '${escapedCommand}' -v 2>&1 | head -1`
|
||||
)
|
||||
if (versionResult.exitCode === 0 && versionResult.stdout.trim()) {
|
||||
version = versionResult.stdout.trim()
|
||||
}
|
||||
} catch {
|
||||
// Version check failed, that's okay
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`[${requestId}] Command '${params.commandName}' ${exists ? 'exists' : 'does not exist'}`
|
||||
)
|
||||
|
||||
return NextResponse.json({
|
||||
exists,
|
||||
path,
|
||||
version,
|
||||
message: exists
|
||||
? `Command '${params.commandName}' found at ${path}`
|
||||
: `Command '${params.commandName}' not found`,
|
||||
})
|
||||
} finally {
|
||||
client.end()
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
logger.warn(`[${requestId}] Invalid request data`, { errors: error.errors })
|
||||
return NextResponse.json(
|
||||
{ error: 'Invalid request data', details: error.errors },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'
|
||||
logger.error(`[${requestId}] SSH check command exists failed:`, error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `SSH check command exists failed: ${errorMessage}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
134
apps/sim/app/api/tools/ssh/check-file-exists/route.ts
Normal file
134
apps/sim/app/api/tools/ssh/check-file-exists/route.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
import { randomUUID } from 'crypto'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import type { Client, SFTPWrapper, Stats } from 'ssh2'
|
||||
import { z } from 'zod'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import {
|
||||
createSSHConnection,
|
||||
getFileType,
|
||||
parsePermissions,
|
||||
sanitizePath,
|
||||
} from '@/app/api/tools/ssh/utils'
|
||||
|
||||
const logger = createLogger('SSHCheckFileExistsAPI')
|
||||
|
||||
const CheckFileExistsSchema = z.object({
|
||||
host: z.string().min(1, 'Host is required'),
|
||||
port: z.coerce.number().int().positive().default(22),
|
||||
username: z.string().min(1, 'Username is required'),
|
||||
password: z.string().nullish(),
|
||||
privateKey: z.string().nullish(),
|
||||
passphrase: z.string().nullish(),
|
||||
path: z.string().min(1, 'Path is required'),
|
||||
type: z.enum(['file', 'directory', 'any']).default('any'),
|
||||
})
|
||||
|
||||
function getSFTP(client: Client): Promise<SFTPWrapper> {
|
||||
return new Promise((resolve, reject) => {
|
||||
client.sftp((err, sftp) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve(sftp)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const requestId = randomUUID().slice(0, 8)
|
||||
|
||||
try {
|
||||
const body = await request.json()
|
||||
const params = CheckFileExistsSchema.parse(body)
|
||||
|
||||
// Validate authentication
|
||||
if (!params.password && !params.privateKey) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Either password or privateKey must be provided' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`[${requestId}] Checking if path exists: ${params.path} on ${params.host}:${params.port}`
|
||||
)
|
||||
|
||||
const client = await createSSHConnection({
|
||||
host: params.host,
|
||||
port: params.port,
|
||||
username: params.username,
|
||||
password: params.password,
|
||||
privateKey: params.privateKey,
|
||||
passphrase: params.passphrase,
|
||||
})
|
||||
|
||||
try {
|
||||
const sftp = await getSFTP(client)
|
||||
const filePath = sanitizePath(params.path)
|
||||
|
||||
const stats = await new Promise<Stats | null>((resolve) => {
|
||||
sftp.stat(filePath, (err, stats) => {
|
||||
if (err) {
|
||||
resolve(null)
|
||||
} else {
|
||||
resolve(stats)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
if (!stats) {
|
||||
logger.info(`[${requestId}] Path does not exist: ${filePath}`)
|
||||
return NextResponse.json({
|
||||
exists: false,
|
||||
type: 'not_found',
|
||||
message: `Path does not exist: ${filePath}`,
|
||||
})
|
||||
}
|
||||
|
||||
const fileType = getFileType(stats)
|
||||
|
||||
// Check if the type matches the expected type
|
||||
if (params.type !== 'any' && fileType !== params.type) {
|
||||
logger.info(`[${requestId}] Path exists but is not a ${params.type}: ${filePath}`)
|
||||
return NextResponse.json({
|
||||
exists: false,
|
||||
type: fileType,
|
||||
size: stats.size,
|
||||
permissions: parsePermissions(stats.mode),
|
||||
modified: new Date((stats.mtime || 0) * 1000).toISOString(),
|
||||
message: `Path exists but is a ${fileType}, not a ${params.type}`,
|
||||
})
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] Path exists: ${filePath} (${fileType})`)
|
||||
|
||||
return NextResponse.json({
|
||||
exists: true,
|
||||
type: fileType,
|
||||
size: stats.size,
|
||||
permissions: parsePermissions(stats.mode),
|
||||
modified: new Date((stats.mtime || 0) * 1000).toISOString(),
|
||||
message: `Path exists: ${filePath} (${fileType})`,
|
||||
})
|
||||
} finally {
|
||||
client.end()
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
logger.warn(`[${requestId}] Invalid request data`, { errors: error.errors })
|
||||
return NextResponse.json(
|
||||
{ error: 'Invalid request data', details: error.errors },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'
|
||||
logger.error(`[${requestId}] SSH check file exists failed:`, error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `SSH check file exists failed: ${errorMessage}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
110
apps/sim/app/api/tools/ssh/create-directory/route.ts
Normal file
110
apps/sim/app/api/tools/ssh/create-directory/route.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import { randomUUID } from 'crypto'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import {
|
||||
createSSHConnection,
|
||||
escapeShellArg,
|
||||
executeSSHCommand,
|
||||
sanitizePath,
|
||||
} from '@/app/api/tools/ssh/utils'
|
||||
|
||||
const logger = createLogger('SSHCreateDirectoryAPI')
|
||||
|
||||
const CreateDirectorySchema = z.object({
|
||||
host: z.string().min(1, 'Host is required'),
|
||||
port: z.coerce.number().int().positive().default(22),
|
||||
username: z.string().min(1, 'Username is required'),
|
||||
password: z.string().nullish(),
|
||||
privateKey: z.string().nullish(),
|
||||
passphrase: z.string().nullish(),
|
||||
path: z.string().min(1, 'Path is required'),
|
||||
recursive: z.boolean().default(true),
|
||||
permissions: z.string().default('0755'),
|
||||
})
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const requestId = randomUUID().slice(0, 8)
|
||||
|
||||
try {
|
||||
const body = await request.json()
|
||||
const params = CreateDirectorySchema.parse(body)
|
||||
|
||||
// Validate authentication
|
||||
if (!params.password && !params.privateKey) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Either password or privateKey must be provided' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] Creating directory ${params.path} on ${params.host}:${params.port}`)
|
||||
|
||||
const client = await createSSHConnection({
|
||||
host: params.host,
|
||||
port: params.port,
|
||||
username: params.username,
|
||||
password: params.password,
|
||||
privateKey: params.privateKey,
|
||||
passphrase: params.passphrase,
|
||||
})
|
||||
|
||||
try {
|
||||
const dirPath = sanitizePath(params.path)
|
||||
const escapedPath = escapeShellArg(dirPath)
|
||||
|
||||
// Check if directory already exists
|
||||
const checkResult = await executeSSHCommand(
|
||||
client,
|
||||
`test -d '${escapedPath}' && echo "exists"`
|
||||
)
|
||||
const alreadyExists = checkResult.stdout.trim() === 'exists'
|
||||
|
||||
if (alreadyExists) {
|
||||
logger.info(`[${requestId}] Directory already exists: ${dirPath}`)
|
||||
return NextResponse.json({
|
||||
created: false,
|
||||
path: dirPath,
|
||||
alreadyExists: true,
|
||||
message: `Directory already exists: ${dirPath}`,
|
||||
})
|
||||
}
|
||||
|
||||
// Create directory
|
||||
const mkdirFlag = params.recursive ? '-p' : ''
|
||||
const command = `mkdir ${mkdirFlag} -m ${params.permissions} '${escapedPath}'`
|
||||
const result = await executeSSHCommand(client, command)
|
||||
|
||||
if (result.exitCode !== 0) {
|
||||
throw new Error(result.stderr || 'Failed to create directory')
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] Directory created successfully: ${dirPath}`)
|
||||
|
||||
return NextResponse.json({
|
||||
created: true,
|
||||
path: dirPath,
|
||||
alreadyExists: false,
|
||||
message: `Directory created successfully: ${dirPath}`,
|
||||
})
|
||||
} finally {
|
||||
client.end()
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
logger.warn(`[${requestId}] Invalid request data`, { errors: error.errors })
|
||||
return NextResponse.json(
|
||||
{ error: 'Invalid request data', details: error.errors },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'
|
||||
logger.error(`[${requestId}] SSH create directory failed:`, error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `SSH create directory failed: ${errorMessage}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
103
apps/sim/app/api/tools/ssh/delete-file/route.ts
Normal file
103
apps/sim/app/api/tools/ssh/delete-file/route.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import { randomUUID } from 'crypto'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import {
|
||||
createSSHConnection,
|
||||
escapeShellArg,
|
||||
executeSSHCommand,
|
||||
sanitizePath,
|
||||
} from '@/app/api/tools/ssh/utils'
|
||||
|
||||
const logger = createLogger('SSHDeleteFileAPI')
|
||||
|
||||
const DeleteFileSchema = z.object({
|
||||
host: z.string().min(1, 'Host is required'),
|
||||
port: z.coerce.number().int().positive().default(22),
|
||||
username: z.string().min(1, 'Username is required'),
|
||||
password: z.string().nullish(),
|
||||
privateKey: z.string().nullish(),
|
||||
passphrase: z.string().nullish(),
|
||||
path: z.string().min(1, 'Path is required'),
|
||||
recursive: z.boolean().default(false),
|
||||
force: z.boolean().default(false),
|
||||
})
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const requestId = randomUUID().slice(0, 8)
|
||||
|
||||
try {
|
||||
const body = await request.json()
|
||||
const params = DeleteFileSchema.parse(body)
|
||||
|
||||
// Validate authentication
|
||||
if (!params.password && !params.privateKey) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Either password or privateKey must be provided' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] Deleting ${params.path} on ${params.host}:${params.port}`)
|
||||
|
||||
const client = await createSSHConnection({
|
||||
host: params.host,
|
||||
port: params.port,
|
||||
username: params.username,
|
||||
password: params.password,
|
||||
privateKey: params.privateKey,
|
||||
passphrase: params.passphrase,
|
||||
})
|
||||
|
||||
try {
|
||||
const filePath = sanitizePath(params.path)
|
||||
const escapedPath = escapeShellArg(filePath)
|
||||
|
||||
// Check if path exists
|
||||
const checkResult = await executeSSHCommand(
|
||||
client,
|
||||
`test -e '${escapedPath}' && echo "exists"`
|
||||
)
|
||||
if (checkResult.stdout.trim() !== 'exists') {
|
||||
return NextResponse.json({ error: `Path does not exist: ${filePath}` }, { status: 404 })
|
||||
}
|
||||
|
||||
// Build delete command
|
||||
let command: string
|
||||
if (params.recursive) {
|
||||
command = params.force ? `rm -rf '${escapedPath}'` : `rm -r '${escapedPath}'`
|
||||
} else {
|
||||
command = params.force ? `rm -f '${escapedPath}'` : `rm '${escapedPath}'`
|
||||
}
|
||||
|
||||
const result = await executeSSHCommand(client, command)
|
||||
|
||||
if (result.exitCode !== 0) {
|
||||
throw new Error(result.stderr || 'Failed to delete path')
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] Path deleted successfully: ${filePath}`)
|
||||
|
||||
return NextResponse.json({
|
||||
deleted: true,
|
||||
path: filePath,
|
||||
message: `Successfully deleted: ${filePath}`,
|
||||
})
|
||||
} finally {
|
||||
client.end()
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
logger.warn(`[${requestId}] Invalid request data`, { errors: error.errors })
|
||||
return NextResponse.json(
|
||||
{ error: 'Invalid request data', details: error.errors },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'
|
||||
logger.error(`[${requestId}] SSH delete file failed:`, error)
|
||||
|
||||
return NextResponse.json({ error: `SSH delete file failed: ${errorMessage}` }, { status: 500 })
|
||||
}
|
||||
}
|
||||
127
apps/sim/app/api/tools/ssh/download-file/route.ts
Normal file
127
apps/sim/app/api/tools/ssh/download-file/route.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import { randomUUID } from 'crypto'
|
||||
import path from 'path'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import type { Client, SFTPWrapper } from 'ssh2'
|
||||
import { z } from 'zod'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { createSSHConnection, sanitizePath } from '@/app/api/tools/ssh/utils'
|
||||
|
||||
const logger = createLogger('SSHDownloadFileAPI')
|
||||
|
||||
const DownloadFileSchema = z.object({
|
||||
host: z.string().min(1, 'Host is required'),
|
||||
port: z.coerce.number().int().positive().default(22),
|
||||
username: z.string().min(1, 'Username is required'),
|
||||
password: z.string().nullish(),
|
||||
privateKey: z.string().nullish(),
|
||||
passphrase: z.string().nullish(),
|
||||
remotePath: z.string().min(1, 'Remote path is required'),
|
||||
})
|
||||
|
||||
function getSFTP(client: Client): Promise<SFTPWrapper> {
|
||||
return new Promise((resolve, reject) => {
|
||||
client.sftp((err, sftp) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve(sftp)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const requestId = randomUUID().slice(0, 8)
|
||||
|
||||
try {
|
||||
const body = await request.json()
|
||||
const params = DownloadFileSchema.parse(body)
|
||||
|
||||
// Validate authentication
|
||||
if (!params.password && !params.privateKey) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Either password or privateKey must be provided' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`[${requestId}] Downloading file from ${params.host}:${params.port}${params.remotePath}`
|
||||
)
|
||||
|
||||
const client = await createSSHConnection({
|
||||
host: params.host,
|
||||
port: params.port,
|
||||
username: params.username,
|
||||
password: params.password,
|
||||
privateKey: params.privateKey,
|
||||
passphrase: params.passphrase,
|
||||
})
|
||||
|
||||
try {
|
||||
const sftp = await getSFTP(client)
|
||||
const remotePath = sanitizePath(params.remotePath)
|
||||
|
||||
// Check if file exists
|
||||
const stats = await new Promise<{ size: number }>((resolve, reject) => {
|
||||
sftp.stat(remotePath, (err, stats) => {
|
||||
if (err) {
|
||||
reject(new Error(`File not found: ${remotePath}`))
|
||||
} else {
|
||||
resolve(stats)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// Read file content
|
||||
const content = await new Promise<Buffer>((resolve, reject) => {
|
||||
const chunks: Buffer[] = []
|
||||
const readStream = sftp.createReadStream(remotePath)
|
||||
|
||||
readStream.on('data', (chunk: Buffer) => {
|
||||
chunks.push(chunk)
|
||||
})
|
||||
|
||||
readStream.on('end', () => {
|
||||
resolve(Buffer.concat(chunks))
|
||||
})
|
||||
|
||||
readStream.on('error', reject)
|
||||
})
|
||||
|
||||
const fileName = path.basename(remotePath)
|
||||
|
||||
// Encode content as base64 for binary safety
|
||||
const base64Content = content.toString('base64')
|
||||
|
||||
logger.info(`[${requestId}] File downloaded successfully from ${remotePath}`)
|
||||
|
||||
return NextResponse.json({
|
||||
downloaded: true,
|
||||
content: base64Content,
|
||||
fileName: fileName,
|
||||
remotePath: remotePath,
|
||||
size: stats.size,
|
||||
message: `File downloaded successfully from ${remotePath}`,
|
||||
})
|
||||
} finally {
|
||||
client.end()
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
logger.warn(`[${requestId}] Invalid request data`, { errors: error.errors })
|
||||
return NextResponse.json(
|
||||
{ error: 'Invalid request data', details: error.errors },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'
|
||||
logger.error(`[${requestId}] SSH file download failed:`, error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `SSH file download failed: ${errorMessage}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
84
apps/sim/app/api/tools/ssh/execute-command/route.ts
Normal file
84
apps/sim/app/api/tools/ssh/execute-command/route.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { randomUUID } from 'crypto'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { createSSHConnection, executeSSHCommand, sanitizeCommand } from '@/app/api/tools/ssh/utils'
|
||||
|
||||
const logger = createLogger('SSHExecuteCommandAPI')
|
||||
|
||||
const ExecuteCommandSchema = z.object({
|
||||
host: z.string().min(1, 'Host is required'),
|
||||
port: z.coerce.number().int().positive().default(22),
|
||||
username: z.string().min(1, 'Username is required'),
|
||||
password: z.string().nullish(),
|
||||
privateKey: z.string().nullish(),
|
||||
passphrase: z.string().nullish(),
|
||||
command: z.string().min(1, 'Command is required'),
|
||||
workingDirectory: z.string().nullish(),
|
||||
})
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const requestId = randomUUID().slice(0, 8)
|
||||
|
||||
try {
|
||||
const body = await request.json()
|
||||
const params = ExecuteCommandSchema.parse(body)
|
||||
|
||||
// Validate authentication
|
||||
if (!params.password && !params.privateKey) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Either password or privateKey must be provided' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] Executing SSH command on ${params.host}:${params.port}`)
|
||||
|
||||
const client = await createSSHConnection({
|
||||
host: params.host,
|
||||
port: params.port,
|
||||
username: params.username,
|
||||
password: params.password,
|
||||
privateKey: params.privateKey,
|
||||
passphrase: params.passphrase,
|
||||
})
|
||||
|
||||
try {
|
||||
// Build command with optional working directory
|
||||
let command = sanitizeCommand(params.command)
|
||||
if (params.workingDirectory) {
|
||||
command = `cd "${params.workingDirectory}" && ${command}`
|
||||
}
|
||||
|
||||
const result = await executeSSHCommand(client, command)
|
||||
|
||||
logger.info(`[${requestId}] Command executed successfully with exit code ${result.exitCode}`)
|
||||
|
||||
return NextResponse.json({
|
||||
stdout: result.stdout,
|
||||
stderr: result.stderr,
|
||||
exitCode: result.exitCode,
|
||||
success: result.exitCode === 0,
|
||||
message: `Command executed with exit code ${result.exitCode}`,
|
||||
})
|
||||
} finally {
|
||||
client.end()
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
logger.warn(`[${requestId}] Invalid request data`, { errors: error.errors })
|
||||
return NextResponse.json(
|
||||
{ error: 'Invalid request data', details: error.errors },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'
|
||||
logger.error(`[${requestId}] SSH command execution failed:`, error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `SSH command execution failed: ${errorMessage}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
104
apps/sim/app/api/tools/ssh/execute-script/route.ts
Normal file
104
apps/sim/app/api/tools/ssh/execute-script/route.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import { randomUUID } from 'crypto'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { createSSHConnection, escapeShellArg, executeSSHCommand } from '@/app/api/tools/ssh/utils'
|
||||
|
||||
const logger = createLogger('SSHExecuteScriptAPI')
|
||||
|
||||
const ExecuteScriptSchema = z.object({
|
||||
host: z.string().min(1, 'Host is required'),
|
||||
port: z.coerce.number().int().positive().default(22),
|
||||
username: z.string().min(1, 'Username is required'),
|
||||
password: z.string().nullish(),
|
||||
privateKey: z.string().nullish(),
|
||||
passphrase: z.string().nullish(),
|
||||
script: z.string().min(1, 'Script content is required'),
|
||||
interpreter: z.string().default('/bin/bash'),
|
||||
workingDirectory: z.string().nullish(),
|
||||
})
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const requestId = randomUUID().slice(0, 8)
|
||||
|
||||
try {
|
||||
const body = await request.json()
|
||||
const params = ExecuteScriptSchema.parse(body)
|
||||
|
||||
// Validate authentication
|
||||
if (!params.password && !params.privateKey) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Either password or privateKey must be provided' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] Executing SSH script on ${params.host}:${params.port}`)
|
||||
|
||||
const client = await createSSHConnection({
|
||||
host: params.host,
|
||||
port: params.port,
|
||||
username: params.username,
|
||||
password: params.password,
|
||||
privateKey: params.privateKey,
|
||||
passphrase: params.passphrase,
|
||||
})
|
||||
|
||||
try {
|
||||
// Create a temporary script file, execute it, and clean up
|
||||
const scriptPath = `/tmp/sim_script_${requestId}.sh`
|
||||
const escapedScriptPath = escapeShellArg(scriptPath)
|
||||
const escapedInterpreter = escapeShellArg(params.interpreter)
|
||||
|
||||
// Build the command to create, execute, and clean up the script
|
||||
// Note: heredoc with quoted delimiter ('SIMEOF') prevents variable expansion
|
||||
let command = `cat > '${escapedScriptPath}' << 'SIMEOF'
|
||||
${params.script}
|
||||
SIMEOF
|
||||
chmod +x '${escapedScriptPath}'`
|
||||
|
||||
if (params.workingDirectory) {
|
||||
const escapedWorkDir = escapeShellArg(params.workingDirectory)
|
||||
command += `
|
||||
cd '${escapedWorkDir}'`
|
||||
}
|
||||
|
||||
command += `
|
||||
'${escapedInterpreter}' '${escapedScriptPath}'
|
||||
exit_code=$?
|
||||
rm -f '${escapedScriptPath}'
|
||||
exit $exit_code`
|
||||
|
||||
const result = await executeSSHCommand(client, command)
|
||||
|
||||
logger.info(`[${requestId}] Script executed successfully with exit code ${result.exitCode}`)
|
||||
|
||||
return NextResponse.json({
|
||||
stdout: result.stdout,
|
||||
stderr: result.stderr,
|
||||
exitCode: result.exitCode,
|
||||
success: result.exitCode === 0,
|
||||
scriptPath: scriptPath,
|
||||
message: `Script executed with exit code ${result.exitCode}`,
|
||||
})
|
||||
} finally {
|
||||
client.end()
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
logger.warn(`[${requestId}] Invalid request data`, { errors: error.errors })
|
||||
return NextResponse.json(
|
||||
{ error: 'Invalid request data', details: error.errors },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'
|
||||
logger.error(`[${requestId}] SSH script execution failed:`, error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `SSH script execution failed: ${errorMessage}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
125
apps/sim/app/api/tools/ssh/get-system-info/route.ts
Normal file
125
apps/sim/app/api/tools/ssh/get-system-info/route.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
import { randomUUID } from 'crypto'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { createSSHConnection, executeSSHCommand } from '@/app/api/tools/ssh/utils'
|
||||
|
||||
const logger = createLogger('SSHGetSystemInfoAPI')
|
||||
|
||||
const GetSystemInfoSchema = z.object({
|
||||
host: z.string().min(1, 'Host is required'),
|
||||
port: z.coerce.number().int().positive().default(22),
|
||||
username: z.string().min(1, 'Username is required'),
|
||||
password: z.string().nullish(),
|
||||
privateKey: z.string().nullish(),
|
||||
passphrase: z.string().nullish(),
|
||||
})
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const requestId = randomUUID().slice(0, 8)
|
||||
|
||||
try {
|
||||
const body = await request.json()
|
||||
const params = GetSystemInfoSchema.parse(body)
|
||||
|
||||
// Validate authentication
|
||||
if (!params.password && !params.privateKey) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Either password or privateKey must be provided' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] Getting system info from ${params.host}:${params.port}`)
|
||||
|
||||
const client = await createSSHConnection({
|
||||
host: params.host,
|
||||
port: params.port,
|
||||
username: params.username,
|
||||
password: params.password,
|
||||
privateKey: params.privateKey,
|
||||
passphrase: params.passphrase,
|
||||
})
|
||||
|
||||
try {
|
||||
// Get hostname
|
||||
const hostnameResult = await executeSSHCommand(client, 'hostname')
|
||||
const hostname = hostnameResult.stdout.trim()
|
||||
|
||||
// Get OS info
|
||||
const osResult = await executeSSHCommand(client, 'uname -s')
|
||||
const os = osResult.stdout.trim()
|
||||
|
||||
// Get architecture
|
||||
const archResult = await executeSSHCommand(client, 'uname -m')
|
||||
const architecture = archResult.stdout.trim()
|
||||
|
||||
// Get uptime in seconds
|
||||
const uptimeResult = await executeSSHCommand(
|
||||
client,
|
||||
"cat /proc/uptime 2>/dev/null | awk '{print int($1)}' || sysctl -n kern.boottime 2>/dev/null | awk '{print int(($(date +%s)) - $4)}'"
|
||||
)
|
||||
const uptime = Number.parseInt(uptimeResult.stdout.trim()) || 0
|
||||
|
||||
// Get memory info
|
||||
const memoryResult = await executeSSHCommand(
|
||||
client,
|
||||
"free -b 2>/dev/null | awk '/Mem:/ {print $2, $7, $3}' || vm_stat 2>/dev/null | awk '/Pages free|Pages active|Pages speculative|Pages wired|page size/ {gsub(/[^0-9]/, \"\"); print}'"
|
||||
)
|
||||
const memParts = memoryResult.stdout.trim().split(/\s+/)
|
||||
let memory = { total: 0, free: 0, used: 0 }
|
||||
if (memParts.length >= 3) {
|
||||
memory = {
|
||||
total: Number.parseInt(memParts[0]) || 0,
|
||||
free: Number.parseInt(memParts[1]) || 0,
|
||||
used: Number.parseInt(memParts[2]) || 0,
|
||||
}
|
||||
}
|
||||
|
||||
// Get disk space
|
||||
const diskResult = await executeSSHCommand(
|
||||
client,
|
||||
"df -B1 / 2>/dev/null | awk 'NR==2 {print $2, $4, $3}' || df -k / 2>/dev/null | awk 'NR==2 {print $2*1024, $4*1024, $3*1024}'"
|
||||
)
|
||||
const diskParts = diskResult.stdout.trim().split(/\s+/)
|
||||
let diskSpace = { total: 0, free: 0, used: 0 }
|
||||
if (diskParts.length >= 3) {
|
||||
diskSpace = {
|
||||
total: Number.parseInt(diskParts[0]) || 0,
|
||||
free: Number.parseInt(diskParts[1]) || 0,
|
||||
used: Number.parseInt(diskParts[2]) || 0,
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] System info retrieved successfully`)
|
||||
|
||||
return NextResponse.json({
|
||||
hostname,
|
||||
os,
|
||||
architecture,
|
||||
uptime,
|
||||
memory,
|
||||
diskSpace,
|
||||
message: `System info retrieved for ${hostname}`,
|
||||
})
|
||||
} finally {
|
||||
client.end()
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
logger.warn(`[${requestId}] Invalid request data`, { errors: error.errors })
|
||||
return NextResponse.json(
|
||||
{ error: 'Invalid request data', details: error.errors },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'
|
||||
logger.error(`[${requestId}] SSH get system info failed:`, error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `SSH get system info failed: ${errorMessage}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
132
apps/sim/app/api/tools/ssh/list-directory/route.ts
Normal file
132
apps/sim/app/api/tools/ssh/list-directory/route.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
import { randomUUID } from 'crypto'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import type { Client, FileEntry, SFTPWrapper } from 'ssh2'
|
||||
import { z } from 'zod'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import {
|
||||
createSSHConnection,
|
||||
getFileType,
|
||||
parsePermissions,
|
||||
sanitizePath,
|
||||
} from '@/app/api/tools/ssh/utils'
|
||||
|
||||
const logger = createLogger('SSHListDirectoryAPI')
|
||||
|
||||
const ListDirectorySchema = z.object({
|
||||
host: z.string().min(1, 'Host is required'),
|
||||
port: z.coerce.number().int().positive().default(22),
|
||||
username: z.string().min(1, 'Username is required'),
|
||||
password: z.string().nullish(),
|
||||
privateKey: z.string().nullish(),
|
||||
passphrase: z.string().nullish(),
|
||||
path: z.string().min(1, 'Path is required'),
|
||||
detailed: z.boolean().default(true),
|
||||
recursive: z.boolean().default(false),
|
||||
})
|
||||
|
||||
function getSFTP(client: Client): Promise<SFTPWrapper> {
|
||||
return new Promise((resolve, reject) => {
|
||||
client.sftp((err, sftp) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve(sftp)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
interface FileInfo {
|
||||
name: string
|
||||
type: 'file' | 'directory' | 'symlink' | 'other'
|
||||
size: number
|
||||
permissions: string
|
||||
modified: string
|
||||
}
|
||||
|
||||
async function listDir(sftp: SFTPWrapper, dirPath: string): Promise<FileEntry[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
sftp.readdir(dirPath, (err, list) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve(list)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const requestId = randomUUID().slice(0, 8)
|
||||
|
||||
try {
|
||||
const body = await request.json()
|
||||
const params = ListDirectorySchema.parse(body)
|
||||
|
||||
// Validate authentication
|
||||
if (!params.password && !params.privateKey) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Either password or privateKey must be provided' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] Listing directory ${params.path} on ${params.host}:${params.port}`)
|
||||
|
||||
const client = await createSSHConnection({
|
||||
host: params.host,
|
||||
port: params.port,
|
||||
username: params.username,
|
||||
password: params.password,
|
||||
privateKey: params.privateKey,
|
||||
passphrase: params.passphrase,
|
||||
})
|
||||
|
||||
try {
|
||||
const sftp = await getSFTP(client)
|
||||
const dirPath = sanitizePath(params.path)
|
||||
|
||||
const list = await listDir(sftp, dirPath)
|
||||
|
||||
const entries: FileInfo[] = list.map((entry) => ({
|
||||
name: entry.filename,
|
||||
type: getFileType(entry.attrs),
|
||||
size: entry.attrs.size,
|
||||
permissions: parsePermissions(entry.attrs.mode),
|
||||
modified: new Date((entry.attrs.mtime || 0) * 1000).toISOString(),
|
||||
}))
|
||||
|
||||
const totalFiles = entries.filter((e) => e.type === 'file').length
|
||||
const totalDirectories = entries.filter((e) => e.type === 'directory').length
|
||||
|
||||
logger.info(
|
||||
`[${requestId}] Directory listed successfully: ${totalFiles} files, ${totalDirectories} directories`
|
||||
)
|
||||
|
||||
return NextResponse.json({
|
||||
entries,
|
||||
totalFiles,
|
||||
totalDirectories,
|
||||
message: `Found ${totalFiles} files and ${totalDirectories} directories`,
|
||||
})
|
||||
} finally {
|
||||
client.end()
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
logger.warn(`[${requestId}] Invalid request data`, { errors: error.errors })
|
||||
return NextResponse.json(
|
||||
{ error: 'Invalid request data', details: error.errors },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'
|
||||
logger.error(`[${requestId}] SSH list directory failed:`, error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `SSH list directory failed: ${errorMessage}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
117
apps/sim/app/api/tools/ssh/move-rename/route.ts
Normal file
117
apps/sim/app/api/tools/ssh/move-rename/route.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import { randomUUID } from 'crypto'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import {
|
||||
createSSHConnection,
|
||||
escapeShellArg,
|
||||
executeSSHCommand,
|
||||
sanitizePath,
|
||||
} from '@/app/api/tools/ssh/utils'
|
||||
|
||||
const logger = createLogger('SSHMoveRenameAPI')
|
||||
|
||||
const MoveRenameSchema = z.object({
|
||||
host: z.string().min(1, 'Host is required'),
|
||||
port: z.coerce.number().int().positive().default(22),
|
||||
username: z.string().min(1, 'Username is required'),
|
||||
password: z.string().nullish(),
|
||||
privateKey: z.string().nullish(),
|
||||
passphrase: z.string().nullish(),
|
||||
sourcePath: z.string().min(1, 'Source path is required'),
|
||||
destinationPath: z.string().min(1, 'Destination path is required'),
|
||||
overwrite: z.boolean().default(false),
|
||||
})
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const requestId = randomUUID().slice(0, 8)
|
||||
|
||||
try {
|
||||
const body = await request.json()
|
||||
const params = MoveRenameSchema.parse(body)
|
||||
|
||||
if (!params.password && !params.privateKey) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Either password or privateKey must be provided' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`[${requestId}] Moving ${params.sourcePath} to ${params.destinationPath} on ${params.host}:${params.port}`
|
||||
)
|
||||
|
||||
const client = await createSSHConnection({
|
||||
host: params.host,
|
||||
port: params.port,
|
||||
username: params.username,
|
||||
password: params.password,
|
||||
privateKey: params.privateKey,
|
||||
passphrase: params.passphrase,
|
||||
})
|
||||
|
||||
try {
|
||||
const sourcePath = sanitizePath(params.sourcePath)
|
||||
const destPath = sanitizePath(params.destinationPath)
|
||||
const escapedSource = escapeShellArg(sourcePath)
|
||||
const escapedDest = escapeShellArg(destPath)
|
||||
|
||||
const sourceCheck = await executeSSHCommand(
|
||||
client,
|
||||
`test -e '${escapedSource}' && echo "exists"`
|
||||
)
|
||||
if (sourceCheck.stdout.trim() !== 'exists') {
|
||||
return NextResponse.json(
|
||||
{ error: `Source path does not exist: ${sourcePath}` },
|
||||
{ status: 404 }
|
||||
)
|
||||
}
|
||||
|
||||
if (!params.overwrite) {
|
||||
const destCheck = await executeSSHCommand(
|
||||
client,
|
||||
`test -e '${escapedDest}' && echo "exists"`
|
||||
)
|
||||
if (destCheck.stdout.trim() === 'exists') {
|
||||
return NextResponse.json(
|
||||
{ error: `Destination already exists and overwrite is disabled: ${destPath}` },
|
||||
{ status: 409 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const command = params.overwrite
|
||||
? `mv -f '${escapedSource}' '${escapedDest}'`
|
||||
: `mv '${escapedSource}' '${escapedDest}'`
|
||||
const result = await executeSSHCommand(client, command)
|
||||
|
||||
if (result.exitCode !== 0) {
|
||||
throw new Error(result.stderr || 'Failed to move/rename')
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] Successfully moved ${sourcePath} to ${destPath}`)
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
sourcePath,
|
||||
destinationPath: destPath,
|
||||
message: `Successfully moved ${sourcePath} to ${destPath}`,
|
||||
})
|
||||
} finally {
|
||||
client.end()
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
logger.warn(`[${requestId}] Invalid request data`, { errors: error.errors })
|
||||
return NextResponse.json(
|
||||
{ error: 'Invalid request data', details: error.errors },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'
|
||||
logger.error(`[${requestId}] SSH move/rename failed:`, error)
|
||||
|
||||
return NextResponse.json({ error: `SSH move/rename failed: ${errorMessage}` }, { status: 500 })
|
||||
}
|
||||
}
|
||||
132
apps/sim/app/api/tools/ssh/read-file-content/route.ts
Normal file
132
apps/sim/app/api/tools/ssh/read-file-content/route.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
import { randomUUID } from 'crypto'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import type { Client, SFTPWrapper } from 'ssh2'
|
||||
import { z } from 'zod'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { createSSHConnection, sanitizePath } from '@/app/api/tools/ssh/utils'
|
||||
|
||||
const logger = createLogger('SSHReadFileContentAPI')
|
||||
|
||||
const ReadFileContentSchema = z.object({
|
||||
host: z.string().min(1, 'Host is required'),
|
||||
port: z.coerce.number().int().positive().default(22),
|
||||
username: z.string().min(1, 'Username is required'),
|
||||
password: z.string().nullish(),
|
||||
privateKey: z.string().nullish(),
|
||||
passphrase: z.string().nullish(),
|
||||
path: z.string().min(1, 'Path is required'),
|
||||
encoding: z.string().default('utf-8'),
|
||||
maxSize: z.coerce.number().default(10), // MB
|
||||
})
|
||||
|
||||
function getSFTP(client: Client): Promise<SFTPWrapper> {
|
||||
return new Promise((resolve, reject) => {
|
||||
client.sftp((err, sftp) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve(sftp)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const requestId = randomUUID().slice(0, 8)
|
||||
|
||||
try {
|
||||
const body = await request.json()
|
||||
const params = ReadFileContentSchema.parse(body)
|
||||
|
||||
if (!params.password && !params.privateKey) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Either password or privateKey must be provided' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`[${requestId}] Reading file content from ${params.path} on ${params.host}:${params.port}`
|
||||
)
|
||||
|
||||
const client = await createSSHConnection({
|
||||
host: params.host,
|
||||
port: params.port,
|
||||
username: params.username,
|
||||
password: params.password,
|
||||
privateKey: params.privateKey,
|
||||
passphrase: params.passphrase,
|
||||
})
|
||||
|
||||
try {
|
||||
const sftp = await getSFTP(client)
|
||||
const filePath = sanitizePath(params.path)
|
||||
const maxBytes = params.maxSize * 1024 * 1024 // Convert MB to bytes
|
||||
|
||||
const stats = await new Promise<{ size: number }>((resolve, reject) => {
|
||||
sftp.stat(filePath, (err, stats) => {
|
||||
if (err) {
|
||||
reject(new Error(`File not found: ${filePath}`))
|
||||
} else {
|
||||
resolve(stats)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
if (stats.size > maxBytes) {
|
||||
return NextResponse.json(
|
||||
{ error: `File size (${stats.size} bytes) exceeds maximum allowed (${maxBytes} bytes)` },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const content = await new Promise<string>((resolve, reject) => {
|
||||
const chunks: Buffer[] = []
|
||||
const readStream = sftp.createReadStream(filePath)
|
||||
|
||||
readStream.on('data', (chunk: Buffer) => {
|
||||
chunks.push(chunk)
|
||||
})
|
||||
|
||||
readStream.on('end', () => {
|
||||
const buffer = Buffer.concat(chunks)
|
||||
resolve(buffer.toString(params.encoding as BufferEncoding))
|
||||
})
|
||||
|
||||
readStream.on('error', reject)
|
||||
})
|
||||
|
||||
const lines = content.split('\n').length
|
||||
|
||||
logger.info(
|
||||
`[${requestId}] File content read successfully: ${stats.size} bytes, ${lines} lines`
|
||||
)
|
||||
|
||||
return NextResponse.json({
|
||||
content,
|
||||
size: stats.size,
|
||||
lines,
|
||||
path: filePath,
|
||||
message: `File read successfully: ${stats.size} bytes, ${lines} lines`,
|
||||
})
|
||||
} finally {
|
||||
client.end()
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
logger.warn(`[${requestId}] Invalid request data`, { errors: error.errors })
|
||||
return NextResponse.json(
|
||||
{ error: 'Invalid request data', details: error.errors },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'
|
||||
logger.error(`[${requestId}] SSH read file content failed:`, error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `SSH read file content failed: ${errorMessage}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
129
apps/sim/app/api/tools/ssh/upload-file/route.ts
Normal file
129
apps/sim/app/api/tools/ssh/upload-file/route.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import { randomUUID } from 'crypto'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import type { Client, SFTPWrapper } from 'ssh2'
|
||||
import { z } from 'zod'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { createSSHConnection, sanitizePath } from '@/app/api/tools/ssh/utils'
|
||||
|
||||
const logger = createLogger('SSHUploadFileAPI')
|
||||
|
||||
const UploadFileSchema = z.object({
|
||||
host: z.string().min(1, 'Host is required'),
|
||||
port: z.coerce.number().int().positive().default(22),
|
||||
username: z.string().min(1, 'Username is required'),
|
||||
password: z.string().nullish(),
|
||||
privateKey: z.string().nullish(),
|
||||
passphrase: z.string().nullish(),
|
||||
fileContent: z.string().min(1, 'File content is required'),
|
||||
fileName: z.string().min(1, 'File name is required'),
|
||||
remotePath: z.string().min(1, 'Remote path is required'),
|
||||
permissions: z.string().nullish(),
|
||||
overwrite: z.boolean().default(true),
|
||||
})
|
||||
|
||||
function getSFTP(client: Client): Promise<SFTPWrapper> {
|
||||
return new Promise((resolve, reject) => {
|
||||
client.sftp((err, sftp) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve(sftp)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const requestId = randomUUID().slice(0, 8)
|
||||
|
||||
try {
|
||||
const body = await request.json()
|
||||
const params = UploadFileSchema.parse(body)
|
||||
|
||||
if (!params.password && !params.privateKey) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Either password or privateKey must be provided' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`[${requestId}] Uploading file to ${params.host}:${params.port}${params.remotePath}`
|
||||
)
|
||||
|
||||
const client = await createSSHConnection({
|
||||
host: params.host,
|
||||
port: params.port,
|
||||
username: params.username,
|
||||
password: params.password,
|
||||
privateKey: params.privateKey,
|
||||
passphrase: params.passphrase,
|
||||
})
|
||||
|
||||
try {
|
||||
const sftp = await getSFTP(client)
|
||||
const remotePath = sanitizePath(params.remotePath)
|
||||
|
||||
if (!params.overwrite) {
|
||||
const exists = await new Promise<boolean>((resolve) => {
|
||||
sftp.stat(remotePath, (err) => {
|
||||
resolve(!err)
|
||||
})
|
||||
})
|
||||
|
||||
if (exists) {
|
||||
return NextResponse.json(
|
||||
{ error: 'File already exists and overwrite is disabled' },
|
||||
{ status: 409 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
let content: Buffer
|
||||
try {
|
||||
content = Buffer.from(params.fileContent, 'base64')
|
||||
const reEncoded = content.toString('base64')
|
||||
if (reEncoded !== params.fileContent) {
|
||||
content = Buffer.from(params.fileContent, 'utf-8')
|
||||
}
|
||||
} catch {
|
||||
content = Buffer.from(params.fileContent, 'utf-8')
|
||||
}
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const writeStream = sftp.createWriteStream(remotePath, {
|
||||
mode: params.permissions ? Number.parseInt(params.permissions, 8) : 0o644,
|
||||
})
|
||||
|
||||
writeStream.on('error', reject)
|
||||
writeStream.on('close', () => resolve())
|
||||
|
||||
writeStream.end(content)
|
||||
})
|
||||
|
||||
logger.info(`[${requestId}] File uploaded successfully to ${remotePath}`)
|
||||
|
||||
return NextResponse.json({
|
||||
uploaded: true,
|
||||
remotePath: remotePath,
|
||||
size: content.length,
|
||||
message: `File uploaded successfully to ${remotePath}`,
|
||||
})
|
||||
} finally {
|
||||
client.end()
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
logger.warn(`[${requestId}] Invalid request data`, { errors: error.errors })
|
||||
return NextResponse.json(
|
||||
{ error: 'Invalid request data', details: error.errors },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'
|
||||
logger.error(`[${requestId}] SSH file upload failed:`, error)
|
||||
|
||||
return NextResponse.json({ error: `SSH file upload failed: ${errorMessage}` }, { status: 500 })
|
||||
}
|
||||
}
|
||||
272
apps/sim/app/api/tools/ssh/utils.ts
Normal file
272
apps/sim/app/api/tools/ssh/utils.ts
Normal file
@@ -0,0 +1,272 @@
|
||||
import { type Attributes, Client, type ConnectConfig } from 'ssh2'
|
||||
|
||||
// File type constants from POSIX
|
||||
const S_IFMT = 0o170000 // bit mask for the file type bit field
|
||||
const S_IFDIR = 0o040000 // directory
|
||||
const S_IFREG = 0o100000 // regular file
|
||||
const S_IFLNK = 0o120000 // symbolic link
|
||||
|
||||
export interface SSHConnectionConfig {
|
||||
host: string
|
||||
port: number
|
||||
username: string
|
||||
password?: string | null
|
||||
privateKey?: string | null
|
||||
passphrase?: string | null
|
||||
timeout?: number
|
||||
keepaliveInterval?: number
|
||||
readyTimeout?: number
|
||||
}
|
||||
|
||||
export interface SSHCommandResult {
|
||||
stdout: string
|
||||
stderr: string
|
||||
exitCode: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Format SSH error with helpful troubleshooting context
|
||||
*/
|
||||
function formatSSHError(err: Error, config: { host: string; port: number }): Error {
|
||||
const errorMessage = err.message.toLowerCase()
|
||||
const host = config.host
|
||||
const port = config.port
|
||||
|
||||
// Connection refused - server not running or wrong port
|
||||
if (errorMessage.includes('econnrefused') || errorMessage.includes('connection refused')) {
|
||||
return new Error(
|
||||
`Connection refused to ${host}:${port}. ` +
|
||||
`Please verify: (1) SSH server is running on the target machine, ` +
|
||||
`(2) Port ${port} is correct (default SSH port is 22), ` +
|
||||
`(3) Firewall allows connections to port ${port}.`
|
||||
)
|
||||
}
|
||||
|
||||
// Connection reset - server closed connection unexpectedly
|
||||
if (errorMessage.includes('econnreset') || errorMessage.includes('connection reset')) {
|
||||
return new Error(
|
||||
`Connection reset by ${host}:${port}. ` +
|
||||
`This usually means: (1) Wrong port number (SSH default is 22), ` +
|
||||
`(2) Server rejected the connection, ` +
|
||||
`(3) Network/firewall interrupted the connection. ` +
|
||||
`Verify your SSH server configuration and port number.`
|
||||
)
|
||||
}
|
||||
|
||||
// Timeout - server unreachable or slow
|
||||
if (errorMessage.includes('etimedout') || errorMessage.includes('timeout')) {
|
||||
return new Error(
|
||||
`Connection timed out to ${host}:${port}. ` +
|
||||
`Please verify: (1) Host "${host}" is reachable, ` +
|
||||
`(2) No firewall is blocking the connection, ` +
|
||||
`(3) The SSH server is responding.`
|
||||
)
|
||||
}
|
||||
|
||||
// DNS/hostname resolution
|
||||
if (errorMessage.includes('enotfound') || errorMessage.includes('getaddrinfo')) {
|
||||
return new Error(
|
||||
`Could not resolve hostname "${host}". ` +
|
||||
`Please verify the hostname or IP address is correct.`
|
||||
)
|
||||
}
|
||||
|
||||
// Authentication failure
|
||||
if (errorMessage.includes('authentication') || errorMessage.includes('auth')) {
|
||||
return new Error(
|
||||
`Authentication failed for user on ${host}:${port}. ` +
|
||||
`Please verify: (1) Username is correct, ` +
|
||||
`(2) Password or private key is valid, ` +
|
||||
`(3) User has SSH access on the server.`
|
||||
)
|
||||
}
|
||||
|
||||
// Private key format issues
|
||||
if (
|
||||
errorMessage.includes('key') &&
|
||||
(errorMessage.includes('parse') || errorMessage.includes('invalid'))
|
||||
) {
|
||||
return new Error(
|
||||
`Invalid private key format. ` +
|
||||
`Please ensure you're using a valid OpenSSH private key. ` +
|
||||
`The key should start with "-----BEGIN" and end with "-----END".`
|
||||
)
|
||||
}
|
||||
|
||||
// Host key verification (first connection)
|
||||
if (errorMessage.includes('host key') || errorMessage.includes('hostkey')) {
|
||||
return new Error(
|
||||
`Host key verification issue for ${host}. ` +
|
||||
`This may be the first connection to this server or the server's key has changed.`
|
||||
)
|
||||
}
|
||||
|
||||
// Return original error with context if no specific match
|
||||
return new Error(`SSH connection to ${host}:${port} failed: ${err.message}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an SSH connection using the provided configuration
|
||||
*
|
||||
* Uses ssh2 library defaults which align with OpenSSH standards:
|
||||
* - readyTimeout: 20000ms (20 seconds)
|
||||
* - keepaliveInterval: 0 (disabled, same as OpenSSH ServerAliveInterval)
|
||||
* - keepaliveCountMax: 3 (same as OpenSSH ServerAliveCountMax)
|
||||
*/
|
||||
export function createSSHConnection(config: SSHConnectionConfig): Promise<Client> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const client = new Client()
|
||||
const port = config.port || 22
|
||||
const host = config.host
|
||||
|
||||
if (!host || host.trim() === '') {
|
||||
reject(new Error('Host is required. Please provide a valid hostname or IP address.'))
|
||||
return
|
||||
}
|
||||
|
||||
const hasPassword = config.password && config.password.trim() !== ''
|
||||
const hasPrivateKey = config.privateKey && config.privateKey.trim() !== ''
|
||||
|
||||
if (!hasPassword && !hasPrivateKey) {
|
||||
reject(new Error('Authentication required. Please provide either a password or private key.'))
|
||||
return
|
||||
}
|
||||
|
||||
const connectConfig: ConnectConfig = {
|
||||
host: host.trim(),
|
||||
port,
|
||||
username: config.username,
|
||||
}
|
||||
|
||||
if (config.readyTimeout !== undefined) {
|
||||
connectConfig.readyTimeout = config.readyTimeout
|
||||
}
|
||||
if (config.keepaliveInterval !== undefined) {
|
||||
connectConfig.keepaliveInterval = config.keepaliveInterval
|
||||
}
|
||||
|
||||
if (hasPrivateKey) {
|
||||
connectConfig.privateKey = config.privateKey!
|
||||
if (config.passphrase && config.passphrase.trim() !== '') {
|
||||
connectConfig.passphrase = config.passphrase
|
||||
}
|
||||
} else if (hasPassword) {
|
||||
connectConfig.password = config.password!
|
||||
}
|
||||
|
||||
client.on('ready', () => {
|
||||
resolve(client)
|
||||
})
|
||||
|
||||
client.on('error', (err) => {
|
||||
reject(formatSSHError(err, { host, port }))
|
||||
})
|
||||
|
||||
try {
|
||||
client.connect(connectConfig)
|
||||
} catch (err) {
|
||||
reject(formatSSHError(err instanceof Error ? err : new Error(String(err)), { host, port }))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a command on the SSH connection
|
||||
*/
|
||||
export function executeSSHCommand(client: Client, command: string): Promise<SSHCommandResult> {
|
||||
return new Promise((resolve, reject) => {
|
||||
client.exec(command, (err, stream) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
return
|
||||
}
|
||||
|
||||
let stdout = ''
|
||||
let stderr = ''
|
||||
|
||||
stream.on('close', (code: number) => {
|
||||
resolve({
|
||||
stdout: stdout.trim(),
|
||||
stderr: stderr.trim(),
|
||||
exitCode: code ?? 0,
|
||||
})
|
||||
})
|
||||
|
||||
stream.on('data', (data: Buffer) => {
|
||||
stdout += data.toString()
|
||||
})
|
||||
|
||||
stream.stderr.on('data', (data: Buffer) => {
|
||||
stderr += data.toString()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize command input to prevent command injection
|
||||
*/
|
||||
export function sanitizeCommand(command: string): string {
|
||||
return command.trim()
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize file path - removes null bytes and trims whitespace
|
||||
*/
|
||||
export function sanitizePath(path: string): string {
|
||||
let sanitized = path.replace(/\0/g, '')
|
||||
|
||||
sanitized = sanitized.trim()
|
||||
|
||||
return sanitized
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape a string for safe use in single-quoted shell arguments
|
||||
* This is standard practice for shell command construction.
|
||||
* e.g., "/tmp/test'file" becomes "/tmp/test'\''file"
|
||||
*
|
||||
* The pattern 'foo'\''bar' works because:
|
||||
* - First ' ends the current single-quoted string
|
||||
* - \' inserts a literal single quote (escaped outside quotes)
|
||||
* - Next ' starts a new single-quoted string
|
||||
*/
|
||||
export function escapeShellArg(arg: string): string {
|
||||
return arg.replace(/'/g, "'\\''")
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that authentication credentials are provided
|
||||
*/
|
||||
export function validateAuth(params: { password?: string; privateKey?: string }): {
|
||||
isValid: boolean
|
||||
error?: string
|
||||
} {
|
||||
if (!params.password && !params.privateKey) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: 'Either password or privateKey must be provided for authentication',
|
||||
}
|
||||
}
|
||||
return { isValid: true }
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse file permissions from octal string
|
||||
*/
|
||||
export function parsePermissions(mode: number): string {
|
||||
return `0${(mode & 0o777).toString(8)}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file type from attributes mode bits
|
||||
*/
|
||||
export function getFileType(attrs: Attributes): 'file' | 'directory' | 'symlink' | 'other' {
|
||||
const mode = attrs.mode
|
||||
const fileType = mode & S_IFMT
|
||||
|
||||
if (fileType === S_IFDIR) return 'directory'
|
||||
if (fileType === S_IFREG) return 'file'
|
||||
if (fileType === S_IFLNK) return 'symlink'
|
||||
return 'other'
|
||||
}
|
||||
152
apps/sim/app/api/tools/ssh/write-file-content/route.ts
Normal file
152
apps/sim/app/api/tools/ssh/write-file-content/route.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
import { randomUUID } from 'crypto'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import type { Client, SFTPWrapper } from 'ssh2'
|
||||
import { z } from 'zod'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { createSSHConnection, sanitizePath } from '@/app/api/tools/ssh/utils'
|
||||
|
||||
const logger = createLogger('SSHWriteFileContentAPI')
|
||||
|
||||
const WriteFileContentSchema = z.object({
|
||||
host: z.string().min(1, 'Host is required'),
|
||||
port: z.coerce.number().int().positive().default(22),
|
||||
username: z.string().min(1, 'Username is required'),
|
||||
password: z.string().nullish(),
|
||||
privateKey: z.string().nullish(),
|
||||
passphrase: z.string().nullish(),
|
||||
path: z.string().min(1, 'Path is required'),
|
||||
content: z.string(),
|
||||
mode: z.enum(['overwrite', 'append', 'create']).default('overwrite'),
|
||||
permissions: z.string().nullish(),
|
||||
})
|
||||
|
||||
function getSFTP(client: Client): Promise<SFTPWrapper> {
|
||||
return new Promise((resolve, reject) => {
|
||||
client.sftp((err, sftp) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve(sftp)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const requestId = randomUUID().slice(0, 8)
|
||||
|
||||
try {
|
||||
const body = await request.json()
|
||||
const params = WriteFileContentSchema.parse(body)
|
||||
|
||||
// Validate authentication
|
||||
if (!params.password && !params.privateKey) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Either password or privateKey must be provided' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`[${requestId}] Writing file content to ${params.path} on ${params.host}:${params.port} (mode: ${params.mode})`
|
||||
)
|
||||
|
||||
const client = await createSSHConnection({
|
||||
host: params.host,
|
||||
port: params.port,
|
||||
username: params.username,
|
||||
password: params.password,
|
||||
privateKey: params.privateKey,
|
||||
passphrase: params.passphrase,
|
||||
})
|
||||
|
||||
try {
|
||||
const sftp = await getSFTP(client)
|
||||
const filePath = sanitizePath(params.path)
|
||||
|
||||
// Check if file exists for 'create' mode
|
||||
if (params.mode === 'create') {
|
||||
const exists = await new Promise<boolean>((resolve) => {
|
||||
sftp.stat(filePath, (err) => {
|
||||
resolve(!err)
|
||||
})
|
||||
})
|
||||
|
||||
if (exists) {
|
||||
return NextResponse.json(
|
||||
{ error: `File already exists and mode is 'create': ${filePath}` },
|
||||
{ status: 409 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Handle append mode by reading existing content first
|
||||
let finalContent = params.content
|
||||
if (params.mode === 'append') {
|
||||
const existingContent = await new Promise<string>((resolve) => {
|
||||
const chunks: Buffer[] = []
|
||||
const readStream = sftp.createReadStream(filePath)
|
||||
|
||||
readStream.on('data', (chunk: Buffer) => {
|
||||
chunks.push(chunk)
|
||||
})
|
||||
|
||||
readStream.on('end', () => {
|
||||
resolve(Buffer.concat(chunks).toString('utf-8'))
|
||||
})
|
||||
|
||||
readStream.on('error', () => {
|
||||
resolve('')
|
||||
})
|
||||
})
|
||||
finalContent = existingContent + params.content
|
||||
}
|
||||
|
||||
// Write file
|
||||
const fileMode = params.permissions ? Number.parseInt(params.permissions, 8) : 0o644
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const writeStream = sftp.createWriteStream(filePath, { mode: fileMode })
|
||||
|
||||
writeStream.on('error', reject)
|
||||
writeStream.on('close', () => resolve())
|
||||
|
||||
writeStream.end(Buffer.from(finalContent, 'utf-8'))
|
||||
})
|
||||
|
||||
// Get final file size
|
||||
const stats = await new Promise<{ size: number }>((resolve, reject) => {
|
||||
sftp.stat(filePath, (err, stats) => {
|
||||
if (err) reject(err)
|
||||
else resolve(stats)
|
||||
})
|
||||
})
|
||||
|
||||
logger.info(`[${requestId}] File written successfully: ${stats.size} bytes`)
|
||||
|
||||
return NextResponse.json({
|
||||
written: true,
|
||||
path: filePath,
|
||||
size: stats.size,
|
||||
message: `File written successfully: ${stats.size} bytes`,
|
||||
})
|
||||
} finally {
|
||||
client.end()
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
logger.warn(`[${requestId}] Invalid request data`, { errors: error.errors })
|
||||
return NextResponse.json(
|
||||
{ error: 'Invalid request data', details: error.errors },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'
|
||||
logger.error(`[${requestId}] SSH write file content failed:`, error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `SSH write file content failed: ${errorMessage}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -232,7 +232,7 @@ function TemplateCardInner({
|
||||
key={index}
|
||||
className='flex h-[18px] w-[18px] flex-shrink-0 items-center justify-center rounded-[4px]'
|
||||
style={{
|
||||
backgroundColor: blockConfig.bgColor || 'gray',
|
||||
background: blockConfig.bgColor || 'gray',
|
||||
marginLeft: index > 0 ? '-4px' : '0',
|
||||
}}
|
||||
>
|
||||
@@ -257,7 +257,7 @@ function TemplateCardInner({
|
||||
key={index}
|
||||
className='flex h-[18px] w-[18px] flex-shrink-0 items-center justify-center rounded-[4px]'
|
||||
style={{
|
||||
backgroundColor: blockConfig.bgColor || 'gray',
|
||||
background: blockConfig.bgColor || 'gray',
|
||||
marginLeft: index > 0 ? '-4px' : '0',
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -232,6 +232,38 @@ const SCOPE_DESCRIPTIONS: Record<string, string> = {
|
||||
'webhooks:read': 'Read your Pipedrive webhooks',
|
||||
'webhooks:full': 'Full access to manage your Pipedrive webhooks',
|
||||
w_member_social: 'Access your LinkedIn profile',
|
||||
// Box scopes
|
||||
root_readwrite: 'Read and write all files and folders in your Box account',
|
||||
root_readonly: 'Read all files and folders in your Box account',
|
||||
// Shopify scopes (write_* implicitly includes read access)
|
||||
write_products: 'Read and manage your Shopify products',
|
||||
write_orders: 'Read and manage your Shopify orders',
|
||||
write_customers: 'Read and manage your Shopify customers',
|
||||
write_inventory: 'Read and manage your Shopify inventory levels',
|
||||
read_locations: 'View your store locations',
|
||||
write_merchant_managed_fulfillment_orders: 'Create fulfillments for orders',
|
||||
// Zoom scopes
|
||||
'user:read:user': 'View your Zoom profile information',
|
||||
'meeting:write:meeting': 'Create Zoom meetings',
|
||||
'meeting:read:meeting': 'View Zoom meeting details',
|
||||
'meeting:read:list_meetings': 'List your Zoom meetings',
|
||||
'meeting:update:meeting': 'Update Zoom meetings',
|
||||
'meeting:delete:meeting': 'Delete Zoom meetings',
|
||||
'meeting:read:invitation': 'View Zoom meeting invitations',
|
||||
'meeting:read:list_past_participants': 'View past meeting participants',
|
||||
'cloud_recording:read:list_user_recordings': 'List your Zoom cloud recordings',
|
||||
'cloud_recording:read:list_recording_files': 'View recording files',
|
||||
'cloud_recording:delete:recording_file': 'Delete cloud recordings',
|
||||
// Dropbox scopes
|
||||
'account_info.read': 'View your Dropbox account information',
|
||||
'files.metadata.read': 'View file and folder names, sizes, and dates',
|
||||
'files.metadata.write': 'Modify file and folder metadata',
|
||||
'files.content.read': 'Download and read your Dropbox files',
|
||||
'files.content.write': 'Upload, copy, move, and delete files in your Dropbox',
|
||||
'sharing.read': 'View your shared files and folders',
|
||||
'sharing.write': 'Share files and folders with others',
|
||||
// WordPress.com scopes
|
||||
global: 'Full access to manage your WordPress.com sites, posts, pages, media, and settings',
|
||||
}
|
||||
|
||||
function getScopeDescription(scope: string): string {
|
||||
@@ -289,6 +321,13 @@ export function OAuthRequiredModal({
|
||||
return
|
||||
}
|
||||
|
||||
if (providerId === 'shopify') {
|
||||
// Pass the current URL so we can redirect back after OAuth
|
||||
const returnUrl = encodeURIComponent(window.location.href)
|
||||
window.location.href = `/api/auth/shopify/authorize?returnUrl=${returnUrl}`
|
||||
return
|
||||
}
|
||||
|
||||
await client.oauth2.link({
|
||||
providerId,
|
||||
callbackURL: window.location.href,
|
||||
|
||||
@@ -353,6 +353,7 @@ export function Editor() {
|
||||
blockId={currentBlockId}
|
||||
config={subBlock}
|
||||
isPreview={false}
|
||||
subBlockValues={subBlockState}
|
||||
disabled={!userPermissions.canEdit}
|
||||
fieldDiffStatus={undefined}
|
||||
allowExpandInPreview={false}
|
||||
|
||||
@@ -564,9 +564,7 @@ export function SearchModal({
|
||||
<div
|
||||
className='relative flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center overflow-hidden rounded-[4px]'
|
||||
style={{
|
||||
backgroundColor: showColoredIcon
|
||||
? item.bgColor
|
||||
: 'transparent',
|
||||
background: showColoredIcon ? item.bgColor : 'transparent',
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
|
||||
458
apps/sim/blocks/blocks/ahrefs.ts
Normal file
458
apps/sim/blocks/blocks/ahrefs.ts
Normal file
@@ -0,0 +1,458 @@
|
||||
import { AhrefsIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import type { AhrefsResponse } from '@/tools/ahrefs/types'
|
||||
|
||||
export const AhrefsBlock: BlockConfig<AhrefsResponse> = {
|
||||
type: 'ahrefs',
|
||||
name: 'Ahrefs',
|
||||
description: 'SEO analysis with Ahrefs',
|
||||
authMode: AuthMode.ApiKey,
|
||||
longDescription:
|
||||
'Integrate Ahrefs SEO tools into your workflow. Analyze domain ratings, backlinks, organic keywords, top pages, and more. Requires an Ahrefs Enterprise plan with API access.',
|
||||
docsLink: 'https://docs.ahrefs.com/docs/api/reference/introduction',
|
||||
category: 'tools',
|
||||
bgColor: '#E0E0E0',
|
||||
icon: AhrefsIcon,
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Domain Rating', id: 'ahrefs_domain_rating' },
|
||||
{ label: 'Backlinks', id: 'ahrefs_backlinks' },
|
||||
{ label: 'Backlinks Stats', id: 'ahrefs_backlinks_stats' },
|
||||
{ label: 'Referring Domains', id: 'ahrefs_referring_domains' },
|
||||
{ label: 'Organic Keywords', id: 'ahrefs_organic_keywords' },
|
||||
{ label: 'Top Pages', id: 'ahrefs_top_pages' },
|
||||
{ label: 'Keyword Overview', id: 'ahrefs_keyword_overview' },
|
||||
{ label: 'Broken Backlinks', id: 'ahrefs_broken_backlinks' },
|
||||
],
|
||||
value: () => 'ahrefs_domain_rating',
|
||||
},
|
||||
// Domain Rating operation inputs
|
||||
{
|
||||
id: 'target',
|
||||
title: 'Target Domain',
|
||||
type: 'short-input',
|
||||
placeholder: 'example.com',
|
||||
condition: { field: 'operation', value: 'ahrefs_domain_rating' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'date',
|
||||
title: 'Date',
|
||||
type: 'short-input',
|
||||
placeholder: 'YYYY-MM-DD (defaults to today)',
|
||||
condition: { field: 'operation', value: 'ahrefs_domain_rating' },
|
||||
},
|
||||
// Backlinks operation inputs
|
||||
{
|
||||
id: 'target',
|
||||
title: 'Target Domain/URL',
|
||||
type: 'short-input',
|
||||
placeholder: 'example.com or https://example.com/page',
|
||||
condition: { field: 'operation', value: 'ahrefs_backlinks' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'mode',
|
||||
title: 'Analysis Mode',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Domain (entire domain)', id: 'domain' },
|
||||
{ label: 'Prefix (URL prefix)', id: 'prefix' },
|
||||
{ label: 'Subdomains (include all)', id: 'subdomains' },
|
||||
{ label: 'Exact (exact URL)', id: 'exact' },
|
||||
],
|
||||
value: () => 'domain',
|
||||
condition: { field: 'operation', value: 'ahrefs_backlinks' },
|
||||
},
|
||||
{
|
||||
id: 'limit',
|
||||
title: 'Limit',
|
||||
type: 'short-input',
|
||||
placeholder: '100',
|
||||
condition: { field: 'operation', value: 'ahrefs_backlinks' },
|
||||
},
|
||||
{
|
||||
id: 'offset',
|
||||
title: 'Offset',
|
||||
type: 'short-input',
|
||||
placeholder: '0',
|
||||
condition: { field: 'operation', value: 'ahrefs_backlinks' },
|
||||
},
|
||||
{
|
||||
id: 'date',
|
||||
title: 'Date',
|
||||
type: 'short-input',
|
||||
placeholder: 'YYYY-MM-DD (defaults to today)',
|
||||
condition: { field: 'operation', value: 'ahrefs_backlinks' },
|
||||
},
|
||||
// Backlinks Stats operation inputs
|
||||
{
|
||||
id: 'target',
|
||||
title: 'Target Domain/URL',
|
||||
type: 'short-input',
|
||||
placeholder: 'example.com',
|
||||
condition: { field: 'operation', value: 'ahrefs_backlinks_stats' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'mode',
|
||||
title: 'Analysis Mode',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Domain (entire domain)', id: 'domain' },
|
||||
{ label: 'Prefix (URL prefix)', id: 'prefix' },
|
||||
{ label: 'Subdomains (include all)', id: 'subdomains' },
|
||||
{ label: 'Exact (exact URL)', id: 'exact' },
|
||||
],
|
||||
value: () => 'domain',
|
||||
condition: { field: 'operation', value: 'ahrefs_backlinks_stats' },
|
||||
},
|
||||
{
|
||||
id: 'date',
|
||||
title: 'Date',
|
||||
type: 'short-input',
|
||||
placeholder: 'YYYY-MM-DD (defaults to today)',
|
||||
condition: { field: 'operation', value: 'ahrefs_backlinks_stats' },
|
||||
},
|
||||
// Referring Domains operation inputs
|
||||
{
|
||||
id: 'target',
|
||||
title: 'Target Domain/URL',
|
||||
type: 'short-input',
|
||||
placeholder: 'example.com',
|
||||
condition: { field: 'operation', value: 'ahrefs_referring_domains' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'mode',
|
||||
title: 'Analysis Mode',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Domain (entire domain)', id: 'domain' },
|
||||
{ label: 'Prefix (URL prefix)', id: 'prefix' },
|
||||
{ label: 'Subdomains (include all)', id: 'subdomains' },
|
||||
{ label: 'Exact (exact URL)', id: 'exact' },
|
||||
],
|
||||
value: () => 'domain',
|
||||
condition: { field: 'operation', value: 'ahrefs_referring_domains' },
|
||||
},
|
||||
{
|
||||
id: 'limit',
|
||||
title: 'Limit',
|
||||
type: 'short-input',
|
||||
placeholder: '100',
|
||||
condition: { field: 'operation', value: 'ahrefs_referring_domains' },
|
||||
},
|
||||
{
|
||||
id: 'offset',
|
||||
title: 'Offset',
|
||||
type: 'short-input',
|
||||
placeholder: '0',
|
||||
condition: { field: 'operation', value: 'ahrefs_referring_domains' },
|
||||
},
|
||||
{
|
||||
id: 'date',
|
||||
title: 'Date',
|
||||
type: 'short-input',
|
||||
placeholder: 'YYYY-MM-DD (defaults to today)',
|
||||
condition: { field: 'operation', value: 'ahrefs_referring_domains' },
|
||||
},
|
||||
// Organic Keywords operation inputs
|
||||
{
|
||||
id: 'target',
|
||||
title: 'Target Domain/URL',
|
||||
type: 'short-input',
|
||||
placeholder: 'example.com',
|
||||
condition: { field: 'operation', value: 'ahrefs_organic_keywords' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'country',
|
||||
title: 'Country',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'United States', id: 'us' },
|
||||
{ label: 'United Kingdom', id: 'gb' },
|
||||
{ label: 'Germany', id: 'de' },
|
||||
{ label: 'France', id: 'fr' },
|
||||
{ label: 'Spain', id: 'es' },
|
||||
{ label: 'Italy', id: 'it' },
|
||||
{ label: 'Canada', id: 'ca' },
|
||||
{ label: 'Australia', id: 'au' },
|
||||
{ label: 'Japan', id: 'jp' },
|
||||
{ label: 'Brazil', id: 'br' },
|
||||
{ label: 'India', id: 'in' },
|
||||
{ label: 'Netherlands', id: 'nl' },
|
||||
{ label: 'Poland', id: 'pl' },
|
||||
{ label: 'Russia', id: 'ru' },
|
||||
{ label: 'Mexico', id: 'mx' },
|
||||
],
|
||||
value: () => 'us',
|
||||
condition: { field: 'operation', value: 'ahrefs_organic_keywords' },
|
||||
},
|
||||
{
|
||||
id: 'mode',
|
||||
title: 'Analysis Mode',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Domain (entire domain)', id: 'domain' },
|
||||
{ label: 'Prefix (URL prefix)', id: 'prefix' },
|
||||
{ label: 'Subdomains (include all)', id: 'subdomains' },
|
||||
{ label: 'Exact (exact URL)', id: 'exact' },
|
||||
],
|
||||
value: () => 'domain',
|
||||
condition: { field: 'operation', value: 'ahrefs_organic_keywords' },
|
||||
},
|
||||
{
|
||||
id: 'limit',
|
||||
title: 'Limit',
|
||||
type: 'short-input',
|
||||
placeholder: '100',
|
||||
condition: { field: 'operation', value: 'ahrefs_organic_keywords' },
|
||||
},
|
||||
{
|
||||
id: 'offset',
|
||||
title: 'Offset',
|
||||
type: 'short-input',
|
||||
placeholder: '0',
|
||||
condition: { field: 'operation', value: 'ahrefs_organic_keywords' },
|
||||
},
|
||||
{
|
||||
id: 'date',
|
||||
title: 'Date',
|
||||
type: 'short-input',
|
||||
placeholder: 'YYYY-MM-DD (defaults to today)',
|
||||
condition: { field: 'operation', value: 'ahrefs_organic_keywords' },
|
||||
},
|
||||
// Top Pages operation inputs
|
||||
{
|
||||
id: 'target',
|
||||
title: 'Target Domain',
|
||||
type: 'short-input',
|
||||
placeholder: 'example.com',
|
||||
condition: { field: 'operation', value: 'ahrefs_top_pages' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'country',
|
||||
title: 'Country',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'United States', id: 'us' },
|
||||
{ label: 'United Kingdom', id: 'gb' },
|
||||
{ label: 'Germany', id: 'de' },
|
||||
{ label: 'France', id: 'fr' },
|
||||
{ label: 'Spain', id: 'es' },
|
||||
{ label: 'Italy', id: 'it' },
|
||||
{ label: 'Canada', id: 'ca' },
|
||||
{ label: 'Australia', id: 'au' },
|
||||
{ label: 'Japan', id: 'jp' },
|
||||
{ label: 'Brazil', id: 'br' },
|
||||
{ label: 'India', id: 'in' },
|
||||
{ label: 'Netherlands', id: 'nl' },
|
||||
{ label: 'Poland', id: 'pl' },
|
||||
{ label: 'Russia', id: 'ru' },
|
||||
{ label: 'Mexico', id: 'mx' },
|
||||
],
|
||||
value: () => 'us',
|
||||
condition: { field: 'operation', value: 'ahrefs_top_pages' },
|
||||
},
|
||||
{
|
||||
id: 'mode',
|
||||
title: 'Analysis Mode',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Domain (entire domain)', id: 'domain' },
|
||||
{ label: 'Prefix (URL prefix)', id: 'prefix' },
|
||||
{ label: 'Subdomains (include all)', id: 'subdomains' },
|
||||
],
|
||||
value: () => 'domain',
|
||||
condition: { field: 'operation', value: 'ahrefs_top_pages' },
|
||||
},
|
||||
{
|
||||
id: 'limit',
|
||||
title: 'Limit',
|
||||
type: 'short-input',
|
||||
placeholder: '100',
|
||||
condition: { field: 'operation', value: 'ahrefs_top_pages' },
|
||||
},
|
||||
{
|
||||
id: 'offset',
|
||||
title: 'Offset',
|
||||
type: 'short-input',
|
||||
placeholder: '0',
|
||||
condition: { field: 'operation', value: 'ahrefs_top_pages' },
|
||||
},
|
||||
{
|
||||
id: 'date',
|
||||
title: 'Date',
|
||||
type: 'short-input',
|
||||
placeholder: 'YYYY-MM-DD (defaults to today)',
|
||||
condition: { field: 'operation', value: 'ahrefs_top_pages' },
|
||||
},
|
||||
// Keyword Overview operation inputs
|
||||
{
|
||||
id: 'keyword',
|
||||
title: 'Keyword',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter keyword to analyze',
|
||||
condition: { field: 'operation', value: 'ahrefs_keyword_overview' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'country',
|
||||
title: 'Country',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'United States', id: 'us' },
|
||||
{ label: 'United Kingdom', id: 'gb' },
|
||||
{ label: 'Germany', id: 'de' },
|
||||
{ label: 'France', id: 'fr' },
|
||||
{ label: 'Spain', id: 'es' },
|
||||
{ label: 'Italy', id: 'it' },
|
||||
{ label: 'Canada', id: 'ca' },
|
||||
{ label: 'Australia', id: 'au' },
|
||||
{ label: 'Japan', id: 'jp' },
|
||||
{ label: 'Brazil', id: 'br' },
|
||||
{ label: 'India', id: 'in' },
|
||||
{ label: 'Netherlands', id: 'nl' },
|
||||
{ label: 'Poland', id: 'pl' },
|
||||
{ label: 'Russia', id: 'ru' },
|
||||
{ label: 'Mexico', id: 'mx' },
|
||||
],
|
||||
value: () => 'us',
|
||||
condition: { field: 'operation', value: 'ahrefs_keyword_overview' },
|
||||
},
|
||||
// Broken Backlinks operation inputs
|
||||
{
|
||||
id: 'target',
|
||||
title: 'Target Domain/URL',
|
||||
type: 'short-input',
|
||||
placeholder: 'example.com',
|
||||
condition: { field: 'operation', value: 'ahrefs_broken_backlinks' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'mode',
|
||||
title: 'Analysis Mode',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Domain (entire domain)', id: 'domain' },
|
||||
{ label: 'Prefix (URL prefix)', id: 'prefix' },
|
||||
{ label: 'Subdomains (include all)', id: 'subdomains' },
|
||||
{ label: 'Exact (exact URL)', id: 'exact' },
|
||||
],
|
||||
value: () => 'domain',
|
||||
condition: { field: 'operation', value: 'ahrefs_broken_backlinks' },
|
||||
},
|
||||
{
|
||||
id: 'limit',
|
||||
title: 'Limit',
|
||||
type: 'short-input',
|
||||
placeholder: '100',
|
||||
condition: { field: 'operation', value: 'ahrefs_broken_backlinks' },
|
||||
},
|
||||
{
|
||||
id: 'offset',
|
||||
title: 'Offset',
|
||||
type: 'short-input',
|
||||
placeholder: '0',
|
||||
condition: { field: 'operation', value: 'ahrefs_broken_backlinks' },
|
||||
},
|
||||
{
|
||||
id: 'date',
|
||||
title: 'Date',
|
||||
type: 'short-input',
|
||||
placeholder: 'YYYY-MM-DD (defaults to today)',
|
||||
condition: { field: 'operation', value: 'ahrefs_broken_backlinks' },
|
||||
},
|
||||
// API Key (common to all operations)
|
||||
{
|
||||
id: 'apiKey',
|
||||
title: 'API Key',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter your Ahrefs API key',
|
||||
password: true,
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: [
|
||||
'ahrefs_domain_rating',
|
||||
'ahrefs_backlinks',
|
||||
'ahrefs_backlinks_stats',
|
||||
'ahrefs_referring_domains',
|
||||
'ahrefs_organic_keywords',
|
||||
'ahrefs_top_pages',
|
||||
'ahrefs_keyword_overview',
|
||||
'ahrefs_broken_backlinks',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => {
|
||||
// Convert numeric string inputs to numbers
|
||||
if (params.limit) {
|
||||
params.limit = Number(params.limit)
|
||||
}
|
||||
if (params.offset) {
|
||||
params.offset = Number(params.offset)
|
||||
}
|
||||
|
||||
switch (params.operation) {
|
||||
case 'ahrefs_domain_rating':
|
||||
return 'ahrefs_domain_rating'
|
||||
case 'ahrefs_backlinks':
|
||||
return 'ahrefs_backlinks'
|
||||
case 'ahrefs_backlinks_stats':
|
||||
return 'ahrefs_backlinks_stats'
|
||||
case 'ahrefs_referring_domains':
|
||||
return 'ahrefs_referring_domains'
|
||||
case 'ahrefs_organic_keywords':
|
||||
return 'ahrefs_organic_keywords'
|
||||
case 'ahrefs_top_pages':
|
||||
return 'ahrefs_top_pages'
|
||||
case 'ahrefs_keyword_overview':
|
||||
return 'ahrefs_keyword_overview'
|
||||
case 'ahrefs_broken_backlinks':
|
||||
return 'ahrefs_broken_backlinks'
|
||||
default:
|
||||
return 'ahrefs_domain_rating'
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'Operation to perform' },
|
||||
apiKey: { type: 'string', description: 'Ahrefs API key' },
|
||||
target: { type: 'string', description: 'Target domain or URL to analyze' },
|
||||
keyword: { type: 'string', description: 'Keyword to analyze' },
|
||||
mode: { type: 'string', description: 'Analysis mode (domain, prefix, subdomains, exact)' },
|
||||
country: { type: 'string', description: 'Country code for geo-specific data' },
|
||||
date: { type: 'string', description: 'Date for historical data in YYYY-MM-DD format' },
|
||||
limit: { type: 'number', description: 'Maximum number of results to return' },
|
||||
offset: { type: 'number', description: 'Number of results to skip for pagination' },
|
||||
},
|
||||
outputs: {
|
||||
// Domain Rating output
|
||||
domainRating: { type: 'number', description: 'Domain Rating score (0-100)' },
|
||||
ahrefsRank: { type: 'number', description: 'Ahrefs Rank (global ranking)' },
|
||||
// Backlinks output
|
||||
backlinks: { type: 'json', description: 'List of backlinks' },
|
||||
// Backlinks Stats output
|
||||
stats: { type: 'json', description: 'Backlink statistics' },
|
||||
// Referring Domains output
|
||||
referringDomains: { type: 'json', description: 'List of referring domains' },
|
||||
// Organic Keywords output
|
||||
keywords: { type: 'json', description: 'List of organic keywords' },
|
||||
// Top Pages output
|
||||
pages: { type: 'json', description: 'List of top pages' },
|
||||
// Keyword Overview output
|
||||
overview: { type: 'json', description: 'Keyword metrics overview' },
|
||||
// Broken Backlinks output
|
||||
brokenBacklinks: { type: 'json', description: 'List of broken backlinks' },
|
||||
},
|
||||
}
|
||||
599
apps/sim/blocks/blocks/datadog.ts
Normal file
599
apps/sim/blocks/blocks/datadog.ts
Normal file
@@ -0,0 +1,599 @@
|
||||
import { DatadogIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import type { DatadogResponse } from '@/tools/datadog/types'
|
||||
|
||||
export const DatadogBlock: BlockConfig<DatadogResponse> = {
|
||||
type: 'datadog',
|
||||
name: 'Datadog',
|
||||
description: 'Monitor infrastructure, applications, and logs with Datadog',
|
||||
authMode: AuthMode.ApiKey,
|
||||
longDescription:
|
||||
'Integrate Datadog monitoring into workflows. Submit metrics, manage monitors, query logs, create events, handle downtimes, and more.',
|
||||
docsLink: 'https://docs.sim.ai/tools/datadog',
|
||||
category: 'tools',
|
||||
bgColor: '#632CA6',
|
||||
icon: DatadogIcon,
|
||||
subBlocks: [
|
||||
// Operation selector
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Submit Metrics', id: 'datadog_submit_metrics' },
|
||||
{ label: 'Query Timeseries', id: 'datadog_query_timeseries' },
|
||||
{ label: 'Create Event', id: 'datadog_create_event' },
|
||||
{ label: 'Create Monitor', id: 'datadog_create_monitor' },
|
||||
{ label: 'Get Monitor', id: 'datadog_get_monitor' },
|
||||
{ label: 'List Monitors', id: 'datadog_list_monitors' },
|
||||
{ label: 'Mute Monitor', id: 'datadog_mute_monitor' },
|
||||
{ label: 'Query Logs', id: 'datadog_query_logs' },
|
||||
{ label: 'Send Logs', id: 'datadog_send_logs' },
|
||||
{ label: 'Create Downtime', id: 'datadog_create_downtime' },
|
||||
{ label: 'List Downtimes', id: 'datadog_list_downtimes' },
|
||||
{ label: 'Cancel Downtime', id: 'datadog_cancel_downtime' },
|
||||
],
|
||||
value: () => 'datadog_submit_metrics',
|
||||
},
|
||||
|
||||
// ========================
|
||||
// Submit Metrics inputs
|
||||
// ========================
|
||||
{
|
||||
id: 'series',
|
||||
title: 'Metrics Data (JSON)',
|
||||
type: 'code',
|
||||
placeholder: `[
|
||||
{
|
||||
"metric": "custom.app.response_time",
|
||||
"type": "gauge",
|
||||
"points": [{"timestamp": ${Math.floor(Date.now() / 1000)}, "value": 0.85}],
|
||||
"tags": ["env:production", "service:api"]
|
||||
}
|
||||
]`,
|
||||
condition: { field: 'operation', value: 'datadog_submit_metrics' },
|
||||
required: true,
|
||||
},
|
||||
|
||||
// ========================
|
||||
// Query Timeseries inputs
|
||||
// ========================
|
||||
{
|
||||
id: 'query',
|
||||
title: 'Query',
|
||||
type: 'long-input',
|
||||
placeholder: 'avg:system.cpu.user{*}',
|
||||
condition: { field: 'operation', value: 'datadog_query_timeseries' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'from',
|
||||
title: 'From (Unix Timestamp)',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., 1701360000',
|
||||
condition: { field: 'operation', value: 'datadog_query_timeseries' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'to',
|
||||
title: 'To (Unix Timestamp)',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., 1701446400',
|
||||
condition: { field: 'operation', value: 'datadog_query_timeseries' },
|
||||
required: true,
|
||||
},
|
||||
|
||||
// ========================
|
||||
// Create Event inputs
|
||||
// ========================
|
||||
{
|
||||
id: 'title',
|
||||
title: 'Event Title',
|
||||
type: 'short-input',
|
||||
placeholder: 'Deployment completed',
|
||||
condition: { field: 'operation', value: 'datadog_create_event' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'text',
|
||||
title: 'Event Text',
|
||||
type: 'long-input',
|
||||
placeholder: 'Describe the event...',
|
||||
condition: { field: 'operation', value: 'datadog_create_event' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'alertType',
|
||||
title: 'Alert Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Info', id: 'info' },
|
||||
{ label: 'Success', id: 'success' },
|
||||
{ label: 'Warning', id: 'warning' },
|
||||
{ label: 'Error', id: 'error' },
|
||||
],
|
||||
value: () => 'info',
|
||||
condition: { field: 'operation', value: 'datadog_create_event' },
|
||||
},
|
||||
{
|
||||
id: 'priority',
|
||||
title: 'Priority',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Normal', id: 'normal' },
|
||||
{ label: 'Low', id: 'low' },
|
||||
],
|
||||
value: () => 'normal',
|
||||
condition: { field: 'operation', value: 'datadog_create_event' },
|
||||
},
|
||||
{
|
||||
id: 'tags',
|
||||
title: 'Tags',
|
||||
type: 'short-input',
|
||||
placeholder: 'env:production, service:api',
|
||||
condition: { field: 'operation', value: 'datadog_create_event' },
|
||||
},
|
||||
|
||||
// ========================
|
||||
// Create Monitor inputs
|
||||
// ========================
|
||||
{
|
||||
id: 'name',
|
||||
title: 'Monitor Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'High CPU Usage Alert',
|
||||
condition: { field: 'operation', value: 'datadog_create_monitor' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'type',
|
||||
title: 'Monitor Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Metric Alert', id: 'metric alert' },
|
||||
{ label: 'Service Check', id: 'service check' },
|
||||
{ label: 'Event Alert', id: 'event alert' },
|
||||
{ label: 'Log Alert', id: 'log alert' },
|
||||
{ label: 'Query Alert', id: 'query alert' },
|
||||
{ label: 'Composite', id: 'composite' },
|
||||
{ label: 'SLO Alert', id: 'slo alert' },
|
||||
],
|
||||
value: () => 'metric alert',
|
||||
condition: { field: 'operation', value: 'datadog_create_monitor' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'monitorQuery',
|
||||
title: 'Monitor Query',
|
||||
type: 'long-input',
|
||||
placeholder: 'avg(last_5m):avg:system.cpu.idle{*} < 20',
|
||||
condition: { field: 'operation', value: 'datadog_create_monitor' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'message',
|
||||
title: 'Notification Message',
|
||||
type: 'long-input',
|
||||
placeholder: 'Alert! CPU usage is high. @slack-alerts',
|
||||
condition: { field: 'operation', value: 'datadog_create_monitor' },
|
||||
},
|
||||
{
|
||||
id: 'monitorTags',
|
||||
title: 'Tags',
|
||||
type: 'short-input',
|
||||
placeholder: 'team:backend, priority:high',
|
||||
condition: { field: 'operation', value: 'datadog_create_monitor' },
|
||||
},
|
||||
{
|
||||
id: 'monitorPriority',
|
||||
title: 'Priority (1-5)',
|
||||
type: 'short-input',
|
||||
placeholder: '3',
|
||||
condition: { field: 'operation', value: 'datadog_create_monitor' },
|
||||
},
|
||||
{
|
||||
id: 'options',
|
||||
title: 'Options (JSON)',
|
||||
type: 'code',
|
||||
placeholder: '{"notify_no_data": true, "thresholds": {"critical": 90}}',
|
||||
condition: { field: 'operation', value: 'datadog_create_monitor' },
|
||||
},
|
||||
|
||||
// ========================
|
||||
// Get Monitor inputs
|
||||
// ========================
|
||||
{
|
||||
id: 'monitorId',
|
||||
title: 'Monitor ID',
|
||||
type: 'short-input',
|
||||
placeholder: '12345678',
|
||||
condition: { field: 'operation', value: 'datadog_get_monitor' },
|
||||
required: true,
|
||||
},
|
||||
|
||||
// ========================
|
||||
// List Monitors inputs
|
||||
// ========================
|
||||
{
|
||||
id: 'listMonitorName',
|
||||
title: 'Filter by Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'CPU',
|
||||
condition: { field: 'operation', value: 'datadog_list_monitors' },
|
||||
},
|
||||
{
|
||||
id: 'listMonitorTags',
|
||||
title: 'Filter by Tags',
|
||||
type: 'short-input',
|
||||
placeholder: 'env:production',
|
||||
condition: { field: 'operation', value: 'datadog_list_monitors' },
|
||||
},
|
||||
|
||||
// ========================
|
||||
// Mute Monitor inputs
|
||||
// ========================
|
||||
{
|
||||
id: 'muteMonitorId',
|
||||
title: 'Monitor ID',
|
||||
type: 'short-input',
|
||||
placeholder: '12345678',
|
||||
condition: { field: 'operation', value: 'datadog_mute_monitor' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'scope',
|
||||
title: 'Scope',
|
||||
type: 'short-input',
|
||||
placeholder: 'host:myhost (optional)',
|
||||
condition: { field: 'operation', value: 'datadog_mute_monitor' },
|
||||
},
|
||||
{
|
||||
id: 'end',
|
||||
title: 'End Time (Unix Timestamp)',
|
||||
type: 'short-input',
|
||||
placeholder: 'Leave empty for indefinite',
|
||||
condition: { field: 'operation', value: 'datadog_mute_monitor' },
|
||||
},
|
||||
|
||||
// ========================
|
||||
// Query Logs inputs
|
||||
// ========================
|
||||
{
|
||||
id: 'logQuery',
|
||||
title: 'Search Query',
|
||||
type: 'long-input',
|
||||
placeholder: 'service:web-app status:error',
|
||||
condition: { field: 'operation', value: 'datadog_query_logs' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'logFrom',
|
||||
title: 'From',
|
||||
type: 'short-input',
|
||||
placeholder: 'now-1h',
|
||||
condition: { field: 'operation', value: 'datadog_query_logs' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'logTo',
|
||||
title: 'To',
|
||||
type: 'short-input',
|
||||
placeholder: 'now',
|
||||
condition: { field: 'operation', value: 'datadog_query_logs' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'logLimit',
|
||||
title: 'Limit',
|
||||
type: 'short-input',
|
||||
placeholder: '50',
|
||||
condition: { field: 'operation', value: 'datadog_query_logs' },
|
||||
},
|
||||
|
||||
// ========================
|
||||
// Send Logs inputs
|
||||
// ========================
|
||||
{
|
||||
id: 'logs',
|
||||
title: 'Logs (JSON)',
|
||||
type: 'code',
|
||||
placeholder: `[
|
||||
{
|
||||
"message": "Application started successfully",
|
||||
"service": "my-app",
|
||||
"ddsource": "custom",
|
||||
"ddtags": "env:production"
|
||||
}
|
||||
]`,
|
||||
condition: { field: 'operation', value: 'datadog_send_logs' },
|
||||
required: true,
|
||||
},
|
||||
|
||||
// ========================
|
||||
// Create Downtime inputs
|
||||
// ========================
|
||||
{
|
||||
id: 'downtimeScope',
|
||||
title: 'Scope',
|
||||
type: 'short-input',
|
||||
placeholder: 'host:myhost or env:production or *',
|
||||
condition: { field: 'operation', value: 'datadog_create_downtime' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'downtimeMessage',
|
||||
title: 'Message',
|
||||
type: 'long-input',
|
||||
placeholder: 'Scheduled maintenance',
|
||||
condition: { field: 'operation', value: 'datadog_create_downtime' },
|
||||
},
|
||||
{
|
||||
id: 'downtimeStart',
|
||||
title: 'Start Time (Unix Timestamp)',
|
||||
type: 'short-input',
|
||||
placeholder: 'Leave empty for now',
|
||||
condition: { field: 'operation', value: 'datadog_create_downtime' },
|
||||
},
|
||||
{
|
||||
id: 'downtimeEnd',
|
||||
title: 'End Time (Unix Timestamp)',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., 1701450000',
|
||||
condition: { field: 'operation', value: 'datadog_create_downtime' },
|
||||
},
|
||||
{
|
||||
id: 'downtimeMonitorId',
|
||||
title: 'Monitor ID (optional)',
|
||||
type: 'short-input',
|
||||
placeholder: '12345678',
|
||||
condition: { field: 'operation', value: 'datadog_create_downtime' },
|
||||
},
|
||||
|
||||
// ========================
|
||||
// List Downtimes inputs
|
||||
// ========================
|
||||
{
|
||||
id: 'currentOnly',
|
||||
title: 'Current Only',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'datadog_list_downtimes' },
|
||||
},
|
||||
|
||||
// ========================
|
||||
// Cancel Downtime inputs
|
||||
// ========================
|
||||
{
|
||||
id: 'downtimeId',
|
||||
title: 'Downtime ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'abc123',
|
||||
condition: { field: 'operation', value: 'datadog_cancel_downtime' },
|
||||
required: true,
|
||||
},
|
||||
|
||||
// ========================
|
||||
// Authentication (common)
|
||||
// ========================
|
||||
{
|
||||
id: 'apiKey',
|
||||
title: 'API Key',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter your Datadog API key',
|
||||
password: true,
|
||||
required: true,
|
||||
},
|
||||
// Application Key - REQUIRED only for read/manage operations (not needed for submit_metrics, create_event, send_logs)
|
||||
{
|
||||
id: 'applicationKey',
|
||||
title: 'Application Key',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter your Datadog application key',
|
||||
password: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'datadog_query_timeseries',
|
||||
'datadog_create_monitor',
|
||||
'datadog_get_monitor',
|
||||
'datadog_list_monitors',
|
||||
'datadog_mute_monitor',
|
||||
'datadog_query_logs',
|
||||
'datadog_create_downtime',
|
||||
'datadog_list_downtimes',
|
||||
'datadog_cancel_downtime',
|
||||
],
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'site',
|
||||
title: 'Datadog Site',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'US1 (datadoghq.com)', id: 'datadoghq.com' },
|
||||
{ label: 'US3 (us3.datadoghq.com)', id: 'us3.datadoghq.com' },
|
||||
{ label: 'US5 (us5.datadoghq.com)', id: 'us5.datadoghq.com' },
|
||||
{ label: 'EU (datadoghq.eu)', id: 'datadoghq.eu' },
|
||||
{ label: 'AP1 (ap1.datadoghq.com)', id: 'ap1.datadoghq.com' },
|
||||
{ label: 'US1-FED (ddog-gov.com)', id: 'ddog-gov.com' },
|
||||
],
|
||||
value: () => 'datadoghq.com',
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: [
|
||||
'datadog_submit_metrics',
|
||||
'datadog_query_timeseries',
|
||||
'datadog_create_event',
|
||||
'datadog_create_monitor',
|
||||
'datadog_get_monitor',
|
||||
'datadog_list_monitors',
|
||||
'datadog_mute_monitor',
|
||||
'datadog_query_logs',
|
||||
'datadog_send_logs',
|
||||
'datadog_create_downtime',
|
||||
'datadog_list_downtimes',
|
||||
'datadog_cancel_downtime',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => params.operation,
|
||||
params: (params) => {
|
||||
// Base params that are always needed
|
||||
const baseParams: Record<string, any> = {
|
||||
apiKey: params.apiKey,
|
||||
applicationKey: params.applicationKey,
|
||||
site: params.site,
|
||||
}
|
||||
|
||||
// Only include params relevant to each operation
|
||||
switch (params.operation) {
|
||||
case 'datadog_submit_metrics':
|
||||
return { ...baseParams, series: params.series }
|
||||
|
||||
case 'datadog_query_timeseries':
|
||||
return {
|
||||
...baseParams,
|
||||
query: params.query,
|
||||
from: params.from ? Number(params.from) : undefined,
|
||||
to: params.to ? Number(params.to) : undefined,
|
||||
}
|
||||
|
||||
case 'datadog_create_event':
|
||||
return {
|
||||
...baseParams,
|
||||
title: params.title,
|
||||
text: params.text,
|
||||
alertType: params.alertType,
|
||||
priority: params.priority,
|
||||
tags: params.tags,
|
||||
}
|
||||
|
||||
case 'datadog_create_monitor':
|
||||
return {
|
||||
...baseParams,
|
||||
name: params.name,
|
||||
type: params.type,
|
||||
query: params.monitorQuery,
|
||||
message: params.message,
|
||||
tags: params.monitorTags,
|
||||
priority: params.monitorPriority ? Number(params.monitorPriority) : undefined,
|
||||
options: params.options,
|
||||
}
|
||||
|
||||
case 'datadog_get_monitor':
|
||||
return { ...baseParams, monitorId: params.monitorId }
|
||||
|
||||
case 'datadog_list_monitors':
|
||||
return {
|
||||
...baseParams,
|
||||
name: params.listMonitorName || undefined,
|
||||
tags: params.listMonitorTags || undefined,
|
||||
}
|
||||
|
||||
case 'datadog_mute_monitor':
|
||||
return {
|
||||
...baseParams,
|
||||
monitorId: params.muteMonitorId,
|
||||
scope: params.scope,
|
||||
end: params.end ? Number(params.end) : undefined,
|
||||
}
|
||||
|
||||
case 'datadog_query_logs':
|
||||
return {
|
||||
...baseParams,
|
||||
query: params.logQuery,
|
||||
from: params.logFrom,
|
||||
to: params.logTo,
|
||||
limit: params.logLimit ? Number(params.logLimit) : undefined,
|
||||
}
|
||||
|
||||
case 'datadog_send_logs':
|
||||
return { ...baseParams, logs: params.logs }
|
||||
|
||||
case 'datadog_create_downtime':
|
||||
return {
|
||||
...baseParams,
|
||||
scope: params.downtimeScope,
|
||||
message: params.downtimeMessage,
|
||||
start: params.downtimeStart ? Number(params.downtimeStart) : undefined,
|
||||
end: params.downtimeEnd ? Number(params.downtimeEnd) : undefined,
|
||||
monitorId: params.downtimeMonitorId,
|
||||
}
|
||||
|
||||
case 'datadog_list_downtimes':
|
||||
return { ...baseParams, currentOnly: params.currentOnly }
|
||||
|
||||
case 'datadog_cancel_downtime':
|
||||
return { ...baseParams, downtimeId: params.downtimeId }
|
||||
|
||||
default:
|
||||
return baseParams
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'Operation to perform' },
|
||||
apiKey: { type: 'string', description: 'Datadog API key' },
|
||||
applicationKey: { type: 'string', description: 'Datadog Application key' },
|
||||
site: { type: 'string', description: 'Datadog site/region' },
|
||||
// Metrics
|
||||
series: { type: 'json', description: 'Metrics data to submit' },
|
||||
query: { type: 'string', description: 'Query string' },
|
||||
from: { type: 'number', description: 'Start time (Unix timestamp)' },
|
||||
to: { type: 'number', description: 'End time (Unix timestamp)' },
|
||||
// Events
|
||||
title: { type: 'string', description: 'Event title' },
|
||||
text: { type: 'string', description: 'Event text/body' },
|
||||
alertType: { type: 'string', description: 'Alert type' },
|
||||
priority: { type: 'string', description: 'Priority level' },
|
||||
tags: { type: 'string', description: 'Comma-separated tags' },
|
||||
// Monitors
|
||||
name: { type: 'string', description: 'Monitor name' },
|
||||
type: { type: 'string', description: 'Monitor type' },
|
||||
monitorQuery: { type: 'string', description: 'Monitor query' },
|
||||
message: { type: 'string', description: 'Notification message' },
|
||||
monitorTags: { type: 'string', description: 'Monitor tags' },
|
||||
monitorPriority: { type: 'number', description: 'Monitor priority (1-5)' },
|
||||
options: { type: 'json', description: 'Monitor options' },
|
||||
monitorId: { type: 'string', description: 'Monitor ID' },
|
||||
muteMonitorId: { type: 'string', description: 'Monitor ID to mute' },
|
||||
scope: { type: 'string', description: 'Scope for muting' },
|
||||
end: { type: 'number', description: 'End time for mute' },
|
||||
// Logs
|
||||
logQuery: { type: 'string', description: 'Log search query' },
|
||||
logFrom: { type: 'string', description: 'Log start time' },
|
||||
logTo: { type: 'string', description: 'Log end time' },
|
||||
logLimit: { type: 'number', description: 'Max logs to return' },
|
||||
logs: { type: 'json', description: 'Logs to send' },
|
||||
// Downtimes
|
||||
downtimeScope: { type: 'string', description: 'Downtime scope' },
|
||||
downtimeMessage: { type: 'string', description: 'Downtime message' },
|
||||
downtimeStart: { type: 'number', description: 'Downtime start time' },
|
||||
downtimeEnd: { type: 'number', description: 'Downtime end time' },
|
||||
downtimeMonitorId: { type: 'string', description: 'Monitor ID for downtime' },
|
||||
currentOnly: { type: 'boolean', description: 'Filter to current downtimes' },
|
||||
downtimeId: { type: 'string', description: 'Downtime ID to cancel' },
|
||||
listMonitorName: { type: 'string', description: 'Filter monitors by name' },
|
||||
listMonitorTags: { type: 'string', description: 'Filter monitors by tags' },
|
||||
},
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the operation succeeded' },
|
||||
// Metrics
|
||||
series: { type: 'json', description: 'Timeseries data' },
|
||||
status: { type: 'string', description: 'Query status' },
|
||||
// Events
|
||||
event: { type: 'json', description: 'Event data' },
|
||||
events: { type: 'json', description: 'List of events' },
|
||||
// Monitors
|
||||
monitor: { type: 'json', description: 'Monitor data' },
|
||||
monitors: { type: 'json', description: 'List of monitors' },
|
||||
// Logs
|
||||
logs: { type: 'json', description: 'Log entries' },
|
||||
nextLogId: { type: 'string', description: 'Pagination cursor for logs' },
|
||||
// Downtimes
|
||||
downtime: { type: 'json', description: 'Downtime data' },
|
||||
downtimes: { type: 'json', description: 'List of downtimes' },
|
||||
},
|
||||
}
|
||||
368
apps/sim/blocks/blocks/dropbox.ts
Normal file
368
apps/sim/blocks/blocks/dropbox.ts
Normal file
@@ -0,0 +1,368 @@
|
||||
import { DropboxIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import type { DropboxResponse } from '@/tools/dropbox/types'
|
||||
|
||||
export const DropboxBlock: BlockConfig<DropboxResponse> = {
|
||||
type: 'dropbox',
|
||||
name: 'Dropbox',
|
||||
description: 'Upload, download, share, and manage files in Dropbox',
|
||||
authMode: AuthMode.OAuth,
|
||||
longDescription:
|
||||
'Integrate Dropbox into your workflow for file management, sharing, and collaboration. Upload files, download content, create folders, manage shared links, and more.',
|
||||
docsLink: 'https://docs.sim.ai/tools/dropbox',
|
||||
category: 'tools',
|
||||
icon: DropboxIcon,
|
||||
bgColor: '#0061FF',
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Upload File', id: 'dropbox_upload' },
|
||||
{ label: 'Download File', id: 'dropbox_download' },
|
||||
{ label: 'List Folder', id: 'dropbox_list_folder' },
|
||||
{ label: 'Create Folder', id: 'dropbox_create_folder' },
|
||||
{ label: 'Delete File/Folder', id: 'dropbox_delete' },
|
||||
{ label: 'Copy File/Folder', id: 'dropbox_copy' },
|
||||
{ label: 'Move File/Folder', id: 'dropbox_move' },
|
||||
{ label: 'Get Metadata', id: 'dropbox_get_metadata' },
|
||||
{ label: 'Create Shared Link', id: 'dropbox_create_shared_link' },
|
||||
{ label: 'Search Files', id: 'dropbox_search' },
|
||||
],
|
||||
value: () => 'dropbox_upload',
|
||||
},
|
||||
{
|
||||
id: 'credential',
|
||||
title: 'Dropbox Account',
|
||||
type: 'oauth-input',
|
||||
serviceId: 'dropbox',
|
||||
requiredScopes: [
|
||||
'account_info.read',
|
||||
'files.metadata.read',
|
||||
'files.metadata.write',
|
||||
'files.content.read',
|
||||
'files.content.write',
|
||||
'sharing.read',
|
||||
'sharing.write',
|
||||
],
|
||||
placeholder: 'Select Dropbox account',
|
||||
required: true,
|
||||
},
|
||||
// Upload operation inputs
|
||||
{
|
||||
id: 'path',
|
||||
title: 'Destination Path',
|
||||
type: 'short-input',
|
||||
placeholder: '/folder/document.pdf',
|
||||
condition: { field: 'operation', value: 'dropbox_upload' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'fileContent',
|
||||
title: 'File Content',
|
||||
type: 'long-input',
|
||||
placeholder: 'Base64 encoded file content or file reference',
|
||||
condition: { field: 'operation', value: 'dropbox_upload' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'mode',
|
||||
title: 'Write Mode',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Add (create new)', id: 'add' },
|
||||
{ label: 'Overwrite (replace existing)', id: 'overwrite' },
|
||||
],
|
||||
value: () => 'add',
|
||||
condition: { field: 'operation', value: 'dropbox_upload' },
|
||||
},
|
||||
{
|
||||
id: 'autorename',
|
||||
title: 'Auto-rename on Conflict',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'dropbox_upload' },
|
||||
},
|
||||
// Download operation inputs
|
||||
{
|
||||
id: 'path',
|
||||
title: 'File Path',
|
||||
type: 'short-input',
|
||||
placeholder: '/folder/document.pdf',
|
||||
condition: { field: 'operation', value: 'dropbox_download' },
|
||||
required: true,
|
||||
},
|
||||
// List folder operation inputs
|
||||
{
|
||||
id: 'path',
|
||||
title: 'Folder Path',
|
||||
type: 'short-input',
|
||||
placeholder: '/ (root) or /folder',
|
||||
condition: { field: 'operation', value: 'dropbox_list_folder' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'recursive',
|
||||
title: 'List Recursively',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'dropbox_list_folder' },
|
||||
},
|
||||
{
|
||||
id: 'limit',
|
||||
title: 'Maximum Results',
|
||||
type: 'short-input',
|
||||
placeholder: '500',
|
||||
condition: { field: 'operation', value: 'dropbox_list_folder' },
|
||||
},
|
||||
// Create folder operation inputs
|
||||
{
|
||||
id: 'path',
|
||||
title: 'Folder Path',
|
||||
type: 'short-input',
|
||||
placeholder: '/new-folder',
|
||||
condition: { field: 'operation', value: 'dropbox_create_folder' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'autorename',
|
||||
title: 'Auto-rename on Conflict',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'dropbox_create_folder' },
|
||||
},
|
||||
// Delete operation inputs
|
||||
{
|
||||
id: 'path',
|
||||
title: 'Path to Delete',
|
||||
type: 'short-input',
|
||||
placeholder: '/folder/file.txt',
|
||||
condition: { field: 'operation', value: 'dropbox_delete' },
|
||||
required: true,
|
||||
},
|
||||
// Copy operation inputs
|
||||
{
|
||||
id: 'fromPath',
|
||||
title: 'Source Path',
|
||||
type: 'short-input',
|
||||
placeholder: '/source/document.pdf',
|
||||
condition: { field: 'operation', value: 'dropbox_copy' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'toPath',
|
||||
title: 'Destination Path',
|
||||
type: 'short-input',
|
||||
placeholder: '/destination/document.pdf',
|
||||
condition: { field: 'operation', value: 'dropbox_copy' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'autorename',
|
||||
title: 'Auto-rename on Conflict',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'dropbox_copy' },
|
||||
},
|
||||
// Move operation inputs
|
||||
{
|
||||
id: 'fromPath',
|
||||
title: 'Source Path',
|
||||
type: 'short-input',
|
||||
placeholder: '/old-location/document.pdf',
|
||||
condition: { field: 'operation', value: 'dropbox_move' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'toPath',
|
||||
title: 'Destination Path',
|
||||
type: 'short-input',
|
||||
placeholder: '/new-location/document.pdf',
|
||||
condition: { field: 'operation', value: 'dropbox_move' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'autorename',
|
||||
title: 'Auto-rename on Conflict',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'dropbox_move' },
|
||||
},
|
||||
// Get metadata operation inputs
|
||||
{
|
||||
id: 'path',
|
||||
title: 'File/Folder Path',
|
||||
type: 'short-input',
|
||||
placeholder: '/folder/document.pdf',
|
||||
condition: { field: 'operation', value: 'dropbox_get_metadata' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'includeMediaInfo',
|
||||
title: 'Include Media Info',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'dropbox_get_metadata' },
|
||||
},
|
||||
// Create shared link operation inputs
|
||||
{
|
||||
id: 'path',
|
||||
title: 'File/Folder Path',
|
||||
type: 'short-input',
|
||||
placeholder: '/folder/document.pdf',
|
||||
condition: { field: 'operation', value: 'dropbox_create_shared_link' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'requestedVisibility',
|
||||
title: 'Visibility',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Public (anyone with link)', id: 'public' },
|
||||
{ label: 'Team Only', id: 'team_only' },
|
||||
{ label: 'Password Protected', id: 'password' },
|
||||
],
|
||||
value: () => 'public',
|
||||
condition: { field: 'operation', value: 'dropbox_create_shared_link' },
|
||||
},
|
||||
{
|
||||
id: 'linkPassword',
|
||||
title: 'Link Password',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter password for the link',
|
||||
password: true,
|
||||
condition: { field: 'operation', value: 'dropbox_create_shared_link' },
|
||||
},
|
||||
{
|
||||
id: 'expires',
|
||||
title: 'Expiration Date',
|
||||
type: 'short-input',
|
||||
placeholder: '2025-12-31T23:59:59Z',
|
||||
condition: { field: 'operation', value: 'dropbox_create_shared_link' },
|
||||
},
|
||||
// Search operation inputs
|
||||
{
|
||||
id: 'query',
|
||||
title: 'Search Query',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter search term...',
|
||||
condition: { field: 'operation', value: 'dropbox_search' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'path',
|
||||
title: 'Search in Folder',
|
||||
type: 'short-input',
|
||||
placeholder: '/ (search all) or /folder',
|
||||
condition: { field: 'operation', value: 'dropbox_search' },
|
||||
},
|
||||
{
|
||||
id: 'fileExtensions',
|
||||
title: 'File Extensions',
|
||||
type: 'short-input',
|
||||
placeholder: 'pdf,xlsx,docx (comma-separated)',
|
||||
condition: { field: 'operation', value: 'dropbox_search' },
|
||||
},
|
||||
{
|
||||
id: 'maxResults',
|
||||
title: 'Maximum Results',
|
||||
type: 'short-input',
|
||||
placeholder: '100',
|
||||
condition: { field: 'operation', value: 'dropbox_search' },
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: [
|
||||
'dropbox_upload',
|
||||
'dropbox_download',
|
||||
'dropbox_list_folder',
|
||||
'dropbox_create_folder',
|
||||
'dropbox_delete',
|
||||
'dropbox_copy',
|
||||
'dropbox_move',
|
||||
'dropbox_get_metadata',
|
||||
'dropbox_create_shared_link',
|
||||
'dropbox_search',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => {
|
||||
// Convert numeric params
|
||||
if (params.limit) {
|
||||
params.limit = Number(params.limit)
|
||||
}
|
||||
if (params.maxResults) {
|
||||
params.maxResults = Number(params.maxResults)
|
||||
}
|
||||
|
||||
switch (params.operation) {
|
||||
case 'dropbox_upload':
|
||||
return 'dropbox_upload'
|
||||
case 'dropbox_download':
|
||||
return 'dropbox_download'
|
||||
case 'dropbox_list_folder':
|
||||
return 'dropbox_list_folder'
|
||||
case 'dropbox_create_folder':
|
||||
return 'dropbox_create_folder'
|
||||
case 'dropbox_delete':
|
||||
return 'dropbox_delete'
|
||||
case 'dropbox_copy':
|
||||
return 'dropbox_copy'
|
||||
case 'dropbox_move':
|
||||
return 'dropbox_move'
|
||||
case 'dropbox_get_metadata':
|
||||
return 'dropbox_get_metadata'
|
||||
case 'dropbox_create_shared_link':
|
||||
return 'dropbox_create_shared_link'
|
||||
case 'dropbox_search':
|
||||
return 'dropbox_search'
|
||||
default:
|
||||
return 'dropbox_upload'
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'Operation to perform' },
|
||||
credential: { type: 'string', description: 'Dropbox OAuth credential' },
|
||||
// Common inputs
|
||||
path: { type: 'string', description: 'Path in Dropbox' },
|
||||
autorename: { type: 'boolean', description: 'Auto-rename on conflict' },
|
||||
// Upload inputs
|
||||
fileContent: { type: 'string', description: 'Base64 encoded file content' },
|
||||
fileName: { type: 'string', description: 'Optional filename' },
|
||||
mode: { type: 'string', description: 'Write mode: add or overwrite' },
|
||||
mute: { type: 'boolean', description: 'Mute notifications' },
|
||||
// List folder inputs
|
||||
recursive: { type: 'boolean', description: 'List recursively' },
|
||||
includeDeleted: { type: 'boolean', description: 'Include deleted files' },
|
||||
includeMediaInfo: { type: 'boolean', description: 'Include media info' },
|
||||
limit: { type: 'number', description: 'Maximum results' },
|
||||
// Copy/Move inputs
|
||||
fromPath: { type: 'string', description: 'Source path' },
|
||||
toPath: { type: 'string', description: 'Destination path' },
|
||||
// Shared link inputs
|
||||
requestedVisibility: { type: 'string', description: 'Link visibility' },
|
||||
linkPassword: { type: 'string', description: 'Password for the link' },
|
||||
expires: { type: 'string', description: 'Expiration date (ISO 8601)' },
|
||||
// Search inputs
|
||||
query: { type: 'string', description: 'Search query' },
|
||||
fileExtensions: { type: 'string', description: 'File extensions filter' },
|
||||
maxResults: { type: 'number', description: 'Maximum search results' },
|
||||
},
|
||||
outputs: {
|
||||
// Upload/Download outputs
|
||||
file: { type: 'json', description: 'File metadata' },
|
||||
content: { type: 'string', description: 'File content (base64)' },
|
||||
temporaryLink: { type: 'string', description: 'Temporary download link' },
|
||||
// List folder outputs
|
||||
entries: { type: 'json', description: 'List of files and folders' },
|
||||
cursor: { type: 'string', description: 'Pagination cursor' },
|
||||
hasMore: { type: 'boolean', description: 'Whether more results exist' },
|
||||
// Create folder output
|
||||
folder: { type: 'json', description: 'Created folder metadata' },
|
||||
// Delete output
|
||||
deleted: { type: 'boolean', description: 'Whether deletion was successful' },
|
||||
// Copy/Move/Get metadata output
|
||||
metadata: { type: 'json', description: 'Item metadata' },
|
||||
// Shared link output
|
||||
sharedLink: { type: 'json', description: 'Shared link details' },
|
||||
// Search outputs
|
||||
matches: { type: 'json', description: 'Search results' },
|
||||
},
|
||||
}
|
||||
435
apps/sim/blocks/blocks/elasticsearch.ts
Normal file
435
apps/sim/blocks/blocks/elasticsearch.ts
Normal file
@@ -0,0 +1,435 @@
|
||||
import { ElasticsearchIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import type { ElasticsearchResponse } from '@/tools/elasticsearch/types'
|
||||
|
||||
export const ElasticsearchBlock: BlockConfig<ElasticsearchResponse> = {
|
||||
type: 'elasticsearch',
|
||||
name: 'Elasticsearch',
|
||||
description: 'Search, index, and manage data in Elasticsearch',
|
||||
authMode: AuthMode.ApiKey,
|
||||
longDescription:
|
||||
'Integrate Elasticsearch into workflows for powerful search, indexing, and data management. Supports document CRUD operations, advanced search queries, bulk operations, index management, and cluster monitoring. Works with both self-hosted and Elastic Cloud deployments.',
|
||||
docsLink: 'https://docs.sim.ai/tools/elasticsearch',
|
||||
category: 'tools',
|
||||
bgColor: '#E0E0E0',
|
||||
icon: ElasticsearchIcon,
|
||||
subBlocks: [
|
||||
// Operation selector
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
// Document Operations
|
||||
{ label: 'Search', id: 'elasticsearch_search' },
|
||||
{ label: 'Index Document', id: 'elasticsearch_index_document' },
|
||||
{ label: 'Get Document', id: 'elasticsearch_get_document' },
|
||||
{ label: 'Update Document', id: 'elasticsearch_update_document' },
|
||||
{ label: 'Delete Document', id: 'elasticsearch_delete_document' },
|
||||
{ label: 'Bulk Operations', id: 'elasticsearch_bulk' },
|
||||
{ label: 'Count Documents', id: 'elasticsearch_count' },
|
||||
// Index Management
|
||||
{ label: 'Create Index', id: 'elasticsearch_create_index' },
|
||||
{ label: 'Delete Index', id: 'elasticsearch_delete_index' },
|
||||
{ label: 'Get Index Info', id: 'elasticsearch_get_index' },
|
||||
// Cluster Operations
|
||||
{ label: 'Cluster Health', id: 'elasticsearch_cluster_health' },
|
||||
{ label: 'Cluster Stats', id: 'elasticsearch_cluster_stats' },
|
||||
],
|
||||
value: () => 'elasticsearch_search',
|
||||
},
|
||||
|
||||
// Deployment type
|
||||
{
|
||||
id: 'deploymentType',
|
||||
title: 'Deployment Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Self-Hosted', id: 'self_hosted' },
|
||||
{ label: 'Elastic Cloud', id: 'cloud' },
|
||||
],
|
||||
value: () => 'self_hosted',
|
||||
},
|
||||
|
||||
// Self-hosted host
|
||||
{
|
||||
id: 'host',
|
||||
title: 'Elasticsearch Host',
|
||||
type: 'short-input',
|
||||
placeholder: 'https://localhost:9200',
|
||||
required: true,
|
||||
condition: { field: 'deploymentType', value: 'self_hosted' },
|
||||
},
|
||||
|
||||
// Cloud ID
|
||||
{
|
||||
id: 'cloudId',
|
||||
title: 'Cloud ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'deployment-name:base64-encoded-data',
|
||||
required: true,
|
||||
condition: { field: 'deploymentType', value: 'cloud' },
|
||||
},
|
||||
|
||||
// Authentication method
|
||||
{
|
||||
id: 'authMethod',
|
||||
title: 'Authentication Method',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'API Key', id: 'api_key' },
|
||||
{ label: 'Basic Auth', id: 'basic_auth' },
|
||||
],
|
||||
value: () => 'api_key',
|
||||
},
|
||||
|
||||
// API Key
|
||||
{
|
||||
id: 'apiKey',
|
||||
title: 'API Key',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter encoded API key',
|
||||
password: true,
|
||||
required: true,
|
||||
condition: { field: 'authMethod', value: 'api_key' },
|
||||
},
|
||||
|
||||
// Username
|
||||
{
|
||||
id: 'username',
|
||||
title: 'Username',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter username',
|
||||
required: true,
|
||||
condition: { field: 'authMethod', value: 'basic_auth' },
|
||||
},
|
||||
|
||||
// Password
|
||||
{
|
||||
id: 'password',
|
||||
title: 'Password',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter password',
|
||||
password: true,
|
||||
required: true,
|
||||
condition: { field: 'authMethod', value: 'basic_auth' },
|
||||
},
|
||||
|
||||
// Index name - for most operations
|
||||
{
|
||||
id: 'index',
|
||||
title: 'Index Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'my-index',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'elasticsearch_search',
|
||||
'elasticsearch_index_document',
|
||||
'elasticsearch_get_document',
|
||||
'elasticsearch_update_document',
|
||||
'elasticsearch_delete_document',
|
||||
'elasticsearch_bulk',
|
||||
'elasticsearch_count',
|
||||
'elasticsearch_create_index',
|
||||
'elasticsearch_delete_index',
|
||||
'elasticsearch_get_index',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
// Document ID - for get/update/delete
|
||||
{
|
||||
id: 'documentId',
|
||||
title: 'Document ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'unique-document-id',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'elasticsearch_get_document',
|
||||
'elasticsearch_update_document',
|
||||
'elasticsearch_delete_document',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
// Optional Document ID - for index document
|
||||
{
|
||||
id: 'documentId',
|
||||
title: 'Document ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Leave empty for auto-generated ID',
|
||||
condition: { field: 'operation', value: 'elasticsearch_index_document' },
|
||||
},
|
||||
|
||||
// Document body - for index
|
||||
{
|
||||
id: 'document',
|
||||
title: 'Document',
|
||||
type: 'code',
|
||||
placeholder: '{ "field": "value", "another_field": 123 }',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'elasticsearch_index_document' },
|
||||
},
|
||||
|
||||
// Document body - for update (partial)
|
||||
{
|
||||
id: 'document',
|
||||
title: 'Partial Document',
|
||||
type: 'code',
|
||||
placeholder: '{ "field_to_update": "new_value" }',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'elasticsearch_update_document' },
|
||||
},
|
||||
|
||||
// Search query
|
||||
{
|
||||
id: 'query',
|
||||
title: 'Search Query',
|
||||
type: 'code',
|
||||
placeholder: '{ "match": { "field": "search term" } }',
|
||||
condition: { field: 'operation', value: 'elasticsearch_search' },
|
||||
},
|
||||
|
||||
// Count query
|
||||
{
|
||||
id: 'query',
|
||||
title: 'Query',
|
||||
type: 'code',
|
||||
placeholder: '{ "match": { "field": "value" } }',
|
||||
condition: { field: 'operation', value: 'elasticsearch_count' },
|
||||
},
|
||||
|
||||
// Search size
|
||||
{
|
||||
id: 'size',
|
||||
title: 'Number of Results',
|
||||
type: 'short-input',
|
||||
placeholder: '10',
|
||||
condition: { field: 'operation', value: 'elasticsearch_search' },
|
||||
},
|
||||
|
||||
// Search from (offset)
|
||||
{
|
||||
id: 'from',
|
||||
title: 'Offset',
|
||||
type: 'short-input',
|
||||
placeholder: '0',
|
||||
condition: { field: 'operation', value: 'elasticsearch_search' },
|
||||
},
|
||||
|
||||
// Sort
|
||||
{
|
||||
id: 'sort',
|
||||
title: 'Sort',
|
||||
type: 'code',
|
||||
placeholder: '[{ "field": { "order": "asc" } }]',
|
||||
condition: { field: 'operation', value: 'elasticsearch_search' },
|
||||
},
|
||||
|
||||
// Source includes
|
||||
{
|
||||
id: 'sourceIncludes',
|
||||
title: 'Fields to Include',
|
||||
type: 'short-input',
|
||||
placeholder: 'field1, field2 (comma-separated)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['elasticsearch_search', 'elasticsearch_get_document'],
|
||||
},
|
||||
},
|
||||
|
||||
// Source excludes
|
||||
{
|
||||
id: 'sourceExcludes',
|
||||
title: 'Fields to Exclude',
|
||||
type: 'short-input',
|
||||
placeholder: 'field1, field2 (comma-separated)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['elasticsearch_search', 'elasticsearch_get_document'],
|
||||
},
|
||||
},
|
||||
|
||||
// Bulk operations
|
||||
{
|
||||
id: 'operations',
|
||||
title: 'Bulk Operations',
|
||||
type: 'code',
|
||||
placeholder:
|
||||
'{ "index": { "_index": "my-index", "_id": "1" } }\n{ "field": "value" }\n{ "delete": { "_index": "my-index", "_id": "2" } }',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'elasticsearch_bulk' },
|
||||
},
|
||||
|
||||
// Index settings
|
||||
{
|
||||
id: 'settings',
|
||||
title: 'Index Settings',
|
||||
type: 'code',
|
||||
placeholder: '{ "number_of_shards": 1, "number_of_replicas": 1 }',
|
||||
condition: { field: 'operation', value: 'elasticsearch_create_index' },
|
||||
},
|
||||
|
||||
// Index mappings
|
||||
{
|
||||
id: 'mappings',
|
||||
title: 'Index Mappings',
|
||||
type: 'code',
|
||||
placeholder: '{ "properties": { "field": { "type": "text" } } }',
|
||||
condition: { field: 'operation', value: 'elasticsearch_create_index' },
|
||||
},
|
||||
|
||||
// Refresh option
|
||||
{
|
||||
id: 'refresh',
|
||||
title: 'Refresh',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Default', id: '' },
|
||||
{ label: 'Wait For', id: 'wait_for' },
|
||||
{ label: 'Immediate', id: 'true' },
|
||||
{ label: 'None', id: 'false' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'elasticsearch_index_document',
|
||||
'elasticsearch_delete_document',
|
||||
'elasticsearch_bulk',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
// Cluster health wait for status
|
||||
{
|
||||
id: 'waitForStatus',
|
||||
title: 'Wait for Status',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'None', id: '' },
|
||||
{ label: 'Green', id: 'green' },
|
||||
{ label: 'Yellow', id: 'yellow' },
|
||||
{ label: 'Red', id: 'red' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'elasticsearch_cluster_health' },
|
||||
},
|
||||
|
||||
// Cluster health timeout
|
||||
{
|
||||
id: 'timeout',
|
||||
title: 'Timeout (seconds)',
|
||||
type: 'short-input',
|
||||
placeholder: '30',
|
||||
condition: { field: 'operation', value: 'elasticsearch_cluster_health' },
|
||||
},
|
||||
|
||||
// Retry on conflict
|
||||
{
|
||||
id: 'retryOnConflict',
|
||||
title: 'Retry on Conflict',
|
||||
type: 'short-input',
|
||||
placeholder: '3',
|
||||
condition: { field: 'operation', value: 'elasticsearch_update_document' },
|
||||
},
|
||||
],
|
||||
|
||||
tools: {
|
||||
access: [
|
||||
'elasticsearch_search',
|
||||
'elasticsearch_index_document',
|
||||
'elasticsearch_get_document',
|
||||
'elasticsearch_update_document',
|
||||
'elasticsearch_delete_document',
|
||||
'elasticsearch_bulk',
|
||||
'elasticsearch_count',
|
||||
'elasticsearch_create_index',
|
||||
'elasticsearch_delete_index',
|
||||
'elasticsearch_get_index',
|
||||
'elasticsearch_cluster_health',
|
||||
'elasticsearch_cluster_stats',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => {
|
||||
// Convert numeric strings to numbers
|
||||
if (params.size) {
|
||||
params.size = Number(params.size)
|
||||
}
|
||||
if (params.from) {
|
||||
params.from = Number(params.from)
|
||||
}
|
||||
if (params.retryOnConflict) {
|
||||
params.retryOnConflict = Number(params.retryOnConflict)
|
||||
}
|
||||
// Append 's' to timeout for Elasticsearch time format
|
||||
if (params.timeout && !params.timeout.endsWith('s')) {
|
||||
params.timeout = `${params.timeout}s`
|
||||
}
|
||||
|
||||
// Return the operation as the tool ID
|
||||
return params.operation || 'elasticsearch_search'
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'Operation to perform' },
|
||||
deploymentType: { type: 'string', description: 'self_hosted or cloud' },
|
||||
host: { type: 'string', description: 'Elasticsearch host URL' },
|
||||
cloudId: { type: 'string', description: 'Elastic Cloud ID' },
|
||||
authMethod: { type: 'string', description: 'api_key or basic_auth' },
|
||||
apiKey: { type: 'string', description: 'API key for authentication' },
|
||||
username: { type: 'string', description: 'Username for basic auth' },
|
||||
password: { type: 'string', description: 'Password for basic auth' },
|
||||
index: { type: 'string', description: 'Index name' },
|
||||
documentId: { type: 'string', description: 'Document ID' },
|
||||
document: { type: 'string', description: 'Document body as JSON' },
|
||||
query: { type: 'string', description: 'Search query as JSON' },
|
||||
size: { type: 'number', description: 'Number of results' },
|
||||
from: { type: 'number', description: 'Starting offset' },
|
||||
sort: { type: 'string', description: 'Sort specification as JSON' },
|
||||
sourceIncludes: { type: 'string', description: 'Fields to include' },
|
||||
sourceExcludes: { type: 'string', description: 'Fields to exclude' },
|
||||
operations: { type: 'string', description: 'Bulk operations as NDJSON' },
|
||||
settings: { type: 'string', description: 'Index settings as JSON' },
|
||||
mappings: { type: 'string', description: 'Index mappings as JSON' },
|
||||
refresh: { type: 'string', description: 'Refresh policy' },
|
||||
waitForStatus: { type: 'string', description: 'Wait for cluster status' },
|
||||
timeout: { type: 'string', description: 'Timeout for wait operations' },
|
||||
retryOnConflict: { type: 'number', description: 'Retry attempts on conflict' },
|
||||
},
|
||||
|
||||
outputs: {
|
||||
// Search outputs
|
||||
hits: { type: 'json', description: 'Search results' },
|
||||
took: { type: 'number', description: 'Time taken in milliseconds' },
|
||||
timed_out: { type: 'boolean', description: 'Whether the operation timed out' },
|
||||
aggregations: { type: 'json', description: 'Aggregation results' },
|
||||
// Document outputs
|
||||
_index: { type: 'string', description: 'Index name' },
|
||||
_id: { type: 'string', description: 'Document ID' },
|
||||
_version: { type: 'number', description: 'Document version' },
|
||||
_source: { type: 'json', description: 'Document content' },
|
||||
result: { type: 'string', description: 'Operation result' },
|
||||
found: { type: 'boolean', description: 'Whether document was found' },
|
||||
// Bulk outputs
|
||||
errors: { type: 'boolean', description: 'Whether any errors occurred' },
|
||||
items: { type: 'json', description: 'Bulk operation results' },
|
||||
// Count outputs
|
||||
count: { type: 'number', description: 'Document count' },
|
||||
// Index outputs
|
||||
acknowledged: { type: 'boolean', description: 'Whether operation was acknowledged' },
|
||||
// Cluster outputs
|
||||
cluster_name: { type: 'string', description: 'Cluster name' },
|
||||
status: { type: 'string', description: 'Cluster health status' },
|
||||
number_of_nodes: { type: 'number', description: 'Number of nodes' },
|
||||
indices: { type: 'json', description: 'Index statistics' },
|
||||
nodes: { type: 'json', description: 'Node statistics' },
|
||||
},
|
||||
}
|
||||
697
apps/sim/blocks/blocks/gitlab.ts
Normal file
697
apps/sim/blocks/blocks/gitlab.ts
Normal file
@@ -0,0 +1,697 @@
|
||||
import { GitLabIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import type { GitLabResponse } from '@/tools/gitlab/types'
|
||||
|
||||
export const GitLabBlock: BlockConfig<GitLabResponse> = {
|
||||
type: 'gitlab',
|
||||
name: 'GitLab',
|
||||
description: 'Interact with GitLab projects, issues, merge requests, and pipelines',
|
||||
authMode: AuthMode.ApiKey,
|
||||
triggerAllowed: false,
|
||||
longDescription:
|
||||
'Integrate GitLab into the workflow. Can manage projects, issues, merge requests, pipelines, and add comments. Supports all core GitLab DevOps operations.',
|
||||
docsLink: 'https://docs.sim.ai/tools/gitlab',
|
||||
category: 'tools',
|
||||
icon: GitLabIcon,
|
||||
bgColor: '#E0E0E0',
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
// Project Operations
|
||||
{ label: 'List Projects', id: 'gitlab_list_projects' },
|
||||
{ label: 'Get Project', id: 'gitlab_get_project' },
|
||||
// Issue Operations
|
||||
{ label: 'List Issues', id: 'gitlab_list_issues' },
|
||||
{ label: 'Get Issue', id: 'gitlab_get_issue' },
|
||||
{ label: 'Create Issue', id: 'gitlab_create_issue' },
|
||||
{ label: 'Update Issue', id: 'gitlab_update_issue' },
|
||||
{ label: 'Delete Issue', id: 'gitlab_delete_issue' },
|
||||
{ label: 'Add Issue Comment', id: 'gitlab_create_issue_note' },
|
||||
// Merge Request Operations
|
||||
{ label: 'List Merge Requests', id: 'gitlab_list_merge_requests' },
|
||||
{ label: 'Get Merge Request', id: 'gitlab_get_merge_request' },
|
||||
{ label: 'Create Merge Request', id: 'gitlab_create_merge_request' },
|
||||
{ label: 'Update Merge Request', id: 'gitlab_update_merge_request' },
|
||||
{ label: 'Merge Merge Request', id: 'gitlab_merge_merge_request' },
|
||||
{ label: 'Add MR Comment', id: 'gitlab_create_merge_request_note' },
|
||||
// Pipeline Operations
|
||||
{ label: 'List Pipelines', id: 'gitlab_list_pipelines' },
|
||||
{ label: 'Get Pipeline', id: 'gitlab_get_pipeline' },
|
||||
{ label: 'Create Pipeline', id: 'gitlab_create_pipeline' },
|
||||
{ label: 'Retry Pipeline', id: 'gitlab_retry_pipeline' },
|
||||
{ label: 'Cancel Pipeline', id: 'gitlab_cancel_pipeline' },
|
||||
],
|
||||
value: () => 'gitlab_list_projects',
|
||||
},
|
||||
{
|
||||
id: 'accessToken',
|
||||
title: 'Personal Access Token',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter your GitLab Personal Access Token',
|
||||
password: true,
|
||||
required: true,
|
||||
},
|
||||
// Project ID (required for most operations)
|
||||
{
|
||||
id: 'projectId',
|
||||
title: 'Project ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter project ID or path (e.g., username/project)',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'gitlab_get_project',
|
||||
'gitlab_list_issues',
|
||||
'gitlab_get_issue',
|
||||
'gitlab_create_issue',
|
||||
'gitlab_update_issue',
|
||||
'gitlab_delete_issue',
|
||||
'gitlab_create_issue_note',
|
||||
'gitlab_list_merge_requests',
|
||||
'gitlab_get_merge_request',
|
||||
'gitlab_create_merge_request',
|
||||
'gitlab_update_merge_request',
|
||||
'gitlab_merge_merge_request',
|
||||
'gitlab_create_merge_request_note',
|
||||
'gitlab_list_pipelines',
|
||||
'gitlab_get_pipeline',
|
||||
'gitlab_create_pipeline',
|
||||
'gitlab_retry_pipeline',
|
||||
'gitlab_cancel_pipeline',
|
||||
],
|
||||
},
|
||||
},
|
||||
// Issue Number (IID) - the # shown in GitLab UI
|
||||
{
|
||||
id: 'issueIid',
|
||||
title: 'Issue Number',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter issue number (e.g., 1 for issue #1)',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'gitlab_get_issue',
|
||||
'gitlab_update_issue',
|
||||
'gitlab_delete_issue',
|
||||
'gitlab_create_issue_note',
|
||||
],
|
||||
},
|
||||
},
|
||||
// Merge Request Number (IID) - the ! number shown in GitLab UI
|
||||
{
|
||||
id: 'mergeRequestIid',
|
||||
title: 'MR Number',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter MR number (e.g., 1 for !1)',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'gitlab_get_merge_request',
|
||||
'gitlab_update_merge_request',
|
||||
'gitlab_merge_merge_request',
|
||||
'gitlab_create_merge_request_note',
|
||||
],
|
||||
},
|
||||
},
|
||||
// Pipeline ID
|
||||
{
|
||||
id: 'pipelineId',
|
||||
title: 'Pipeline ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter pipeline ID',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['gitlab_get_pipeline', 'gitlab_retry_pipeline', 'gitlab_cancel_pipeline'],
|
||||
},
|
||||
},
|
||||
// Title (for issue/MR creation)
|
||||
{
|
||||
id: 'title',
|
||||
title: 'Title',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter title',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['gitlab_create_issue', 'gitlab_create_merge_request'],
|
||||
},
|
||||
},
|
||||
// Description
|
||||
{
|
||||
id: 'description',
|
||||
title: 'Description',
|
||||
type: 'long-input',
|
||||
placeholder: 'Enter description (Markdown supported)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'gitlab_create_issue',
|
||||
'gitlab_update_issue',
|
||||
'gitlab_create_merge_request',
|
||||
'gitlab_update_merge_request',
|
||||
],
|
||||
},
|
||||
},
|
||||
// Comment body
|
||||
{
|
||||
id: 'body',
|
||||
title: 'Comment',
|
||||
type: 'long-input',
|
||||
placeholder: 'Enter comment text (Markdown supported)',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['gitlab_create_issue_note', 'gitlab_create_merge_request_note'],
|
||||
},
|
||||
},
|
||||
// Source branch (for MR creation)
|
||||
{
|
||||
id: 'sourceBranch',
|
||||
title: 'Source Branch',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter source branch name',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['gitlab_create_merge_request'],
|
||||
},
|
||||
},
|
||||
// Target branch (for MR creation)
|
||||
{
|
||||
id: 'targetBranch',
|
||||
title: 'Target Branch',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter target branch name (e.g., main)',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['gitlab_create_merge_request'],
|
||||
},
|
||||
},
|
||||
// Ref (for pipeline creation)
|
||||
{
|
||||
id: 'ref',
|
||||
title: 'Branch/Tag',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter branch or tag name',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['gitlab_create_pipeline'],
|
||||
},
|
||||
},
|
||||
// Labels
|
||||
{
|
||||
id: 'labels',
|
||||
title: 'Labels',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter labels (comma-separated)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'gitlab_create_issue',
|
||||
'gitlab_update_issue',
|
||||
'gitlab_list_issues',
|
||||
'gitlab_create_merge_request',
|
||||
'gitlab_update_merge_request',
|
||||
'gitlab_list_merge_requests',
|
||||
],
|
||||
},
|
||||
},
|
||||
// Assignee IDs
|
||||
{
|
||||
id: 'assigneeIds',
|
||||
title: 'Assignee IDs',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter assignee user IDs (comma-separated)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'gitlab_create_issue',
|
||||
'gitlab_update_issue',
|
||||
'gitlab_create_merge_request',
|
||||
'gitlab_update_merge_request',
|
||||
],
|
||||
},
|
||||
},
|
||||
// Milestone ID
|
||||
{
|
||||
id: 'milestoneId',
|
||||
title: 'Milestone ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter milestone ID',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['gitlab_create_issue', 'gitlab_update_issue'],
|
||||
},
|
||||
},
|
||||
// State filter for issues
|
||||
{
|
||||
id: 'issueState',
|
||||
title: 'State',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'All', id: 'all' },
|
||||
{ label: 'Open', id: 'opened' },
|
||||
{ label: 'Closed', id: 'closed' },
|
||||
],
|
||||
value: () => 'all',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['gitlab_list_issues'],
|
||||
},
|
||||
},
|
||||
// State filter for merge requests
|
||||
{
|
||||
id: 'mrState',
|
||||
title: 'State',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'All', id: 'all' },
|
||||
{ label: 'Open', id: 'opened' },
|
||||
{ label: 'Closed', id: 'closed' },
|
||||
{ label: 'Merged', id: 'merged' },
|
||||
],
|
||||
value: () => 'all',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['gitlab_list_merge_requests'],
|
||||
},
|
||||
},
|
||||
// State event (for updates)
|
||||
{
|
||||
id: 'stateEvent',
|
||||
title: 'State Event',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'No Change', id: '' },
|
||||
{ label: 'Close', id: 'close' },
|
||||
{ label: 'Reopen', id: 'reopen' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['gitlab_update_issue', 'gitlab_update_merge_request'],
|
||||
},
|
||||
},
|
||||
// Pipeline status filter
|
||||
{
|
||||
id: 'pipelineStatus',
|
||||
title: 'Pipeline Status',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'All', id: '' },
|
||||
{ label: 'Running', id: 'running' },
|
||||
{ label: 'Pending', id: 'pending' },
|
||||
{ label: 'Success', id: 'success' },
|
||||
{ label: 'Failed', id: 'failed' },
|
||||
{ label: 'Canceled', id: 'canceled' },
|
||||
{ label: 'Skipped', id: 'skipped' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['gitlab_list_pipelines'],
|
||||
},
|
||||
},
|
||||
// Remove source branch after merge
|
||||
{
|
||||
id: 'removeSourceBranch',
|
||||
title: 'Remove Source Branch',
|
||||
type: 'switch',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['gitlab_create_merge_request', 'gitlab_merge_merge_request'],
|
||||
},
|
||||
},
|
||||
// Squash commits
|
||||
{
|
||||
id: 'squash',
|
||||
title: 'Squash Commits',
|
||||
type: 'switch',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['gitlab_merge_merge_request'],
|
||||
},
|
||||
},
|
||||
// Merge commit message
|
||||
{
|
||||
id: 'mergeCommitMessage',
|
||||
title: 'Merge Commit Message',
|
||||
type: 'long-input',
|
||||
placeholder: 'Enter custom merge commit message (optional)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['gitlab_merge_merge_request'],
|
||||
},
|
||||
},
|
||||
// Per page (pagination)
|
||||
{
|
||||
id: 'perPage',
|
||||
title: 'Results Per Page',
|
||||
type: 'short-input',
|
||||
placeholder: 'Number of results per page (default: 20, max: 100)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'gitlab_list_projects',
|
||||
'gitlab_list_issues',
|
||||
'gitlab_list_merge_requests',
|
||||
'gitlab_list_pipelines',
|
||||
],
|
||||
},
|
||||
},
|
||||
// Page number
|
||||
{
|
||||
id: 'page',
|
||||
title: 'Page Number',
|
||||
type: 'short-input',
|
||||
placeholder: 'Page number (default: 1)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'gitlab_list_projects',
|
||||
'gitlab_list_issues',
|
||||
'gitlab_list_merge_requests',
|
||||
'gitlab_list_pipelines',
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: [
|
||||
'gitlab_list_projects',
|
||||
'gitlab_get_project',
|
||||
'gitlab_list_issues',
|
||||
'gitlab_get_issue',
|
||||
'gitlab_create_issue',
|
||||
'gitlab_update_issue',
|
||||
'gitlab_delete_issue',
|
||||
'gitlab_create_issue_note',
|
||||
'gitlab_list_merge_requests',
|
||||
'gitlab_get_merge_request',
|
||||
'gitlab_create_merge_request',
|
||||
'gitlab_update_merge_request',
|
||||
'gitlab_merge_merge_request',
|
||||
'gitlab_create_merge_request_note',
|
||||
'gitlab_list_pipelines',
|
||||
'gitlab_get_pipeline',
|
||||
'gitlab_create_pipeline',
|
||||
'gitlab_retry_pipeline',
|
||||
'gitlab_cancel_pipeline',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => {
|
||||
return params.operation || 'gitlab_list_projects'
|
||||
},
|
||||
params: (params) => {
|
||||
const baseParams: Record<string, any> = {
|
||||
accessToken: params.accessToken,
|
||||
}
|
||||
|
||||
switch (params.operation) {
|
||||
case 'gitlab_list_projects':
|
||||
return {
|
||||
...baseParams,
|
||||
perPage: params.perPage ? Number(params.perPage) : undefined,
|
||||
page: params.page ? Number(params.page) : undefined,
|
||||
}
|
||||
|
||||
case 'gitlab_get_project':
|
||||
if (!params.projectId?.trim()) {
|
||||
throw new Error('Project ID is required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
projectId: params.projectId.trim(),
|
||||
}
|
||||
|
||||
case 'gitlab_list_issues':
|
||||
if (!params.projectId?.trim()) {
|
||||
throw new Error('Project ID is required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
projectId: params.projectId.trim(),
|
||||
state: params.issueState !== 'all' ? params.issueState : undefined,
|
||||
labels: params.labels?.trim() || undefined,
|
||||
perPage: params.perPage ? Number(params.perPage) : undefined,
|
||||
page: params.page ? Number(params.page) : undefined,
|
||||
}
|
||||
|
||||
case 'gitlab_get_issue':
|
||||
if (!params.projectId?.trim() || !params.issueIid) {
|
||||
throw new Error('Project ID and Issue Number are required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
projectId: params.projectId.trim(),
|
||||
issueIid: Number(params.issueIid),
|
||||
}
|
||||
|
||||
case 'gitlab_create_issue':
|
||||
if (!params.projectId?.trim() || !params.title?.trim()) {
|
||||
throw new Error('Project ID and title are required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
projectId: params.projectId.trim(),
|
||||
title: params.title.trim(),
|
||||
description: params.description?.trim() || undefined,
|
||||
labels: params.labels?.trim() || undefined,
|
||||
assigneeIds: params.assigneeIds
|
||||
? params.assigneeIds.split(',').map((id: string) => Number(id.trim()))
|
||||
: undefined,
|
||||
milestoneId: params.milestoneId ? Number(params.milestoneId) : undefined,
|
||||
}
|
||||
|
||||
case 'gitlab_update_issue':
|
||||
if (!params.projectId?.trim() || !params.issueIid) {
|
||||
throw new Error('Project ID and Issue IID are required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
projectId: params.projectId.trim(),
|
||||
issueIid: Number(params.issueIid),
|
||||
title: params.title?.trim() || undefined,
|
||||
description: params.description?.trim() || undefined,
|
||||
labels: params.labels?.trim() || undefined,
|
||||
assigneeIds: params.assigneeIds
|
||||
? params.assigneeIds.split(',').map((id: string) => Number(id.trim()))
|
||||
: undefined,
|
||||
stateEvent: params.stateEvent || undefined,
|
||||
}
|
||||
|
||||
case 'gitlab_delete_issue':
|
||||
if (!params.projectId?.trim() || !params.issueIid) {
|
||||
throw new Error('Project ID and Issue IID are required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
projectId: params.projectId.trim(),
|
||||
issueIid: Number(params.issueIid),
|
||||
}
|
||||
|
||||
case 'gitlab_create_issue_note':
|
||||
if (!params.projectId?.trim() || !params.issueIid || !params.body?.trim()) {
|
||||
throw new Error('Project ID, Issue IID, and comment body are required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
projectId: params.projectId.trim(),
|
||||
issueIid: Number(params.issueIid),
|
||||
body: params.body.trim(),
|
||||
}
|
||||
|
||||
case 'gitlab_list_merge_requests':
|
||||
if (!params.projectId?.trim()) {
|
||||
throw new Error('Project ID is required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
projectId: params.projectId.trim(),
|
||||
state: params.mrState !== 'all' ? params.mrState : undefined,
|
||||
labels: params.labels?.trim() || undefined,
|
||||
perPage: params.perPage ? Number(params.perPage) : undefined,
|
||||
page: params.page ? Number(params.page) : undefined,
|
||||
}
|
||||
|
||||
case 'gitlab_get_merge_request':
|
||||
if (!params.projectId?.trim() || !params.mergeRequestIid) {
|
||||
throw new Error('Project ID and Merge Request IID are required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
projectId: params.projectId.trim(),
|
||||
mergeRequestIid: Number(params.mergeRequestIid),
|
||||
}
|
||||
|
||||
case 'gitlab_create_merge_request':
|
||||
if (
|
||||
!params.projectId?.trim() ||
|
||||
!params.title?.trim() ||
|
||||
!params.sourceBranch?.trim() ||
|
||||
!params.targetBranch?.trim()
|
||||
) {
|
||||
throw new Error('Project ID, title, source branch, and target branch are required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
projectId: params.projectId.trim(),
|
||||
title: params.title.trim(),
|
||||
sourceBranch: params.sourceBranch.trim(),
|
||||
targetBranch: params.targetBranch.trim(),
|
||||
description: params.description?.trim() || undefined,
|
||||
labels: params.labels?.trim() || undefined,
|
||||
assigneeIds: params.assigneeIds
|
||||
? params.assigneeIds.split(',').map((id: string) => Number(id.trim()))
|
||||
: undefined,
|
||||
removeSourceBranch: params.removeSourceBranch || undefined,
|
||||
}
|
||||
|
||||
case 'gitlab_update_merge_request':
|
||||
if (!params.projectId?.trim() || !params.mergeRequestIid) {
|
||||
throw new Error('Project ID and Merge Request IID are required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
projectId: params.projectId.trim(),
|
||||
mergeRequestIid: Number(params.mergeRequestIid),
|
||||
title: params.title?.trim() || undefined,
|
||||
description: params.description?.trim() || undefined,
|
||||
labels: params.labels?.trim() || undefined,
|
||||
assigneeIds: params.assigneeIds
|
||||
? params.assigneeIds.split(',').map((id: string) => Number(id.trim()))
|
||||
: undefined,
|
||||
stateEvent: params.stateEvent || undefined,
|
||||
}
|
||||
|
||||
case 'gitlab_merge_merge_request':
|
||||
if (!params.projectId?.trim() || !params.mergeRequestIid) {
|
||||
throw new Error('Project ID and Merge Request IID are required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
projectId: params.projectId.trim(),
|
||||
mergeRequestIid: Number(params.mergeRequestIid),
|
||||
mergeCommitMessage: params.mergeCommitMessage?.trim() || undefined,
|
||||
squash: params.squash || undefined,
|
||||
shouldRemoveSourceBranch: params.removeSourceBranch || undefined,
|
||||
}
|
||||
|
||||
case 'gitlab_create_merge_request_note':
|
||||
if (!params.projectId?.trim() || !params.mergeRequestIid || !params.body?.trim()) {
|
||||
throw new Error('Project ID, Merge Request IID, and comment body are required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
projectId: params.projectId.trim(),
|
||||
mergeRequestIid: Number(params.mergeRequestIid),
|
||||
body: params.body.trim(),
|
||||
}
|
||||
|
||||
case 'gitlab_list_pipelines':
|
||||
if (!params.projectId?.trim()) {
|
||||
throw new Error('Project ID is required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
projectId: params.projectId.trim(),
|
||||
status: params.pipelineStatus || undefined,
|
||||
perPage: params.perPage ? Number(params.perPage) : undefined,
|
||||
page: params.page ? Number(params.page) : undefined,
|
||||
}
|
||||
|
||||
case 'gitlab_get_pipeline':
|
||||
if (!params.projectId?.trim() || !params.pipelineId) {
|
||||
throw new Error('Project ID and Pipeline ID are required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
projectId: params.projectId.trim(),
|
||||
pipelineId: Number(params.pipelineId),
|
||||
}
|
||||
|
||||
case 'gitlab_create_pipeline':
|
||||
if (!params.projectId?.trim() || !params.ref?.trim()) {
|
||||
throw new Error('Project ID and branch/tag ref are required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
projectId: params.projectId.trim(),
|
||||
ref: params.ref.trim(),
|
||||
}
|
||||
|
||||
case 'gitlab_retry_pipeline':
|
||||
case 'gitlab_cancel_pipeline':
|
||||
if (!params.projectId?.trim() || !params.pipelineId) {
|
||||
throw new Error('Project ID and Pipeline ID are required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
projectId: params.projectId.trim(),
|
||||
pipelineId: Number(params.pipelineId),
|
||||
}
|
||||
|
||||
default:
|
||||
return baseParams
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'Operation to perform' },
|
||||
credential: { type: 'string', description: 'GitLab access token' },
|
||||
projectId: { type: 'string', description: 'Project ID or URL-encoded path' },
|
||||
issueIid: { type: 'number', description: 'Issue internal ID' },
|
||||
mergeRequestIid: { type: 'number', description: 'Merge request internal ID' },
|
||||
pipelineId: { type: 'number', description: 'Pipeline ID' },
|
||||
title: { type: 'string', description: 'Title for issue or merge request' },
|
||||
description: { type: 'string', description: 'Description (Markdown supported)' },
|
||||
body: { type: 'string', description: 'Comment body' },
|
||||
sourceBranch: { type: 'string', description: 'Source branch for merge request' },
|
||||
targetBranch: { type: 'string', description: 'Target branch for merge request' },
|
||||
ref: { type: 'string', description: 'Branch or tag reference for pipeline' },
|
||||
labels: { type: 'string', description: 'Labels (comma-separated)' },
|
||||
assigneeIds: { type: 'string', description: 'Assignee user IDs (comma-separated)' },
|
||||
milestoneId: { type: 'number', description: 'Milestone ID' },
|
||||
issueState: { type: 'string', description: 'Issue state filter (opened, closed, all)' },
|
||||
mrState: {
|
||||
type: 'string',
|
||||
description: 'Merge request state filter (opened, closed, merged, all)',
|
||||
},
|
||||
stateEvent: { type: 'string', description: 'State event (close, reopen)' },
|
||||
pipelineStatus: { type: 'string', description: 'Pipeline status filter' },
|
||||
removeSourceBranch: { type: 'boolean', description: 'Remove source branch after merge' },
|
||||
squash: { type: 'boolean', description: 'Squash commits on merge' },
|
||||
mergeCommitMessage: { type: 'string', description: 'Custom merge commit message' },
|
||||
perPage: { type: 'number', description: 'Results per page' },
|
||||
page: { type: 'number', description: 'Page number' },
|
||||
},
|
||||
outputs: {
|
||||
// Project outputs
|
||||
projects: { type: 'json', description: 'List of projects' },
|
||||
project: { type: 'json', description: 'Project details' },
|
||||
// Issue outputs
|
||||
issues: { type: 'json', description: 'List of issues' },
|
||||
issue: { type: 'json', description: 'Issue details' },
|
||||
// Merge request outputs
|
||||
mergeRequests: { type: 'json', description: 'List of merge requests' },
|
||||
mergeRequest: { type: 'json', description: 'Merge request details' },
|
||||
// Pipeline outputs
|
||||
pipelines: { type: 'json', description: 'List of pipelines' },
|
||||
pipeline: { type: 'json', description: 'Pipeline details' },
|
||||
// Note outputs
|
||||
note: { type: 'json', description: 'Comment/note details' },
|
||||
// Success indicator
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
503
apps/sim/blocks/blocks/grafana.ts
Normal file
503
apps/sim/blocks/blocks/grafana.ts
Normal file
@@ -0,0 +1,503 @@
|
||||
import { GrafanaIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import type { GrafanaResponse } from '@/tools/grafana/types'
|
||||
|
||||
export const GrafanaBlock: BlockConfig<GrafanaResponse> = {
|
||||
type: 'grafana',
|
||||
name: 'Grafana',
|
||||
description: 'Interact with Grafana dashboards, alerts, and annotations',
|
||||
authMode: AuthMode.ApiKey,
|
||||
longDescription:
|
||||
'Integrate Grafana into workflows. Manage dashboards, alerts, annotations, data sources, folders, and monitor health status.',
|
||||
docsLink: 'https://docs.sim.ai/tools/grafana',
|
||||
category: 'tools',
|
||||
bgColor: '#E0E0E0',
|
||||
icon: GrafanaIcon,
|
||||
subBlocks: [
|
||||
// Operation dropdown
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
// Dashboards
|
||||
{ label: 'List Dashboards', id: 'grafana_list_dashboards' },
|
||||
{ label: 'Get Dashboard', id: 'grafana_get_dashboard' },
|
||||
{ label: 'Create Dashboard', id: 'grafana_create_dashboard' },
|
||||
{ label: 'Update Dashboard', id: 'grafana_update_dashboard' },
|
||||
{ label: 'Delete Dashboard', id: 'grafana_delete_dashboard' },
|
||||
// Alerts
|
||||
{ label: 'List Alert Rules', id: 'grafana_list_alert_rules' },
|
||||
{ label: 'Get Alert Rule', id: 'grafana_get_alert_rule' },
|
||||
{ label: 'Create Alert Rule', id: 'grafana_create_alert_rule' },
|
||||
{ label: 'Update Alert Rule', id: 'grafana_update_alert_rule' },
|
||||
{ label: 'Delete Alert Rule', id: 'grafana_delete_alert_rule' },
|
||||
{ label: 'List Contact Points', id: 'grafana_list_contact_points' },
|
||||
// Annotations
|
||||
{ label: 'Create Annotation', id: 'grafana_create_annotation' },
|
||||
{ label: 'List Annotations', id: 'grafana_list_annotations' },
|
||||
{ label: 'Update Annotation', id: 'grafana_update_annotation' },
|
||||
{ label: 'Delete Annotation', id: 'grafana_delete_annotation' },
|
||||
// Data Sources
|
||||
{ label: 'List Data Sources', id: 'grafana_list_data_sources' },
|
||||
{ label: 'Get Data Source', id: 'grafana_get_data_source' },
|
||||
// Folders
|
||||
{ label: 'List Folders', id: 'grafana_list_folders' },
|
||||
{ label: 'Create Folder', id: 'grafana_create_folder' },
|
||||
],
|
||||
value: () => 'grafana_list_dashboards',
|
||||
},
|
||||
|
||||
// Base Configuration (common to all operations)
|
||||
{
|
||||
id: 'baseUrl',
|
||||
title: 'Grafana URL',
|
||||
type: 'short-input',
|
||||
placeholder: 'https://your-grafana.com',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'apiKey',
|
||||
title: 'Service Account Token',
|
||||
type: 'short-input',
|
||||
placeholder: 'glsa_...',
|
||||
password: true,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'organizationId',
|
||||
title: 'Organization ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Optional - for multi-org instances',
|
||||
},
|
||||
|
||||
// Data Source operations
|
||||
{
|
||||
id: 'dataSourceId',
|
||||
title: 'Data Source ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter data source ID or UID',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'grafana_get_data_source',
|
||||
},
|
||||
},
|
||||
|
||||
// Dashboard operations
|
||||
{
|
||||
id: 'dashboardUid',
|
||||
title: 'Dashboard UID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter dashboard UID',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['grafana_get_dashboard', 'grafana_update_dashboard', 'grafana_delete_dashboard'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'query',
|
||||
title: 'Search Query',
|
||||
type: 'short-input',
|
||||
placeholder: 'Filter dashboards by title',
|
||||
condition: { field: 'operation', value: 'grafana_list_dashboards' },
|
||||
},
|
||||
{
|
||||
id: 'tag',
|
||||
title: 'Filter by Tag',
|
||||
type: 'short-input',
|
||||
placeholder: 'tag1, tag2 (comma-separated)',
|
||||
condition: { field: 'operation', value: 'grafana_list_dashboards' },
|
||||
},
|
||||
|
||||
// Create/Update Dashboard
|
||||
{
|
||||
id: 'title',
|
||||
title: 'Dashboard Title',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter dashboard title',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'grafana_create_dashboard' },
|
||||
},
|
||||
{
|
||||
id: 'folderUid',
|
||||
title: 'Folder UID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Optional - folder to create dashboard in',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'grafana_create_dashboard',
|
||||
'grafana_update_dashboard',
|
||||
'grafana_create_alert_rule',
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'tags',
|
||||
title: 'Tags',
|
||||
type: 'short-input',
|
||||
placeholder: 'tag1, tag2 (comma-separated)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['grafana_create_dashboard', 'grafana_update_dashboard'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'panels',
|
||||
title: 'Panels (JSON)',
|
||||
type: 'long-input',
|
||||
placeholder: 'JSON array of panel configurations',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['grafana_create_dashboard', 'grafana_update_dashboard'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'message',
|
||||
title: 'Commit Message',
|
||||
type: 'short-input',
|
||||
placeholder: 'Optional version message',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['grafana_create_dashboard', 'grafana_update_dashboard'],
|
||||
},
|
||||
},
|
||||
|
||||
// Alert Rule operations
|
||||
{
|
||||
id: 'alertRuleUid',
|
||||
title: 'Alert Rule UID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter alert rule UID',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['grafana_get_alert_rule', 'grafana_update_alert_rule', 'grafana_delete_alert_rule'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'alertTitle',
|
||||
title: 'Alert Title',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter alert rule name',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['grafana_create_alert_rule', 'grafana_update_alert_rule'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'folderUid',
|
||||
title: 'Folder UID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Folder UID for the alert rule',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['grafana_create_alert_rule', 'grafana_update_alert_rule'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'ruleGroup',
|
||||
title: 'Rule Group',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter rule group name',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['grafana_create_alert_rule', 'grafana_update_alert_rule'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'condition',
|
||||
title: 'Condition',
|
||||
type: 'short-input',
|
||||
placeholder: 'Condition refId (e.g., A)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['grafana_create_alert_rule', 'grafana_update_alert_rule'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'data',
|
||||
title: 'Query Data (JSON)',
|
||||
type: 'long-input',
|
||||
placeholder: 'JSON array of query/expression data objects',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['grafana_create_alert_rule', 'grafana_update_alert_rule'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'forDuration',
|
||||
title: 'For Duration',
|
||||
type: 'short-input',
|
||||
placeholder: '5m (e.g., 5m, 1h)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['grafana_create_alert_rule', 'grafana_update_alert_rule'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'noDataState',
|
||||
title: 'No Data State',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'No Data', id: 'NoData' },
|
||||
{ label: 'Alerting', id: 'Alerting' },
|
||||
{ label: 'OK', id: 'OK' },
|
||||
],
|
||||
value: () => 'NoData',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['grafana_create_alert_rule', 'grafana_update_alert_rule'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'execErrState',
|
||||
title: 'Error State',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Alerting', id: 'Alerting' },
|
||||
{ label: 'OK', id: 'OK' },
|
||||
],
|
||||
value: () => 'Alerting',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['grafana_create_alert_rule', 'grafana_update_alert_rule'],
|
||||
},
|
||||
},
|
||||
|
||||
// Annotation operations
|
||||
{
|
||||
id: 'text',
|
||||
title: 'Annotation Text',
|
||||
type: 'long-input',
|
||||
placeholder: 'Enter annotation text...',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['grafana_create_annotation', 'grafana_update_annotation'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'annotationTags',
|
||||
title: 'Tags',
|
||||
type: 'short-input',
|
||||
placeholder: 'tag1, tag2 (comma-separated)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'grafana_create_annotation',
|
||||
'grafana_update_annotation',
|
||||
'grafana_list_annotations',
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'annotationDashboardUid',
|
||||
title: 'Dashboard UID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Optional - attach to specific dashboard',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['grafana_create_annotation', 'grafana_list_annotations'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'panelId',
|
||||
title: 'Panel ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Optional - attach to specific panel',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['grafana_create_annotation', 'grafana_list_annotations'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'time',
|
||||
title: 'Time (epoch ms)',
|
||||
type: 'short-input',
|
||||
placeholder: 'Optional - defaults to now',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['grafana_create_annotation', 'grafana_update_annotation'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'timeEnd',
|
||||
title: 'End Time (epoch ms)',
|
||||
type: 'short-input',
|
||||
placeholder: 'Optional - for range annotations',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['grafana_create_annotation', 'grafana_update_annotation'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'annotationId',
|
||||
title: 'Annotation ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter annotation ID',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['grafana_update_annotation', 'grafana_delete_annotation'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'from',
|
||||
title: 'From Time (epoch ms)',
|
||||
type: 'short-input',
|
||||
placeholder: 'Filter from time',
|
||||
condition: { field: 'operation', value: 'grafana_list_annotations' },
|
||||
},
|
||||
{
|
||||
id: 'to',
|
||||
title: 'To Time (epoch ms)',
|
||||
type: 'short-input',
|
||||
placeholder: 'Filter to time',
|
||||
condition: { field: 'operation', value: 'grafana_list_annotations' },
|
||||
},
|
||||
|
||||
// Folder operations
|
||||
{
|
||||
id: 'folderTitle',
|
||||
title: 'Folder Title',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter folder title',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'grafana_create_folder' },
|
||||
},
|
||||
{
|
||||
id: 'folderUidNew',
|
||||
title: 'Folder UID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Optional - auto-generated if not provided',
|
||||
condition: { field: 'operation', value: 'grafana_create_folder' },
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: [
|
||||
'grafana_get_dashboard',
|
||||
'grafana_list_dashboards',
|
||||
'grafana_create_dashboard',
|
||||
'grafana_update_dashboard',
|
||||
'grafana_delete_dashboard',
|
||||
'grafana_list_alert_rules',
|
||||
'grafana_get_alert_rule',
|
||||
'grafana_create_alert_rule',
|
||||
'grafana_update_alert_rule',
|
||||
'grafana_delete_alert_rule',
|
||||
'grafana_list_contact_points',
|
||||
'grafana_create_annotation',
|
||||
'grafana_list_annotations',
|
||||
'grafana_update_annotation',
|
||||
'grafana_delete_annotation',
|
||||
'grafana_list_data_sources',
|
||||
'grafana_get_data_source',
|
||||
'grafana_list_folders',
|
||||
'grafana_create_folder',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => {
|
||||
// Convert numeric string fields to numbers
|
||||
if (params.panelId) {
|
||||
params.panelId = Number(params.panelId)
|
||||
}
|
||||
if (params.annotationId) {
|
||||
params.annotationId = Number(params.annotationId)
|
||||
}
|
||||
if (params.time) {
|
||||
params.time = Number(params.time)
|
||||
}
|
||||
if (params.timeEnd) {
|
||||
params.timeEnd = Number(params.timeEnd)
|
||||
}
|
||||
if (params.from) {
|
||||
params.from = Number(params.from)
|
||||
}
|
||||
if (params.to) {
|
||||
params.to = Number(params.to)
|
||||
}
|
||||
|
||||
// Map subblock fields to tool parameter names
|
||||
if (params.alertTitle) {
|
||||
params.title = params.alertTitle
|
||||
}
|
||||
if (params.folderTitle) {
|
||||
params.title = params.folderTitle
|
||||
}
|
||||
if (params.folderUidNew) {
|
||||
params.uid = params.folderUidNew
|
||||
}
|
||||
if (params.annotationTags) {
|
||||
params.tags = params.annotationTags
|
||||
}
|
||||
if (params.annotationDashboardUid) {
|
||||
params.dashboardUid = params.annotationDashboardUid
|
||||
}
|
||||
|
||||
return params.operation
|
||||
},
|
||||
},
|
||||
},
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'Operation to perform' },
|
||||
baseUrl: { type: 'string', description: 'Grafana instance URL' },
|
||||
apiKey: { type: 'string', description: 'Service Account Token' },
|
||||
organizationId: { type: 'string', description: 'Organization ID (optional)' },
|
||||
// Dashboard inputs
|
||||
dashboardUid: { type: 'string', description: 'Dashboard UID' },
|
||||
title: { type: 'string', description: 'Dashboard or folder title' },
|
||||
folderUid: { type: 'string', description: 'Folder UID' },
|
||||
tags: { type: 'string', description: 'Comma-separated tags' },
|
||||
panels: { type: 'string', description: 'JSON array of panels' },
|
||||
message: { type: 'string', description: 'Commit message' },
|
||||
query: { type: 'string', description: 'Search query' },
|
||||
tag: { type: 'string', description: 'Filter by tag' },
|
||||
// Alert inputs
|
||||
alertRuleUid: { type: 'string', description: 'Alert rule UID' },
|
||||
alertTitle: { type: 'string', description: 'Alert rule title' },
|
||||
ruleGroup: { type: 'string', description: 'Rule group name' },
|
||||
condition: { type: 'string', description: 'Alert condition refId' },
|
||||
data: { type: 'string', description: 'Query data JSON' },
|
||||
forDuration: { type: 'string', description: 'Duration before firing' },
|
||||
noDataState: { type: 'string', description: 'State on no data' },
|
||||
execErrState: { type: 'string', description: 'State on error' },
|
||||
// Annotation inputs
|
||||
text: { type: 'string', description: 'Annotation text' },
|
||||
annotationId: { type: 'number', description: 'Annotation ID' },
|
||||
panelId: { type: 'number', description: 'Panel ID' },
|
||||
time: { type: 'number', description: 'Start time (epoch ms)' },
|
||||
timeEnd: { type: 'number', description: 'End time (epoch ms)' },
|
||||
from: { type: 'number', description: 'Filter from time' },
|
||||
to: { type: 'number', description: 'Filter to time' },
|
||||
// Data source inputs
|
||||
dataSourceId: { type: 'string', description: 'Data source ID or UID' },
|
||||
},
|
||||
outputs: {
|
||||
// Health outputs
|
||||
version: { type: 'string', description: 'Grafana version' },
|
||||
database: { type: 'string', description: 'Database health status' },
|
||||
status: { type: 'string', description: 'Health status' },
|
||||
// Dashboard outputs
|
||||
dashboard: { type: 'json', description: 'Dashboard JSON' },
|
||||
meta: { type: 'json', description: 'Dashboard metadata' },
|
||||
dashboards: { type: 'json', description: 'List of dashboards' },
|
||||
uid: { type: 'string', description: 'Created/updated UID' },
|
||||
url: { type: 'string', description: 'Dashboard URL' },
|
||||
// Alert outputs
|
||||
rules: { type: 'json', description: 'Alert rules list' },
|
||||
contactPoints: { type: 'json', description: 'Contact points list' },
|
||||
// Annotation outputs
|
||||
annotations: { type: 'json', description: 'Annotations list' },
|
||||
id: { type: 'number', description: 'Annotation ID' },
|
||||
// Data source outputs
|
||||
dataSources: { type: 'json', description: 'Data sources list' },
|
||||
// Folder outputs
|
||||
folders: { type: 'json', description: 'Folders list' },
|
||||
// Common
|
||||
message: { type: 'string', description: 'Status message' },
|
||||
},
|
||||
}
|
||||
399
apps/sim/blocks/blocks/kalshi.ts
Normal file
399
apps/sim/blocks/blocks/kalshi.ts
Normal file
@@ -0,0 +1,399 @@
|
||||
import { KalshiIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
|
||||
export const KalshiBlock: BlockConfig = {
|
||||
type: 'kalshi',
|
||||
name: 'Kalshi',
|
||||
description: 'Access prediction markets data from Kalshi',
|
||||
longDescription:
|
||||
'Integrate Kalshi prediction markets into the workflow. Can get markets, market, events, event, balance, positions, orders, orderbook, trades, candlesticks, fills, series, and exchange status.',
|
||||
docsLink: 'https://docs.sim.ai/tools/kalshi',
|
||||
authMode: AuthMode.ApiKey,
|
||||
category: 'tools',
|
||||
bgColor: '#09C285',
|
||||
icon: KalshiIcon,
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Get Markets', id: 'get_markets' },
|
||||
{ label: 'Get Market', id: 'get_market' },
|
||||
{ label: 'Get Events', id: 'get_events' },
|
||||
{ label: 'Get Event', id: 'get_event' },
|
||||
{ label: 'Get Balance', id: 'get_balance' },
|
||||
{ label: 'Get Positions', id: 'get_positions' },
|
||||
{ label: 'Get Orders', id: 'get_orders' },
|
||||
{ label: 'Get Orderbook', id: 'get_orderbook' },
|
||||
{ label: 'Get Trades', id: 'get_trades' },
|
||||
{ label: 'Get Candlesticks', id: 'get_candlesticks' },
|
||||
{ label: 'Get Fills', id: 'get_fills' },
|
||||
{ label: 'Get Series by Ticker', id: 'get_series_by_ticker' },
|
||||
{ label: 'Get Exchange Status', id: 'get_exchange_status' },
|
||||
],
|
||||
value: () => 'get_markets',
|
||||
},
|
||||
// Auth fields (for authenticated operations)
|
||||
{
|
||||
id: 'keyId',
|
||||
title: 'API Key ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Your Kalshi API Key ID',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['get_balance', 'get_positions', 'get_orders', 'get_fills'],
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'privateKey',
|
||||
title: 'Private Key',
|
||||
type: 'long-input',
|
||||
password: true,
|
||||
placeholder: 'Your RSA Private Key (PEM format)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['get_balance', 'get_positions', 'get_orders', 'get_fills'],
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
// Get Markets fields
|
||||
{
|
||||
id: 'status',
|
||||
title: 'Status',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'All', id: '' },
|
||||
{ label: 'Unopened', id: 'unopened' },
|
||||
{ label: 'Open', id: 'open' },
|
||||
{ label: 'Closed', id: 'closed' },
|
||||
{ label: 'Settled', id: 'settled' },
|
||||
],
|
||||
condition: { field: 'operation', value: ['get_markets', 'get_events'] },
|
||||
},
|
||||
{
|
||||
id: 'seriesTicker',
|
||||
title: 'Series Ticker',
|
||||
type: 'short-input',
|
||||
placeholder: 'Filter by series ticker',
|
||||
condition: { field: 'operation', value: ['get_markets', 'get_events'] },
|
||||
},
|
||||
{
|
||||
id: 'eventTicker',
|
||||
title: 'Event Ticker',
|
||||
type: 'short-input',
|
||||
placeholder: 'Event ticker',
|
||||
required: {
|
||||
field: 'operation',
|
||||
value: ['get_event'],
|
||||
},
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['get_markets', 'get_event', 'get_positions', 'get_orders'],
|
||||
},
|
||||
},
|
||||
// Get Market fields - ticker is REQUIRED for get_market (path param)
|
||||
{
|
||||
id: 'ticker',
|
||||
title: 'Market Ticker',
|
||||
type: 'short-input',
|
||||
placeholder: 'Market ticker (e.g., KXBTC-24DEC31)',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: ['get_market', 'get_orderbook'] },
|
||||
},
|
||||
// Ticker filter for get_orders and get_positions - OPTIONAL
|
||||
{
|
||||
id: 'tickerFilter',
|
||||
title: 'Market Ticker',
|
||||
type: 'short-input',
|
||||
placeholder: 'Filter by market ticker (optional)',
|
||||
condition: { field: 'operation', value: ['get_orders', 'get_positions'] },
|
||||
},
|
||||
// Nested markets option
|
||||
{
|
||||
id: 'withNestedMarkets',
|
||||
title: 'Include Markets',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'No', id: '' },
|
||||
{ label: 'Yes', id: 'true' },
|
||||
],
|
||||
condition: { field: 'operation', value: ['get_events', 'get_event'] },
|
||||
},
|
||||
// Get Positions fields
|
||||
{
|
||||
id: 'settlementStatus',
|
||||
title: 'Settlement Status',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'All', id: '' },
|
||||
{ label: 'Unsettled', id: 'unsettled' },
|
||||
{ label: 'Settled', id: 'settled' },
|
||||
],
|
||||
condition: { field: 'operation', value: ['get_positions'] },
|
||||
},
|
||||
// Get Orders fields
|
||||
{
|
||||
id: 'orderStatus',
|
||||
title: 'Order Status',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'All', id: '' },
|
||||
{ label: 'Resting', id: 'resting' },
|
||||
{ label: 'Canceled', id: 'canceled' },
|
||||
{ label: 'Executed', id: 'executed' },
|
||||
],
|
||||
condition: { field: 'operation', value: ['get_orders'] },
|
||||
},
|
||||
// Get Orderbook fields
|
||||
{
|
||||
id: 'depth',
|
||||
title: 'Depth',
|
||||
type: 'short-input',
|
||||
placeholder: 'Number of price levels per side',
|
||||
condition: { field: 'operation', value: ['get_orderbook'] },
|
||||
},
|
||||
// Get Trades fields
|
||||
{
|
||||
id: 'tickerTrades',
|
||||
title: 'Market Ticker',
|
||||
type: 'short-input',
|
||||
placeholder: 'Filter by market ticker (optional)',
|
||||
condition: { field: 'operation', value: ['get_trades'] },
|
||||
},
|
||||
{
|
||||
id: 'minTs',
|
||||
title: 'Min Timestamp',
|
||||
type: 'short-input',
|
||||
placeholder: 'Minimum timestamp (Unix milliseconds)',
|
||||
condition: { field: 'operation', value: ['get_trades', 'get_fills'] },
|
||||
},
|
||||
{
|
||||
id: 'maxTs',
|
||||
title: 'Max Timestamp',
|
||||
type: 'short-input',
|
||||
placeholder: 'Maximum timestamp (Unix milliseconds)',
|
||||
condition: { field: 'operation', value: ['get_trades', 'get_fills'] },
|
||||
},
|
||||
// Get Candlesticks fields
|
||||
{
|
||||
id: 'seriesTickerCandlesticks',
|
||||
title: 'Series Ticker',
|
||||
type: 'short-input',
|
||||
placeholder: 'Series ticker',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: ['get_candlesticks'] },
|
||||
},
|
||||
{
|
||||
id: 'tickerCandlesticks',
|
||||
title: 'Market Ticker',
|
||||
type: 'short-input',
|
||||
placeholder: 'Market ticker (e.g., KXBTC-24DEC31)',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: ['get_candlesticks'] },
|
||||
},
|
||||
{
|
||||
id: 'startTs',
|
||||
title: 'Start Timestamp',
|
||||
type: 'short-input',
|
||||
placeholder: 'Start timestamp (Unix milliseconds)',
|
||||
condition: { field: 'operation', value: ['get_candlesticks'] },
|
||||
},
|
||||
{
|
||||
id: 'endTs',
|
||||
title: 'End Timestamp',
|
||||
type: 'short-input',
|
||||
placeholder: 'End timestamp (Unix milliseconds)',
|
||||
condition: { field: 'operation', value: ['get_candlesticks'] },
|
||||
},
|
||||
{
|
||||
id: 'periodInterval',
|
||||
title: 'Period Interval',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'All', id: '' },
|
||||
{ label: '1 minute', id: '1' },
|
||||
{ label: '1 hour', id: '60' },
|
||||
{ label: '1 day', id: '1440' },
|
||||
],
|
||||
condition: { field: 'operation', value: ['get_candlesticks'] },
|
||||
},
|
||||
// Get Fills fields
|
||||
{
|
||||
id: 'tickerFills',
|
||||
title: 'Market Ticker',
|
||||
type: 'short-input',
|
||||
placeholder: 'Filter by market ticker (optional)',
|
||||
condition: { field: 'operation', value: ['get_fills'] },
|
||||
},
|
||||
{
|
||||
id: 'orderId',
|
||||
title: 'Order ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Filter by order ID (optional)',
|
||||
condition: { field: 'operation', value: ['get_fills'] },
|
||||
},
|
||||
// Get Series by Ticker fields
|
||||
{
|
||||
id: 'seriesTickerGet',
|
||||
title: 'Series Ticker',
|
||||
type: 'short-input',
|
||||
placeholder: 'Series ticker',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: ['get_series_by_ticker'] },
|
||||
},
|
||||
// Pagination fields
|
||||
{
|
||||
id: 'limit',
|
||||
title: 'Limit',
|
||||
type: 'short-input',
|
||||
placeholder: 'Number of results (1-1000, default: 100)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'get_markets',
|
||||
'get_events',
|
||||
'get_positions',
|
||||
'get_orders',
|
||||
'get_trades',
|
||||
'get_fills',
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'cursor',
|
||||
title: 'Cursor',
|
||||
type: 'short-input',
|
||||
placeholder: 'Pagination cursor',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'get_markets',
|
||||
'get_events',
|
||||
'get_positions',
|
||||
'get_orders',
|
||||
'get_trades',
|
||||
'get_fills',
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: [
|
||||
'kalshi_get_markets',
|
||||
'kalshi_get_market',
|
||||
'kalshi_get_events',
|
||||
'kalshi_get_event',
|
||||
'kalshi_get_balance',
|
||||
'kalshi_get_positions',
|
||||
'kalshi_get_orders',
|
||||
'kalshi_get_orderbook',
|
||||
'kalshi_get_trades',
|
||||
'kalshi_get_candlesticks',
|
||||
'kalshi_get_fills',
|
||||
'kalshi_get_series_by_ticker',
|
||||
'kalshi_get_exchange_status',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => {
|
||||
switch (params.operation) {
|
||||
case 'get_markets':
|
||||
return 'kalshi_get_markets'
|
||||
case 'get_market':
|
||||
return 'kalshi_get_market'
|
||||
case 'get_events':
|
||||
return 'kalshi_get_events'
|
||||
case 'get_event':
|
||||
return 'kalshi_get_event'
|
||||
case 'get_balance':
|
||||
return 'kalshi_get_balance'
|
||||
case 'get_positions':
|
||||
return 'kalshi_get_positions'
|
||||
case 'get_orders':
|
||||
return 'kalshi_get_orders'
|
||||
case 'get_orderbook':
|
||||
return 'kalshi_get_orderbook'
|
||||
case 'get_trades':
|
||||
return 'kalshi_get_trades'
|
||||
case 'get_candlesticks':
|
||||
return 'kalshi_get_candlesticks'
|
||||
case 'get_fills':
|
||||
return 'kalshi_get_fills'
|
||||
case 'get_series_by_ticker':
|
||||
return 'kalshi_get_series_by_ticker'
|
||||
case 'get_exchange_status':
|
||||
return 'kalshi_get_exchange_status'
|
||||
default:
|
||||
return 'kalshi_get_markets'
|
||||
}
|
||||
},
|
||||
params: (params) => {
|
||||
const {
|
||||
operation,
|
||||
orderStatus,
|
||||
tickerFilter,
|
||||
tickerTrades,
|
||||
tickerFills,
|
||||
tickerCandlesticks,
|
||||
seriesTickerCandlesticks,
|
||||
seriesTickerGet,
|
||||
...rest
|
||||
} = params
|
||||
const cleanParams: Record<string, any> = {}
|
||||
|
||||
// Map orderStatus to status for get_orders
|
||||
if (operation === 'get_orders' && orderStatus) {
|
||||
cleanParams.status = orderStatus
|
||||
}
|
||||
|
||||
// Map tickerFilter to ticker for get_orders and get_positions
|
||||
if ((operation === 'get_orders' || operation === 'get_positions') && tickerFilter) {
|
||||
cleanParams.ticker = tickerFilter
|
||||
}
|
||||
|
||||
// Map tickerTrades to ticker for get_trades
|
||||
if (operation === 'get_trades' && tickerTrades) {
|
||||
cleanParams.ticker = tickerTrades
|
||||
}
|
||||
|
||||
// Map tickerFills to ticker for get_fills
|
||||
if (operation === 'get_fills' && tickerFills) {
|
||||
cleanParams.ticker = tickerFills
|
||||
}
|
||||
|
||||
// Map fields for get_candlesticks
|
||||
if (operation === 'get_candlesticks') {
|
||||
if (seriesTickerCandlesticks) cleanParams.seriesTicker = seriesTickerCandlesticks
|
||||
if (tickerCandlesticks) cleanParams.ticker = tickerCandlesticks
|
||||
}
|
||||
|
||||
// Map seriesTickerGet to seriesTicker for get_series_by_ticker
|
||||
if (operation === 'get_series_by_ticker' && seriesTickerGet) {
|
||||
cleanParams.seriesTicker = seriesTickerGet
|
||||
}
|
||||
|
||||
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' },
|
||||
keyId: { type: 'string', description: 'Kalshi API Key ID' },
|
||||
privateKey: { type: 'string', description: 'RSA Private Key (PEM format)' },
|
||||
ticker: { type: 'string', description: 'Market ticker' },
|
||||
eventTicker: { type: 'string', description: 'Event ticker' },
|
||||
status: { type: 'string', description: 'Filter by status' },
|
||||
},
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: { type: 'json', description: 'Operation result data' },
|
||||
},
|
||||
}
|
||||
355
apps/sim/blocks/blocks/polymarket.ts
Normal file
355
apps/sim/blocks/blocks/polymarket.ts
Normal file
@@ -0,0 +1,355 @@
|
||||
import { PolymarketIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
|
||||
export const PolymarketBlock: BlockConfig = {
|
||||
type: 'polymarket',
|
||||
name: 'Polymarket',
|
||||
description: 'Access prediction markets data from Polymarket',
|
||||
longDescription:
|
||||
'Integrate Polymarket prediction markets into the workflow. Can get markets, market, events, event, tags, series, orderbook, price, midpoint, price history, last trade price, spread, tick size, positions, trades, and search.',
|
||||
docsLink: 'https://docs.sim.ai/tools/polymarket',
|
||||
category: 'tools',
|
||||
bgColor: '#4C82FB',
|
||||
icon: PolymarketIcon,
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Get Markets', id: 'get_markets' },
|
||||
{ label: 'Get Market', id: 'get_market' },
|
||||
{ label: 'Get Events', id: 'get_events' },
|
||||
{ label: 'Get Event', id: 'get_event' },
|
||||
{ label: 'Get Tags', id: 'get_tags' },
|
||||
{ label: 'Search', id: 'search' },
|
||||
{ label: 'Get Series', id: 'get_series' },
|
||||
{ label: 'Get Series by ID', id: 'get_series_by_id' },
|
||||
{ label: 'Get Orderbook', id: 'get_orderbook' },
|
||||
{ label: 'Get Price', id: 'get_price' },
|
||||
{ label: 'Get Midpoint', id: 'get_midpoint' },
|
||||
{ label: 'Get Price History', id: 'get_price_history' },
|
||||
{ label: 'Get Last Trade Price', id: 'get_last_trade_price' },
|
||||
{ label: 'Get Spread', id: 'get_spread' },
|
||||
{ label: 'Get Tick Size', id: 'get_tick_size' },
|
||||
{ label: 'Get Positions', id: 'get_positions' },
|
||||
{ label: 'Get Trades', id: 'get_trades' },
|
||||
],
|
||||
value: () => 'get_markets',
|
||||
},
|
||||
// Get Market fields - marketId or slug (one is required)
|
||||
{
|
||||
id: 'marketId',
|
||||
title: 'Market ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Market ID (required if no slug)',
|
||||
condition: { field: 'operation', value: ['get_market'] },
|
||||
},
|
||||
{
|
||||
id: 'marketSlug',
|
||||
title: 'Market Slug',
|
||||
type: 'short-input',
|
||||
placeholder: 'Market slug (required if no ID)',
|
||||
condition: { field: 'operation', value: ['get_market'] },
|
||||
},
|
||||
// Get Event fields - eventId or slug (one is required)
|
||||
{
|
||||
id: 'eventId',
|
||||
title: 'Event ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Event ID (required if no slug)',
|
||||
condition: { field: 'operation', value: ['get_event'] },
|
||||
},
|
||||
{
|
||||
id: 'eventSlug',
|
||||
title: 'Event Slug',
|
||||
type: 'short-input',
|
||||
placeholder: 'Event slug (required if no ID)',
|
||||
condition: { field: 'operation', value: ['get_event'] },
|
||||
},
|
||||
// Series ID for get_series_by_id
|
||||
{
|
||||
id: 'seriesId',
|
||||
title: 'Series ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Series ID',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: ['get_series_by_id'] },
|
||||
},
|
||||
// Search query
|
||||
{
|
||||
id: 'query',
|
||||
title: 'Search Query',
|
||||
type: 'short-input',
|
||||
placeholder: 'Search term',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: ['search'] },
|
||||
},
|
||||
// User wallet address for Data API operations
|
||||
{
|
||||
id: 'user',
|
||||
title: 'User Wallet Address',
|
||||
type: 'short-input',
|
||||
placeholder: 'Wallet address',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: ['get_positions'] },
|
||||
},
|
||||
{
|
||||
id: 'user',
|
||||
title: 'User Wallet Address',
|
||||
type: 'short-input',
|
||||
placeholder: 'Wallet address (optional filter)',
|
||||
condition: { field: 'operation', value: ['get_trades'] },
|
||||
},
|
||||
// Market filter for positions and trades
|
||||
{
|
||||
id: 'market',
|
||||
title: 'Market ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Market ID (optional filter)',
|
||||
condition: { field: 'operation', value: ['get_positions', 'get_trades'] },
|
||||
},
|
||||
// Token ID for CLOB operations
|
||||
{
|
||||
id: 'tokenId',
|
||||
title: 'Token ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'CLOB Token ID from market',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'get_orderbook',
|
||||
'get_price',
|
||||
'get_midpoint',
|
||||
'get_price_history',
|
||||
'get_last_trade_price',
|
||||
'get_spread',
|
||||
'get_tick_size',
|
||||
],
|
||||
},
|
||||
},
|
||||
// Side for price query
|
||||
{
|
||||
id: 'side',
|
||||
title: 'Side',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Buy', id: 'buy' },
|
||||
{ label: 'Sell', id: 'sell' },
|
||||
],
|
||||
condition: { field: 'operation', value: ['get_price'] },
|
||||
required: true,
|
||||
},
|
||||
// Price history specific fields
|
||||
{
|
||||
id: 'interval',
|
||||
title: 'Interval',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'None (use timestamps)', id: '' },
|
||||
{ label: '1 Minute', id: '1m' },
|
||||
{ label: '1 Hour', id: '1h' },
|
||||
{ label: '6 Hours', id: '6h' },
|
||||
{ label: '1 Day', id: '1d' },
|
||||
{ label: '1 Week', id: '1w' },
|
||||
{ label: 'Max', id: 'max' },
|
||||
],
|
||||
condition: { field: 'operation', value: ['get_price_history'] },
|
||||
},
|
||||
{
|
||||
id: 'fidelity',
|
||||
title: 'Fidelity (minutes)',
|
||||
type: 'short-input',
|
||||
placeholder: 'Data resolution in minutes (e.g., 60)',
|
||||
condition: { field: 'operation', value: ['get_price_history'] },
|
||||
},
|
||||
{
|
||||
id: 'startTs',
|
||||
title: 'Start Timestamp',
|
||||
type: 'short-input',
|
||||
placeholder: 'Unix timestamp UTC (if no interval)',
|
||||
condition: { field: 'operation', value: ['get_price_history'] },
|
||||
},
|
||||
{
|
||||
id: 'endTs',
|
||||
title: 'End Timestamp',
|
||||
type: 'short-input',
|
||||
placeholder: 'Unix timestamp UTC (if no interval)',
|
||||
condition: { field: 'operation', value: ['get_price_history'] },
|
||||
},
|
||||
// Filters for list operations
|
||||
{
|
||||
id: 'closed',
|
||||
title: 'Status',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'All', id: '' },
|
||||
{ label: 'Active Only', id: 'false' },
|
||||
{ label: 'Closed Only', id: 'true' },
|
||||
],
|
||||
condition: { field: 'operation', value: ['get_markets', 'get_events'] },
|
||||
},
|
||||
{
|
||||
id: 'order',
|
||||
title: 'Sort By',
|
||||
type: 'short-input',
|
||||
placeholder: 'Sort field (e.g., id, volume, liquidity)',
|
||||
condition: { field: 'operation', value: ['get_markets', 'get_events'] },
|
||||
},
|
||||
{
|
||||
id: 'ascending',
|
||||
title: 'Sort Order',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Descending (newest first)', id: 'false' },
|
||||
{ label: 'Ascending (oldest first)', id: 'true' },
|
||||
],
|
||||
condition: { field: 'operation', value: ['get_markets', 'get_events'] },
|
||||
},
|
||||
{
|
||||
id: 'tagId',
|
||||
title: 'Tag ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Filter by tag ID',
|
||||
condition: { field: 'operation', value: ['get_markets', 'get_events'] },
|
||||
},
|
||||
// Pagination fields
|
||||
{
|
||||
id: 'limit',
|
||||
title: 'Limit',
|
||||
type: 'short-input',
|
||||
placeholder: 'Number of results (recommended: 25-50)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['get_markets', 'get_events', 'get_tags', 'search', 'get_series', 'get_trades'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'offset',
|
||||
title: 'Offset',
|
||||
type: 'short-input',
|
||||
placeholder: 'Pagination offset',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['get_markets', 'get_events', 'get_tags', 'search', 'get_series', 'get_trades'],
|
||||
},
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: [
|
||||
'polymarket_get_markets',
|
||||
'polymarket_get_market',
|
||||
'polymarket_get_events',
|
||||
'polymarket_get_event',
|
||||
'polymarket_get_tags',
|
||||
'polymarket_search',
|
||||
'polymarket_get_series',
|
||||
'polymarket_get_series_by_id',
|
||||
'polymarket_get_orderbook',
|
||||
'polymarket_get_price',
|
||||
'polymarket_get_midpoint',
|
||||
'polymarket_get_price_history',
|
||||
'polymarket_get_last_trade_price',
|
||||
'polymarket_get_spread',
|
||||
'polymarket_get_tick_size',
|
||||
'polymarket_get_positions',
|
||||
'polymarket_get_trades',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => {
|
||||
switch (params.operation) {
|
||||
case 'get_markets':
|
||||
return 'polymarket_get_markets'
|
||||
case 'get_market':
|
||||
return 'polymarket_get_market'
|
||||
case 'get_events':
|
||||
return 'polymarket_get_events'
|
||||
case 'get_event':
|
||||
return 'polymarket_get_event'
|
||||
case 'get_tags':
|
||||
return 'polymarket_get_tags'
|
||||
case 'search':
|
||||
return 'polymarket_search'
|
||||
case 'get_series':
|
||||
return 'polymarket_get_series'
|
||||
case 'get_series_by_id':
|
||||
return 'polymarket_get_series_by_id'
|
||||
case 'get_orderbook':
|
||||
return 'polymarket_get_orderbook'
|
||||
case 'get_price':
|
||||
return 'polymarket_get_price'
|
||||
case 'get_midpoint':
|
||||
return 'polymarket_get_midpoint'
|
||||
case 'get_price_history':
|
||||
return 'polymarket_get_price_history'
|
||||
case 'get_last_trade_price':
|
||||
return 'polymarket_get_last_trade_price'
|
||||
case 'get_spread':
|
||||
return 'polymarket_get_spread'
|
||||
case 'get_tick_size':
|
||||
return 'polymarket_get_tick_size'
|
||||
case 'get_positions':
|
||||
return 'polymarket_get_positions'
|
||||
case 'get_trades':
|
||||
return 'polymarket_get_trades'
|
||||
default:
|
||||
return 'polymarket_get_markets'
|
||||
}
|
||||
},
|
||||
params: (params) => {
|
||||
const { operation, marketSlug, eventSlug, ...rest } = params
|
||||
const cleanParams: Record<string, any> = {}
|
||||
|
||||
// Map marketSlug to slug for get_market
|
||||
if (operation === 'get_market' && marketSlug) {
|
||||
cleanParams.slug = marketSlug
|
||||
}
|
||||
|
||||
// Map eventSlug to slug for get_event
|
||||
if (operation === 'get_event' && eventSlug) {
|
||||
cleanParams.slug = eventSlug
|
||||
}
|
||||
|
||||
// Convert numeric fields from string to number for get_price_history
|
||||
if (operation === 'get_price_history') {
|
||||
if (rest.fidelity) cleanParams.fidelity = Number(rest.fidelity)
|
||||
if (rest.startTs) cleanParams.startTs = Number(rest.startTs)
|
||||
if (rest.endTs) cleanParams.endTs = Number(rest.endTs)
|
||||
rest.fidelity = undefined
|
||||
rest.startTs = undefined
|
||||
rest.endTs = undefined
|
||||
}
|
||||
|
||||
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' },
|
||||
marketId: { type: 'string', description: 'Market ID' },
|
||||
marketSlug: { type: 'string', description: 'Market slug' },
|
||||
eventId: { type: 'string', description: 'Event ID' },
|
||||
eventSlug: { type: 'string', description: 'Event slug' },
|
||||
seriesId: { type: 'string', description: 'Series ID' },
|
||||
query: { type: 'string', description: 'Search query' },
|
||||
user: { type: 'string', description: 'User wallet address' },
|
||||
market: { type: 'string', description: 'Market ID filter' },
|
||||
tokenId: { type: 'string', description: 'CLOB Token ID' },
|
||||
side: { type: 'string', description: 'Order side (buy/sell)' },
|
||||
interval: { type: 'string', description: 'Price history interval' },
|
||||
fidelity: { type: 'number', description: 'Data resolution in minutes' },
|
||||
startTs: { type: 'number', description: 'Start timestamp (Unix)' },
|
||||
endTs: { type: 'number', description: 'End timestamp (Unix)' },
|
||||
},
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
output: { type: 'json', description: 'Operation result data' },
|
||||
},
|
||||
}
|
||||
845
apps/sim/blocks/blocks/shopify.ts
Normal file
845
apps/sim/blocks/blocks/shopify.ts
Normal file
@@ -0,0 +1,845 @@
|
||||
import { ShopifyIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
|
||||
interface ShopifyResponse {
|
||||
success: boolean
|
||||
error?: string
|
||||
output: Record<string, unknown>
|
||||
}
|
||||
|
||||
export const ShopifyBlock: BlockConfig<ShopifyResponse> = {
|
||||
type: 'shopify',
|
||||
name: 'Shopify',
|
||||
description: 'Manage products, orders, customers, and inventory in your Shopify store',
|
||||
authMode: AuthMode.OAuth,
|
||||
longDescription:
|
||||
'Integrate Shopify into your workflow. Manage products, orders, customers, and inventory. Create, read, update, and delete products. List and manage orders. Handle customer data and adjust inventory levels.',
|
||||
docsLink: 'https://docs.sim.ai/tools/shopify',
|
||||
category: 'tools',
|
||||
icon: ShopifyIcon,
|
||||
bgColor: '#FFFFFF',
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
// Product Operations
|
||||
{ label: 'Create Product', id: 'shopify_create_product' },
|
||||
{ label: 'Get Product', id: 'shopify_get_product' },
|
||||
{ label: 'List Products', id: 'shopify_list_products' },
|
||||
{ label: 'Update Product', id: 'shopify_update_product' },
|
||||
{ label: 'Delete Product', id: 'shopify_delete_product' },
|
||||
// Order Operations
|
||||
{ label: 'Get Order', id: 'shopify_get_order' },
|
||||
{ label: 'List Orders', id: 'shopify_list_orders' },
|
||||
{ label: 'Update Order', id: 'shopify_update_order' },
|
||||
{ label: 'Cancel Order', id: 'shopify_cancel_order' },
|
||||
// Customer Operations
|
||||
{ label: 'Create Customer', id: 'shopify_create_customer' },
|
||||
{ label: 'Get Customer', id: 'shopify_get_customer' },
|
||||
{ label: 'List Customers', id: 'shopify_list_customers' },
|
||||
{ label: 'Update Customer', id: 'shopify_update_customer' },
|
||||
{ label: 'Delete Customer', id: 'shopify_delete_customer' },
|
||||
// Inventory Operations
|
||||
{ label: 'List Inventory Items', id: 'shopify_list_inventory_items' },
|
||||
{ label: 'Get Inventory Level', id: 'shopify_get_inventory_level' },
|
||||
{ label: 'Adjust Inventory', id: 'shopify_adjust_inventory' },
|
||||
// Location Operations
|
||||
{ label: 'List Locations', id: 'shopify_list_locations' },
|
||||
// Fulfillment Operations
|
||||
{ label: 'Create Fulfillment', id: 'shopify_create_fulfillment' },
|
||||
// Collection Operations
|
||||
{ label: 'List Collections', id: 'shopify_list_collections' },
|
||||
{ label: 'Get Collection', id: 'shopify_get_collection' },
|
||||
],
|
||||
value: () => 'shopify_list_products',
|
||||
},
|
||||
{
|
||||
id: 'credential',
|
||||
title: 'Shopify Account',
|
||||
type: 'oauth-input',
|
||||
serviceId: 'shopify',
|
||||
requiredScopes: [
|
||||
'write_products',
|
||||
'write_orders',
|
||||
'write_customers',
|
||||
'write_inventory',
|
||||
'read_locations',
|
||||
'write_merchant_managed_fulfillment_orders',
|
||||
],
|
||||
placeholder: 'Select Shopify account',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'shopDomain',
|
||||
title: 'Shop Domain',
|
||||
type: 'short-input',
|
||||
placeholder: 'Auto-detected from OAuth or enter manually',
|
||||
hidden: true, // Auto-detected from OAuth credential's idToken field
|
||||
},
|
||||
// Product ID (for get/update/delete operations)
|
||||
{
|
||||
id: 'productId',
|
||||
title: 'Product ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'gid://shopify/Product/123456789',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_get_product', 'shopify_update_product', 'shopify_delete_product'],
|
||||
},
|
||||
},
|
||||
// Product Title (for create/update)
|
||||
{
|
||||
id: 'title',
|
||||
title: 'Product Title',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter product title',
|
||||
required: {
|
||||
field: 'operation',
|
||||
value: ['shopify_create_product'],
|
||||
},
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_create_product', 'shopify_update_product'],
|
||||
},
|
||||
},
|
||||
// Product Description
|
||||
{
|
||||
id: 'descriptionHtml',
|
||||
title: 'Description (HTML)',
|
||||
type: 'long-input',
|
||||
placeholder: 'Enter product description',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_create_product', 'shopify_update_product'],
|
||||
},
|
||||
},
|
||||
// Product Type
|
||||
{
|
||||
id: 'productType',
|
||||
title: 'Product Type',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., Shoes, Electronics',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_create_product', 'shopify_update_product'],
|
||||
},
|
||||
},
|
||||
// Vendor
|
||||
{
|
||||
id: 'vendor',
|
||||
title: 'Vendor',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter vendor name',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_create_product', 'shopify_update_product'],
|
||||
},
|
||||
},
|
||||
// Tags
|
||||
{
|
||||
id: 'tags',
|
||||
title: 'Tags',
|
||||
type: 'short-input',
|
||||
placeholder: 'tag1, tag2, tag3 (comma-separated)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_create_product', 'shopify_update_product'],
|
||||
},
|
||||
},
|
||||
// Status
|
||||
{
|
||||
id: 'status',
|
||||
title: 'Status',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Active', id: 'ACTIVE' },
|
||||
{ label: 'Draft', id: 'DRAFT' },
|
||||
{ label: 'Archived', id: 'ARCHIVED' },
|
||||
],
|
||||
value: () => 'ACTIVE',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_create_product', 'shopify_update_product'],
|
||||
},
|
||||
},
|
||||
// Query for listing products
|
||||
{
|
||||
id: 'productQuery',
|
||||
title: 'Search Query',
|
||||
type: 'short-input',
|
||||
placeholder: 'Filter products (optional)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_list_products'],
|
||||
},
|
||||
},
|
||||
// Query for listing customers
|
||||
{
|
||||
id: 'customerQuery',
|
||||
title: 'Search Query',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., first_name:John OR email:*@gmail.com',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_list_customers'],
|
||||
},
|
||||
},
|
||||
// Query for listing inventory items
|
||||
{
|
||||
id: 'inventoryQuery',
|
||||
title: 'Search Query',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., sku:ABC123',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_list_inventory_items'],
|
||||
},
|
||||
},
|
||||
// Order ID
|
||||
{
|
||||
id: 'orderId',
|
||||
title: 'Order ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'gid://shopify/Order/123456789',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_get_order', 'shopify_update_order', 'shopify_cancel_order'],
|
||||
},
|
||||
},
|
||||
// Order Status (for listing)
|
||||
{
|
||||
id: 'orderStatus',
|
||||
title: 'Order Status',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Any', id: 'any' },
|
||||
{ label: 'Open', id: 'open' },
|
||||
{ label: 'Closed', id: 'closed' },
|
||||
{ label: 'Cancelled', id: 'cancelled' },
|
||||
],
|
||||
value: () => 'any',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_list_orders'],
|
||||
},
|
||||
},
|
||||
// Order Note (for update)
|
||||
{
|
||||
id: 'orderNote',
|
||||
title: 'Order Note',
|
||||
type: 'long-input',
|
||||
placeholder: 'Enter order note',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_update_order'],
|
||||
},
|
||||
},
|
||||
// Order Email (for update)
|
||||
{
|
||||
id: 'orderEmail',
|
||||
title: 'Customer Email',
|
||||
type: 'short-input',
|
||||
placeholder: 'customer@example.com',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_update_order'],
|
||||
},
|
||||
},
|
||||
// Order Tags (for update)
|
||||
{
|
||||
id: 'orderTags',
|
||||
title: 'Order Tags',
|
||||
type: 'short-input',
|
||||
placeholder: 'tag1, tag2, tag3 (comma-separated)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_update_order'],
|
||||
},
|
||||
},
|
||||
// Cancel Order Reason
|
||||
{
|
||||
id: 'cancelReason',
|
||||
title: 'Cancel Reason',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Customer Request', id: 'CUSTOMER' },
|
||||
{ label: 'Declined Payment', id: 'DECLINED' },
|
||||
{ label: 'Fraud', id: 'FRAUD' },
|
||||
{ label: 'Inventory Issue', id: 'INVENTORY' },
|
||||
{ label: 'Other', id: 'OTHER' },
|
||||
],
|
||||
value: () => 'OTHER',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_cancel_order'],
|
||||
},
|
||||
},
|
||||
// Staff Note (for cancel order)
|
||||
{
|
||||
id: 'staffNote',
|
||||
title: 'Staff Note',
|
||||
type: 'long-input',
|
||||
placeholder: 'Internal note about this cancellation',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_cancel_order'],
|
||||
},
|
||||
},
|
||||
// Customer ID
|
||||
{
|
||||
id: 'customerId',
|
||||
title: 'Customer ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'gid://shopify/Customer/123456789',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_get_customer', 'shopify_update_customer', 'shopify_delete_customer'],
|
||||
},
|
||||
},
|
||||
// Customer Email (at least one of email/phone/firstName/lastName required for create)
|
||||
{
|
||||
id: 'customerEmail',
|
||||
title: 'Email',
|
||||
type: 'short-input',
|
||||
placeholder: 'customer@example.com',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_create_customer', 'shopify_update_customer'],
|
||||
},
|
||||
},
|
||||
// Customer First Name
|
||||
{
|
||||
id: 'firstName',
|
||||
title: 'First Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter first name',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_create_customer', 'shopify_update_customer'],
|
||||
},
|
||||
},
|
||||
// Customer Last Name
|
||||
{
|
||||
id: 'lastName',
|
||||
title: 'Last Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter last name',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_create_customer', 'shopify_update_customer'],
|
||||
},
|
||||
},
|
||||
// Customer Phone
|
||||
{
|
||||
id: 'phone',
|
||||
title: 'Phone',
|
||||
type: 'short-input',
|
||||
placeholder: '+1234567890',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_create_customer', 'shopify_update_customer'],
|
||||
},
|
||||
},
|
||||
// Customer Note
|
||||
{
|
||||
id: 'customerNote',
|
||||
title: 'Customer Note',
|
||||
type: 'long-input',
|
||||
placeholder: 'Enter note about customer',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_create_customer', 'shopify_update_customer'],
|
||||
},
|
||||
},
|
||||
// Customer Tags
|
||||
{
|
||||
id: 'customerTags',
|
||||
title: 'Customer Tags',
|
||||
type: 'short-input',
|
||||
placeholder: 'vip, wholesale (comma-separated)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_create_customer', 'shopify_update_customer'],
|
||||
},
|
||||
},
|
||||
// Accepts Marketing
|
||||
{
|
||||
id: 'acceptsMarketing',
|
||||
title: 'Accepts Marketing',
|
||||
type: 'switch',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_create_customer', 'shopify_update_customer'],
|
||||
},
|
||||
},
|
||||
// Inventory Item ID
|
||||
{
|
||||
id: 'inventoryItemId',
|
||||
title: 'Inventory Item ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'gid://shopify/InventoryItem/123456789',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_get_inventory_level', 'shopify_adjust_inventory'],
|
||||
},
|
||||
},
|
||||
// Location ID
|
||||
{
|
||||
id: 'locationId',
|
||||
title: 'Location ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'gid://shopify/Location/123456789',
|
||||
required: {
|
||||
field: 'operation',
|
||||
value: 'shopify_adjust_inventory',
|
||||
},
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_get_inventory_level', 'shopify_adjust_inventory'],
|
||||
},
|
||||
},
|
||||
// Delta (for inventory adjustment)
|
||||
{
|
||||
id: 'delta',
|
||||
title: 'Quantity Change',
|
||||
type: 'short-input',
|
||||
placeholder: 'Positive to add, negative to subtract',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_adjust_inventory'],
|
||||
},
|
||||
},
|
||||
// Fulfillment Order ID
|
||||
{
|
||||
id: 'fulfillmentOrderId',
|
||||
title: 'Fulfillment Order ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'gid://shopify/FulfillmentOrder/123456789',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_create_fulfillment'],
|
||||
},
|
||||
},
|
||||
// Tracking Number
|
||||
{
|
||||
id: 'trackingNumber',
|
||||
title: 'Tracking Number',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter tracking number',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_create_fulfillment'],
|
||||
},
|
||||
},
|
||||
// Tracking Company
|
||||
{
|
||||
id: 'trackingCompany',
|
||||
title: 'Shipping Carrier',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., UPS, FedEx, USPS, DHL',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_create_fulfillment'],
|
||||
},
|
||||
},
|
||||
// Tracking URL
|
||||
{
|
||||
id: 'trackingUrl',
|
||||
title: 'Tracking URL',
|
||||
type: 'short-input',
|
||||
placeholder: 'https://...',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_create_fulfillment'],
|
||||
},
|
||||
},
|
||||
// Notify Customer (for fulfillment)
|
||||
{
|
||||
id: 'notifyCustomer',
|
||||
title: 'Notify Customer',
|
||||
type: 'switch',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_create_fulfillment'],
|
||||
},
|
||||
},
|
||||
// Collection ID
|
||||
{
|
||||
id: 'collectionId',
|
||||
title: 'Collection ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'gid://shopify/Collection/123456789',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_get_collection'],
|
||||
},
|
||||
},
|
||||
// Collection Query
|
||||
{
|
||||
id: 'collectionQuery',
|
||||
title: 'Search Query',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., title:Summer OR collection_type:smart',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['shopify_list_collections'],
|
||||
},
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: [
|
||||
'shopify_create_product',
|
||||
'shopify_get_product',
|
||||
'shopify_list_products',
|
||||
'shopify_update_product',
|
||||
'shopify_delete_product',
|
||||
'shopify_get_order',
|
||||
'shopify_list_orders',
|
||||
'shopify_update_order',
|
||||
'shopify_cancel_order',
|
||||
'shopify_create_customer',
|
||||
'shopify_get_customer',
|
||||
'shopify_list_customers',
|
||||
'shopify_update_customer',
|
||||
'shopify_delete_customer',
|
||||
'shopify_list_inventory_items',
|
||||
'shopify_get_inventory_level',
|
||||
'shopify_adjust_inventory',
|
||||
'shopify_list_locations',
|
||||
'shopify_create_fulfillment',
|
||||
'shopify_list_collections',
|
||||
'shopify_get_collection',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => {
|
||||
return params.operation || 'shopify_list_products'
|
||||
},
|
||||
params: (params) => {
|
||||
const baseParams: Record<string, unknown> = {
|
||||
credential: params.credential,
|
||||
shopDomain: params.shopDomain?.trim(),
|
||||
}
|
||||
|
||||
switch (params.operation) {
|
||||
// Product Operations
|
||||
case 'shopify_create_product':
|
||||
if (!params.title?.trim()) {
|
||||
throw new Error('Product title is required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
title: params.title.trim(),
|
||||
descriptionHtml: params.descriptionHtml?.trim(),
|
||||
productType: params.productType?.trim(),
|
||||
vendor: params.vendor?.trim(),
|
||||
tags: params.tags
|
||||
?.split(',')
|
||||
.map((t: string) => t.trim())
|
||||
.filter(Boolean),
|
||||
status: params.status,
|
||||
}
|
||||
|
||||
case 'shopify_get_product':
|
||||
if (!params.productId?.trim()) {
|
||||
throw new Error('Product ID is required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
productId: params.productId.trim(),
|
||||
}
|
||||
|
||||
case 'shopify_list_products':
|
||||
return {
|
||||
...baseParams,
|
||||
query: params.productQuery?.trim(),
|
||||
}
|
||||
|
||||
case 'shopify_update_product':
|
||||
if (!params.productId?.trim()) {
|
||||
throw new Error('Product ID is required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
productId: params.productId.trim(),
|
||||
title: params.title?.trim(),
|
||||
descriptionHtml: params.descriptionHtml?.trim(),
|
||||
productType: params.productType?.trim(),
|
||||
vendor: params.vendor?.trim(),
|
||||
tags: params.tags
|
||||
?.split(',')
|
||||
.map((t: string) => t.trim())
|
||||
.filter(Boolean),
|
||||
status: params.status,
|
||||
}
|
||||
|
||||
case 'shopify_delete_product':
|
||||
if (!params.productId?.trim()) {
|
||||
throw new Error('Product ID is required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
productId: params.productId.trim(),
|
||||
}
|
||||
|
||||
// Order Operations
|
||||
case 'shopify_get_order':
|
||||
if (!params.orderId?.trim()) {
|
||||
throw new Error('Order ID is required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
orderId: params.orderId.trim(),
|
||||
}
|
||||
|
||||
case 'shopify_list_orders':
|
||||
return {
|
||||
...baseParams,
|
||||
status: params.orderStatus !== 'any' ? params.orderStatus : undefined,
|
||||
}
|
||||
|
||||
case 'shopify_update_order':
|
||||
if (!params.orderId?.trim()) {
|
||||
throw new Error('Order ID is required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
orderId: params.orderId.trim(),
|
||||
note: params.orderNote?.trim(),
|
||||
email: params.orderEmail?.trim(),
|
||||
tags: params.orderTags
|
||||
?.split(',')
|
||||
.map((t: string) => t.trim())
|
||||
.filter(Boolean),
|
||||
}
|
||||
|
||||
case 'shopify_cancel_order':
|
||||
if (!params.orderId?.trim()) {
|
||||
throw new Error('Order ID is required.')
|
||||
}
|
||||
if (!params.cancelReason) {
|
||||
throw new Error('Cancel reason is required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
orderId: params.orderId.trim(),
|
||||
reason: params.cancelReason,
|
||||
staffNote: params.staffNote?.trim(),
|
||||
}
|
||||
|
||||
// Customer Operations
|
||||
case 'shopify_create_customer':
|
||||
// At least one of email/phone/firstName/lastName required (validated in tool)
|
||||
return {
|
||||
...baseParams,
|
||||
email: params.customerEmail?.trim(),
|
||||
firstName: params.firstName?.trim(),
|
||||
lastName: params.lastName?.trim(),
|
||||
phone: params.phone?.trim(),
|
||||
note: params.customerNote?.trim(),
|
||||
tags: params.customerTags
|
||||
?.split(',')
|
||||
.map((t: string) => t.trim())
|
||||
.filter(Boolean),
|
||||
acceptsMarketing: params.acceptsMarketing,
|
||||
}
|
||||
|
||||
case 'shopify_get_customer':
|
||||
if (!params.customerId?.trim()) {
|
||||
throw new Error('Customer ID is required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
customerId: params.customerId.trim(),
|
||||
}
|
||||
|
||||
case 'shopify_list_customers':
|
||||
return {
|
||||
...baseParams,
|
||||
query: params.customerQuery?.trim(),
|
||||
}
|
||||
|
||||
case 'shopify_update_customer':
|
||||
if (!params.customerId?.trim()) {
|
||||
throw new Error('Customer ID is required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
customerId: params.customerId.trim(),
|
||||
email: params.customerEmail?.trim(),
|
||||
firstName: params.firstName?.trim(),
|
||||
lastName: params.lastName?.trim(),
|
||||
phone: params.phone?.trim(),
|
||||
note: params.customerNote?.trim(),
|
||||
tags: params.customerTags
|
||||
?.split(',')
|
||||
.map((t: string) => t.trim())
|
||||
.filter(Boolean),
|
||||
}
|
||||
|
||||
case 'shopify_delete_customer':
|
||||
if (!params.customerId?.trim()) {
|
||||
throw new Error('Customer ID is required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
customerId: params.customerId.trim(),
|
||||
}
|
||||
|
||||
// Inventory Operations
|
||||
case 'shopify_list_inventory_items':
|
||||
return {
|
||||
...baseParams,
|
||||
query: params.inventoryQuery?.trim(),
|
||||
}
|
||||
|
||||
case 'shopify_get_inventory_level':
|
||||
if (!params.inventoryItemId?.trim()) {
|
||||
throw new Error('Inventory Item ID is required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
inventoryItemId: params.inventoryItemId.trim(),
|
||||
locationId: params.locationId?.trim(),
|
||||
}
|
||||
|
||||
case 'shopify_adjust_inventory':
|
||||
if (!params.inventoryItemId?.trim()) {
|
||||
throw new Error('Inventory Item ID is required.')
|
||||
}
|
||||
if (!params.locationId?.trim()) {
|
||||
throw new Error('Location ID is required.')
|
||||
}
|
||||
if (params.delta === undefined || params.delta === '') {
|
||||
throw new Error('Quantity change (delta) is required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
inventoryItemId: params.inventoryItemId.trim(),
|
||||
locationId: params.locationId.trim(),
|
||||
delta: Number(params.delta),
|
||||
}
|
||||
|
||||
// Location Operations
|
||||
case 'shopify_list_locations':
|
||||
return {
|
||||
...baseParams,
|
||||
}
|
||||
|
||||
// Fulfillment Operations
|
||||
case 'shopify_create_fulfillment':
|
||||
if (!params.fulfillmentOrderId?.trim()) {
|
||||
throw new Error('Fulfillment Order ID is required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
fulfillmentOrderId: params.fulfillmentOrderId.trim(),
|
||||
trackingNumber: params.trackingNumber?.trim(),
|
||||
trackingCompany: params.trackingCompany?.trim(),
|
||||
trackingUrl: params.trackingUrl?.trim(),
|
||||
notifyCustomer: params.notifyCustomer,
|
||||
}
|
||||
|
||||
// Collection Operations
|
||||
case 'shopify_list_collections':
|
||||
return {
|
||||
...baseParams,
|
||||
query: params.collectionQuery?.trim(),
|
||||
}
|
||||
|
||||
case 'shopify_get_collection':
|
||||
if (!params.collectionId?.trim()) {
|
||||
throw new Error('Collection ID is required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
collectionId: params.collectionId.trim(),
|
||||
}
|
||||
|
||||
default:
|
||||
return baseParams
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'Operation to perform' },
|
||||
credential: { type: 'string', description: 'Shopify access token' },
|
||||
shopDomain: { type: 'string', description: 'Shopify store domain' },
|
||||
// Product inputs
|
||||
productId: { type: 'string', description: 'Product ID' },
|
||||
title: { type: 'string', description: 'Product title' },
|
||||
descriptionHtml: { type: 'string', description: 'Product description (HTML)' },
|
||||
productType: { type: 'string', description: 'Product type' },
|
||||
vendor: { type: 'string', description: 'Product vendor' },
|
||||
tags: { type: 'string', description: 'Tags (comma-separated)' },
|
||||
status: { type: 'string', description: 'Product status' },
|
||||
query: { type: 'string', description: 'Search query' },
|
||||
// Order inputs
|
||||
orderId: { type: 'string', description: 'Order ID' },
|
||||
orderStatus: { type: 'string', description: 'Order status filter' },
|
||||
orderNote: { type: 'string', description: 'Order note' },
|
||||
orderEmail: { type: 'string', description: 'Order customer email' },
|
||||
orderTags: { type: 'string', description: 'Order tags' },
|
||||
cancelReason: { type: 'string', description: 'Order cancellation reason' },
|
||||
staffNote: { type: 'string', description: 'Staff note for order cancellation' },
|
||||
// Customer inputs
|
||||
customerId: { type: 'string', description: 'Customer ID' },
|
||||
customerEmail: { type: 'string', description: 'Customer email' },
|
||||
firstName: { type: 'string', description: 'Customer first name' },
|
||||
lastName: { type: 'string', description: 'Customer last name' },
|
||||
phone: { type: 'string', description: 'Customer phone' },
|
||||
customerNote: { type: 'string', description: 'Customer note' },
|
||||
customerTags: { type: 'string', description: 'Customer tags' },
|
||||
acceptsMarketing: { type: 'boolean', description: 'Accepts marketing' },
|
||||
// Inventory inputs
|
||||
inventoryQuery: { type: 'string', description: 'Inventory search query' },
|
||||
inventoryItemId: { type: 'string', description: 'Inventory item ID' },
|
||||
locationId: { type: 'string', description: 'Location ID' },
|
||||
delta: { type: 'number', description: 'Quantity change' },
|
||||
// Fulfillment inputs
|
||||
fulfillmentOrderId: { type: 'string', description: 'Fulfillment order ID' },
|
||||
trackingNumber: { type: 'string', description: 'Shipment tracking number' },
|
||||
trackingCompany: { type: 'string', description: 'Shipping carrier name' },
|
||||
trackingUrl: { type: 'string', description: 'Tracking URL' },
|
||||
notifyCustomer: { type: 'boolean', description: 'Send shipping notification email' },
|
||||
// Collection inputs
|
||||
collectionId: { type: 'string', description: 'Collection ID' },
|
||||
collectionQuery: { type: 'string', description: 'Collection search query' },
|
||||
},
|
||||
outputs: {
|
||||
// Product outputs
|
||||
product: { type: 'json', description: 'Product data' },
|
||||
products: { type: 'json', description: 'Products list' },
|
||||
// Order outputs
|
||||
order: { type: 'json', description: 'Order data' },
|
||||
orders: { type: 'json', description: 'Orders list' },
|
||||
// Customer outputs
|
||||
customer: { type: 'json', description: 'Customer data' },
|
||||
customers: { type: 'json', description: 'Customers list' },
|
||||
// Inventory outputs
|
||||
inventoryItems: { type: 'json', description: 'Inventory items list' },
|
||||
inventoryLevel: { type: 'json', description: 'Inventory level data' },
|
||||
// Location outputs
|
||||
locations: { type: 'json', description: 'Locations list' },
|
||||
// Fulfillment outputs
|
||||
fulfillment: { type: 'json', description: 'Fulfillment data' },
|
||||
// Collection outputs
|
||||
collection: { type: 'json', description: 'Collection data with products' },
|
||||
collections: { type: 'json', description: 'Collections list' },
|
||||
// Delete outputs
|
||||
deletedId: { type: 'string', description: 'ID of deleted resource' },
|
||||
// Success indicator
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
518
apps/sim/blocks/blocks/ssh.ts
Normal file
518
apps/sim/blocks/blocks/ssh.ts
Normal file
@@ -0,0 +1,518 @@
|
||||
import { SshIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import type { SSHResponse } from '@/tools/ssh/types'
|
||||
|
||||
export const SSHBlock: BlockConfig<SSHResponse> = {
|
||||
type: 'ssh',
|
||||
name: 'SSH',
|
||||
description: 'Connect to remote servers via SSH',
|
||||
authMode: AuthMode.ApiKey,
|
||||
longDescription:
|
||||
'Execute commands, transfer files, and manage remote servers via SSH. Supports password and private key authentication for secure server access.',
|
||||
docsLink: 'https://docs.sim.ai/tools/ssh',
|
||||
category: 'tools',
|
||||
bgColor: '#000000',
|
||||
icon: SshIcon,
|
||||
subBlocks: [
|
||||
// Operation selector
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Execute Command', id: 'ssh_execute_command' },
|
||||
{ label: 'Execute Script', id: 'ssh_execute_script' },
|
||||
{ label: 'Check Command Exists', id: 'ssh_check_command_exists' },
|
||||
{ label: 'Upload File', id: 'ssh_upload_file' },
|
||||
{ label: 'Download File', id: 'ssh_download_file' },
|
||||
{ label: 'List Directory', id: 'ssh_list_directory' },
|
||||
{ label: 'Check File/Directory Exists', id: 'ssh_check_file_exists' },
|
||||
{ label: 'Create Directory', id: 'ssh_create_directory' },
|
||||
{ label: 'Delete File/Directory', id: 'ssh_delete_file' },
|
||||
{ label: 'Move/Rename', id: 'ssh_move_rename' },
|
||||
{ label: 'Get System Info', id: 'ssh_get_system_info' },
|
||||
{ label: 'Read File Content', id: 'ssh_read_file_content' },
|
||||
{ label: 'Write File Content', id: 'ssh_write_file_content' },
|
||||
],
|
||||
value: () => 'ssh_execute_command',
|
||||
},
|
||||
|
||||
// Connection parameters
|
||||
{
|
||||
id: 'host',
|
||||
title: 'Host',
|
||||
type: 'short-input',
|
||||
placeholder: 'example.com or 192.168.1.100',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'port',
|
||||
title: 'Port',
|
||||
type: 'short-input',
|
||||
placeholder: '22',
|
||||
value: () => '22',
|
||||
},
|
||||
{
|
||||
id: 'username',
|
||||
title: 'Username',
|
||||
type: 'short-input',
|
||||
placeholder: 'ubuntu, root, or deploy',
|
||||
required: true,
|
||||
},
|
||||
|
||||
// Authentication method selector
|
||||
{
|
||||
id: 'authMethod',
|
||||
title: 'Authentication Method',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Password', id: 'password' },
|
||||
{ label: 'Private Key', id: 'privateKey' },
|
||||
],
|
||||
value: () => 'password',
|
||||
},
|
||||
|
||||
// Password authentication
|
||||
{
|
||||
id: 'password',
|
||||
title: 'Password',
|
||||
type: 'short-input',
|
||||
password: true,
|
||||
placeholder: 'Your SSH password',
|
||||
condition: { field: 'authMethod', value: 'password' },
|
||||
},
|
||||
|
||||
// Private key authentication
|
||||
{
|
||||
id: 'privateKey',
|
||||
title: 'Private Key',
|
||||
type: 'code',
|
||||
placeholder: '-----BEGIN OPENSSH PRIVATE KEY-----\n...',
|
||||
condition: { field: 'authMethod', value: 'privateKey' },
|
||||
},
|
||||
{
|
||||
id: 'passphrase',
|
||||
title: 'Passphrase',
|
||||
type: 'short-input',
|
||||
password: true,
|
||||
placeholder: 'Passphrase for encrypted key (optional)',
|
||||
condition: { field: 'authMethod', value: 'privateKey' },
|
||||
},
|
||||
|
||||
// EXECUTE COMMAND
|
||||
{
|
||||
id: 'command',
|
||||
title: 'Command',
|
||||
type: 'code',
|
||||
placeholder: 'ls -la /var/www',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'ssh_execute_command' },
|
||||
},
|
||||
{
|
||||
id: 'workingDirectory',
|
||||
title: 'Working Directory',
|
||||
type: 'short-input',
|
||||
placeholder: '/var/www/html (optional)',
|
||||
condition: { field: 'operation', value: 'ssh_execute_command' },
|
||||
},
|
||||
|
||||
// EXECUTE SCRIPT
|
||||
{
|
||||
id: 'script',
|
||||
title: 'Script Content',
|
||||
type: 'code',
|
||||
placeholder: '#!/bin/bash\necho "Hello World"',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'ssh_execute_script' },
|
||||
},
|
||||
{
|
||||
id: 'interpreter',
|
||||
title: 'Interpreter',
|
||||
type: 'short-input',
|
||||
placeholder: '/bin/bash',
|
||||
condition: { field: 'operation', value: 'ssh_execute_script' },
|
||||
},
|
||||
{
|
||||
id: 'scriptWorkingDirectory',
|
||||
title: 'Working Directory',
|
||||
type: 'short-input',
|
||||
placeholder: '/var/www/html (optional)',
|
||||
condition: { field: 'operation', value: 'ssh_execute_script' },
|
||||
},
|
||||
|
||||
// CHECK COMMAND EXISTS
|
||||
{
|
||||
id: 'commandName',
|
||||
title: 'Command Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'docker, git, python3',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'ssh_check_command_exists' },
|
||||
},
|
||||
|
||||
// UPLOAD FILE
|
||||
{
|
||||
id: 'fileContent',
|
||||
title: 'File Content',
|
||||
type: 'code',
|
||||
placeholder: 'Content to upload...',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'ssh_upload_file' },
|
||||
},
|
||||
{
|
||||
id: 'fileName',
|
||||
title: 'File Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'config.json',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'ssh_upload_file' },
|
||||
},
|
||||
{
|
||||
id: 'remotePath',
|
||||
title: 'Remote Path',
|
||||
type: 'short-input',
|
||||
placeholder: '/var/www/html/config.json',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'ssh_upload_file' },
|
||||
},
|
||||
{
|
||||
id: 'permissions',
|
||||
title: 'Permissions',
|
||||
type: 'short-input',
|
||||
placeholder: '0644',
|
||||
condition: { field: 'operation', value: 'ssh_upload_file' },
|
||||
},
|
||||
|
||||
// DOWNLOAD FILE
|
||||
{
|
||||
id: 'downloadRemotePath',
|
||||
title: 'Remote File Path',
|
||||
type: 'short-input',
|
||||
placeholder: '/var/log/app.log',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'ssh_download_file' },
|
||||
},
|
||||
|
||||
// LIST DIRECTORY
|
||||
{
|
||||
id: 'listPath',
|
||||
title: 'Directory Path',
|
||||
type: 'short-input',
|
||||
placeholder: '/var/www',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'ssh_list_directory' },
|
||||
},
|
||||
{
|
||||
id: 'detailed',
|
||||
title: 'Show Details',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'ssh_list_directory' },
|
||||
},
|
||||
|
||||
// CHECK FILE EXISTS
|
||||
{
|
||||
id: 'checkPath',
|
||||
title: 'Path to Check',
|
||||
type: 'short-input',
|
||||
placeholder: '/etc/nginx/nginx.conf',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'ssh_check_file_exists' },
|
||||
},
|
||||
{
|
||||
id: 'checkType',
|
||||
title: 'Expected Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Any', id: 'any' },
|
||||
{ label: 'File', id: 'file' },
|
||||
{ label: 'Directory', id: 'directory' },
|
||||
],
|
||||
value: () => 'any',
|
||||
condition: { field: 'operation', value: 'ssh_check_file_exists' },
|
||||
},
|
||||
|
||||
// CREATE DIRECTORY
|
||||
{
|
||||
id: 'createPath',
|
||||
title: 'Directory Path',
|
||||
type: 'short-input',
|
||||
placeholder: '/var/www/new-site',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'ssh_create_directory' },
|
||||
},
|
||||
{
|
||||
id: 'recursive',
|
||||
title: 'Create Parent Directories',
|
||||
type: 'switch',
|
||||
defaultValue: true,
|
||||
condition: { field: 'operation', value: 'ssh_create_directory' },
|
||||
},
|
||||
|
||||
// DELETE FILE
|
||||
{
|
||||
id: 'deletePath',
|
||||
title: 'Path to Delete',
|
||||
type: 'short-input',
|
||||
placeholder: '/tmp/old-file.txt',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'ssh_delete_file' },
|
||||
},
|
||||
{
|
||||
id: 'deleteRecursive',
|
||||
title: 'Recursive Delete',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'ssh_delete_file' },
|
||||
},
|
||||
{
|
||||
id: 'force',
|
||||
title: 'Force Delete',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'ssh_delete_file' },
|
||||
},
|
||||
|
||||
// MOVE/RENAME
|
||||
{
|
||||
id: 'sourcePath',
|
||||
title: 'Source Path',
|
||||
type: 'short-input',
|
||||
placeholder: '/var/www/old-name',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'ssh_move_rename' },
|
||||
},
|
||||
{
|
||||
id: 'destinationPath',
|
||||
title: 'Destination Path',
|
||||
type: 'short-input',
|
||||
placeholder: '/var/www/new-name',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'ssh_move_rename' },
|
||||
},
|
||||
{
|
||||
id: 'overwrite',
|
||||
title: 'Overwrite if Exists',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'ssh_move_rename' },
|
||||
},
|
||||
|
||||
// READ FILE CONTENT
|
||||
{
|
||||
id: 'readPath',
|
||||
title: 'File Path',
|
||||
type: 'short-input',
|
||||
placeholder: '/var/log/app.log',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'ssh_read_file_content' },
|
||||
},
|
||||
{
|
||||
id: 'encoding',
|
||||
title: 'Encoding',
|
||||
type: 'short-input',
|
||||
placeholder: 'utf-8',
|
||||
condition: { field: 'operation', value: 'ssh_read_file_content' },
|
||||
},
|
||||
{
|
||||
id: 'maxSize',
|
||||
title: 'Max Size (MB)',
|
||||
type: 'short-input',
|
||||
placeholder: '10',
|
||||
condition: { field: 'operation', value: 'ssh_read_file_content' },
|
||||
},
|
||||
|
||||
// WRITE FILE CONTENT
|
||||
{
|
||||
id: 'writePath',
|
||||
title: 'File Path',
|
||||
type: 'short-input',
|
||||
placeholder: '/etc/config.json',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'ssh_write_file_content' },
|
||||
},
|
||||
{
|
||||
id: 'content',
|
||||
title: 'File Content',
|
||||
type: 'code',
|
||||
placeholder: 'Content to write...',
|
||||
required: true,
|
||||
condition: { field: 'operation', value: 'ssh_write_file_content' },
|
||||
},
|
||||
{
|
||||
id: 'writeMode',
|
||||
title: 'Write Mode',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Overwrite', id: 'overwrite' },
|
||||
{ label: 'Append', id: 'append' },
|
||||
{ label: 'Create (fail if exists)', id: 'create' },
|
||||
],
|
||||
value: () => 'overwrite',
|
||||
condition: { field: 'operation', value: 'ssh_write_file_content' },
|
||||
},
|
||||
{
|
||||
id: 'writePermissions',
|
||||
title: 'Permissions',
|
||||
type: 'short-input',
|
||||
placeholder: '0644',
|
||||
condition: { field: 'operation', value: 'ssh_write_file_content' },
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: [
|
||||
'ssh_execute_command',
|
||||
'ssh_execute_script',
|
||||
'ssh_check_command_exists',
|
||||
'ssh_upload_file',
|
||||
'ssh_download_file',
|
||||
'ssh_list_directory',
|
||||
'ssh_check_file_exists',
|
||||
'ssh_create_directory',
|
||||
'ssh_delete_file',
|
||||
'ssh_move_rename',
|
||||
'ssh_get_system_info',
|
||||
'ssh_read_file_content',
|
||||
'ssh_write_file_content',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => {
|
||||
return params.operation || 'ssh_execute_command'
|
||||
},
|
||||
params: (params) => {
|
||||
// Build connection config
|
||||
const connectionConfig: Record<string, unknown> = {
|
||||
host: params.host,
|
||||
port:
|
||||
typeof params.port === 'string' ? Number.parseInt(params.port, 10) : params.port || 22,
|
||||
username: params.username,
|
||||
}
|
||||
|
||||
// Add authentication based on method
|
||||
if (params.authMethod === 'privateKey') {
|
||||
connectionConfig.privateKey = params.privateKey
|
||||
if (params.passphrase) {
|
||||
connectionConfig.passphrase = params.passphrase
|
||||
}
|
||||
} else {
|
||||
connectionConfig.password = params.password
|
||||
}
|
||||
|
||||
// Build operation-specific parameters based on the selected operation
|
||||
const operation = params.operation || 'ssh_execute_command'
|
||||
|
||||
switch (operation) {
|
||||
case 'ssh_execute_command':
|
||||
return {
|
||||
...connectionConfig,
|
||||
command: params.command,
|
||||
workingDirectory: params.workingDirectory,
|
||||
}
|
||||
case 'ssh_execute_script':
|
||||
return {
|
||||
...connectionConfig,
|
||||
script: params.script,
|
||||
interpreter: params.interpreter || '/bin/bash',
|
||||
workingDirectory: params.scriptWorkingDirectory,
|
||||
}
|
||||
case 'ssh_check_command_exists':
|
||||
return {
|
||||
...connectionConfig,
|
||||
commandName: params.commandName,
|
||||
}
|
||||
case 'ssh_upload_file':
|
||||
return {
|
||||
...connectionConfig,
|
||||
fileContent: params.fileContent,
|
||||
fileName: params.fileName,
|
||||
remotePath: params.remotePath,
|
||||
permissions: params.permissions,
|
||||
}
|
||||
case 'ssh_download_file':
|
||||
return {
|
||||
...connectionConfig,
|
||||
remotePath: params.downloadRemotePath,
|
||||
}
|
||||
case 'ssh_list_directory':
|
||||
return {
|
||||
...connectionConfig,
|
||||
path: params.listPath,
|
||||
detailed: params.detailed,
|
||||
}
|
||||
case 'ssh_check_file_exists':
|
||||
return {
|
||||
...connectionConfig,
|
||||
path: params.checkPath,
|
||||
type: params.checkType || 'any',
|
||||
}
|
||||
case 'ssh_create_directory':
|
||||
return {
|
||||
...connectionConfig,
|
||||
path: params.createPath,
|
||||
recursive: params.recursive !== false,
|
||||
}
|
||||
case 'ssh_delete_file':
|
||||
return {
|
||||
...connectionConfig,
|
||||
path: params.deletePath,
|
||||
recursive: params.deleteRecursive,
|
||||
force: params.force,
|
||||
}
|
||||
case 'ssh_move_rename':
|
||||
return {
|
||||
...connectionConfig,
|
||||
sourcePath: params.sourcePath,
|
||||
destinationPath: params.destinationPath,
|
||||
overwrite: params.overwrite,
|
||||
}
|
||||
case 'ssh_get_system_info':
|
||||
return connectionConfig
|
||||
case 'ssh_read_file_content':
|
||||
return {
|
||||
...connectionConfig,
|
||||
path: params.readPath,
|
||||
encoding: params.encoding || 'utf-8',
|
||||
maxSize: params.maxSize ? Number.parseInt(params.maxSize, 10) : 10,
|
||||
}
|
||||
case 'ssh_write_file_content':
|
||||
return {
|
||||
...connectionConfig,
|
||||
path: params.writePath,
|
||||
content: params.content,
|
||||
mode: params.writeMode || 'overwrite',
|
||||
permissions: params.writePermissions,
|
||||
}
|
||||
default:
|
||||
return connectionConfig
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'SSH operation to perform' },
|
||||
host: { type: 'string', description: 'SSH server hostname' },
|
||||
port: { type: 'number', description: 'SSH server port' },
|
||||
username: { type: 'string', description: 'SSH username' },
|
||||
authMethod: { type: 'string', description: 'Authentication method' },
|
||||
password: { type: 'string', description: 'Password for authentication' },
|
||||
privateKey: { type: 'string', description: 'Private key for authentication' },
|
||||
passphrase: { type: 'string', description: 'Passphrase for encrypted key' },
|
||||
command: { type: 'string', description: 'Command to execute' },
|
||||
script: { type: 'string', description: 'Script content to execute' },
|
||||
commandName: { type: 'string', description: 'Command name to check' },
|
||||
fileContent: { type: 'string', description: 'File content to upload' },
|
||||
fileName: { type: 'string', description: 'Name of the file' },
|
||||
remotePath: { type: 'string', description: 'Remote file/directory path' },
|
||||
content: { type: 'string', description: 'File content' },
|
||||
},
|
||||
outputs: {
|
||||
stdout: { type: 'string', description: 'Command standard output' },
|
||||
stderr: { type: 'string', description: 'Command standard error' },
|
||||
exitCode: { type: 'number', description: 'Command exit code' },
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
fileContent: { type: 'string', description: 'Downloaded/read file content' },
|
||||
entries: { type: 'json', description: 'Directory entries' },
|
||||
exists: { type: 'boolean', description: 'File/directory existence' },
|
||||
content: { type: 'string', description: 'File content' },
|
||||
hostname: { type: 'string', description: 'Server hostname' },
|
||||
os: { type: 'string', description: 'Operating system' },
|
||||
message: { type: 'string', description: 'Operation status message' },
|
||||
},
|
||||
}
|
||||
958
apps/sim/blocks/blocks/wordpress.ts
Normal file
958
apps/sim/blocks/blocks/wordpress.ts
Normal file
@@ -0,0 +1,958 @@
|
||||
import { WordpressIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import type { WordPressResponse } from '@/tools/wordpress/types'
|
||||
|
||||
export const WordPressBlock: BlockConfig<WordPressResponse> = {
|
||||
type: 'wordpress',
|
||||
name: 'WordPress',
|
||||
description: 'Manage WordPress content',
|
||||
authMode: AuthMode.OAuth,
|
||||
longDescription:
|
||||
'Integrate with WordPress to create, update, and manage posts, pages, media, comments, categories, tags, and users. Supports WordPress.com sites via OAuth and self-hosted WordPress sites using Application Passwords authentication.',
|
||||
docsLink: 'https://docs.sim.ai/tools/wordpress',
|
||||
category: 'tools',
|
||||
bgColor: '#21759B',
|
||||
icon: WordpressIcon,
|
||||
subBlocks: [
|
||||
// Operation Selection
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
// Posts
|
||||
{ label: 'Create Post', id: 'wordpress_create_post' },
|
||||
{ label: 'Update Post', id: 'wordpress_update_post' },
|
||||
{ label: 'Delete Post', id: 'wordpress_delete_post' },
|
||||
{ label: 'Get Post', id: 'wordpress_get_post' },
|
||||
{ label: 'List Posts', id: 'wordpress_list_posts' },
|
||||
// Pages
|
||||
{ label: 'Create Page', id: 'wordpress_create_page' },
|
||||
{ label: 'Update Page', id: 'wordpress_update_page' },
|
||||
{ label: 'Delete Page', id: 'wordpress_delete_page' },
|
||||
{ label: 'Get Page', id: 'wordpress_get_page' },
|
||||
{ label: 'List Pages', id: 'wordpress_list_pages' },
|
||||
// Media
|
||||
{ label: 'Upload Media', id: 'wordpress_upload_media' },
|
||||
{ label: 'Get Media', id: 'wordpress_get_media' },
|
||||
{ label: 'List Media', id: 'wordpress_list_media' },
|
||||
{ label: 'Delete Media', id: 'wordpress_delete_media' },
|
||||
// Comments
|
||||
{ label: 'Create Comment', id: 'wordpress_create_comment' },
|
||||
{ label: 'List Comments', id: 'wordpress_list_comments' },
|
||||
{ label: 'Update Comment', id: 'wordpress_update_comment' },
|
||||
{ label: 'Delete Comment', id: 'wordpress_delete_comment' },
|
||||
// Categories
|
||||
{ label: 'Create Category', id: 'wordpress_create_category' },
|
||||
{ label: 'List Categories', id: 'wordpress_list_categories' },
|
||||
// Tags
|
||||
{ label: 'Create Tag', id: 'wordpress_create_tag' },
|
||||
{ label: 'List Tags', id: 'wordpress_list_tags' },
|
||||
// Users
|
||||
{ label: 'Get Current User', id: 'wordpress_get_current_user' },
|
||||
{ label: 'List Users', id: 'wordpress_list_users' },
|
||||
{ label: 'Get User', id: 'wordpress_get_user' },
|
||||
// Search
|
||||
{ label: 'Search Content', id: 'wordpress_search_content' },
|
||||
],
|
||||
value: () => 'wordpress_create_post',
|
||||
},
|
||||
|
||||
// Credential selector for OAuth
|
||||
{
|
||||
id: 'credential',
|
||||
title: 'WordPress Account',
|
||||
type: 'oauth-input',
|
||||
serviceId: 'wordpress',
|
||||
requiredScopes: ['global'],
|
||||
placeholder: 'Select WordPress account',
|
||||
required: true,
|
||||
},
|
||||
|
||||
// Site ID for WordPress.com (required for OAuth)
|
||||
{
|
||||
id: 'siteId',
|
||||
title: 'Site ID or Domain',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., 12345678 or yoursite.wordpress.com',
|
||||
description: 'Your WordPress.com site ID or domain. Find it in Settings → General.',
|
||||
required: true,
|
||||
},
|
||||
|
||||
// Post Operations - Post ID
|
||||
{
|
||||
id: 'postId',
|
||||
title: 'Post ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter post ID',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['wordpress_update_post', 'wordpress_delete_post', 'wordpress_get_post'],
|
||||
},
|
||||
required: {
|
||||
field: 'operation',
|
||||
value: ['wordpress_update_post', 'wordpress_delete_post', 'wordpress_get_post'],
|
||||
},
|
||||
},
|
||||
|
||||
// Post/Page Title
|
||||
{
|
||||
id: 'title',
|
||||
title: 'Title',
|
||||
type: 'short-input',
|
||||
placeholder: 'Post or page title',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'wordpress_create_post',
|
||||
'wordpress_update_post',
|
||||
'wordpress_create_page',
|
||||
'wordpress_update_page',
|
||||
],
|
||||
},
|
||||
required: {
|
||||
field: 'operation',
|
||||
value: ['wordpress_create_post', 'wordpress_create_page'],
|
||||
},
|
||||
},
|
||||
|
||||
// Post/Page Content
|
||||
{
|
||||
id: 'content',
|
||||
title: 'Content',
|
||||
type: 'long-input',
|
||||
placeholder: 'Post or page content (HTML or plain text)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'wordpress_create_post',
|
||||
'wordpress_update_post',
|
||||
'wordpress_create_page',
|
||||
'wordpress_update_page',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
// Post/Page Status
|
||||
{
|
||||
id: 'status',
|
||||
title: 'Status',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Publish', id: 'publish' },
|
||||
{ label: 'Draft', id: 'draft' },
|
||||
{ label: 'Pending', id: 'pending' },
|
||||
{ label: 'Private', id: 'private' },
|
||||
],
|
||||
value: () => 'publish',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'wordpress_create_post',
|
||||
'wordpress_update_post',
|
||||
'wordpress_create_page',
|
||||
'wordpress_update_page',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
// Excerpt (for posts and pages)
|
||||
{
|
||||
id: 'excerpt',
|
||||
title: 'Excerpt',
|
||||
type: 'long-input',
|
||||
placeholder: 'Post or page excerpt',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'wordpress_create_post',
|
||||
'wordpress_update_post',
|
||||
'wordpress_create_page',
|
||||
'wordpress_update_page',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
// Slug (for posts and pages)
|
||||
{
|
||||
id: 'slug',
|
||||
title: 'Slug',
|
||||
type: 'short-input',
|
||||
placeholder: 'URL slug (optional)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'wordpress_create_post',
|
||||
'wordpress_update_post',
|
||||
'wordpress_create_page',
|
||||
'wordpress_update_page',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
// Categories (for posts only)
|
||||
{
|
||||
id: 'categories',
|
||||
title: 'Categories',
|
||||
type: 'short-input',
|
||||
placeholder: 'Comma-separated category IDs',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['wordpress_create_post', 'wordpress_update_post'],
|
||||
},
|
||||
},
|
||||
|
||||
// Tags (for posts only)
|
||||
{
|
||||
id: 'tags',
|
||||
title: 'Tags',
|
||||
type: 'short-input',
|
||||
placeholder: 'Comma-separated tag IDs',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['wordpress_create_post', 'wordpress_update_post'],
|
||||
},
|
||||
},
|
||||
|
||||
// Featured Media ID
|
||||
{
|
||||
id: 'featuredMedia',
|
||||
title: 'Featured Image ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Media ID for featured image',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'wordpress_create_post',
|
||||
'wordpress_update_post',
|
||||
'wordpress_create_page',
|
||||
'wordpress_update_page',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
// Page-specific: Page ID
|
||||
{
|
||||
id: 'pageId',
|
||||
title: 'Page ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter page ID',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['wordpress_update_page', 'wordpress_delete_page', 'wordpress_get_page'],
|
||||
},
|
||||
required: {
|
||||
field: 'operation',
|
||||
value: ['wordpress_update_page', 'wordpress_delete_page', 'wordpress_get_page'],
|
||||
},
|
||||
},
|
||||
|
||||
// Page-specific: Parent Page
|
||||
{
|
||||
id: 'parent',
|
||||
title: 'Parent Page ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Parent page ID (for hierarchy)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['wordpress_create_page', 'wordpress_update_page'],
|
||||
},
|
||||
},
|
||||
|
||||
// Page-specific: Menu Order
|
||||
{
|
||||
id: 'menuOrder',
|
||||
title: 'Menu Order',
|
||||
type: 'short-input',
|
||||
placeholder: 'Order in menu (number)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['wordpress_create_page', 'wordpress_update_page'],
|
||||
},
|
||||
},
|
||||
|
||||
// Media Operations
|
||||
{
|
||||
id: 'file',
|
||||
title: 'File',
|
||||
type: 'short-input',
|
||||
placeholder: 'Base64 encoded file data or file URL',
|
||||
condition: { field: 'operation', value: 'wordpress_upload_media' },
|
||||
required: { field: 'operation', value: 'wordpress_upload_media' },
|
||||
},
|
||||
{
|
||||
id: 'filename',
|
||||
title: 'Filename',
|
||||
type: 'short-input',
|
||||
placeholder: 'image.jpg',
|
||||
condition: { field: 'operation', value: 'wordpress_upload_media' },
|
||||
required: { field: 'operation', value: 'wordpress_upload_media' },
|
||||
},
|
||||
{
|
||||
id: 'mediaTitle',
|
||||
title: 'Media Title',
|
||||
type: 'short-input',
|
||||
placeholder: 'Title for the media',
|
||||
condition: { field: 'operation', value: 'wordpress_upload_media' },
|
||||
},
|
||||
{
|
||||
id: 'caption',
|
||||
title: 'Caption',
|
||||
type: 'short-input',
|
||||
placeholder: 'Media caption',
|
||||
condition: { field: 'operation', value: 'wordpress_upload_media' },
|
||||
},
|
||||
{
|
||||
id: 'altText',
|
||||
title: 'Alt Text',
|
||||
type: 'short-input',
|
||||
placeholder: 'Alternative text for accessibility',
|
||||
condition: { field: 'operation', value: 'wordpress_upload_media' },
|
||||
},
|
||||
{
|
||||
id: 'mediaId',
|
||||
title: 'Media ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter media ID',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['wordpress_get_media', 'wordpress_delete_media'],
|
||||
},
|
||||
required: {
|
||||
field: 'operation',
|
||||
value: ['wordpress_get_media', 'wordpress_delete_media'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'mediaType',
|
||||
title: 'Media Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'All Types', id: '' },
|
||||
{ label: 'Image', id: 'image' },
|
||||
{ label: 'Video', id: 'video' },
|
||||
{ label: 'Audio', id: 'audio' },
|
||||
{ label: 'Application', id: 'application' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'wordpress_list_media' },
|
||||
},
|
||||
|
||||
// Comment Operations
|
||||
{
|
||||
id: 'commentPostId',
|
||||
title: 'Post ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Post ID to comment on',
|
||||
condition: { field: 'operation', value: 'wordpress_create_comment' },
|
||||
required: { field: 'operation', value: 'wordpress_create_comment' },
|
||||
},
|
||||
{
|
||||
id: 'commentContent',
|
||||
title: 'Comment Content',
|
||||
type: 'long-input',
|
||||
placeholder: 'Comment text',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['wordpress_create_comment', 'wordpress_update_comment'],
|
||||
},
|
||||
required: { field: 'operation', value: 'wordpress_create_comment' },
|
||||
},
|
||||
{
|
||||
id: 'commentId',
|
||||
title: 'Comment ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter comment ID',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['wordpress_update_comment', 'wordpress_delete_comment'],
|
||||
},
|
||||
required: {
|
||||
field: 'operation',
|
||||
value: ['wordpress_update_comment', 'wordpress_delete_comment'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'commentStatus',
|
||||
title: 'Comment Status',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Approved', id: 'approved' },
|
||||
{ label: 'Hold', id: 'hold' },
|
||||
{ label: 'Spam', id: 'spam' },
|
||||
{ label: 'Trash', id: 'trash' },
|
||||
],
|
||||
value: () => 'approved',
|
||||
condition: { field: 'operation', value: 'wordpress_update_comment' },
|
||||
},
|
||||
|
||||
// Category Operations
|
||||
{
|
||||
id: 'categoryName',
|
||||
title: 'Category Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Category name',
|
||||
condition: { field: 'operation', value: 'wordpress_create_category' },
|
||||
required: { field: 'operation', value: 'wordpress_create_category' },
|
||||
},
|
||||
{
|
||||
id: 'categoryDescription',
|
||||
title: 'Description',
|
||||
type: 'long-input',
|
||||
placeholder: 'Category description',
|
||||
condition: { field: 'operation', value: 'wordpress_create_category' },
|
||||
},
|
||||
{
|
||||
id: 'categoryParent',
|
||||
title: 'Parent Category ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Parent category ID',
|
||||
condition: { field: 'operation', value: 'wordpress_create_category' },
|
||||
},
|
||||
{
|
||||
id: 'categorySlug',
|
||||
title: 'Category Slug',
|
||||
type: 'short-input',
|
||||
placeholder: 'URL slug (optional)',
|
||||
condition: { field: 'operation', value: 'wordpress_create_category' },
|
||||
},
|
||||
|
||||
// Tag Operations
|
||||
{
|
||||
id: 'tagName',
|
||||
title: 'Tag Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Tag name',
|
||||
condition: { field: 'operation', value: 'wordpress_create_tag' },
|
||||
required: { field: 'operation', value: 'wordpress_create_tag' },
|
||||
},
|
||||
{
|
||||
id: 'tagDescription',
|
||||
title: 'Description',
|
||||
type: 'long-input',
|
||||
placeholder: 'Tag description',
|
||||
condition: { field: 'operation', value: 'wordpress_create_tag' },
|
||||
},
|
||||
{
|
||||
id: 'tagSlug',
|
||||
title: 'Tag Slug',
|
||||
type: 'short-input',
|
||||
placeholder: 'URL slug (optional)',
|
||||
condition: { field: 'operation', value: 'wordpress_create_tag' },
|
||||
},
|
||||
|
||||
// User Operations
|
||||
{
|
||||
id: 'userId',
|
||||
title: 'User ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter user ID',
|
||||
condition: { field: 'operation', value: 'wordpress_get_user' },
|
||||
required: { field: 'operation', value: 'wordpress_get_user' },
|
||||
},
|
||||
{
|
||||
id: 'roles',
|
||||
title: 'User Roles',
|
||||
type: 'short-input',
|
||||
placeholder: 'Comma-separated role names (e.g., administrator, editor)',
|
||||
condition: { field: 'operation', value: 'wordpress_list_users' },
|
||||
},
|
||||
|
||||
// Search Operations
|
||||
{
|
||||
id: 'query',
|
||||
title: 'Search Query',
|
||||
type: 'short-input',
|
||||
placeholder: 'Search keywords',
|
||||
condition: { field: 'operation', value: 'wordpress_search_content' },
|
||||
required: { field: 'operation', value: 'wordpress_search_content' },
|
||||
},
|
||||
{
|
||||
id: 'searchType',
|
||||
title: 'Content Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'All Types', id: '' },
|
||||
{ label: 'Post', id: 'post' },
|
||||
{ label: 'Page', id: 'page' },
|
||||
{ label: 'Attachment', id: 'attachment' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'wordpress_search_content' },
|
||||
},
|
||||
|
||||
// List Operations - Common Parameters
|
||||
{
|
||||
id: 'perPage',
|
||||
title: 'Results Per Page',
|
||||
type: 'short-input',
|
||||
placeholder: '10 (max 100)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'wordpress_list_posts',
|
||||
'wordpress_list_pages',
|
||||
'wordpress_list_media',
|
||||
'wordpress_list_comments',
|
||||
'wordpress_list_categories',
|
||||
'wordpress_list_tags',
|
||||
'wordpress_list_users',
|
||||
'wordpress_search_content',
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'page',
|
||||
title: 'Page Number',
|
||||
type: 'short-input',
|
||||
placeholder: '1',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'wordpress_list_posts',
|
||||
'wordpress_list_pages',
|
||||
'wordpress_list_media',
|
||||
'wordpress_list_comments',
|
||||
'wordpress_list_categories',
|
||||
'wordpress_list_tags',
|
||||
'wordpress_list_users',
|
||||
'wordpress_search_content',
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'search',
|
||||
title: 'Search Filter',
|
||||
type: 'short-input',
|
||||
placeholder: 'Search term to filter results',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'wordpress_list_posts',
|
||||
'wordpress_list_pages',
|
||||
'wordpress_list_media',
|
||||
'wordpress_list_comments',
|
||||
'wordpress_list_categories',
|
||||
'wordpress_list_tags',
|
||||
'wordpress_list_users',
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'orderBy',
|
||||
title: 'Order By',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Date', id: 'date' },
|
||||
{ label: 'ID', id: 'id' },
|
||||
{ label: 'Title', id: 'title' },
|
||||
{ label: 'Slug', id: 'slug' },
|
||||
{ label: 'Modified', id: 'modified' },
|
||||
],
|
||||
value: () => 'date',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'wordpress_list_posts',
|
||||
'wordpress_list_pages',
|
||||
'wordpress_list_media',
|
||||
'wordpress_list_comments',
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'order',
|
||||
title: 'Order',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Descending', id: 'desc' },
|
||||
{ label: 'Ascending', id: 'asc' },
|
||||
],
|
||||
value: () => 'desc',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'wordpress_list_posts',
|
||||
'wordpress_list_pages',
|
||||
'wordpress_list_media',
|
||||
'wordpress_list_comments',
|
||||
'wordpress_list_categories',
|
||||
'wordpress_list_tags',
|
||||
'wordpress_list_users',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
// List Posts - Status filter
|
||||
{
|
||||
id: 'listStatus',
|
||||
title: 'Status Filter',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'All', id: '' },
|
||||
{ label: 'Published', id: 'publish' },
|
||||
{ label: 'Draft', id: 'draft' },
|
||||
{ label: 'Pending', id: 'pending' },
|
||||
{ label: 'Private', id: 'private' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['wordpress_list_posts', 'wordpress_list_pages'],
|
||||
},
|
||||
},
|
||||
|
||||
// Delete Operations - Force delete
|
||||
{
|
||||
id: 'force',
|
||||
title: 'Force Delete',
|
||||
type: 'switch',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'wordpress_delete_post',
|
||||
'wordpress_delete_page',
|
||||
'wordpress_delete_media',
|
||||
'wordpress_delete_comment',
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: [
|
||||
'wordpress_create_post',
|
||||
'wordpress_update_post',
|
||||
'wordpress_delete_post',
|
||||
'wordpress_get_post',
|
||||
'wordpress_list_posts',
|
||||
'wordpress_create_page',
|
||||
'wordpress_update_page',
|
||||
'wordpress_delete_page',
|
||||
'wordpress_get_page',
|
||||
'wordpress_list_pages',
|
||||
'wordpress_upload_media',
|
||||
'wordpress_get_media',
|
||||
'wordpress_list_media',
|
||||
'wordpress_delete_media',
|
||||
'wordpress_create_comment',
|
||||
'wordpress_list_comments',
|
||||
'wordpress_update_comment',
|
||||
'wordpress_delete_comment',
|
||||
'wordpress_create_category',
|
||||
'wordpress_list_categories',
|
||||
'wordpress_create_tag',
|
||||
'wordpress_list_tags',
|
||||
'wordpress_get_current_user',
|
||||
'wordpress_list_users',
|
||||
'wordpress_get_user',
|
||||
'wordpress_search_content',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => params.operation || 'wordpress_create_post',
|
||||
params: (params) => {
|
||||
// OAuth authentication for WordPress.com
|
||||
const baseParams: Record<string, any> = {
|
||||
credential: params.credential,
|
||||
siteId: params.siteId,
|
||||
}
|
||||
|
||||
switch (params.operation) {
|
||||
case 'wordpress_create_post':
|
||||
return {
|
||||
...baseParams,
|
||||
title: params.title,
|
||||
content: params.content,
|
||||
status: params.status,
|
||||
excerpt: params.excerpt,
|
||||
slug: params.slug,
|
||||
categories: params.categories,
|
||||
tags: params.tags,
|
||||
featuredMedia: params.featuredMedia ? Number(params.featuredMedia) : undefined,
|
||||
}
|
||||
case 'wordpress_update_post':
|
||||
return {
|
||||
...baseParams,
|
||||
postId: Number(params.postId),
|
||||
title: params.title,
|
||||
content: params.content,
|
||||
status: params.status,
|
||||
excerpt: params.excerpt,
|
||||
slug: params.slug,
|
||||
categories: params.categories,
|
||||
tags: params.tags,
|
||||
featuredMedia: params.featuredMedia ? Number(params.featuredMedia) : undefined,
|
||||
}
|
||||
case 'wordpress_delete_post':
|
||||
return {
|
||||
...baseParams,
|
||||
postId: Number(params.postId),
|
||||
force: params.force,
|
||||
}
|
||||
case 'wordpress_get_post':
|
||||
return {
|
||||
...baseParams,
|
||||
postId: Number(params.postId),
|
||||
}
|
||||
case 'wordpress_list_posts':
|
||||
return {
|
||||
...baseParams,
|
||||
perPage: params.perPage ? Number(params.perPage) : undefined,
|
||||
page: params.page ? Number(params.page) : undefined,
|
||||
status: params.listStatus || undefined,
|
||||
search: params.search,
|
||||
orderBy: params.orderBy,
|
||||
order: params.order,
|
||||
categories: params.categories,
|
||||
tags: params.tags,
|
||||
}
|
||||
case 'wordpress_create_page':
|
||||
return {
|
||||
...baseParams,
|
||||
title: params.title,
|
||||
content: params.content,
|
||||
status: params.status,
|
||||
excerpt: params.excerpt,
|
||||
slug: params.slug,
|
||||
parent: params.parent ? Number(params.parent) : undefined,
|
||||
menuOrder: params.menuOrder ? Number(params.menuOrder) : undefined,
|
||||
featuredMedia: params.featuredMedia ? Number(params.featuredMedia) : undefined,
|
||||
}
|
||||
case 'wordpress_update_page':
|
||||
return {
|
||||
...baseParams,
|
||||
pageId: Number(params.pageId),
|
||||
title: params.title,
|
||||
content: params.content,
|
||||
status: params.status,
|
||||
excerpt: params.excerpt,
|
||||
slug: params.slug,
|
||||
parent: params.parent ? Number(params.parent) : undefined,
|
||||
menuOrder: params.menuOrder ? Number(params.menuOrder) : undefined,
|
||||
featuredMedia: params.featuredMedia ? Number(params.featuredMedia) : undefined,
|
||||
}
|
||||
case 'wordpress_delete_page':
|
||||
return {
|
||||
...baseParams,
|
||||
pageId: Number(params.pageId),
|
||||
force: params.force,
|
||||
}
|
||||
case 'wordpress_get_page':
|
||||
return {
|
||||
...baseParams,
|
||||
pageId: Number(params.pageId),
|
||||
}
|
||||
case 'wordpress_list_pages':
|
||||
return {
|
||||
...baseParams,
|
||||
perPage: params.perPage ? Number(params.perPage) : undefined,
|
||||
page: params.page ? Number(params.page) : undefined,
|
||||
status: params.listStatus || undefined,
|
||||
search: params.search,
|
||||
orderBy: params.orderBy,
|
||||
order: params.order,
|
||||
parent: params.parent ? Number(params.parent) : undefined,
|
||||
}
|
||||
case 'wordpress_upload_media':
|
||||
return {
|
||||
...baseParams,
|
||||
file: params.file,
|
||||
filename: params.filename,
|
||||
title: params.mediaTitle,
|
||||
caption: params.caption,
|
||||
altText: params.altText,
|
||||
}
|
||||
case 'wordpress_get_media':
|
||||
return {
|
||||
...baseParams,
|
||||
mediaId: Number(params.mediaId),
|
||||
}
|
||||
case 'wordpress_list_media':
|
||||
return {
|
||||
...baseParams,
|
||||
perPage: params.perPage ? Number(params.perPage) : undefined,
|
||||
page: params.page ? Number(params.page) : undefined,
|
||||
search: params.search,
|
||||
mediaType: params.mediaType || undefined,
|
||||
orderBy: params.orderBy,
|
||||
order: params.order,
|
||||
}
|
||||
case 'wordpress_delete_media':
|
||||
return {
|
||||
...baseParams,
|
||||
mediaId: Number(params.mediaId),
|
||||
force: params.force,
|
||||
}
|
||||
case 'wordpress_create_comment':
|
||||
return {
|
||||
...baseParams,
|
||||
postId: Number(params.commentPostId),
|
||||
content: params.commentContent,
|
||||
}
|
||||
case 'wordpress_list_comments':
|
||||
return {
|
||||
...baseParams,
|
||||
perPage: params.perPage ? Number(params.perPage) : undefined,
|
||||
page: params.page ? Number(params.page) : undefined,
|
||||
postId: params.commentPostId ? Number(params.commentPostId) : undefined,
|
||||
search: params.search,
|
||||
orderBy: params.orderBy,
|
||||
order: params.order,
|
||||
}
|
||||
case 'wordpress_update_comment':
|
||||
return {
|
||||
...baseParams,
|
||||
commentId: Number(params.commentId),
|
||||
content: params.commentContent,
|
||||
status: params.commentStatus,
|
||||
}
|
||||
case 'wordpress_delete_comment':
|
||||
return {
|
||||
...baseParams,
|
||||
commentId: Number(params.commentId),
|
||||
force: params.force,
|
||||
}
|
||||
case 'wordpress_create_category':
|
||||
return {
|
||||
...baseParams,
|
||||
name: params.categoryName,
|
||||
description: params.categoryDescription,
|
||||
parent: params.categoryParent ? Number(params.categoryParent) : undefined,
|
||||
slug: params.categorySlug,
|
||||
}
|
||||
case 'wordpress_list_categories':
|
||||
return {
|
||||
...baseParams,
|
||||
perPage: params.perPage ? Number(params.perPage) : undefined,
|
||||
page: params.page ? Number(params.page) : undefined,
|
||||
search: params.search,
|
||||
order: params.order,
|
||||
}
|
||||
case 'wordpress_create_tag':
|
||||
return {
|
||||
...baseParams,
|
||||
name: params.tagName,
|
||||
description: params.tagDescription,
|
||||
slug: params.tagSlug,
|
||||
}
|
||||
case 'wordpress_list_tags':
|
||||
return {
|
||||
...baseParams,
|
||||
perPage: params.perPage ? Number(params.perPage) : undefined,
|
||||
page: params.page ? Number(params.page) : undefined,
|
||||
search: params.search,
|
||||
order: params.order,
|
||||
}
|
||||
case 'wordpress_get_current_user':
|
||||
return baseParams
|
||||
case 'wordpress_list_users':
|
||||
return {
|
||||
...baseParams,
|
||||
perPage: params.perPage ? Number(params.perPage) : undefined,
|
||||
page: params.page ? Number(params.page) : undefined,
|
||||
search: params.search,
|
||||
roles: params.roles,
|
||||
order: params.order,
|
||||
}
|
||||
case 'wordpress_get_user':
|
||||
return {
|
||||
...baseParams,
|
||||
userId: Number(params.userId),
|
||||
}
|
||||
case 'wordpress_search_content':
|
||||
return {
|
||||
...baseParams,
|
||||
query: params.query,
|
||||
perPage: params.perPage ? Number(params.perPage) : undefined,
|
||||
page: params.page ? Number(params.page) : undefined,
|
||||
type: params.searchType || undefined,
|
||||
}
|
||||
default:
|
||||
return baseParams
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'Operation to perform' },
|
||||
siteId: { type: 'string', description: 'WordPress.com site ID or domain' },
|
||||
// Post inputs
|
||||
postId: { type: 'number', description: 'Post ID' },
|
||||
title: { type: 'string', description: 'Post or page title' },
|
||||
content: { type: 'string', description: 'Post or page content' },
|
||||
status: { type: 'string', description: 'Post or page status' },
|
||||
excerpt: { type: 'string', description: 'Post or page excerpt' },
|
||||
slug: { type: 'string', description: 'URL slug' },
|
||||
categories: { type: 'string', description: 'Category IDs (comma-separated)' },
|
||||
tags: { type: 'string', description: 'Tag IDs (comma-separated)' },
|
||||
featuredMedia: { type: 'number', description: 'Featured media ID' },
|
||||
// Page inputs
|
||||
pageId: { type: 'number', description: 'Page ID' },
|
||||
parent: { type: 'number', description: 'Parent page ID' },
|
||||
menuOrder: { type: 'number', description: 'Menu order' },
|
||||
// Media inputs
|
||||
file: { type: 'string', description: 'File data (base64) or URL' },
|
||||
filename: { type: 'string', description: 'Filename with extension' },
|
||||
mediaTitle: { type: 'string', description: 'Media title' },
|
||||
caption: { type: 'string', description: 'Media caption' },
|
||||
altText: { type: 'string', description: 'Alt text' },
|
||||
mediaId: { type: 'number', description: 'Media ID' },
|
||||
mediaType: { type: 'string', description: 'Media type filter' },
|
||||
// Comment inputs
|
||||
commentPostId: { type: 'number', description: 'Post ID for comment' },
|
||||
commentContent: { type: 'string', description: 'Comment content' },
|
||||
commentId: { type: 'number', description: 'Comment ID' },
|
||||
commentStatus: { type: 'string', description: 'Comment status' },
|
||||
// Category inputs
|
||||
categoryName: { type: 'string', description: 'Category name' },
|
||||
categoryDescription: { type: 'string', description: 'Category description' },
|
||||
categoryParent: { type: 'number', description: 'Parent category ID' },
|
||||
categorySlug: { type: 'string', description: 'Category slug' },
|
||||
// Tag inputs
|
||||
tagName: { type: 'string', description: 'Tag name' },
|
||||
tagDescription: { type: 'string', description: 'Tag description' },
|
||||
tagSlug: { type: 'string', description: 'Tag slug' },
|
||||
// User inputs
|
||||
userId: { type: 'number', description: 'User ID' },
|
||||
roles: { type: 'string', description: 'User roles filter' },
|
||||
// Search inputs
|
||||
query: { type: 'string', description: 'Search query' },
|
||||
searchType: { type: 'string', description: 'Content type filter' },
|
||||
// List inputs
|
||||
perPage: { type: 'number', description: 'Results per page' },
|
||||
page: { type: 'number', description: 'Page number' },
|
||||
search: { type: 'string', description: 'Search filter' },
|
||||
orderBy: { type: 'string', description: 'Order by field' },
|
||||
order: { type: 'string', description: 'Order direction' },
|
||||
listStatus: { type: 'string', description: 'Status filter' },
|
||||
force: { type: 'boolean', description: 'Force delete' },
|
||||
hideEmpty: { type: 'boolean', description: 'Hide empty taxonomies' },
|
||||
},
|
||||
outputs: {
|
||||
// Post outputs
|
||||
post: { type: 'json', description: 'Post data' },
|
||||
posts: { type: 'json', description: 'List of posts' },
|
||||
// Page outputs
|
||||
page: { type: 'json', description: 'Page data' },
|
||||
pages: { type: 'json', description: 'List of pages' },
|
||||
// Media outputs
|
||||
media: { type: 'json', description: 'Media data' },
|
||||
// Comment outputs
|
||||
comment: { type: 'json', description: 'Comment data' },
|
||||
comments: { type: 'json', description: 'List of comments' },
|
||||
// Category outputs
|
||||
category: { type: 'json', description: 'Category data' },
|
||||
categories: { type: 'json', description: 'List of categories' },
|
||||
// Tag outputs
|
||||
tag: { type: 'json', description: 'Tag data' },
|
||||
// User outputs
|
||||
user: { type: 'json', description: 'User data' },
|
||||
users: { type: 'json', description: 'List of users' },
|
||||
// Search outputs
|
||||
results: { type: 'json', description: 'Search results' },
|
||||
// Common outputs
|
||||
deleted: { type: 'boolean', description: 'Deletion status' },
|
||||
total: { type: 'number', description: 'Total count' },
|
||||
totalPages: { type: 'number', description: 'Total pages' },
|
||||
},
|
||||
}
|
||||
572
apps/sim/blocks/blocks/zoom.ts
Normal file
572
apps/sim/blocks/blocks/zoom.ts
Normal file
@@ -0,0 +1,572 @@
|
||||
import { ZoomIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import type { ZoomResponse } from '@/tools/zoom/types'
|
||||
|
||||
export const ZoomBlock: BlockConfig<ZoomResponse> = {
|
||||
type: 'zoom',
|
||||
name: 'Zoom',
|
||||
description: 'Create and manage Zoom meetings and recordings',
|
||||
authMode: AuthMode.OAuth,
|
||||
longDescription:
|
||||
'Integrate Zoom into workflows. Create, list, update, and delete Zoom meetings. Get meeting details, invitations, recordings, and participants. Manage cloud recordings programmatically.',
|
||||
docsLink: 'https://docs.sim.ai/tools/zoom',
|
||||
category: 'tools',
|
||||
bgColor: '#2D8CFF',
|
||||
icon: ZoomIcon,
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Create Meeting', id: 'zoom_create_meeting' },
|
||||
{ label: 'List Meetings', id: 'zoom_list_meetings' },
|
||||
{ label: 'Get Meeting', id: 'zoom_get_meeting' },
|
||||
{ label: 'Update Meeting', id: 'zoom_update_meeting' },
|
||||
{ label: 'Delete Meeting', id: 'zoom_delete_meeting' },
|
||||
{ label: 'Get Meeting Invitation', id: 'zoom_get_meeting_invitation' },
|
||||
{ label: 'List Recordings', id: 'zoom_list_recordings' },
|
||||
{ label: 'Get Meeting Recordings', id: 'zoom_get_meeting_recordings' },
|
||||
{ label: 'Delete Recording', id: 'zoom_delete_recording' },
|
||||
{ label: 'List Past Participants', id: 'zoom_list_past_participants' },
|
||||
],
|
||||
value: () => 'zoom_create_meeting',
|
||||
},
|
||||
{
|
||||
id: 'credential',
|
||||
title: 'Zoom Account',
|
||||
type: 'oauth-input',
|
||||
serviceId: 'zoom',
|
||||
requiredScopes: [
|
||||
'user:read:user',
|
||||
'meeting:write:meeting',
|
||||
'meeting:read:meeting',
|
||||
'meeting:read:list_meetings',
|
||||
'meeting:update:meeting',
|
||||
'meeting:delete:meeting',
|
||||
'meeting:read:invitation',
|
||||
'meeting:read:list_past_participants',
|
||||
'cloud_recording:read:list_user_recordings',
|
||||
'cloud_recording:read:list_recording_files',
|
||||
'cloud_recording:delete:recording_file',
|
||||
],
|
||||
placeholder: 'Select Zoom account',
|
||||
required: true,
|
||||
},
|
||||
// User ID for create/list operations
|
||||
{
|
||||
id: 'userId',
|
||||
title: 'User ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'me (or user ID/email)',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['zoom_create_meeting', 'zoom_list_meetings', 'zoom_list_recordings'],
|
||||
},
|
||||
},
|
||||
// Meeting ID for get/update/delete/invitation/recordings/participants operations
|
||||
{
|
||||
id: 'meetingId',
|
||||
title: 'Meeting ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter meeting ID',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'zoom_get_meeting',
|
||||
'zoom_update_meeting',
|
||||
'zoom_delete_meeting',
|
||||
'zoom_get_meeting_invitation',
|
||||
'zoom_get_meeting_recordings',
|
||||
'zoom_delete_recording',
|
||||
'zoom_list_past_participants',
|
||||
],
|
||||
},
|
||||
},
|
||||
// Topic for create/update
|
||||
{
|
||||
id: 'topic',
|
||||
title: 'Topic',
|
||||
type: 'short-input',
|
||||
placeholder: 'Meeting topic',
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['zoom_create_meeting'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'topicUpdate',
|
||||
title: 'Topic',
|
||||
type: 'short-input',
|
||||
placeholder: 'Meeting topic (optional)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['zoom_update_meeting'],
|
||||
},
|
||||
},
|
||||
// Meeting type
|
||||
{
|
||||
id: 'type',
|
||||
title: 'Meeting Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Scheduled', id: '2' },
|
||||
{ label: 'Instant', id: '1' },
|
||||
{ label: 'Recurring (no fixed time)', id: '3' },
|
||||
{ label: 'Recurring (fixed time)', id: '8' },
|
||||
],
|
||||
value: () => '2',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['zoom_create_meeting', 'zoom_update_meeting'],
|
||||
},
|
||||
},
|
||||
// Start time
|
||||
{
|
||||
id: 'startTime',
|
||||
title: 'Start Time',
|
||||
type: 'short-input',
|
||||
placeholder: '2025-06-03T10:00:00Z',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['zoom_create_meeting', 'zoom_update_meeting'],
|
||||
},
|
||||
},
|
||||
// Duration
|
||||
{
|
||||
id: 'duration',
|
||||
title: 'Duration (minutes)',
|
||||
type: 'short-input',
|
||||
placeholder: '30',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['zoom_create_meeting', 'zoom_update_meeting'],
|
||||
},
|
||||
},
|
||||
// Timezone
|
||||
{
|
||||
id: 'timezone',
|
||||
title: 'Timezone',
|
||||
type: 'short-input',
|
||||
placeholder: 'America/Los_Angeles',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['zoom_create_meeting', 'zoom_update_meeting'],
|
||||
},
|
||||
},
|
||||
// Password
|
||||
{
|
||||
id: 'password',
|
||||
title: 'Password',
|
||||
type: 'short-input',
|
||||
placeholder: 'Meeting password',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['zoom_create_meeting', 'zoom_update_meeting'],
|
||||
},
|
||||
},
|
||||
// Agenda
|
||||
{
|
||||
id: 'agenda',
|
||||
title: 'Agenda',
|
||||
type: 'long-input',
|
||||
placeholder: 'Meeting agenda',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['zoom_create_meeting', 'zoom_update_meeting'],
|
||||
},
|
||||
},
|
||||
// Meeting settings
|
||||
{
|
||||
id: 'hostVideo',
|
||||
title: 'Host Video',
|
||||
type: 'switch',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['zoom_create_meeting', 'zoom_update_meeting'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'participantVideo',
|
||||
title: 'Participant Video',
|
||||
type: 'switch',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['zoom_create_meeting', 'zoom_update_meeting'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'joinBeforeHost',
|
||||
title: 'Join Before Host',
|
||||
type: 'switch',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['zoom_create_meeting', 'zoom_update_meeting'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'muteUponEntry',
|
||||
title: 'Mute Upon Entry',
|
||||
type: 'switch',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['zoom_create_meeting', 'zoom_update_meeting'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'waitingRoom',
|
||||
title: 'Waiting Room',
|
||||
type: 'switch',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['zoom_create_meeting', 'zoom_update_meeting'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'autoRecording',
|
||||
title: 'Auto Recording',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'None', id: 'none' },
|
||||
{ label: 'Local', id: 'local' },
|
||||
{ label: 'Cloud', id: 'cloud' },
|
||||
],
|
||||
value: () => 'none',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['zoom_create_meeting', 'zoom_update_meeting'],
|
||||
},
|
||||
},
|
||||
// List meetings filter
|
||||
{
|
||||
id: 'listType',
|
||||
title: 'Meeting Type Filter',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Scheduled', id: 'scheduled' },
|
||||
{ label: 'Live', id: 'live' },
|
||||
{ label: 'Upcoming', id: 'upcoming' },
|
||||
{ label: 'Upcoming Meetings', id: 'upcoming_meetings' },
|
||||
{ label: 'Previous Meetings', id: 'previous_meetings' },
|
||||
],
|
||||
value: () => 'scheduled',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['zoom_list_meetings'],
|
||||
},
|
||||
},
|
||||
// Pagination
|
||||
{
|
||||
id: 'pageSize',
|
||||
title: 'Page Size',
|
||||
type: 'short-input',
|
||||
placeholder: 'Number of results (max 300)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['zoom_list_meetings', 'zoom_list_recordings', 'zoom_list_past_participants'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'nextPageToken',
|
||||
title: 'Page Token',
|
||||
type: 'short-input',
|
||||
placeholder: 'Token for next page',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['zoom_list_meetings', 'zoom_list_recordings', 'zoom_list_past_participants'],
|
||||
},
|
||||
},
|
||||
// Recording date range
|
||||
{
|
||||
id: 'fromDate',
|
||||
title: 'From Date',
|
||||
type: 'short-input',
|
||||
placeholder: 'yyyy-mm-dd (within last 6 months)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['zoom_list_recordings'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'toDate',
|
||||
title: 'To Date',
|
||||
type: 'short-input',
|
||||
placeholder: 'yyyy-mm-dd',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['zoom_list_recordings'],
|
||||
},
|
||||
},
|
||||
// Recording ID for delete
|
||||
{
|
||||
id: 'recordingId',
|
||||
title: 'Recording ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Specific recording file ID (optional)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['zoom_delete_recording'],
|
||||
},
|
||||
},
|
||||
// Delete action
|
||||
{
|
||||
id: 'deleteAction',
|
||||
title: 'Delete Action',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Move to Trash', id: 'trash' },
|
||||
{ label: 'Permanently Delete', id: 'delete' },
|
||||
],
|
||||
value: () => 'trash',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['zoom_delete_recording'],
|
||||
},
|
||||
},
|
||||
// Delete options
|
||||
{
|
||||
id: 'occurrenceId',
|
||||
title: 'Occurrence ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'For recurring meetings',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['zoom_get_meeting', 'zoom_delete_meeting'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'cancelMeetingReminder',
|
||||
title: 'Send Cancellation Email',
|
||||
type: 'switch',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['zoom_delete_meeting'],
|
||||
},
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: [
|
||||
'zoom_create_meeting',
|
||||
'zoom_list_meetings',
|
||||
'zoom_get_meeting',
|
||||
'zoom_update_meeting',
|
||||
'zoom_delete_meeting',
|
||||
'zoom_get_meeting_invitation',
|
||||
'zoom_list_recordings',
|
||||
'zoom_get_meeting_recordings',
|
||||
'zoom_delete_recording',
|
||||
'zoom_list_past_participants',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => {
|
||||
return params.operation || 'zoom_create_meeting'
|
||||
},
|
||||
params: (params) => {
|
||||
const baseParams: Record<string, any> = {
|
||||
credential: params.credential,
|
||||
}
|
||||
|
||||
switch (params.operation) {
|
||||
case 'zoom_create_meeting':
|
||||
if (!params.userId?.trim()) {
|
||||
throw new Error('User ID is required.')
|
||||
}
|
||||
if (!params.topic?.trim()) {
|
||||
throw new Error('Topic is required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
userId: params.userId.trim(),
|
||||
topic: params.topic.trim(),
|
||||
type: params.type ? Number(params.type) : 2,
|
||||
startTime: params.startTime,
|
||||
duration: params.duration ? Number(params.duration) : undefined,
|
||||
timezone: params.timezone,
|
||||
password: params.password,
|
||||
agenda: params.agenda,
|
||||
hostVideo: params.hostVideo,
|
||||
participantVideo: params.participantVideo,
|
||||
joinBeforeHost: params.joinBeforeHost,
|
||||
muteUponEntry: params.muteUponEntry,
|
||||
waitingRoom: params.waitingRoom,
|
||||
autoRecording: params.autoRecording !== 'none' ? params.autoRecording : undefined,
|
||||
}
|
||||
|
||||
case 'zoom_list_meetings':
|
||||
if (!params.userId?.trim()) {
|
||||
throw new Error('User ID is required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
userId: params.userId.trim(),
|
||||
type: params.listType,
|
||||
pageSize: params.pageSize ? Number(params.pageSize) : undefined,
|
||||
nextPageToken: params.nextPageToken,
|
||||
}
|
||||
|
||||
case 'zoom_get_meeting':
|
||||
if (!params.meetingId?.trim()) {
|
||||
throw new Error('Meeting ID is required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
meetingId: params.meetingId.trim(),
|
||||
occurrenceId: params.occurrenceId,
|
||||
}
|
||||
|
||||
case 'zoom_update_meeting':
|
||||
if (!params.meetingId?.trim()) {
|
||||
throw new Error('Meeting ID is required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
meetingId: params.meetingId.trim(),
|
||||
topic: params.topicUpdate,
|
||||
type: params.type ? Number(params.type) : undefined,
|
||||
startTime: params.startTime,
|
||||
duration: params.duration ? Number(params.duration) : undefined,
|
||||
timezone: params.timezone,
|
||||
password: params.password,
|
||||
agenda: params.agenda,
|
||||
hostVideo: params.hostVideo,
|
||||
participantVideo: params.participantVideo,
|
||||
joinBeforeHost: params.joinBeforeHost,
|
||||
muteUponEntry: params.muteUponEntry,
|
||||
waitingRoom: params.waitingRoom,
|
||||
autoRecording: params.autoRecording !== 'none' ? params.autoRecording : undefined,
|
||||
}
|
||||
|
||||
case 'zoom_delete_meeting':
|
||||
if (!params.meetingId?.trim()) {
|
||||
throw new Error('Meeting ID is required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
meetingId: params.meetingId.trim(),
|
||||
occurrenceId: params.occurrenceId,
|
||||
cancelMeetingReminder: params.cancelMeetingReminder,
|
||||
}
|
||||
|
||||
case 'zoom_get_meeting_invitation':
|
||||
if (!params.meetingId?.trim()) {
|
||||
throw new Error('Meeting ID is required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
meetingId: params.meetingId.trim(),
|
||||
}
|
||||
|
||||
case 'zoom_list_recordings':
|
||||
if (!params.userId?.trim()) {
|
||||
throw new Error('User ID is required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
userId: params.userId.trim(),
|
||||
from: params.fromDate,
|
||||
to: params.toDate,
|
||||
pageSize: params.pageSize ? Number(params.pageSize) : undefined,
|
||||
nextPageToken: params.nextPageToken,
|
||||
}
|
||||
|
||||
case 'zoom_get_meeting_recordings':
|
||||
if (!params.meetingId?.trim()) {
|
||||
throw new Error('Meeting ID is required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
meetingId: params.meetingId.trim(),
|
||||
}
|
||||
|
||||
case 'zoom_delete_recording':
|
||||
if (!params.meetingId?.trim()) {
|
||||
throw new Error('Meeting ID is required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
meetingId: params.meetingId.trim(),
|
||||
recordingId: params.recordingId,
|
||||
action: params.deleteAction,
|
||||
}
|
||||
|
||||
case 'zoom_list_past_participants':
|
||||
if (!params.meetingId?.trim()) {
|
||||
throw new Error('Meeting ID is required.')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
meetingId: params.meetingId.trim(),
|
||||
pageSize: params.pageSize ? Number(params.pageSize) : undefined,
|
||||
nextPageToken: params.nextPageToken,
|
||||
}
|
||||
|
||||
default:
|
||||
return baseParams
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'Operation to perform' },
|
||||
credential: { type: 'string', description: 'Zoom access token' },
|
||||
userId: { type: 'string', description: 'User ID or email (use "me" for authenticated user)' },
|
||||
meetingId: { type: 'string', description: 'Meeting ID' },
|
||||
topic: { type: 'string', description: 'Meeting topic' },
|
||||
topicUpdate: { type: 'string', description: 'Meeting topic for update' },
|
||||
type: { type: 'string', description: 'Meeting type' },
|
||||
startTime: { type: 'string', description: 'Start time in ISO 8601 format' },
|
||||
duration: { type: 'string', description: 'Duration in minutes' },
|
||||
timezone: { type: 'string', description: 'Timezone' },
|
||||
password: { type: 'string', description: 'Meeting password' },
|
||||
agenda: { type: 'string', description: 'Meeting agenda' },
|
||||
hostVideo: { type: 'boolean', description: 'Host video on' },
|
||||
participantVideo: { type: 'boolean', description: 'Participant video on' },
|
||||
joinBeforeHost: { type: 'boolean', description: 'Allow join before host' },
|
||||
muteUponEntry: { type: 'boolean', description: 'Mute upon entry' },
|
||||
waitingRoom: { type: 'boolean', description: 'Enable waiting room' },
|
||||
autoRecording: { type: 'string', description: 'Auto recording setting' },
|
||||
listType: { type: 'string', description: 'Meeting type filter for list' },
|
||||
pageSize: { type: 'string', description: 'Page size for list' },
|
||||
nextPageToken: { type: 'string', description: 'Page token for pagination' },
|
||||
occurrenceId: { type: 'string', description: 'Occurrence ID for recurring meetings' },
|
||||
cancelMeetingReminder: { type: 'boolean', description: 'Send cancellation email' },
|
||||
fromDate: { type: 'string', description: 'Start date for recordings list (yyyy-mm-dd)' },
|
||||
toDate: { type: 'string', description: 'End date for recordings list (yyyy-mm-dd)' },
|
||||
recordingId: { type: 'string', description: 'Specific recording file ID' },
|
||||
deleteAction: { type: 'string', description: 'Delete action (trash or delete)' },
|
||||
},
|
||||
outputs: {
|
||||
// Meeting outputs
|
||||
meeting: { type: 'json', description: 'Meeting data' },
|
||||
meetings: { type: 'json', description: 'List of meetings' },
|
||||
// Specific meeting fields
|
||||
id: { type: 'number', description: 'Meeting ID' },
|
||||
uuid: { type: 'string', description: 'Meeting UUID' },
|
||||
topic: { type: 'string', description: 'Meeting topic' },
|
||||
join_url: { type: 'string', description: 'Join URL for participants' },
|
||||
start_url: { type: 'string', description: 'Start URL for host' },
|
||||
start_time: { type: 'string', description: 'Start time' },
|
||||
duration: { type: 'number', description: 'Duration in minutes' },
|
||||
timezone: { type: 'string', description: 'Timezone' },
|
||||
password: { type: 'string', description: 'Meeting password' },
|
||||
agenda: { type: 'string', description: 'Meeting agenda' },
|
||||
settings: { type: 'json', description: 'Meeting settings' },
|
||||
// Invitation
|
||||
invitation: { type: 'string', description: 'Meeting invitation text' },
|
||||
// Recording outputs
|
||||
recording: { type: 'json', description: 'Recording data' },
|
||||
recordings: { type: 'json', description: 'List of recordings' },
|
||||
recording_files: { type: 'json', description: 'Recording files' },
|
||||
share_url: { type: 'string', description: 'Share URL for recording' },
|
||||
// Participant outputs
|
||||
participants: { type: 'json', description: 'List of participants' },
|
||||
// Pagination
|
||||
pageInfo: { type: 'json', description: 'Pagination information' },
|
||||
// Success indicator
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { AgentBlock } from '@/blocks/blocks/agent'
|
||||
import { AhrefsBlock } from '@/blocks/blocks/ahrefs'
|
||||
import { AirtableBlock } from '@/blocks/blocks/airtable'
|
||||
import { ApiBlock } from '@/blocks/blocks/api'
|
||||
import { ApiTriggerBlock } from '@/blocks/blocks/api_trigger'
|
||||
@@ -6,14 +7,18 @@ import { ApifyBlock } from '@/blocks/blocks/apify'
|
||||
import { ApolloBlock } from '@/blocks/blocks/apollo'
|
||||
import { ArxivBlock } from '@/blocks/blocks/arxiv'
|
||||
import { AsanaBlock } from '@/blocks/blocks/asana'
|
||||
// import { BoxBlock } from '@/blocks/blocks/box' // TODO: Box OAuth integration
|
||||
import { BrowserUseBlock } from '@/blocks/blocks/browser_use'
|
||||
import { CalendlyBlock } from '@/blocks/blocks/calendly'
|
||||
import { ChatTriggerBlock } from '@/blocks/blocks/chat_trigger'
|
||||
import { ClayBlock } from '@/blocks/blocks/clay'
|
||||
import { ConditionBlock } from '@/blocks/blocks/condition'
|
||||
import { ConfluenceBlock } from '@/blocks/blocks/confluence'
|
||||
import { DatadogBlock } from '@/blocks/blocks/datadog'
|
||||
import { DiscordBlock } from '@/blocks/blocks/discord'
|
||||
import { DropboxBlock } from '@/blocks/blocks/dropbox'
|
||||
import { DynamoDBBlock } from '@/blocks/blocks/dynamodb'
|
||||
import { ElasticsearchBlock } from '@/blocks/blocks/elasticsearch'
|
||||
import { ElevenLabsBlock } from '@/blocks/blocks/elevenlabs'
|
||||
import { EvaluatorBlock } from '@/blocks/blocks/evaluator'
|
||||
import { ExaBlock } from '@/blocks/blocks/exa'
|
||||
@@ -22,6 +27,7 @@ import { FirecrawlBlock } from '@/blocks/blocks/firecrawl'
|
||||
import { FunctionBlock } from '@/blocks/blocks/function'
|
||||
import { GenericWebhookBlock } from '@/blocks/blocks/generic_webhook'
|
||||
import { GitHubBlock } from '@/blocks/blocks/github'
|
||||
import { GitLabBlock } from '@/blocks/blocks/gitlab'
|
||||
import { GmailBlock } from '@/blocks/blocks/gmail'
|
||||
import { GoogleSearchBlock } from '@/blocks/blocks/google'
|
||||
import { GoogleCalendarBlock } from '@/blocks/blocks/google_calendar'
|
||||
@@ -30,6 +36,7 @@ import { GoogleDriveBlock } from '@/blocks/blocks/google_drive'
|
||||
import { GoogleFormsBlock } from '@/blocks/blocks/google_form'
|
||||
import { GoogleSheetsBlock } from '@/blocks/blocks/google_sheets'
|
||||
import { GoogleVaultBlock } from '@/blocks/blocks/google_vault'
|
||||
import { GrafanaBlock } from '@/blocks/blocks/grafana'
|
||||
import { GuardrailsBlock } from '@/blocks/blocks/guardrails'
|
||||
import { HubSpotBlock } from '@/blocks/blocks/hubspot'
|
||||
import { HuggingFaceBlock } from '@/blocks/blocks/huggingface'
|
||||
@@ -41,6 +48,7 @@ import { InputTriggerBlock } from '@/blocks/blocks/input_trigger'
|
||||
import { IntercomBlock } from '@/blocks/blocks/intercom'
|
||||
import { JinaBlock } from '@/blocks/blocks/jina'
|
||||
import { JiraBlock } from '@/blocks/blocks/jira'
|
||||
import { KalshiBlock } from '@/blocks/blocks/kalshi'
|
||||
import { KnowledgeBlock } from '@/blocks/blocks/knowledge'
|
||||
import { LinearBlock } from '@/blocks/blocks/linear'
|
||||
import { LinkedInBlock } from '@/blocks/blocks/linkedin'
|
||||
@@ -67,6 +75,7 @@ import { ParallelBlock } from '@/blocks/blocks/parallel'
|
||||
import { PerplexityBlock } from '@/blocks/blocks/perplexity'
|
||||
import { PineconeBlock } from '@/blocks/blocks/pinecone'
|
||||
import { PipedriveBlock } from '@/blocks/blocks/pipedrive'
|
||||
import { PolymarketBlock } from '@/blocks/blocks/polymarket'
|
||||
import { PostgreSQLBlock } from '@/blocks/blocks/postgresql'
|
||||
import { PostHogBlock } from '@/blocks/blocks/posthog'
|
||||
import { PylonBlock } from '@/blocks/blocks/pylon'
|
||||
@@ -84,8 +93,10 @@ import { SendGridBlock } from '@/blocks/blocks/sendgrid'
|
||||
import { SentryBlock } from '@/blocks/blocks/sentry'
|
||||
import { SerperBlock } from '@/blocks/blocks/serper'
|
||||
import { SharepointBlock } from '@/blocks/blocks/sharepoint'
|
||||
import { ShopifyBlock } from '@/blocks/blocks/shopify'
|
||||
import { SlackBlock } from '@/blocks/blocks/slack'
|
||||
import { SmtpBlock } from '@/blocks/blocks/smtp'
|
||||
import { SSHBlock } from '@/blocks/blocks/ssh'
|
||||
import { StagehandBlock } from '@/blocks/blocks/stagehand'
|
||||
import { StagehandAgentBlock } from '@/blocks/blocks/stagehand_agent'
|
||||
import { StartTriggerBlock } from '@/blocks/blocks/start_trigger'
|
||||
@@ -111,17 +122,20 @@ import { WebflowBlock } from '@/blocks/blocks/webflow'
|
||||
import { WebhookBlock } from '@/blocks/blocks/webhook'
|
||||
import { WhatsAppBlock } from '@/blocks/blocks/whatsapp'
|
||||
import { WikipediaBlock } from '@/blocks/blocks/wikipedia'
|
||||
import { WordPressBlock } from '@/blocks/blocks/wordpress'
|
||||
import { WorkflowBlock } from '@/blocks/blocks/workflow'
|
||||
import { WorkflowInputBlock } from '@/blocks/blocks/workflow_input'
|
||||
import { XBlock } from '@/blocks/blocks/x'
|
||||
import { YouTubeBlock } from '@/blocks/blocks/youtube'
|
||||
import { ZendeskBlock } from '@/blocks/blocks/zendesk'
|
||||
import { ZepBlock } from '@/blocks/blocks/zep'
|
||||
import { ZoomBlock } from '@/blocks/blocks/zoom'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
|
||||
// Registry of all available blocks, alphabetically sorted
|
||||
export const registry: Record<string, BlockConfig> = {
|
||||
agent: AgentBlock,
|
||||
ahrefs: AhrefsBlock,
|
||||
airtable: AirtableBlock,
|
||||
api: ApiBlock,
|
||||
api_trigger: ApiTriggerBlock,
|
||||
@@ -129,14 +143,18 @@ export const registry: Record<string, BlockConfig> = {
|
||||
apollo: ApolloBlock,
|
||||
arxiv: ArxivBlock,
|
||||
asana: AsanaBlock,
|
||||
// box: BoxBlock, // TODO: Box OAuth integration
|
||||
browser_use: BrowserUseBlock,
|
||||
calendly: CalendlyBlock,
|
||||
chat_trigger: ChatTriggerBlock,
|
||||
clay: ClayBlock,
|
||||
condition: ConditionBlock,
|
||||
confluence: ConfluenceBlock,
|
||||
datadog: DatadogBlock,
|
||||
discord: DiscordBlock,
|
||||
dropbox: DropboxBlock,
|
||||
elevenlabs: ElevenLabsBlock,
|
||||
elasticsearch: ElasticsearchBlock,
|
||||
evaluator: EvaluatorBlock,
|
||||
exa: ExaBlock,
|
||||
file: FileBlock,
|
||||
@@ -144,7 +162,9 @@ export const registry: Record<string, BlockConfig> = {
|
||||
function: FunctionBlock,
|
||||
generic_webhook: GenericWebhookBlock,
|
||||
github: GitHubBlock,
|
||||
gitlab: GitLabBlock,
|
||||
gmail: GmailBlock,
|
||||
grafana: GrafanaBlock,
|
||||
guardrails: GuardrailsBlock,
|
||||
google_calendar: GoogleCalendarBlock,
|
||||
google_docs: GoogleDocsBlock,
|
||||
@@ -163,6 +183,7 @@ export const registry: Record<string, BlockConfig> = {
|
||||
intercom: IntercomBlock,
|
||||
jina: JinaBlock,
|
||||
jira: JiraBlock,
|
||||
kalshi: KalshiBlock,
|
||||
knowledge: KnowledgeBlock,
|
||||
linear: LinearBlock,
|
||||
linkedin: LinkedInBlock,
|
||||
@@ -189,6 +210,7 @@ export const registry: Record<string, BlockConfig> = {
|
||||
perplexity: PerplexityBlock,
|
||||
pinecone: PineconeBlock,
|
||||
pipedrive: PipedriveBlock,
|
||||
polymarket: PolymarketBlock,
|
||||
postgresql: PostgreSQLBlock,
|
||||
posthog: PostHogBlock,
|
||||
pylon: PylonBlock,
|
||||
@@ -207,8 +229,10 @@ export const registry: Record<string, BlockConfig> = {
|
||||
sentry: SentryBlock,
|
||||
serper: SerperBlock,
|
||||
sharepoint: SharepointBlock,
|
||||
shopify: ShopifyBlock,
|
||||
slack: SlackBlock,
|
||||
smtp: SmtpBlock,
|
||||
ssh: SSHBlock,
|
||||
stagehand: StagehandBlock,
|
||||
stagehand_agent: StagehandAgentBlock,
|
||||
starter: StarterBlock,
|
||||
@@ -234,12 +258,14 @@ export const registry: Record<string, BlockConfig> = {
|
||||
webhook: WebhookBlock,
|
||||
whatsapp: WhatsAppBlock,
|
||||
wikipedia: WikipediaBlock,
|
||||
wordpress: WordPressBlock,
|
||||
workflow: WorkflowBlock,
|
||||
workflow_input: WorkflowInputBlock,
|
||||
x: XBlock,
|
||||
youtube: YouTubeBlock,
|
||||
zep: ZepBlock,
|
||||
zendesk: ZendeskBlock,
|
||||
zoom: ZoomBlock,
|
||||
}
|
||||
|
||||
export const getBlock = (type: string): BlockConfig | undefined => registry[type]
|
||||
|
||||
@@ -473,6 +473,30 @@ export function GithubIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function GitLabIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} width='24' height='24' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
|
||||
<path
|
||||
d='M22.65 14.39L12 22.13 1.35 14.39a.84.84 0 0 1-.3-.94l1.22-3.78 2.44-7.51A.42.42 0 0 1 4.82 2a.43.43 0 0 1 .58 0 .42.42 0 0 1 .11.18l2.44 7.49h8.1l2.44-7.51A.42.42 0 0 1 18.6 2a.43.43 0 0 1 .58 0 .42.42 0 0 1 .11.18l2.44 7.51L23 13.45a.84.84 0 0 1-.35.94z'
|
||||
fill='#FC6D26'
|
||||
/>
|
||||
<path d='M12 22.13L16.05 9.67H7.95L12 22.13z' fill='#E24329' />
|
||||
<path d='M12 22.13L7.95 9.67H1.69L12 22.13z' fill='#FC6D26' />
|
||||
<path d='M1.69 9.67L.47 13.45a.84.84 0 0 0 .3.94L12 22.13 1.69 9.67z' fill='#FCA326' />
|
||||
<path
|
||||
d='M1.69 9.67H7.95L5.51 2.16A.42.42 0 0 0 4.93 2a.42.42 0 0 0-.11.18L1.69 9.67z'
|
||||
fill='#E24329'
|
||||
/>
|
||||
<path d='M12 22.13L16.05 9.67H22.31L12 22.13z' fill='#FC6D26' />
|
||||
<path d='M22.31 9.67L23.53 13.45a.84.84 0 0 1-.3.94L12 22.13 22.31 9.67z' fill='#FCA326' />
|
||||
<path
|
||||
d='M22.31 9.67H16.05l2.44-7.51A.42.42 0 0 1 19.07 2a.42.42 0 0 1 .11.18l3.13 7.49z'
|
||||
fill='#E24329'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function SerperIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg viewBox='0 0 654 600' xmlns='http://www.w3.org/2000/svg' {...props}>
|
||||
@@ -649,6 +673,37 @@ export function GmailIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function GrafanaIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
width='800px'
|
||||
height='800px'
|
||||
viewBox='0 0 16 16'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
fill='none'
|
||||
>
|
||||
<path
|
||||
fill='url(#grafana-color-16__paint0_linear_2372_364)'
|
||||
d='M13.985 7.175a4.408 4.408 0 00-.138-.802 5.035 5.035 0 00-1.054-1.998 2.96 2.96 0 00-.366-.393c.198-.787-.245-1.468-.245-1.468-.764-.046-1.237.227-1.42.363-.031-.015-.062-.03-.092-.03-.122-.046-.26-.106-.397-.137-.138-.045-.275-.075-.413-.12-.137-.031-.29-.061-.443-.092-.03 0-.046 0-.076-.015C9.005 1.44 8.058 1 8.058 1 7.004 1.666 6.79 2.604 6.79 2.604s0 .015-.016.06l-.183.046c-.076.03-.168.06-.244.076-.077.03-.168.06-.245.09-.153.076-.32.152-.473.228-.153.09-.306.181-.443.272-.016-.015-.03-.015-.03-.015-1.467-.545-2.766.136-2.766.136-.122 1.544.58 2.528.733 2.71-.03.09-.06.196-.091.287a8.104 8.104 0 00-.245 1.09c0 .06-.015.106-.015.166C1.397 8.386 1 9.748 1 9.748c1.13 1.287 2.46 1.377 2.46 1.377.167.303.366.575.58.848.092.106.183.212.29.318a3.014 3.014 0 00.061 2.149c1.268.045 2.093-.545 2.261-.681.122.045.26.076.382.106.382.106.78.151 1.176.181h.49c.595.848 1.634.954 1.634.954.748-.772.779-1.544.779-1.71v-.015-.03-.03c.153-.107.305-.228.443-.35a5.37 5.37 0 00.779-.892c.015-.03.046-.06.061-.09.84.045 1.436-.515 1.436-.515-.138-.863-.642-1.287-.749-1.378l-.015-.015h-.015s-.015 0-.015-.015c0-.045.015-.106.015-.151 0-.091.015-.182.015-.288V9.4v-.166-.076-.152l-.015-.075c-.015-.091-.03-.197-.061-.288a3.506 3.506 0 00-.428-1.044 3.856 3.856 0 00-.718-.848 3.784 3.784 0 00-.901-.575 3.347 3.347 0 00-.993-.272c-.168-.015-.336-.03-.504-.03H9.37 9.204c-.092.015-.169.015-.26.03-.336.06-.642.181-.932.348-.275.166-.52.363-.718.605a2.579 2.579 0 00-.459.757 2.63 2.63 0 00-.183.817v.393c.015.137.046.273.077.394.076.258.183.485.336.666.137.197.32.348.504.485.183.12.382.212.58.272.199.06.382.076.565.076h.244c.031 0 .047 0 .062-.015.015 0 .046-.015.061-.015.046-.016.076-.016.122-.03l.23-.092a.869.869 0 00.198-.12c.015-.016.03-.03.046-.03a.129.129 0 00.015-.198c-.046-.06-.122-.075-.183-.03-.015.015-.03.015-.046.03-.046.03-.107.046-.168.06l-.183.046c-.03 0-.061.015-.092.015H8.73a1.519 1.519 0 01-.825-.378 1.452 1.452 0 01-.306-.378 1.655 1.655 0 01-.168-.485c-.015-.09-.015-.166-.015-.257v-.106-.03c0-.046.015-.091.015-.136.061-.364.26-.727.55-1 .077-.075.153-.136.23-.181.076-.06.167-.106.259-.151.092-.046.183-.076.29-.106a.993.993 0 01.306-.046h.321c.107.015.229.03.336.046.214.045.427.12.626.242.397.212.733.56.947.969.107.211.183.423.214.65.015.06.015.121.015.167v.363c0 .06-.015.121-.015.182 0 .06-.015.12-.03.181l-.046.182c-.03.121-.077.242-.123.363a3.183 3.183 0 01-.366.666 3.002 3.002 0 01-1.91 1.18c-.122.016-.26.03-.382.046h-.198c-.061 0-.138 0-.199-.015a3.637 3.637 0 01-.81-.151 4.068 4.068 0 01-.748-.303 4.098 4.098 0 01-1.696-1.695 4.398 4.398 0 01-.29-.742c-.076-.257-.107-.514-.137-.772v-.302-.091c0-.136.015-.258.03-.394s.046-.272.061-.393c.03-.137.061-.258.092-.394a5.33 5.33 0 01.275-.741c.214-.47.504-.893.855-1.226.092-.091.184-.167.275-.243.092-.075.184-.136.29-.211a5.39 5.39 0 01.306-.182c.046-.03.107-.045.153-.076a.26.26 0 01.076-.03.26.26 0 01.077-.03c.107-.046.229-.091.336-.121.03-.015.06-.015.091-.03.03-.016.061-.016.092-.03.061-.016.122-.031.168-.046.03-.015.061-.015.092-.015.03 0 .06-.016.091-.016.03 0 .061-.015.092-.015l.046-.015h.046c.03 0 .06-.015.091-.015.03 0 .061-.015.107-.015.03 0 .077-.015.107-.015h.764c.23.015.443.03.657.075.428.076.84.212 1.207.394.366.182.702.393.977.636l.046.045.046.045c.03.03.061.061.107.091l.092.091.091.09c.123.122.23.258.336.394.199.258.367.515.49.772.014.015.014.03.03.046.015.015.015.03.015.045l.046.09.046.092.045.09c.046.122.092.228.123.333.06.167.107.318.137.455.015.045.061.09.122.075a.104.104 0 00.107-.106c.092-.227.092-.393.077-.575z'
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id='grafana-color-16__paint0_linear_2372_364'
|
||||
x1='7.502'
|
||||
x2='7.502'
|
||||
y1='18.142'
|
||||
y2='5.356'
|
||||
gradientUnits='userSpaceOnUse'
|
||||
>
|
||||
<stop stop-color='#FFF200' />
|
||||
<stop offset='1' stop-color='#F15A29' />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function GoogleDriveIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
@@ -1259,7 +1314,7 @@ export function ConfluenceIcon(props: SVGProps<SVGSVGElement>) {
|
||||
|
||||
export function TwilioIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'>
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='-8 -8 272 272'>
|
||||
<path
|
||||
fill='currentColor'
|
||||
d='M128 0c70.656 0 128 57.344 128 128s-57.344 128-128 128S0 198.656 0 128 57.344 0 128 0zm0 33.792c-52.224 0-94.208 41.984-94.208 94.208S75.776 222.208 128 222.208s94.208-41.984 94.208-94.208S180.224 33.792 128 33.792zm31.744 99.328c14.704 0 26.624 11.92 26.624 26.624 0 14.704-11.92 26.624-26.624 26.624-14.704 0-26.624-11.92-26.624-26.624 0-14.704 11.92-26.624 26.624-26.624zm-63.488 0c14.704 0 26.624 11.92 26.624 26.624 0 14.704-11.92 26.624-26.624 26.624-14.704 0-26.624-11.92-26.624-26.624 0-14.704 11.92-26.624 26.624-26.624zm63.488-63.488c14.704 0 26.624 11.92 26.624 26.624 0 14.704-11.92 26.624-26.624 26.624-14.704 0-26.624-11.92-26.624-26.624 0-14.704 11.92-26.624 26.624-26.624zm-63.488 0c14.704 0 26.624 11.92 26.624 26.624 0 14.704-11.92 26.624-26.624 26.624-14.704 0-26.624-11.92-26.624-26.624 0-14.704 11.92-26.624 26.624-26.624z'
|
||||
@@ -1666,21 +1721,13 @@ export function ElevenLabsIcon(props: SVGProps<SVGSVGElement>) {
|
||||
|
||||
export function LinkupIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
viewBox='0 0 24 24'
|
||||
width='24'
|
||||
height='24'
|
||||
fill='none'
|
||||
>
|
||||
<path
|
||||
d='M20.2 14.1c-.4-.3-1.6-.4-2.9-.2.5-1.4 1.3-3.9.1-5-.6-.5-1.5-.7-2.6-.5-.3 0-.6.1-1 .2-1.1-1.6-2.4-2.5-3.8-2.5-1.6 0-3.1 1-4.1 2.9-1.2 2.1-1.9 5.1-1.9 8.8v.03l.4.3c3-.9 7.5-2.3 10.7-2.9 0 .9.1 1.9.1 2.8v.03l.4.3c.1 0 5.4-1.7 5.3-3.3 0-.2-.1-.5-.3-.7zM19.9 14.7c.03.4-1.7 1.4-4 2.3.5-.7 1-1.6 1.3-2.5 1.4-.1 2.4-.1 2.7.2zM16.4 14.6c-.3.7-.7 1.4-1.2 2-.02-.6-.1-1.2-.2-1.8.4-.1.9-.1 1.4-.2zM16.5 9.4c.8.7.9 2.4.1 5.1-.5.1-1 .1-1.5.2-.3-2-.9-3.8-1.7-5.3.3-.1.6-.2.8-.2.9-.1 1.7.05 2.3.2zM9.5 6.8c1.2 0 2.3.7 3.2 2.1-2.8 1.1-5.9 3.4-8.4 7.8.2-5.1 1.9-9.9 5.2-9.9zM4.7 17c3.4-4.9 6.4-6.8 8.4-7.8.7 1.3 1.2 2.9 1.5 4.8-3.2.6-7.3 1.8-9.9 3z'
|
||||
fill='currentColor'
|
||||
stroke='currentColor'
|
||||
strokeWidth='0.5'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'>
|
||||
<g transform='translate(12, 12) scale(1.3) translate(-12, -12)'>
|
||||
<path
|
||||
d='M20.2 14.1c-.4-.3-1.6-.4-2.9-.2.5-1.4 1.3-3.9.1-5-.6-.5-1.5-.7-2.6-.5-.3 0-.6.1-1 .2-1.1-1.6-2.4-2.5-3.8-2.5-1.6 0-3.1 1-4.1 2.9-1.2 2.1-1.9 5.1-1.9 8.8v.03l.4.3c3-.9 7.5-2.3 10.7-2.9 0 .9.1 1.9.1 2.8v.03l.4.3c.1 0 5.4-1.7 5.3-3.3 0-.2-.1-.5-.3-.7zM19.9 14.7c.03.4-1.7 1.4-4 2.3.5-.7 1-1.6 1.3-2.5 1.4-.1 2.4-.1 2.7.2zM16.4 14.6c-.3.7-.7 1.4-1.2 2-.02-.6-.1-1.2-.2-1.8.4-.1.9-.1 1.4-.2zM16.5 9.4c.8.7.9 2.4.1 5.1-.5.1-1 .1-1.5.2-.3-2-.9-3.8-1.7-5.3.3-.1.6-.2.8-.2.9-.1 1.7.05 2.3.2zM9.5 6.8c1.2 0 2.3.7 3.2 2.1-2.8 1.1-5.9 3.4-8.4 7.8.2-5.1 1.9-9.9 5.2-9.9zM4.7 17c3.4-4.9 6.4-6.8 8.4-7.8.7 1.3 1.2 2.9 1.5 4.8-3.2.6-7.3 1.8-9.9 3z'
|
||||
fill='#000000'
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -3583,6 +3630,22 @@ export function ZendeskIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function ZoomIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
fill='currentColor'
|
||||
width='800px'
|
||||
height='800px'
|
||||
viewBox='0 0 32 32'
|
||||
version='1.1'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
>
|
||||
<path d='M19.283 17.4c-0.367 0.374-0.879 0.606-1.444 0.606-1.117 0-2.023-0.906-2.023-2.023s0.906-2.023 2.023-2.023c0.929 0 1.712 0.626 1.949 1.479l0.003 0.014c0.045 0.159 0.071 0.341 0.071 0.53 0 0.552-0.221 1.052-0.579 1.417l0-0zM15.471 13.586c-0.648 0.615-1.052 1.483-1.052 2.446 0 1.861 1.509 3.37 3.37 3.37s3.37-1.509 3.37-3.37c0-1.54-1.033-2.838-2.444-3.241l-0.024-0.006c-0.27-0.078-0.581-0.123-0.902-0.123-0.899 0-1.716 0.352-2.32 0.925l0.002-0.001zM28.296 12.601c-0.802 0.001-1.522 0.352-2.016 0.909l-0.002 0.003c-0.496-0.562-1.219-0.915-2.023-0.915-0.563 0-1.086 0.173-1.519 0.468l0.009-0.006c-0.316-0.278-0.73-0.451-1.184-0.462l-0.002-0v6.742l0.337-0.016c0.544-0.014 0.981-0.451 0.995-0.993l0-0.001 0.016-0.337v-2.361l0.017-0.337c0-0.001 0-0.002 0-0.003 0-0.245 0.061-0.477 0.169-0.679l-0.004 0.008c0.238-0.405 0.671-0.672 1.166-0.672s0.928 0.267 1.162 0.664l0.003 0.006c0.103 0.196 0.164 0.428 0.165 0.675v0l0.017 0.339v2.361l0.016 0.336c0.022 0.54 0.454 0.972 0.991 0.995l0.002 0 0.337 0.016v-3.708l0.015-0.337c0-0.001 0-0.002 0-0.003 0-0.247 0.062-0.48 0.171-0.683l-0.004 0.008c0.238-0.403 0.67-0.669 1.165-0.669 0.496 0 0.929 0.268 1.164 0.666l0.003 0.006c0.102 0.195 0.162 0.427 0.162 0.673 0 0.001 0 0.001 0 0.002v-0l0.019 0.337v2.361l0.016 0.336c0.020 0.541 0.454 0.975 0.993 0.995l0.002 0 0.337 0.016v-4.045c-0.001-1.488-1.208-2.694-2.697-2.694-0.001 0-0.002 0-0.003 0h0zM12.206 17.4c-0.37 0.393-0.894 0.638-1.475 0.638-1.117 0-2.023-0.906-2.023-2.023s0.906-2.023 2.023-2.023c0.924 0 1.703 0.619 1.945 1.465l0.004 0.014c0.047 0.163 0.075 0.351 0.075 0.544 0 0.536-0.209 1.024-0.549 1.386l0.001-0.001zM10.78 12.6h-0.005c-1.86 0.001-3.367 1.509-3.367 3.368s1.508 3.368 3.368 3.368 3.368-1.508 3.368-3.368c0-1.86-1.507-3.367-3.366-3.368h-0zM6.734 18.008l-0.337-0.015h-3.035l4.044-4.045-0.016-0.337c-0.013-0.544-0.451-0.982-0.994-0.995l-0.001-0-0.337-0.016h-5.052l0.018 0.337c0.026 0.538 0.455 0.967 0.99 0.995l0.002 0 0.337 0.016h3.037l-4.049 4.045 0.017 0.336c0.019 0.541 0.453 0.975 0.992 0.995l0.002 0 0.337 0.016h5.056l-0.018-0.337c-0.024-0.539-0.455-0.969-0.991-0.993l-0.002-0z' />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function PylonIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
@@ -3690,6 +3753,30 @@ export function SmtpIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function SshIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
width='24'
|
||||
height='24'
|
||||
viewBox='0 0 24 24'
|
||||
fill='none'
|
||||
stroke='currentColor'
|
||||
strokeWidth='2'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
>
|
||||
<rect x='2' y='4' width='20' height='16' rx='2' />
|
||||
<path d='M6 8h.01' />
|
||||
<path d='M10 8h.01' />
|
||||
<path d='M14 8h8' />
|
||||
<path d='M6 12l3 3-3 3' />
|
||||
<path d='M12 18h6' />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function ApifyIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
@@ -3803,3 +3890,181 @@ export function McpIcon(props: SVGProps<SVGSVGElement>) {
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function WordpressIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 25.925 25.925'>
|
||||
<g fill='currentColor'>
|
||||
<path d='M1.843,12.962c0,4.401,2.557,8.205,6.267,10.008L2.805,8.437C2.189,9.819,1.843,11.35,1.843,12.962z M20.469,12.4c0-1.374-0.493-2.326-0.917-3.066c-0.563-0.917-1.092-1.691-1.092-2.608c0-1.021,0.775-1.973,1.867-1.973c0.049,0,0.096,0.006,0.145,0.008c-1.979-1.813-4.615-2.919-7.509-2.919c-3.885,0-7.303,1.993-9.291,5.013c0.261,0.008,0.507,0.013,0.716,0.013c1.163,0,2.963-0.142,2.963-0.142c0.599-0.035,0.67,0.846,0.071,0.917c0,0-0.603,0.07-1.272,0.105l4.049,12.045l2.434-7.298l-1.732-4.747c-0.599-0.035-1.167-0.105-1.167-0.105c-0.6-0.036-0.529-0.953,0.07-0.917c0,0,1.836,0.142,2.928,0.142c1.163,0,2.964-0.142,2.964-0.142c0.6-0.035,0.67,0.846,0.071,0.917c0,0-0.604,0.07-1.272,0.105l4.018,11.953l1.11-3.706C20.187,14.55,20.469,13.353,20.469,12.4z M13.158,13.935l-3.337,9.694c0.997,0.293,2.05,0.453,3.142,0.453c1.294,0,2.537-0.224,3.693-0.63c-0.029-0.048-0.057-0.099-0.08-0.153L13.158,13.935z M22.72,7.627c0.049,0.354,0.075,0.734,0.075,1.144c0,1.128-0.212,2.396-0.846,3.982l-3.396,9.82c3.306-1.928,5.529-5.509,5.529-9.611C24.082,11.028,23.588,9.21,22.72,7.627z' />
|
||||
<path d='M0,12.962c0,7.147,5.815,12.963,12.962,12.963c7.149,0,12.963-5.816,12.963-12.963S20.111,0,12.963,0S0,5.814,0,12.962z M0.594,12.962c0-6.819,5.548-12.368,12.368-12.368s12.369,5.549,12.369,12.368S19.782,25.33,12.963,25.33S0.594,19.781,0.594,12.962z' />
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function AhrefsIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1065 1300'>
|
||||
<path
|
||||
fillRule='evenodd'
|
||||
fill='#ff8800'
|
||||
d='m111.3 0.7h953.4v1264.4h-256.9v-137.8h-21.2c-127 129.9-230.4 172.4-410.4 172.4-227.8 0-376.1-127.3-376.1-339.5v-58.3c0-288.9 121.8-392.4 511-421.5l241-18.6v-180.2h-640.8zm640.8 707.7l-193.4 18.6c-203.9 18.6-248.8 47.8-251.5 169.7-2.7 87.5 42.4 127.2 148.3 127.2 98 0 217.1-53 296.6-132.5z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function ShopifyIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
viewBox='0 0 48 48'
|
||||
width='48px'
|
||||
height='48px'
|
||||
>
|
||||
<path
|
||||
fill='#7cb342'
|
||||
d='M37.216,11.78c-0.023-0.211-0.211-0.305-0.351-0.305s-3.21-0.234-3.21-0.234s-2.132-2.132-2.39-2.343 c-0.234-0.234-0.68-0.164-0.867-0.117c-0.023,0-0.469,0.141-1.195,0.375c-0.726-2.086-1.968-3.984-4.194-3.984h-0.211 C24.187,4.375,23.391,4,22.735,4c-5.155,0-7.639,6.444-8.412,9.725c-2.015,0.633-3.445,1.054-3.609,1.125 c-1.125,0.351-1.148,0.375-1.289,1.429c-0.117,0.797-3.046,23.456-3.046,23.456L29.179,44l12.373-2.671 C41.575,41.282,37.24,11.991,37.216,11.78z M27.937,9.483c-0.562,0.164-1.242,0.375-1.921,0.609V9.671 c0-1.265-0.164-2.296-0.469-3.117C26.718,6.695,27.445,7.984,27.937,9.483L27.937,9.483z M24.117,6.812 c0.305,0.797,0.516,1.922,0.516,3.468v0.234c-1.265,0.398-2.601,0.797-3.984,1.242C21.422,8.804,22.899,7.351,24.117,6.812 L24.117,6.812z M22.617,5.359c0.234,0,0.469,0.094,0.656,0.234c-1.664,0.773-3.421,2.718-4.148,6.655 c-1.101,0.351-2.156,0.656-3.163,0.984C16.806,10.233,18.915,5.359,22.617,5.359z'
|
||||
/>
|
||||
<path
|
||||
fill='#558b2f'
|
||||
d='M36.865,11.428c-0.141,0-3.21-0.234-3.21-0.234s-2.132-2.132-2.39-2.343 C31.17,8.757,31.053,8.71,30.96,8.71L29.249,44l12.373-2.671c0,0-4.335-29.338-4.359-29.549 C37.169,11.569,37.005,11.475,36.865,11.428z'
|
||||
/>
|
||||
<path
|
||||
fill='#fff'
|
||||
d='M24.792,18.593l-1.475,4.449c0,0-1.337-0.715-2.927-0.715c-2.374,0-2.489,1.498-2.489,1.867 c0,2.028,5.301,2.812,5.301,7.583c0,3.757-2.374,6.177-5.578,6.177c-3.872,0-5.808-2.397-5.808-2.397l1.037-3.411 c0,0,2.028,1.752,3.734,1.752c1.129,0,1.59-0.876,1.59-1.521c0-2.651-4.333-2.766-4.333-7.145c0-3.665,2.628-7.214,7.952-7.214 C23.777,17.994,24.792,18.593,24.792,18.593z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function BoxCompanyIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 41 22'>
|
||||
<path
|
||||
d='M39.7 19.2c.5.7.4 1.6-.2 2.1-.7.5-1.7.4-2.2-.2l-3.5-4.5-3.4 4.4c-.5.7-1.5.7-2.2.2-.7-.5-.8-1.4-.3-2.1l4-5.2-4-5.2c-.5-.7-.3-1.7.3-2.2.7-.5 1.7-.3 2.2.3l3.4 4.5L37.3 7c.5-.7 1.4-.8 2.2-.3.7.5.7 1.5.2 2.2L35.8 14l3.9 5.2zm-18.2-.6c-2.6 0-4.7-2-4.7-4.6 0-2.5 2.1-4.6 4.7-4.6s4.7 2.1 4.7 4.6c-.1 2.6-2.2 4.6-4.7 4.6zm-13.8 0c-2.6 0-4.7-2-4.7-4.6 0-2.5 2.1-4.6 4.7-4.6s4.7 2.1 4.7 4.6c0 2.6-2.1 4.6-4.7 4.6zM21.5 6.4c-2.9 0-5.5 1.6-6.8 4-1.3-2.4-3.9-4-6.9-4-1.8 0-3.4.6-4.7 1.5V1.5C3.1.7 2.4 0 1.6 0 .7 0 0 .7 0 1.5v12.6c.1 4.2 3.5 7.5 7.7 7.5 3 0 5.6-1.7 6.9-4.1 1.3 2.4 3.9 4.1 6.8 4.1 4.3 0 7.8-3.4 7.8-7.7.1-4.1-3.4-7.5-7.7-7.5z'
|
||||
fill='currentColor'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function DropboxIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 43 40'>
|
||||
<path
|
||||
d='m12.5 0l-12.5 8.1 8.7 7 12.5-7.8-8.7-7.3zm-12.5 21.9l12.5 8.2 8.7-7.3-12.5-7.7-8.7 6.8zm21.2 0.9l8.8 7.3 12.4-8.1-8.6-6.9-12.6 7.7zm21.2-14.7l-12.4-8.1-8.8 7.3 12.6 7.8 8.6-7zm-21.1 16.3l-8.8 7.3-3.7-2.5v2.8l12.5 7.5 12.5-7.5v-2.8l-3.8 2.5-8.7-7.3z'
|
||||
fill='currentColor'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function ElasticsearchIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 128 128'>
|
||||
<path
|
||||
fill='#343741'
|
||||
d='M4 64c0 5.535.777 10.879 2.098 16H84c8.836 0 16-7.164 16-16s-7.164-16-16-16H6.098A63.738 63.738 0 0 0 4 64'
|
||||
/>
|
||||
<path
|
||||
fill='#fec514'
|
||||
d='M111.695 30.648A61.485 61.485 0 0 0 117.922 24C106.188 9.379 88.199 0 68 0 42.715 0 20.957 14.71 10.574 36H98.04a20.123 20.123 0 0 0 13.652-5.352'
|
||||
/>
|
||||
<path
|
||||
fill='#00bfb3'
|
||||
d='M98.04 92H10.577C20.961 113.29 42.715 128 68 128c20.2 0 38.188-9.383 49.922-24a61.1 61.1 0 0 0-6.227-6.648A20.133 20.133 0 0 0 98.04 92'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function GitlabIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 380 380'>
|
||||
<path
|
||||
fill='#e24329'
|
||||
d='M265.26416,174.37243l-.2134-.55822-21.19899-55.30908c-.4236-1.08359-1.18542-1.99642-2.17699-2.62689-.98837-.63373-2.14749-.93253-3.32305-.87014-1.1689.06239-2.29195.48925-3.20809,1.21821-.90957.73554-1.56629,1.73047-1.87493,2.85346l-14.31327,43.80662h-57.90965l-14.31327-43.80662c-.30864-1.12299-.96536-2.11791-1.87493-2.85346-.91614-.72895-2.03911-1.15582-3.20809-1.21821-1.17548-.06239-2.33468.23641-3.32297.87014-.99166.63047-1.75348,1.5433-2.17707,2.62689l-21.19891,55.31237-.21348.55493c-6.28158,16.38521-.92929,34.90803,13.05891,45.48782.02621.01641.04922.03611.07552.05582l.18719.14119,32.29094,24.17392,15.97151,12.09024,9.71951,7.34871c2.34117,1.77316,5.57877,1.77316,7.92002,0l9.71943-7.34871,15.96822-12.09024,32.48142-24.31511c.02958-.02299.05588-.04269.08538-.06568,13.97834-10.57977,19.32735-29.09604,13.04905-45.47796Z'
|
||||
/>
|
||||
<path
|
||||
fill='#fc6d26'
|
||||
d='M265.26416,174.37243l-.2134-.55822c-10.5174,2.16062-20.20405,6.6099-28.49844,12.81593-.1346.0985-25.20497,19.05805-46.55171,35.19699,15.84998,11.98517,29.6477,22.40405,29.6477,22.40405l32.48142-24.31511c.02958-.02299.05588-.04269.08538-.06568,13.97834-10.57977,19.32735-29.09604,13.04905-45.47796Z'
|
||||
/>
|
||||
<path
|
||||
fill='#fca326'
|
||||
d='M160.34962,244.23117l15.97151,12.09024,9.71951,7.34871c2.34117,1.77316,5.57877,1.77316,7.92002,0l9.71943-7.34871,15.96822-12.09024s-13.79772-10.41888-29.6477-22.40405c-15.85327,11.98517-29.65099,22.40405-29.65099,22.40405Z'
|
||||
/>
|
||||
<path
|
||||
fill='#fc6d26'
|
||||
d='M143.44561,186.63014c-8.29111-6.20274-17.97446-10.65531-28.49507-12.81264l-.21348.55493c-6.28158,16.38521-.92929,34.90803,13.05891,45.48782.02621.01641.04922.03611.07552.05582l.18719.14119,32.29094,24.17392s13.79772-10.41888,29.65099-22.40405c-21.34673-16.13894-46.42031-35.09848-46.55499-35.19699Z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function SSHIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
viewBox='0 0 48 48'
|
||||
width='48px'
|
||||
height='48px'
|
||||
>
|
||||
<path
|
||||
fill='#7cb342'
|
||||
d='M37.216,11.78c-0.023-0.211-0.211-0.305-0.351-0.305s-3.21-0.234-3.21-0.234s-2.132-2.132-2.39-2.343 c-0.234-0.234-0.68-0.164-0.867-0.117c-0.023,0-0.469,0.141-1.195,0.375c-0.726-2.086-1.968-3.984-4.194-3.984h-0.211 C24.187,4.375,23.391,4,22.735,4c-5.155,0-7.639,6.444-8.412,9.725c-2.015,0.633-3.445,1.054-3.609,1.125 c-1.125,0.351-1.148,0.375-1.289,1.429c-0.117,0.797-3.046,23.456-3.046,23.456L29.179,44l12.373-2.671 C41.575,41.282,37.24,11.991,37.216,11.78z M27.937,9.483c-0.562,0.164-1.242,0.375-1.921,0.609V9.671 c0-1.265-0.164-2.296-0.469-3.117C26.718,6.695,27.445,7.984,27.937,9.483L27.937,9.483z M24.117,6.812 c0.305,0.797,0.516,1.922,0.516,3.468v0.234c-1.265,0.398-2.601,0.797-3.984,1.242C21.422,8.804,22.899,7.351,24.117,6.812 L24.117,6.812z M22.617,5.359c0.234,0,0.469,0.094,0.656,0.234c-1.664,0.773-3.421,2.718-4.148,6.655 c-1.101,0.351-2.156,0.656-3.163,0.984C16.806,10.233,18.915,5.359,22.617,5.359z'
|
||||
/>
|
||||
<path
|
||||
fill='#558b2f'
|
||||
d='M36.865,11.428c-0.141,0-3.21-0.234-3.21-0.234s-2.132-2.132-2.39-2.343 C31.17,8.757,31.053,8.71,30.96,8.71L29.249,44l12.373-2.671c0,0-4.335-29.338-4.359-29.549 C37.169,11.569,37.005,11.475,36.865,11.428z'
|
||||
/>
|
||||
<path
|
||||
fill='#fff'
|
||||
d='M24.792,18.593l-1.475,4.449c0,0-1.337-0.715-2.927-0.715c-2.374,0-2.489,1.498-2.489,1.867 c0,2.028,5.301,2.812,5.301,7.583c0,3.757-2.374,6.177-5.578,6.177c-3.872,0-5.808-2.397-5.808-2.397l1.037-3.411 c0,0,2.028,1.752,3.734,1.752c1.129,0,1.59-0.876,1.59-1.521c0-2.651-4.333-2.766-4.333-7.145c0-3.665,2.628-7.214,7.952-7.214 C23.777,17.994,24.792,18.593,24.792,18.593z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function DatadogIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'>
|
||||
<g fill='currentColor'>
|
||||
<path d='M57.705 33.717l1.575 20.1L31.4 58.82 29 38.72l3.983-.556c.648.278 1.112.37 1.853.556 1.204.278 2.594.648 4.63-.463.463-.278 1.482-1.112 1.853-1.667zm-43.073 23.9c-.463-.74-1.297-1.575-2.594-2.594-1.853-1.482-1.204-4.076-.093-5.65 1.4-2.686 8.707-6.206 8.337-10.56-.185-1.575-.37-3.613-1.853-5.095-.093.556 0 1.112 0 1.112s-.648-.74-.926-1.853c-.278-.37-.556-.556-.834-1.112-.185.648-.185 1.297-.185 1.297s-.463-1.204-.556-2.13c-.278.463-.37 1.297-.37 1.297s-.648-1.853-.463-2.87c-.278-.834-1.204-2.594-.926-6.484 1.575 1.112 5.095.834 6.484-1.204.463-.648.74-2.5-.185-6.114-.648-2.316-2.223-5.743-2.87-7.04l-.093.093c.37 1.02 1.02 3.242 1.297 4.354.74 3.242 1.02 4.354.648 5.836-.278 1.297-1.02 2.13-2.964 3.057-1.853.926-4.446-1.4-4.54-1.482-1.853-1.482-3.242-3.9-3.427-5.002-.185-1.297.74-2.038 1.204-3.057-.648.185-1.4.556-1.4.556s.834-.926 1.945-1.667a9.43 9.43 0 0 0 1.204-.834H10.28s1.112-.648 2.316-1.02H10.93l4.446-1.945c1.4-.556 2.686-.37 3.427.648 1.02 1.4 2.038 2.13 4.168 2.686 1.297-.556 1.76-.926 3.427-1.4 1.482-1.667 2.686-1.853 2.686-1.853s-.834.74-.926 1.575c.834-.648 1.76-1.204 1.76-1.204s-.37.463-.648 1.112l.093.093c1.02-.556 2.13-1.02 2.13-1.02s-.37.37-.74.926c.74 0 2.223 0 2.78.093 3.427.093 4.168-3.613 5.465-4.168 1.667-.556 2.408-.926 5.187 1.853 2.408 2.408 4.26 6.577 3.335 7.503-.74.74-2.316-.278-4.076-2.408-.926-1.112-1.575-2.5-1.945-4.168-.185-1.4-1.204-2.223-1.204-2.223s.648 1.4.648 2.594c0 .648.093 3.057 1.112 4.446-.093.185-.185 1.02-.278 1.112-1.204-1.482-3.9-2.5-4.26-2.87 1.482 1.204 4.817 3.9 6.114 6.484 1.204 2.5.463 4.724 1.112 5.28.185.185 2.594 3.15 3.057 4.724.834 2.686.093 5.465-1.02 7.132l-2.964.463a3.92 3.92 0 0 1-1.112-.371c.185-.37.648-1.297.648-1.482l-.185-.278c-.926 1.297-2.5 2.594-3.798 3.335-1.667.926-3.613.834-4.9.37-3.613-1.112-7.04-3.52-7.78-4.168 0 0 0 .463.093.648a28.07 28.07 0 0 0 5.002 4.168l-4.26.463 2.038 15.747c-.926.093-1.02.185-2.038.37-.834-3.057-2.5-5.002-4.354-6.206-1.575-1.02-3.798-1.297-5.928-.834l-.093.185c1.482-.185 3.242.093 5.002 1.204s3.15 3.983 3.705 5.65c.648 2.223 1.112 4.54-.648 7.04-1.297 1.76-5.002 2.78-7.966.648.834 1.297 1.853 2.316 3.335 2.5 2.13.278 4.168-.093 5.65-1.482 1.204-1.204 1.853-3.798 1.667-6.577l1.945-.278.648 4.9L62.06 56.7l-2.78-25.195-1.575.278L54.556 0 1.942 6.114l6.484 52.428z' />
|
||||
<path d='M39.665 30.06c1.4 1.02 2.594 1.667 3.798 1.575.74-.093 1.482-1.297 1.945-2.408.37-.74.37-1.575-.185-1.853-.278-.093-1.4-.093-2.223 0-1.575.185-3.15.74-3.52 1.02-.556.37-.278 1.297.185 1.667m.37-10.84v.093l.093.185c.37.74.74 1.482 1.482 1.853.185 0 .37-.093.556-.093.648 0 1.02.093 1.297.185v-.556c-.093-.926.185-2.594-1.667-3.52-.74-.37-1.667-.185-2.038.185h.185c.463.185.185.37.093.556-.093.37-.185.463 0 1.112' />
|
||||
<path d='M31.698 19.407c.463-.37-2.13-.926-4.168.37-1.482 1.02-1.482 3.15-.093 4.354a1.27 1.27 0 0 1 .37.278c.37-.185.926-.37 1.4-.556.926-.278 1.667-.463 2.316-.556.278-.37.648-.926.556-1.945-.093-1.4-1.204-1.112-.37-1.945m15.098 22.5l-4.446 7.4-5.187-1.575-4.54 6.947.185 2.223 24.825-4.54-1.482-15.47-4.076 8.522z' />
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function KalshiIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 78 20' fill='currentColor' xmlns='http://www.w3.org/2000/svg'>
|
||||
<path d='M40.1043 0H36.0332V19.9986H40.1043V0Z' />
|
||||
<path d='M0.416887 0.0221237H4.73849V8.99348L12.818 0.0221237H18.0582L10.6468 8.24586L18.5384 20H13.3608L7.59868 11.5686L4.73849 14.7459V20H0.416887V0.0221237Z' />
|
||||
<path
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
d='M34.4675 19.8117H32.4007C30.5426 19.8117 29.624 19.0017 29.6658 17.4027C29.1229 18.2334 28.4549 18.8771 27.6824 19.3132C26.8891 19.7494 25.9496 19.9778 24.8222 19.9778C23.1729 19.9778 21.8368 19.604 20.8138 18.8564C19.8117 18.088 19.3106 17.0289 19.3106 15.6582C19.3106 14.1007 19.8952 12.8962 21.0434 12.0656C22.2126 11.2141 23.9036 10.778 26.1166 10.778H29.0603V10.0719C29.0603 9.40737 28.8098 8.8882 28.3087 8.49362C27.8077 8.09905 27.1396 7.89138 26.2836 7.89138C25.532 7.89138 24.9266 8.05752 24.4464 8.36902C23.9662 8.70129 23.674 9.1374 23.5905 9.67734H19.6446C19.7699 8.18212 20.4589 7.01916 21.6697 6.18848C22.8806 5.3578 24.4882 4.92169 26.4924 4.92169C28.5801 4.92169 30.2086 5.37857 31.3359 6.29232C32.4842 7.20607 33.0688 8.53516 33.0688 10.2588V15.4298C33.0688 15.7828 33.1523 16.0321 33.2984 16.1774C33.4445 16.302 33.6951 16.3851 34.0291 16.3851H34.4675V19.8117ZM26.0749 13.4569C25.2398 13.4569 24.5717 13.6231 24.0915 13.9761C23.6322 14.3084 23.4026 14.7653 23.4026 15.3675C23.4026 15.8867 23.5905 16.2813 23.9871 16.5928C24.3838 16.9043 24.9266 17.0496 25.5947 17.0496C26.6594 17.0496 27.4945 16.7589 28.1 16.1567C28.7054 15.5544 29.0394 14.7445 29.0603 13.7269V13.4569H26.0749Z'
|
||||
/>
|
||||
<path d='M45.5115 14.9314C45.5741 15.5752 45.8873 16.0944 46.4718 16.5097C47.0564 16.9043 47.7871 17.112 48.6848 17.112C49.5408 17.112 50.2297 16.9874 50.7308 16.7174C51.2318 16.4266 51.4824 16.0321 51.4824 15.5129C51.4824 15.1391 51.3571 14.8483 51.1275 14.6614C50.8978 14.4745 50.5638 14.3292 50.1462 14.2669C49.7287 14.163 49.0397 14.0592 48.0794 13.9554C46.7641 13.7892 45.6785 13.5608 44.8225 13.2908C43.9665 13.0208 43.2567 12.6055 42.7557 12.024C42.2337 11.4426 41.9832 10.6949 41.9832 9.73966C41.9832 8.78438 42.2337 7.9537 42.7557 7.22685C43.2985 6.47924 44.0501 5.91853 45.0104 5.50319C45.9708 5.10861 47.0773 4.90094 48.3299 4.90094C50.355 4.92171 51.9625 5.35782 53.1943 6.1885C54.4469 7.01918 55.115 8.18213 55.2194 9.67736H51.3571C51.2945 9.11665 51.0022 8.68054 50.4594 8.3275C49.9374 7.97446 49.2694 7.78756 48.4343 7.78756C47.6618 7.78756 47.0355 7.93293 46.5553 8.22367C46.096 8.5144 45.8664 8.88821 45.8664 9.36585C45.8664 9.71889 45.9916 9.9681 46.2422 10.1342C46.4927 10.3004 46.8267 10.425 47.2234 10.508C47.6201 10.5911 48.309 10.6742 49.2485 10.7572C51.2527 10.9857 52.7768 11.4218 53.8206 12.0448C54.9062 12.647 55.4282 13.7062 55.4282 15.2222C55.4282 16.1774 55.1359 17.0081 54.5722 17.735C54.0085 18.4618 53.2361 19.0225 52.2131 19.4171C51.211 19.7909 50.0418 19.9986 48.7266 19.9986C46.6806 19.9986 44.9895 19.5417 43.716 18.6487C42.4216 17.735 41.7535 16.4889 41.67 14.9314H45.5115Z' />
|
||||
<path d='M69.7503 6.72852C68.623 5.6694 67.2033 5.12946 65.4496 5.12946C63.6333 5.12946 62.1719 5.794 61.0654 7.12309V0H56.9943V19.9986H61.0654V12.4602C61.0654 11.1934 61.3368 10.2174 61.9213 9.5113C62.5059 8.80522 63.3201 8.45218 64.364 8.45218C65.3661 8.45218 66.1177 8.78445 66.6187 9.42823C67.1198 10.0512 67.3703 10.965 67.3703 12.1902V19.9986H71.4414V12.0241C71.4414 9.55283 70.8777 7.78763 69.7503 6.72852Z' />
|
||||
<path d='M73.0068 5.29551H77.0779V19.9778H73.0068V5.29551Z' />
|
||||
<path d='M76.473 0.581477C76.0972 0.20767 75.617 0 75.0324 0C74.4688 0 73.9677 0.20767 73.571 0.581477C73.1952 0.955283 72.9865 1.41216 72.9865 1.97287C72.9865 2.53358 73.1952 3.01122 73.571 3.38503C73.9677 3.75883 74.4688 3.9665 75.0324 3.9665C75.5961 3.9665 76.0972 3.7796 76.473 3.38503C76.8488 2.99045 77.0575 2.53358 77.0575 1.97287C77.0575 1.41216 76.8488 0.934516 76.473 0.581477Z' />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function PolymarketIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='51 209 123 155'>
|
||||
<path
|
||||
fill='currentColor'
|
||||
d='M173.2,363.2L51.1,328.3v-83.7l122.1-34.9V363.2z M161.4,296.2l-89.8,25.6l89.8,25.6L161.4,296.2z M62.9,260.8v51.3l89.8-25.6L62.9,260.8z M161.4,225.3L71.6,251l89.8,25.6L161.4,225.3z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -135,6 +135,13 @@ export function useConnectOAuthService() {
|
||||
return { success: true }
|
||||
}
|
||||
|
||||
// Shopify requires a custom OAuth flow with shop domain input
|
||||
if (providerId === 'shopify') {
|
||||
const returnUrl = encodeURIComponent(callbackURL)
|
||||
window.location.href = `/api/auth/shopify/authorize?returnUrl=${returnUrl}`
|
||||
return { success: true }
|
||||
}
|
||||
|
||||
await client.oauth2.link({
|
||||
providerId,
|
||||
callbackURL,
|
||||
|
||||
@@ -1423,6 +1423,66 @@ export const auth = betterAuth({
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
providerId: 'dropbox',
|
||||
clientId: env.DROPBOX_CLIENT_ID as string,
|
||||
clientSecret: env.DROPBOX_CLIENT_SECRET as string,
|
||||
authorizationUrl: 'https://www.dropbox.com/oauth2/authorize',
|
||||
tokenUrl: 'https://api.dropboxapi.com/oauth2/token',
|
||||
scopes: [
|
||||
'account_info.read',
|
||||
'files.metadata.read',
|
||||
'files.metadata.write',
|
||||
'files.content.read',
|
||||
'files.content.write',
|
||||
'sharing.read',
|
||||
'sharing.write',
|
||||
],
|
||||
responseType: 'code',
|
||||
redirectURI: `${getBaseUrl()}/api/auth/oauth2/callback/dropbox`,
|
||||
pkce: true,
|
||||
accessType: 'offline',
|
||||
prompt: 'consent',
|
||||
getUserInfo: async (tokens) => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
'https://api.dropboxapi.com/2/users/get_current_account',
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens.accessToken}`,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text()
|
||||
logger.error('Dropbox API error:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
body: errorText,
|
||||
})
|
||||
throw new Error(`Dropbox API error: ${response.status} ${response.statusText}`)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
id: data.account_id,
|
||||
email: data.email,
|
||||
name: data.name?.display_name || data.email,
|
||||
emailVerified: data.email_verified || false,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
image: data.profile_photo_url || undefined,
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Error in getUserInfo:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
providerId: 'asana',
|
||||
clientId: env.ASANA_CLIENT_ID as string,
|
||||
@@ -1634,6 +1694,117 @@ export const auth = betterAuth({
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
// Zoom provider
|
||||
{
|
||||
providerId: 'zoom',
|
||||
clientId: env.ZOOM_CLIENT_ID as string,
|
||||
clientSecret: env.ZOOM_CLIENT_SECRET as string,
|
||||
authorizationUrl: 'https://zoom.us/oauth/authorize',
|
||||
tokenUrl: 'https://zoom.us/oauth/token',
|
||||
userInfoUrl: 'https://api.zoom.us/v2/users/me',
|
||||
scopes: [
|
||||
'user:read:user',
|
||||
'meeting:write:meeting',
|
||||
'meeting:read:meeting',
|
||||
'meeting:read:list_meetings',
|
||||
'meeting:update:meeting',
|
||||
'meeting:delete:meeting',
|
||||
'meeting:read:invitation',
|
||||
'meeting:read:list_past_participants',
|
||||
'cloud_recording:read:list_user_recordings',
|
||||
'cloud_recording:read:list_recording_files',
|
||||
'cloud_recording:delete:recording_file',
|
||||
],
|
||||
responseType: 'code',
|
||||
accessType: 'offline',
|
||||
authentication: 'basic',
|
||||
prompt: 'consent',
|
||||
redirectURI: `${getBaseUrl()}/api/auth/oauth2/callback/zoom`,
|
||||
getUserInfo: async (tokens) => {
|
||||
try {
|
||||
logger.info('Fetching Zoom user profile')
|
||||
|
||||
const response = await fetch('https://api.zoom.us/v2/users/me', {
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens.accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
logger.error('Failed to fetch Zoom user info', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
})
|
||||
throw new Error('Failed to fetch user info')
|
||||
}
|
||||
|
||||
const profile = await response.json()
|
||||
|
||||
return {
|
||||
id: profile.id,
|
||||
name:
|
||||
`${profile.first_name || ''} ${profile.last_name || ''}`.trim() || 'Zoom User',
|
||||
email: profile.email || `${profile.id}@zoom.user`,
|
||||
emailVerified: profile.verified === 1,
|
||||
image: profile.pic_url || undefined,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Error in Zoom getUserInfo:', { error })
|
||||
return null
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
// WordPress.com provider
|
||||
{
|
||||
providerId: 'wordpress',
|
||||
clientId: env.WORDPRESS_CLIENT_ID as string,
|
||||
clientSecret: env.WORDPRESS_CLIENT_SECRET as string,
|
||||
authorizationUrl: 'https://public-api.wordpress.com/oauth2/authorize',
|
||||
tokenUrl: 'https://public-api.wordpress.com/oauth2/token',
|
||||
userInfoUrl: 'https://public-api.wordpress.com/rest/v1.1/me',
|
||||
scopes: ['global'],
|
||||
responseType: 'code',
|
||||
prompt: 'consent',
|
||||
redirectURI: `${getBaseUrl()}/api/auth/oauth2/callback/wordpress`,
|
||||
getUserInfo: async (tokens) => {
|
||||
try {
|
||||
logger.info('Fetching WordPress.com user profile')
|
||||
|
||||
const response = await fetch('https://public-api.wordpress.com/rest/v1.1/me', {
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens.accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
logger.error('Failed to fetch WordPress.com user info', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
})
|
||||
throw new Error('Failed to fetch user info')
|
||||
}
|
||||
|
||||
const profile = await response.json()
|
||||
|
||||
return {
|
||||
id: profile.ID?.toString() || profile.id?.toString(),
|
||||
name: profile.display_name || profile.username || 'WordPress User',
|
||||
email: profile.email || `${profile.username}@wordpress.com`,
|
||||
emailVerified: profile.email_verified || false,
|
||||
image: profile.avatar_URL || undefined,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Error in WordPress.com getUserInfo:', { error })
|
||||
return null
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
// Include SSO plugin when enabled
|
||||
|
||||
@@ -210,6 +210,8 @@ export const env = createEnv({
|
||||
PIPEDRIVE_CLIENT_SECRET: z.string().optional(), // Pipedrive OAuth client secret
|
||||
LINEAR_CLIENT_ID: z.string().optional(), // Linear OAuth client ID
|
||||
LINEAR_CLIENT_SECRET: z.string().optional(), // Linear OAuth client secret
|
||||
DROPBOX_CLIENT_ID: z.string().optional(), // Dropbox OAuth client ID
|
||||
DROPBOX_CLIENT_SECRET: z.string().optional(), // Dropbox OAuth client secret
|
||||
SLACK_CLIENT_ID: z.string().optional(), // Slack OAuth client ID
|
||||
SLACK_CLIENT_SECRET: z.string().optional(), // Slack OAuth client secret
|
||||
REDDIT_CLIENT_ID: z.string().optional(), // Reddit OAuth client ID
|
||||
@@ -219,6 +221,12 @@ export const env = createEnv({
|
||||
TRELLO_API_KEY: z.string().optional(), // Trello API Key
|
||||
LINKEDIN_CLIENT_ID: z.string().optional(), // LinkedIn OAuth client ID
|
||||
LINKEDIN_CLIENT_SECRET: z.string().optional(), // LinkedIn OAuth client secret
|
||||
SHOPIFY_CLIENT_ID: z.string().optional(), // Shopify OAuth client ID
|
||||
SHOPIFY_CLIENT_SECRET: z.string().optional(), // Shopify OAuth client secret
|
||||
ZOOM_CLIENT_ID: z.string().optional(), // Zoom OAuth client ID
|
||||
ZOOM_CLIENT_SECRET: z.string().optional(), // Zoom OAuth client secret
|
||||
WORDPRESS_CLIENT_ID: z.string().optional(), // WordPress.com OAuth client ID
|
||||
WORDPRESS_CLIENT_SECRET: z.string().optional(), // WordPress.com OAuth client secret
|
||||
|
||||
// E2B Remote Code Execution
|
||||
E2B_ENABLED: z.string().optional(), // Enable E2B remote code execution
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
AsanaIcon,
|
||||
ConfluenceIcon,
|
||||
// DiscordIcon,
|
||||
DropboxIcon,
|
||||
GithubIcon,
|
||||
GmailIcon,
|
||||
GoogleCalendarIcon,
|
||||
@@ -27,12 +28,15 @@ import {
|
||||
PipedriveIcon,
|
||||
RedditIcon,
|
||||
SalesforceIcon,
|
||||
ShopifyIcon,
|
||||
SlackIcon,
|
||||
// SupabaseIcon,
|
||||
TrelloIcon,
|
||||
WealthboxIcon,
|
||||
WebflowIcon,
|
||||
WordpressIcon,
|
||||
xIcon,
|
||||
ZoomIcon,
|
||||
} from '@/components/icons'
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
@@ -49,6 +53,7 @@ export type OAuthProvider =
|
||||
| 'notion'
|
||||
| 'jira'
|
||||
// | 'discord'
|
||||
| 'dropbox'
|
||||
| 'microsoft'
|
||||
| 'linear'
|
||||
| 'slack'
|
||||
@@ -61,6 +66,9 @@ export type OAuthProvider =
|
||||
| 'hubspot'
|
||||
| 'salesforce'
|
||||
| 'linkedin'
|
||||
| 'shopify'
|
||||
| 'zoom'
|
||||
| 'wordpress'
|
||||
| string
|
||||
|
||||
export type OAuthService =
|
||||
@@ -80,6 +88,7 @@ export type OAuthService =
|
||||
| 'notion'
|
||||
| 'jira'
|
||||
// | 'discord'
|
||||
| 'dropbox'
|
||||
| 'microsoft-excel'
|
||||
| 'microsoft-teams'
|
||||
| 'microsoft-planner'
|
||||
@@ -97,6 +106,9 @@ export type OAuthService =
|
||||
| 'hubspot'
|
||||
| 'salesforce'
|
||||
| 'linkedin'
|
||||
| 'shopify'
|
||||
| 'zoom'
|
||||
| 'wordpress'
|
||||
export interface OAuthProviderConfig {
|
||||
id: OAuthProvider
|
||||
name: string
|
||||
@@ -538,6 +550,55 @@ export const OAUTH_PROVIDERS: Record<string, OAuthProviderConfig> = {
|
||||
},
|
||||
defaultService: 'linear',
|
||||
},
|
||||
dropbox: {
|
||||
id: 'dropbox',
|
||||
name: 'Dropbox',
|
||||
icon: (props) => DropboxIcon(props),
|
||||
services: {
|
||||
dropbox: {
|
||||
id: 'dropbox',
|
||||
name: 'Dropbox',
|
||||
description: 'Upload, download, share, and manage files in Dropbox.',
|
||||
providerId: 'dropbox',
|
||||
icon: (props) => DropboxIcon(props),
|
||||
baseProviderIcon: (props) => DropboxIcon(props),
|
||||
scopes: [
|
||||
'account_info.read',
|
||||
'files.metadata.read',
|
||||
'files.metadata.write',
|
||||
'files.content.read',
|
||||
'files.content.write',
|
||||
'sharing.read',
|
||||
'sharing.write',
|
||||
],
|
||||
},
|
||||
},
|
||||
defaultService: 'dropbox',
|
||||
},
|
||||
shopify: {
|
||||
id: 'shopify',
|
||||
name: 'Shopify',
|
||||
icon: (props) => ShopifyIcon(props),
|
||||
services: {
|
||||
shopify: {
|
||||
id: 'shopify',
|
||||
name: 'Shopify',
|
||||
description: 'Manage products, orders, and customers in your Shopify store.',
|
||||
providerId: 'shopify',
|
||||
icon: (props) => ShopifyIcon(props),
|
||||
baseProviderIcon: (props) => ShopifyIcon(props),
|
||||
scopes: [
|
||||
'write_products',
|
||||
'write_orders',
|
||||
'write_customers',
|
||||
'write_inventory',
|
||||
'read_locations',
|
||||
'write_merchant_managed_fulfillment_orders',
|
||||
],
|
||||
},
|
||||
},
|
||||
defaultService: 'shopify',
|
||||
},
|
||||
slack: {
|
||||
id: 'slack',
|
||||
name: 'Slack',
|
||||
@@ -769,6 +830,52 @@ export const OAUTH_PROVIDERS: Record<string, OAuthProviderConfig> = {
|
||||
},
|
||||
defaultService: 'salesforce',
|
||||
},
|
||||
zoom: {
|
||||
id: 'zoom',
|
||||
name: 'Zoom',
|
||||
icon: (props) => ZoomIcon(props),
|
||||
services: {
|
||||
zoom: {
|
||||
id: 'zoom',
|
||||
name: 'Zoom',
|
||||
description: 'Create and manage Zoom meetings, users, and recordings.',
|
||||
providerId: 'zoom',
|
||||
icon: (props) => ZoomIcon(props),
|
||||
baseProviderIcon: (props) => ZoomIcon(props),
|
||||
scopes: [
|
||||
'user:read:user',
|
||||
'meeting:write:meeting',
|
||||
'meeting:read:meeting',
|
||||
'meeting:read:list_meetings',
|
||||
'meeting:update:meeting',
|
||||
'meeting:delete:meeting',
|
||||
'meeting:read:invitation',
|
||||
'meeting:read:list_past_participants',
|
||||
'cloud_recording:read:list_user_recordings',
|
||||
'cloud_recording:read:list_recording_files',
|
||||
'cloud_recording:delete:recording_file',
|
||||
],
|
||||
},
|
||||
},
|
||||
defaultService: 'zoom',
|
||||
},
|
||||
wordpress: {
|
||||
id: 'wordpress',
|
||||
name: 'WordPress',
|
||||
icon: (props) => WordpressIcon(props),
|
||||
services: {
|
||||
wordpress: {
|
||||
id: 'wordpress',
|
||||
name: 'WordPress.com',
|
||||
description: 'Manage posts, pages, media, comments, and more on WordPress.com sites.',
|
||||
providerId: 'wordpress',
|
||||
icon: (props) => WordpressIcon(props),
|
||||
baseProviderIcon: (props) => WordpressIcon(props),
|
||||
scopes: ['global'],
|
||||
},
|
||||
},
|
||||
defaultService: 'wordpress',
|
||||
},
|
||||
}
|
||||
|
||||
export function getServiceByProviderAndId(
|
||||
@@ -1149,6 +1256,18 @@ function getProviderAuthConfig(provider: string): ProviderAuthConfig {
|
||||
useBasicAuth: true,
|
||||
}
|
||||
}
|
||||
case 'dropbox': {
|
||||
const { clientId, clientSecret } = getCredentials(
|
||||
env.DROPBOX_CLIENT_ID,
|
||||
env.DROPBOX_CLIENT_SECRET
|
||||
)
|
||||
return {
|
||||
tokenEndpoint: 'https://api.dropboxapi.com/oauth2/token',
|
||||
clientId,
|
||||
clientSecret,
|
||||
useBasicAuth: false,
|
||||
}
|
||||
}
|
||||
case 'slack': {
|
||||
const { clientId, clientSecret } = getCredentials(
|
||||
env.SLACK_CLIENT_ID,
|
||||
@@ -1265,6 +1384,46 @@ function getProviderAuthConfig(provider: string): ProviderAuthConfig {
|
||||
supportsRefreshTokenRotation: false,
|
||||
}
|
||||
}
|
||||
case 'shopify': {
|
||||
// Shopify access tokens don't expire and don't support refresh tokens
|
||||
// This configuration is provided for completeness but won't be used for token refresh
|
||||
const { clientId, clientSecret } = getCredentials(
|
||||
env.SHOPIFY_CLIENT_ID,
|
||||
env.SHOPIFY_CLIENT_SECRET
|
||||
)
|
||||
return {
|
||||
tokenEndpoint: 'https://accounts.shopify.com/oauth/token',
|
||||
clientId,
|
||||
clientSecret,
|
||||
useBasicAuth: false,
|
||||
supportsRefreshTokenRotation: false,
|
||||
}
|
||||
}
|
||||
case 'zoom': {
|
||||
const { clientId, clientSecret } = getCredentials(env.ZOOM_CLIENT_ID, env.ZOOM_CLIENT_SECRET)
|
||||
return {
|
||||
tokenEndpoint: 'https://zoom.us/oauth/token',
|
||||
clientId,
|
||||
clientSecret,
|
||||
useBasicAuth: true,
|
||||
supportsRefreshTokenRotation: false,
|
||||
}
|
||||
}
|
||||
case 'wordpress': {
|
||||
// WordPress.com does NOT support refresh tokens
|
||||
// Users will need to re-authorize when tokens expire (~2 weeks)
|
||||
const { clientId, clientSecret } = getCredentials(
|
||||
env.WORDPRESS_CLIENT_ID,
|
||||
env.WORDPRESS_CLIENT_SECRET
|
||||
)
|
||||
return {
|
||||
tokenEndpoint: 'https://public-api.wordpress.com/oauth2/token',
|
||||
clientId,
|
||||
clientSecret,
|
||||
useBasicAuth: false,
|
||||
supportsRefreshTokenRotation: false,
|
||||
}
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unsupported provider: ${provider}`)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Logger } from '@/lib/logs/console/logger'
|
||||
import type { StorageContext } from '@/lib/uploads'
|
||||
import { ACCEPTED_FILE_TYPES } from '@/lib/uploads/utils/validation'
|
||||
import { ACCEPTED_FILE_TYPES, SUPPORTED_DOCUMENT_EXTENSIONS } from '@/lib/uploads/utils/validation'
|
||||
import type { UserFile } from '@/executor/types'
|
||||
|
||||
export interface FileAttachment {
|
||||
@@ -258,11 +258,22 @@ export function validateKnowledgeBaseFile(
|
||||
return `File "${file.name}" is too large. Maximum size is ${maxSizeMB}MB.`
|
||||
}
|
||||
|
||||
if (!ACCEPTED_FILE_TYPES.includes(file.type)) {
|
||||
return `File "${file.name}" has an unsupported format. Please use PDF, DOC, DOCX, TXT, CSV, XLS, XLSX, MD, PPT, PPTX, HTML, JSON, YAML, or YML files.`
|
||||
// Check MIME type first
|
||||
if (ACCEPTED_FILE_TYPES.includes(file.type)) {
|
||||
return null
|
||||
}
|
||||
|
||||
return null
|
||||
// Fallback: check file extension (browsers often misidentify file types like .md)
|
||||
const extension = getFileExtension(file.name)
|
||||
if (
|
||||
SUPPORTED_DOCUMENT_EXTENSIONS.includes(
|
||||
extension as (typeof SUPPORTED_DOCUMENT_EXTENSIONS)[number]
|
||||
)
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
return `File "${file.name}" has an unsupported format. Please use PDF, DOC, DOCX, TXT, CSV, XLS, XLSX, MD, PPT, PPTX, HTML, JSON, YAML, or YML files.`
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -50,7 +50,13 @@ export const SUPPORTED_MIME_TYPES: Record<SupportedDocumentExtension, string[]>
|
||||
'application/octet-stream',
|
||||
],
|
||||
txt: ['text/plain', 'text/x-plain', 'application/txt'],
|
||||
md: ['text/markdown', 'text/x-markdown', 'text/plain', 'application/markdown'],
|
||||
md: [
|
||||
'text/markdown',
|
||||
'text/x-markdown',
|
||||
'text/plain',
|
||||
'application/markdown',
|
||||
'application/octet-stream',
|
||||
],
|
||||
xlsx: [
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'application/octet-stream',
|
||||
|
||||
@@ -122,6 +122,7 @@
|
||||
"resend": "^4.1.2",
|
||||
"sharp": "0.34.3",
|
||||
"socket.io": "^4.8.1",
|
||||
"ssh2": "^1.17.0",
|
||||
"stripe": "18.5.0",
|
||||
"tailwind-merge": "^2.6.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
@@ -144,6 +145,7 @@
|
||||
"@types/prismjs": "^1.26.5",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"@types/ssh2": "^1.15.5",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"@vitest/coverage-v8": "^3.0.8",
|
||||
"autoprefixer": "10.4.21",
|
||||
|
||||
@@ -195,7 +195,7 @@ describe('Validation Integration Tests', () => {
|
||||
} as any,
|
||||
mergedParams
|
||||
)
|
||||
}).toThrow('"Url" is required for Jina Reader')
|
||||
}).toThrow('Url is required for Jina Reader')
|
||||
}
|
||||
)
|
||||
|
||||
@@ -314,7 +314,7 @@ describe('Validation Integration Tests', () => {
|
||||
} as any,
|
||||
mergedParams
|
||||
)
|
||||
}).toThrow('"Subreddit" is required for Reddit Posts')
|
||||
}).toThrow('Subreddit is required for Reddit Posts')
|
||||
})
|
||||
|
||||
it.concurrent('complete success: all required fields provided correctly', () => {
|
||||
|
||||
116
apps/sim/tools/ahrefs/backlinks.ts
Normal file
116
apps/sim/tools/ahrefs/backlinks.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import type { AhrefsBacklinksParams, AhrefsBacklinksResponse } from '@/tools/ahrefs/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const backlinksTool: ToolConfig<AhrefsBacklinksParams, AhrefsBacklinksResponse> = {
|
||||
id: 'ahrefs_backlinks',
|
||||
name: 'Ahrefs Backlinks',
|
||||
description:
|
||||
'Get a list of backlinks pointing to a target domain or URL. Returns details about each backlink including source URL, anchor text, and domain rating.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
target: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The target domain or URL to analyze',
|
||||
},
|
||||
mode: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description:
|
||||
'Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains), exact (exact URL match)',
|
||||
},
|
||||
date: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Date for historical data in YYYY-MM-DD format (defaults to today)',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Maximum number of results to return (default: 100)',
|
||||
},
|
||||
offset: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Number of results to skip for pagination',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Ahrefs API Key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://api.ahrefs.com/v3/site-explorer/backlinks')
|
||||
url.searchParams.set('target', params.target)
|
||||
// Date is required - default to today if not provided
|
||||
const date = params.date || new Date().toISOString().split('T')[0]
|
||||
url.searchParams.set('date', date)
|
||||
if (params.mode) url.searchParams.set('mode', params.mode)
|
||||
if (params.limit) url.searchParams.set('limit', String(params.limit))
|
||||
if (params.offset) url.searchParams.set('offset', String(params.offset))
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error?.message || data.error || 'Failed to get backlinks')
|
||||
}
|
||||
|
||||
const backlinks = (data.backlinks || []).map((link: any) => ({
|
||||
urlFrom: link.url_from || '',
|
||||
urlTo: link.url_to || '',
|
||||
anchor: link.anchor || '',
|
||||
domainRatingSource: link.domain_rating_source ?? link.domain_rating ?? 0,
|
||||
isDofollow: link.is_dofollow ?? link.dofollow ?? false,
|
||||
firstSeen: link.first_seen || '',
|
||||
lastVisited: link.last_visited || '',
|
||||
}))
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
backlinks,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
backlinks: {
|
||||
type: 'array',
|
||||
description: 'List of backlinks pointing to the target',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
urlFrom: { type: 'string', description: 'The URL of the page containing the backlink' },
|
||||
urlTo: { type: 'string', description: 'The URL being linked to' },
|
||||
anchor: { type: 'string', description: 'The anchor text of the link' },
|
||||
domainRatingSource: {
|
||||
type: 'number',
|
||||
description: 'Domain Rating of the linking domain',
|
||||
},
|
||||
isDofollow: { type: 'boolean', description: 'Whether the link is dofollow' },
|
||||
firstSeen: { type: 'string', description: 'When the backlink was first discovered' },
|
||||
lastVisited: { type: 'string', description: 'When the backlink was last checked' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
95
apps/sim/tools/ahrefs/backlinks_stats.ts
Normal file
95
apps/sim/tools/ahrefs/backlinks_stats.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import type { AhrefsBacklinksStatsParams, AhrefsBacklinksStatsResponse } from '@/tools/ahrefs/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const backlinksStatsTool: ToolConfig<
|
||||
AhrefsBacklinksStatsParams,
|
||||
AhrefsBacklinksStatsResponse
|
||||
> = {
|
||||
id: 'ahrefs_backlinks_stats',
|
||||
name: 'Ahrefs Backlinks Stats',
|
||||
description:
|
||||
'Get backlink statistics for a target domain or URL. Returns totals for different backlink types including dofollow, nofollow, text, image, and redirect links.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
target: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The target domain or URL to analyze',
|
||||
},
|
||||
mode: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description:
|
||||
'Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains), exact (exact URL match)',
|
||||
},
|
||||
date: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Date for historical data in YYYY-MM-DD format (defaults to today)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Ahrefs API Key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://api.ahrefs.com/v3/site-explorer/backlinks-stats')
|
||||
url.searchParams.set('target', params.target)
|
||||
// Date is required - default to today if not provided
|
||||
const date = params.date || new Date().toISOString().split('T')[0]
|
||||
url.searchParams.set('date', date)
|
||||
if (params.mode) url.searchParams.set('mode', params.mode)
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error?.message || data.error || 'Failed to get backlinks stats')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
stats: {
|
||||
total: data.live ?? data.total ?? 0,
|
||||
dofollow: data.live_dofollow ?? data.dofollow ?? 0,
|
||||
nofollow: data.live_nofollow ?? data.nofollow ?? 0,
|
||||
text: data.text ?? 0,
|
||||
image: data.image ?? 0,
|
||||
redirect: data.redirect ?? 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
stats: {
|
||||
type: 'object',
|
||||
description: 'Backlink statistics summary',
|
||||
properties: {
|
||||
total: { type: 'number', description: 'Total number of live backlinks' },
|
||||
dofollow: { type: 'number', description: 'Number of dofollow backlinks' },
|
||||
nofollow: { type: 'number', description: 'Number of nofollow backlinks' },
|
||||
text: { type: 'number', description: 'Number of text backlinks' },
|
||||
image: { type: 'number', description: 'Number of image backlinks' },
|
||||
redirect: { type: 'number', description: 'Number of redirect backlinks' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
121
apps/sim/tools/ahrefs/broken_backlinks.ts
Normal file
121
apps/sim/tools/ahrefs/broken_backlinks.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import type {
|
||||
AhrefsBrokenBacklinksParams,
|
||||
AhrefsBrokenBacklinksResponse,
|
||||
} from '@/tools/ahrefs/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brokenBacklinksTool: ToolConfig<
|
||||
AhrefsBrokenBacklinksParams,
|
||||
AhrefsBrokenBacklinksResponse
|
||||
> = {
|
||||
id: 'ahrefs_broken_backlinks',
|
||||
name: 'Ahrefs Broken Backlinks',
|
||||
description:
|
||||
'Get a list of broken backlinks pointing to a target domain or URL. Useful for identifying link reclamation opportunities.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
target: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The target domain or URL to analyze',
|
||||
},
|
||||
mode: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description:
|
||||
'Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains), exact (exact URL match)',
|
||||
},
|
||||
date: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Date for historical data in YYYY-MM-DD format (defaults to today)',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Maximum number of results to return (default: 100)',
|
||||
},
|
||||
offset: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Number of results to skip for pagination',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Ahrefs API Key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://api.ahrefs.com/v3/site-explorer/broken-backlinks')
|
||||
url.searchParams.set('target', params.target)
|
||||
// Date is required - default to today if not provided
|
||||
const date = params.date || new Date().toISOString().split('T')[0]
|
||||
url.searchParams.set('date', date)
|
||||
if (params.mode) url.searchParams.set('mode', params.mode)
|
||||
if (params.limit) url.searchParams.set('limit', String(params.limit))
|
||||
if (params.offset) url.searchParams.set('offset', String(params.offset))
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error?.message || data.error || 'Failed to get broken backlinks')
|
||||
}
|
||||
|
||||
const brokenBacklinks = (data.backlinks || data.broken_backlinks || []).map((link: any) => ({
|
||||
urlFrom: link.url_from || '',
|
||||
urlTo: link.url_to || '',
|
||||
httpCode: link.http_code ?? link.status_code ?? 404,
|
||||
anchor: link.anchor || '',
|
||||
domainRatingSource: link.domain_rating_source ?? link.domain_rating ?? 0,
|
||||
}))
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
brokenBacklinks,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
brokenBacklinks: {
|
||||
type: 'array',
|
||||
description: 'List of broken backlinks',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
urlFrom: {
|
||||
type: 'string',
|
||||
description: 'The URL of the page containing the broken link',
|
||||
},
|
||||
urlTo: { type: 'string', description: 'The broken URL being linked to' },
|
||||
httpCode: { type: 'number', description: 'HTTP status code (e.g., 404, 410)' },
|
||||
anchor: { type: 'string', description: 'The anchor text of the link' },
|
||||
domainRatingSource: {
|
||||
type: 'number',
|
||||
description: 'Domain Rating of the linking domain',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
74
apps/sim/tools/ahrefs/domain_rating.ts
Normal file
74
apps/sim/tools/ahrefs/domain_rating.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import type { AhrefsDomainRatingParams, AhrefsDomainRatingResponse } from '@/tools/ahrefs/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const domainRatingTool: ToolConfig<AhrefsDomainRatingParams, AhrefsDomainRatingResponse> = {
|
||||
id: 'ahrefs_domain_rating',
|
||||
name: 'Ahrefs Domain Rating',
|
||||
description:
|
||||
"Get the Domain Rating (DR) and Ahrefs Rank for a target domain. Domain Rating shows the strength of a website's backlink profile on a scale from 0 to 100.",
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
target: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The target domain to analyze (e.g., example.com)',
|
||||
},
|
||||
date: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Date for historical data in YYYY-MM-DD format (defaults to today)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Ahrefs API Key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://api.ahrefs.com/v3/site-explorer/domain-rating')
|
||||
url.searchParams.set('target', params.target)
|
||||
// Date is required - default to today if not provided
|
||||
const date = params.date || new Date().toISOString().split('T')[0]
|
||||
url.searchParams.set('date', date)
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error?.message || data.error || 'Failed to get domain rating')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
domainRating: data.domain_rating ?? 0,
|
||||
ahrefsRank: data.ahrefs_rank ?? 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
domainRating: {
|
||||
type: 'number',
|
||||
description: 'Domain Rating score (0-100)',
|
||||
},
|
||||
ahrefsRank: {
|
||||
type: 'number',
|
||||
description: 'Ahrefs Rank - global ranking based on backlink profile strength',
|
||||
},
|
||||
},
|
||||
}
|
||||
17
apps/sim/tools/ahrefs/index.ts
Normal file
17
apps/sim/tools/ahrefs/index.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { backlinksTool } from '@/tools/ahrefs/backlinks'
|
||||
import { backlinksStatsTool } from '@/tools/ahrefs/backlinks_stats'
|
||||
import { brokenBacklinksTool } from '@/tools/ahrefs/broken_backlinks'
|
||||
import { domainRatingTool } from '@/tools/ahrefs/domain_rating'
|
||||
import { keywordOverviewTool } from '@/tools/ahrefs/keyword_overview'
|
||||
import { organicKeywordsTool } from '@/tools/ahrefs/organic_keywords'
|
||||
import { referringDomainsTool } from '@/tools/ahrefs/referring_domains'
|
||||
import { topPagesTool } from '@/tools/ahrefs/top_pages'
|
||||
|
||||
export const ahrefsDomainRatingTool = domainRatingTool
|
||||
export const ahrefsBacklinksTool = backlinksTool
|
||||
export const ahrefsBacklinksStatsTool = backlinksStatsTool
|
||||
export const ahrefsReferringDomainsTool = referringDomainsTool
|
||||
export const ahrefsOrganicKeywordsTool = organicKeywordsTool
|
||||
export const ahrefsTopPagesTool = topPagesTool
|
||||
export const ahrefsKeywordOverviewTool = keywordOverviewTool
|
||||
export const ahrefsBrokenBacklinksTool = brokenBacklinksTool
|
||||
101
apps/sim/tools/ahrefs/keyword_overview.ts
Normal file
101
apps/sim/tools/ahrefs/keyword_overview.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import type {
|
||||
AhrefsKeywordOverviewParams,
|
||||
AhrefsKeywordOverviewResponse,
|
||||
} from '@/tools/ahrefs/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const keywordOverviewTool: ToolConfig<
|
||||
AhrefsKeywordOverviewParams,
|
||||
AhrefsKeywordOverviewResponse
|
||||
> = {
|
||||
id: 'ahrefs_keyword_overview',
|
||||
name: 'Ahrefs Keyword Overview',
|
||||
description:
|
||||
'Get detailed metrics for a keyword including search volume, keyword difficulty, CPC, clicks, and traffic potential.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
keyword: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The keyword to analyze',
|
||||
},
|
||||
country: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Country code for keyword data (e.g., us, gb, de). Default: us',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Ahrefs API Key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://api.ahrefs.com/v3/keywords-explorer/overview')
|
||||
url.searchParams.set('keyword', params.keyword)
|
||||
url.searchParams.set('country', params.country || 'us')
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error?.message || data.error || 'Failed to get keyword overview')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
overview: {
|
||||
keyword: data.keyword || '',
|
||||
searchVolume: data.volume ?? 0,
|
||||
keywordDifficulty: data.keyword_difficulty ?? data.difficulty ?? 0,
|
||||
cpc: data.cpc ?? 0,
|
||||
clicks: data.clicks ?? 0,
|
||||
clicksPercentage: data.clicks_percentage ?? 0,
|
||||
parentTopic: data.parent_topic || '',
|
||||
trafficPotential: data.traffic_potential ?? 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
overview: {
|
||||
type: 'object',
|
||||
description: 'Keyword metrics overview',
|
||||
properties: {
|
||||
keyword: { type: 'string', description: 'The analyzed keyword' },
|
||||
searchVolume: { type: 'number', description: 'Monthly search volume' },
|
||||
keywordDifficulty: {
|
||||
type: 'number',
|
||||
description: 'Keyword difficulty score (0-100)',
|
||||
},
|
||||
cpc: { type: 'number', description: 'Cost per click in USD' },
|
||||
clicks: { type: 'number', description: 'Estimated clicks per month' },
|
||||
clicksPercentage: {
|
||||
type: 'number',
|
||||
description: 'Percentage of searches that result in clicks',
|
||||
},
|
||||
parentTopic: { type: 'string', description: 'The parent topic for this keyword' },
|
||||
trafficPotential: {
|
||||
type: 'number',
|
||||
description: 'Estimated traffic potential if ranking #1',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
127
apps/sim/tools/ahrefs/organic_keywords.ts
Normal file
127
apps/sim/tools/ahrefs/organic_keywords.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import type {
|
||||
AhrefsOrganicKeywordsParams,
|
||||
AhrefsOrganicKeywordsResponse,
|
||||
} from '@/tools/ahrefs/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const organicKeywordsTool: ToolConfig<
|
||||
AhrefsOrganicKeywordsParams,
|
||||
AhrefsOrganicKeywordsResponse
|
||||
> = {
|
||||
id: 'ahrefs_organic_keywords',
|
||||
name: 'Ahrefs Organic Keywords',
|
||||
description:
|
||||
'Get organic keywords that a target domain or URL ranks for in Google search results. Returns keyword details including search volume, ranking position, and estimated traffic.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
target: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The target domain or URL to analyze',
|
||||
},
|
||||
country: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Country code for search results (e.g., us, gb, de). Default: us',
|
||||
},
|
||||
mode: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description:
|
||||
'Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains), exact (exact URL match)',
|
||||
},
|
||||
date: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Date for historical data in YYYY-MM-DD format (defaults to today)',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Maximum number of results to return (default: 100)',
|
||||
},
|
||||
offset: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Number of results to skip for pagination',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Ahrefs API Key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://api.ahrefs.com/v3/site-explorer/organic-keywords')
|
||||
url.searchParams.set('target', params.target)
|
||||
url.searchParams.set('country', params.country || 'us')
|
||||
// Date is required - default to today if not provided
|
||||
const date = params.date || new Date().toISOString().split('T')[0]
|
||||
url.searchParams.set('date', date)
|
||||
if (params.mode) url.searchParams.set('mode', params.mode)
|
||||
if (params.limit) url.searchParams.set('limit', String(params.limit))
|
||||
if (params.offset) url.searchParams.set('offset', String(params.offset))
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error?.message || data.error || 'Failed to get organic keywords')
|
||||
}
|
||||
|
||||
const keywords = (data.keywords || data.organic_keywords || []).map((kw: any) => ({
|
||||
keyword: kw.keyword || '',
|
||||
volume: kw.volume ?? 0,
|
||||
position: kw.position ?? 0,
|
||||
url: kw.url || '',
|
||||
traffic: kw.traffic ?? 0,
|
||||
keywordDifficulty: kw.keyword_difficulty ?? kw.difficulty ?? 0,
|
||||
}))
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
keywords,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
keywords: {
|
||||
type: 'array',
|
||||
description: 'List of organic keywords the target ranks for',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
keyword: { type: 'string', description: 'The keyword' },
|
||||
volume: { type: 'number', description: 'Monthly search volume' },
|
||||
position: { type: 'number', description: 'Current ranking position' },
|
||||
url: { type: 'string', description: 'The URL that ranks for this keyword' },
|
||||
traffic: { type: 'number', description: 'Estimated monthly organic traffic' },
|
||||
keywordDifficulty: {
|
||||
type: 'number',
|
||||
description: 'Keyword difficulty score (0-100)',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
125
apps/sim/tools/ahrefs/referring_domains.ts
Normal file
125
apps/sim/tools/ahrefs/referring_domains.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
import type {
|
||||
AhrefsReferringDomainsParams,
|
||||
AhrefsReferringDomainsResponse,
|
||||
} from '@/tools/ahrefs/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const referringDomainsTool: ToolConfig<
|
||||
AhrefsReferringDomainsParams,
|
||||
AhrefsReferringDomainsResponse
|
||||
> = {
|
||||
id: 'ahrefs_referring_domains',
|
||||
name: 'Ahrefs Referring Domains',
|
||||
description:
|
||||
'Get a list of domains that link to a target domain or URL. Returns unique referring domains with their domain rating, backlink counts, and discovery dates.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
target: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The target domain or URL to analyze',
|
||||
},
|
||||
mode: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description:
|
||||
'Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains), exact (exact URL match)',
|
||||
},
|
||||
date: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Date for historical data in YYYY-MM-DD format (defaults to today)',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Maximum number of results to return (default: 100)',
|
||||
},
|
||||
offset: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Number of results to skip for pagination',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Ahrefs API Key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://api.ahrefs.com/v3/site-explorer/refdomains')
|
||||
url.searchParams.set('target', params.target)
|
||||
// Date is required - default to today if not provided
|
||||
const date = params.date || new Date().toISOString().split('T')[0]
|
||||
url.searchParams.set('date', date)
|
||||
if (params.mode) url.searchParams.set('mode', params.mode)
|
||||
if (params.limit) url.searchParams.set('limit', String(params.limit))
|
||||
if (params.offset) url.searchParams.set('offset', String(params.offset))
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error?.message || data.error || 'Failed to get referring domains')
|
||||
}
|
||||
|
||||
const referringDomains = (data.refdomains || data.referring_domains || []).map(
|
||||
(domain: any) => ({
|
||||
domain: domain.domain || domain.refdomain || '',
|
||||
domainRating: domain.domain_rating ?? 0,
|
||||
backlinks: domain.backlinks ?? 0,
|
||||
dofollowBacklinks: domain.dofollow_backlinks ?? domain.dofollow ?? 0,
|
||||
firstSeen: domain.first_seen || '',
|
||||
lastVisited: domain.last_visited || '',
|
||||
})
|
||||
)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
referringDomains,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
referringDomains: {
|
||||
type: 'array',
|
||||
description: 'List of domains linking to the target',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
domain: { type: 'string', description: 'The referring domain' },
|
||||
domainRating: { type: 'number', description: 'Domain Rating of the referring domain' },
|
||||
backlinks: {
|
||||
type: 'number',
|
||||
description: 'Total number of backlinks from this domain',
|
||||
},
|
||||
dofollowBacklinks: {
|
||||
type: 'number',
|
||||
description: 'Number of dofollow backlinks from this domain',
|
||||
},
|
||||
firstSeen: { type: 'string', description: 'When the domain was first seen linking' },
|
||||
lastVisited: { type: 'string', description: 'When the domain was last checked' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
119
apps/sim/tools/ahrefs/top_pages.ts
Normal file
119
apps/sim/tools/ahrefs/top_pages.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import type { AhrefsTopPagesParams, AhrefsTopPagesResponse } from '@/tools/ahrefs/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const topPagesTool: ToolConfig<AhrefsTopPagesParams, AhrefsTopPagesResponse> = {
|
||||
id: 'ahrefs_top_pages',
|
||||
name: 'Ahrefs Top Pages',
|
||||
description:
|
||||
'Get the top pages of a target domain sorted by organic traffic. Returns page URLs with their traffic, keyword counts, and estimated traffic value.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
target: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The target domain to analyze',
|
||||
},
|
||||
country: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Country code for traffic data (e.g., us, gb, de). Default: us',
|
||||
},
|
||||
mode: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description:
|
||||
'Analysis mode: domain (entire domain), prefix (URL prefix), subdomains (include all subdomains)',
|
||||
},
|
||||
date: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Date for historical data in YYYY-MM-DD format (defaults to today)',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Maximum number of results to return (default: 100)',
|
||||
},
|
||||
offset: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Number of results to skip for pagination',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Ahrefs API Key',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://api.ahrefs.com/v3/site-explorer/top-pages')
|
||||
url.searchParams.set('target', params.target)
|
||||
url.searchParams.set('country', params.country || 'us')
|
||||
// Date is required - default to today if not provided
|
||||
const date = params.date || new Date().toISOString().split('T')[0]
|
||||
url.searchParams.set('date', date)
|
||||
if (params.mode) url.searchParams.set('mode', params.mode)
|
||||
if (params.limit) url.searchParams.set('limit', String(params.limit))
|
||||
if (params.offset) url.searchParams.set('offset', String(params.offset))
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error?.message || data.error || 'Failed to get top pages')
|
||||
}
|
||||
|
||||
const pages = (data.pages || data.top_pages || []).map((page: any) => ({
|
||||
url: page.url || '',
|
||||
traffic: page.traffic ?? 0,
|
||||
keywords: page.keywords ?? page.keyword_count ?? 0,
|
||||
topKeyword: page.top_keyword || '',
|
||||
value: page.value ?? page.traffic_value ?? 0,
|
||||
}))
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
pages,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
pages: {
|
||||
type: 'array',
|
||||
description: 'List of top pages by organic traffic',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
url: { type: 'string', description: 'The page URL' },
|
||||
traffic: { type: 'number', description: 'Estimated monthly organic traffic' },
|
||||
keywords: { type: 'number', description: 'Number of keywords the page ranks for' },
|
||||
topKeyword: {
|
||||
type: 'string',
|
||||
description: 'The top keyword driving traffic to this page',
|
||||
},
|
||||
value: { type: 'number', description: 'Estimated traffic value in USD' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
199
apps/sim/tools/ahrefs/types.ts
Normal file
199
apps/sim/tools/ahrefs/types.ts
Normal file
@@ -0,0 +1,199 @@
|
||||
// Common types for Ahrefs API tools
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
// Common parameters for all Ahrefs tools
|
||||
export interface AhrefsBaseParams {
|
||||
apiKey: string
|
||||
date?: string // Date in YYYY-MM-DD format, defaults to today
|
||||
}
|
||||
|
||||
// Target mode for analysis
|
||||
export type AhrefsTargetMode = 'domain' | 'prefix' | 'subdomains' | 'exact'
|
||||
|
||||
// Domain Rating tool types
|
||||
export interface AhrefsDomainRatingParams extends AhrefsBaseParams {
|
||||
target: string
|
||||
}
|
||||
|
||||
export interface AhrefsDomainRatingResult {
|
||||
domain_rating: number
|
||||
ahrefs_rank: number
|
||||
}
|
||||
|
||||
export interface AhrefsDomainRatingResponse extends ToolResponse {
|
||||
output: {
|
||||
domainRating: number
|
||||
ahrefsRank: number
|
||||
}
|
||||
}
|
||||
|
||||
// Backlinks tool types
|
||||
export interface AhrefsBacklinksParams extends AhrefsBaseParams {
|
||||
target: string
|
||||
mode?: AhrefsTargetMode
|
||||
limit?: number
|
||||
offset?: number
|
||||
}
|
||||
|
||||
export interface AhrefsBacklink {
|
||||
urlFrom: string
|
||||
urlTo: string
|
||||
anchor: string
|
||||
domainRatingSource: number
|
||||
isDofollow: boolean
|
||||
firstSeen: string
|
||||
lastVisited: string
|
||||
}
|
||||
|
||||
export interface AhrefsBacklinksResponse extends ToolResponse {
|
||||
output: {
|
||||
backlinks: AhrefsBacklink[]
|
||||
}
|
||||
}
|
||||
|
||||
// Backlinks Stats tool types
|
||||
export interface AhrefsBacklinksStatsParams extends AhrefsBaseParams {
|
||||
target: string
|
||||
mode?: AhrefsTargetMode
|
||||
}
|
||||
|
||||
export interface AhrefsBacklinksStatsResult {
|
||||
total: number
|
||||
dofollow: number
|
||||
nofollow: number
|
||||
text: number
|
||||
image: number
|
||||
redirect: number
|
||||
}
|
||||
|
||||
export interface AhrefsBacklinksStatsResponse extends ToolResponse {
|
||||
output: {
|
||||
stats: AhrefsBacklinksStatsResult
|
||||
}
|
||||
}
|
||||
|
||||
// Referring Domains tool types
|
||||
export interface AhrefsReferringDomainsParams extends AhrefsBaseParams {
|
||||
target: string
|
||||
mode?: AhrefsTargetMode
|
||||
limit?: number
|
||||
offset?: number
|
||||
}
|
||||
|
||||
export interface AhrefsReferringDomain {
|
||||
domain: string
|
||||
domainRating: number
|
||||
backlinks: number
|
||||
dofollowBacklinks: number
|
||||
firstSeen: string
|
||||
lastVisited: string
|
||||
}
|
||||
|
||||
export interface AhrefsReferringDomainsResponse extends ToolResponse {
|
||||
output: {
|
||||
referringDomains: AhrefsReferringDomain[]
|
||||
}
|
||||
}
|
||||
|
||||
// Organic Keywords tool types
|
||||
export interface AhrefsOrganicKeywordsParams extends AhrefsBaseParams {
|
||||
target: string
|
||||
country?: string
|
||||
mode?: AhrefsTargetMode
|
||||
limit?: number
|
||||
offset?: number
|
||||
}
|
||||
|
||||
export interface AhrefsOrganicKeyword {
|
||||
keyword: string
|
||||
volume: number
|
||||
position: number
|
||||
url: string
|
||||
traffic: number
|
||||
keywordDifficulty: number
|
||||
}
|
||||
|
||||
export interface AhrefsOrganicKeywordsResponse extends ToolResponse {
|
||||
output: {
|
||||
keywords: AhrefsOrganicKeyword[]
|
||||
}
|
||||
}
|
||||
|
||||
// Top Pages tool types
|
||||
export interface AhrefsTopPagesParams extends AhrefsBaseParams {
|
||||
target: string
|
||||
country?: string
|
||||
mode?: AhrefsTargetMode
|
||||
limit?: number
|
||||
offset?: number
|
||||
}
|
||||
|
||||
export interface AhrefsTopPage {
|
||||
url: string
|
||||
traffic: number
|
||||
keywords: number
|
||||
topKeyword: string
|
||||
value: number
|
||||
}
|
||||
|
||||
export interface AhrefsTopPagesResponse extends ToolResponse {
|
||||
output: {
|
||||
pages: AhrefsTopPage[]
|
||||
}
|
||||
}
|
||||
|
||||
// Keyword Overview tool types
|
||||
export interface AhrefsKeywordOverviewParams extends AhrefsBaseParams {
|
||||
keyword: string
|
||||
country?: string
|
||||
}
|
||||
|
||||
export interface AhrefsKeywordOverviewResult {
|
||||
keyword: string
|
||||
searchVolume: number
|
||||
keywordDifficulty: number
|
||||
cpc: number
|
||||
clicks: number
|
||||
clicksPercentage: number
|
||||
parentTopic: string
|
||||
trafficPotential: number
|
||||
}
|
||||
|
||||
export interface AhrefsKeywordOverviewResponse extends ToolResponse {
|
||||
output: {
|
||||
overview: AhrefsKeywordOverviewResult
|
||||
}
|
||||
}
|
||||
|
||||
// Broken Backlinks tool types
|
||||
export interface AhrefsBrokenBacklinksParams extends AhrefsBaseParams {
|
||||
target: string
|
||||
mode?: AhrefsTargetMode
|
||||
limit?: number
|
||||
offset?: number
|
||||
}
|
||||
|
||||
export interface AhrefsBrokenBacklink {
|
||||
urlFrom: string
|
||||
urlTo: string
|
||||
httpCode: number
|
||||
anchor: string
|
||||
domainRatingSource: number
|
||||
}
|
||||
|
||||
export interface AhrefsBrokenBacklinksResponse extends ToolResponse {
|
||||
output: {
|
||||
brokenBacklinks: AhrefsBrokenBacklink[]
|
||||
}
|
||||
}
|
||||
|
||||
// Union type for all possible responses
|
||||
export type AhrefsResponse =
|
||||
| AhrefsDomainRatingResponse
|
||||
| AhrefsBacklinksResponse
|
||||
| AhrefsBacklinksStatsResponse
|
||||
| AhrefsReferringDomainsResponse
|
||||
| AhrefsOrganicKeywordsResponse
|
||||
| AhrefsTopPagesResponse
|
||||
| AhrefsKeywordOverviewResponse
|
||||
| AhrefsBrokenBacklinksResponse
|
||||
76
apps/sim/tools/datadog/cancel_downtime.ts
Normal file
76
apps/sim/tools/datadog/cancel_downtime.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import type { CancelDowntimeParams, CancelDowntimeResponse } from '@/tools/datadog/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const cancelDowntimeTool: ToolConfig<CancelDowntimeParams, CancelDowntimeResponse> = {
|
||||
id: 'datadog_cancel_downtime',
|
||||
name: 'Datadog Cancel Downtime',
|
||||
description: 'Cancel a scheduled downtime.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
downtimeId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the downtime to cancel',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog API key',
|
||||
},
|
||||
applicationKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog Application key',
|
||||
},
|
||||
site: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog site/region (default: datadoghq.com)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const site = params.site || 'datadoghq.com'
|
||||
return `https://api.${site}/api/v2/downtime/${params.downtimeId}`
|
||||
},
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/json',
|
||||
'DD-API-KEY': params.apiKey,
|
||||
'DD-APPLICATION-KEY': params.applicationKey,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok && response.status !== 204) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
success: false,
|
||||
},
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the downtime was successfully canceled',
|
||||
},
|
||||
},
|
||||
}
|
||||
178
apps/sim/tools/datadog/create_downtime.ts
Normal file
178
apps/sim/tools/datadog/create_downtime.ts
Normal file
@@ -0,0 +1,178 @@
|
||||
import type { CreateDowntimeParams, CreateDowntimeResponse } from '@/tools/datadog/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const createDowntimeTool: ToolConfig<CreateDowntimeParams, CreateDowntimeResponse> = {
|
||||
id: 'datadog_create_downtime',
|
||||
name: 'Datadog Create Downtime',
|
||||
description: 'Schedule a downtime to suppress monitor notifications during maintenance windows.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
scope: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Scope to apply downtime to (e.g., "host:myhost", "env:production", or "*" for all)',
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Message to display during downtime',
|
||||
},
|
||||
start: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unix timestamp for downtime start (defaults to now)',
|
||||
},
|
||||
end: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unix timestamp for downtime end',
|
||||
},
|
||||
timezone: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Timezone for the downtime (e.g., "America/New_York")',
|
||||
},
|
||||
monitorId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Specific monitor ID to mute',
|
||||
},
|
||||
monitorTags: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated monitor tags to match (e.g., "team:backend,priority:high")',
|
||||
},
|
||||
muteFirstRecoveryNotification: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Mute the first recovery notification',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog API key',
|
||||
},
|
||||
applicationKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog Application key',
|
||||
},
|
||||
site: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog site/region (default: datadoghq.com)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const site = params.site || 'datadoghq.com'
|
||||
return `https://api.${site}/api/v2/downtime`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/json',
|
||||
'DD-API-KEY': params.apiKey,
|
||||
'DD-APPLICATION-KEY': params.applicationKey,
|
||||
}),
|
||||
body: (params) => {
|
||||
const schedule: Record<string, any> = {}
|
||||
if (params.start) schedule.start = new Date(params.start * 1000).toISOString()
|
||||
if (params.end) schedule.end = new Date(params.end * 1000).toISOString()
|
||||
if (params.timezone) schedule.timezone = params.timezone
|
||||
|
||||
const body: Record<string, any> = {
|
||||
data: {
|
||||
type: 'downtime',
|
||||
attributes: {
|
||||
scope: params.scope,
|
||||
schedule: Object.keys(schedule).length > 0 ? schedule : undefined,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if (params.message) body.data.attributes.message = params.message
|
||||
if (params.muteFirstRecoveryNotification !== undefined) {
|
||||
body.data.attributes.mute_first_recovery_notification = params.muteFirstRecoveryNotification
|
||||
}
|
||||
|
||||
if (params.monitorId) {
|
||||
body.data.attributes.monitor_identifier = {
|
||||
monitor_id: Number.parseInt(params.monitorId, 10),
|
||||
}
|
||||
} else if (params.monitorTags) {
|
||||
body.data.attributes.monitor_identifier = {
|
||||
monitor_tags: params.monitorTags
|
||||
.split(',')
|
||||
.map((t: string) => t.trim())
|
||||
.filter((t: string) => t.length > 0),
|
||||
}
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
downtime: {} as any,
|
||||
},
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const attrs = data.data?.attributes || {}
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
downtime: {
|
||||
id: data.data?.id,
|
||||
scope: attrs.scope ? [attrs.scope] : [],
|
||||
message: attrs.message,
|
||||
start: attrs.schedule?.start
|
||||
? new Date(attrs.schedule.start).getTime() / 1000
|
||||
: undefined,
|
||||
end: attrs.schedule?.end ? new Date(attrs.schedule.end).getTime() / 1000 : undefined,
|
||||
timezone: attrs.schedule?.timezone,
|
||||
disabled: attrs.disabled,
|
||||
active: attrs.status === 'active',
|
||||
created: attrs.created ? new Date(attrs.created).getTime() / 1000 : undefined,
|
||||
modified: attrs.modified ? new Date(attrs.modified).getTime() / 1000 : undefined,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
downtime: {
|
||||
type: 'object',
|
||||
description: 'The created downtime details',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'Downtime ID' },
|
||||
scope: { type: 'array', description: 'Downtime scope' },
|
||||
message: { type: 'string', description: 'Downtime message' },
|
||||
start: { type: 'number', description: 'Start time (Unix timestamp)' },
|
||||
end: { type: 'number', description: 'End time (Unix timestamp)' },
|
||||
active: { type: 'boolean', description: 'Whether downtime is currently active' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
163
apps/sim/tools/datadog/create_event.ts
Normal file
163
apps/sim/tools/datadog/create_event.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
import type { CreateEventParams, CreateEventResponse } from '@/tools/datadog/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const createEventTool: ToolConfig<CreateEventParams, CreateEventResponse> = {
|
||||
id: 'datadog_create_event',
|
||||
name: 'Datadog Create Event',
|
||||
description:
|
||||
'Post an event to the Datadog event stream. Use for deployment notifications, alerts, or any significant occurrences.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
title: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Event title',
|
||||
},
|
||||
text: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Event body/description. Supports markdown.',
|
||||
},
|
||||
alertType: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Alert type: error, warning, info, success, user_update, recommendation, or snapshot',
|
||||
},
|
||||
priority: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Event priority: normal or low',
|
||||
},
|
||||
host: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Host name to associate with this event',
|
||||
},
|
||||
tags: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of tags (e.g., "env:production,service:api")',
|
||||
},
|
||||
aggregationKey: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Key to aggregate events together',
|
||||
},
|
||||
sourceTypeName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Source type name for the event',
|
||||
},
|
||||
dateHappened: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unix timestamp when the event occurred (defaults to now)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog API key',
|
||||
},
|
||||
site: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog site/region (default: datadoghq.com)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const site = params.site || 'datadoghq.com'
|
||||
return `https://api.${site}/api/v1/events`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/json',
|
||||
'DD-API-KEY': params.apiKey,
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, any> = {
|
||||
title: params.title,
|
||||
text: params.text,
|
||||
}
|
||||
|
||||
if (params.alertType) body.alert_type = params.alertType
|
||||
if (params.priority) body.priority = params.priority
|
||||
if (params.host) body.host = params.host
|
||||
if (params.aggregationKey) body.aggregation_key = params.aggregationKey
|
||||
if (params.sourceTypeName) body.source_type_name = params.sourceTypeName
|
||||
if (params.dateHappened) body.date_happened = params.dateHappened
|
||||
|
||||
if (params.tags) {
|
||||
body.tags = params.tags
|
||||
.split(',')
|
||||
.map((t: string) => t.trim())
|
||||
.filter((t: string) => t.length > 0)
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
event: {} as any,
|
||||
},
|
||||
error: errorData.errors?.[0] || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
event: {
|
||||
id: data.event?.id,
|
||||
title: data.event?.title,
|
||||
text: data.event?.text,
|
||||
date_happened: data.event?.date_happened,
|
||||
priority: data.event?.priority,
|
||||
alert_type: data.event?.alert_type,
|
||||
host: data.event?.host,
|
||||
tags: data.event?.tags,
|
||||
url: data.event?.url,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
event: {
|
||||
type: 'object',
|
||||
description: 'The created event details',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'Event ID' },
|
||||
title: { type: 'string', description: 'Event title' },
|
||||
text: { type: 'string', description: 'Event text' },
|
||||
date_happened: { type: 'number', description: 'Unix timestamp when event occurred' },
|
||||
priority: { type: 'string', description: 'Event priority' },
|
||||
alert_type: { type: 'string', description: 'Alert type' },
|
||||
host: { type: 'string', description: 'Associated host' },
|
||||
tags: { type: 'array', description: 'Event tags' },
|
||||
url: { type: 'string', description: 'URL to view the event in Datadog' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
169
apps/sim/tools/datadog/create_monitor.ts
Normal file
169
apps/sim/tools/datadog/create_monitor.ts
Normal file
@@ -0,0 +1,169 @@
|
||||
import type { CreateMonitorParams, CreateMonitorResponse } from '@/tools/datadog/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const createMonitorTool: ToolConfig<CreateMonitorParams, CreateMonitorResponse> = {
|
||||
id: 'datadog_create_monitor',
|
||||
name: 'Datadog Create Monitor',
|
||||
description:
|
||||
'Create a new monitor/alert in Datadog. Monitors can track metrics, service checks, events, and more.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Monitor name',
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Monitor type: metric alert, service check, event alert, process alert, log alert, query alert, composite, synthetics alert, slo alert',
|
||||
},
|
||||
query: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Monitor query (e.g., "avg(last_5m):avg:system.cpu.idle{*} < 20")',
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Message to include with notifications. Can include @-mentions and markdown.',
|
||||
},
|
||||
tags: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of tags',
|
||||
},
|
||||
priority: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Monitor priority (1-5, where 1 is highest)',
|
||||
},
|
||||
options: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'JSON string of monitor options (thresholds, notify_no_data, renotify_interval, etc.)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog API key',
|
||||
},
|
||||
applicationKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog Application key',
|
||||
},
|
||||
site: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog site/region (default: datadoghq.com)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const site = params.site || 'datadoghq.com'
|
||||
return `https://api.${site}/api/v1/monitor`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/json',
|
||||
'DD-API-KEY': params.apiKey,
|
||||
'DD-APPLICATION-KEY': params.applicationKey,
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, any> = {
|
||||
name: params.name,
|
||||
type: params.type,
|
||||
query: params.query,
|
||||
}
|
||||
|
||||
if (params.message) body.message = params.message
|
||||
if (params.priority) body.priority = params.priority
|
||||
|
||||
if (params.tags) {
|
||||
body.tags = params.tags
|
||||
.split(',')
|
||||
.map((t: string) => t.trim())
|
||||
.filter((t: string) => t.length > 0)
|
||||
}
|
||||
|
||||
if (params.options) {
|
||||
try {
|
||||
body.options =
|
||||
typeof params.options === 'string' ? JSON.parse(params.options) : params.options
|
||||
} catch {
|
||||
// If options parsing fails, skip it
|
||||
}
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
monitor: {} as any,
|
||||
},
|
||||
error: errorData.errors?.[0] || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
monitor: {
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
type: data.type,
|
||||
query: data.query,
|
||||
message: data.message,
|
||||
tags: data.tags,
|
||||
priority: data.priority,
|
||||
options: data.options,
|
||||
overall_state: data.overall_state,
|
||||
created: data.created,
|
||||
modified: data.modified,
|
||||
creator: data.creator,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
monitor: {
|
||||
type: 'object',
|
||||
description: 'The created monitor details',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'Monitor ID' },
|
||||
name: { type: 'string', description: 'Monitor name' },
|
||||
type: { type: 'string', description: 'Monitor type' },
|
||||
query: { type: 'string', description: 'Monitor query' },
|
||||
message: { type: 'string', description: 'Notification message' },
|
||||
tags: { type: 'array', description: 'Monitor tags' },
|
||||
priority: { type: 'number', description: 'Monitor priority' },
|
||||
overall_state: { type: 'string', description: 'Current monitor state' },
|
||||
created: { type: 'string', description: 'Creation timestamp' },
|
||||
modified: { type: 'string', description: 'Last modification timestamp' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
120
apps/sim/tools/datadog/get_monitor.ts
Normal file
120
apps/sim/tools/datadog/get_monitor.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import type { GetMonitorParams, GetMonitorResponse } from '@/tools/datadog/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const getMonitorTool: ToolConfig<GetMonitorParams, GetMonitorResponse> = {
|
||||
id: 'datadog_get_monitor',
|
||||
name: 'Datadog Get Monitor',
|
||||
description: 'Retrieve details of a specific monitor by ID.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
monitorId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the monitor to retrieve',
|
||||
},
|
||||
groupStates: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated group states to include: alert, warn, no data, ok',
|
||||
},
|
||||
withDowntimes: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Include downtime data with the monitor',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog API key',
|
||||
},
|
||||
applicationKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog Application key',
|
||||
},
|
||||
site: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog site/region (default: datadoghq.com)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const site = params.site || 'datadoghq.com'
|
||||
const queryParams = new URLSearchParams()
|
||||
|
||||
if (params.groupStates) queryParams.set('group_states', params.groupStates)
|
||||
if (params.withDowntimes) queryParams.set('with_downtimes', 'true')
|
||||
|
||||
const queryString = queryParams.toString()
|
||||
return `https://api.${site}/api/v1/monitor/${params.monitorId}${queryString ? `?${queryString}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/json',
|
||||
'DD-API-KEY': params.apiKey,
|
||||
'DD-APPLICATION-KEY': params.applicationKey,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
monitor: {} as any,
|
||||
},
|
||||
error: errorData.errors?.[0] || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
monitor: {
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
type: data.type,
|
||||
query: data.query,
|
||||
message: data.message,
|
||||
tags: data.tags,
|
||||
priority: data.priority,
|
||||
options: data.options,
|
||||
overall_state: data.overall_state,
|
||||
created: data.created,
|
||||
modified: data.modified,
|
||||
creator: data.creator,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
monitor: {
|
||||
type: 'object',
|
||||
description: 'The monitor details',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'Monitor ID' },
|
||||
name: { type: 'string', description: 'Monitor name' },
|
||||
type: { type: 'string', description: 'Monitor type' },
|
||||
query: { type: 'string', description: 'Monitor query' },
|
||||
message: { type: 'string', description: 'Notification message' },
|
||||
tags: { type: 'array', description: 'Monitor tags' },
|
||||
priority: { type: 'number', description: 'Monitor priority' },
|
||||
overall_state: { type: 'string', description: 'Current monitor state' },
|
||||
created: { type: 'string', description: 'Creation timestamp' },
|
||||
modified: { type: 'string', description: 'Last modification timestamp' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
25
apps/sim/tools/datadog/index.ts
Normal file
25
apps/sim/tools/datadog/index.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { cancelDowntimeTool } from '@/tools/datadog/cancel_downtime'
|
||||
import { createDowntimeTool } from '@/tools/datadog/create_downtime'
|
||||
import { createEventTool } from '@/tools/datadog/create_event'
|
||||
import { createMonitorTool } from '@/tools/datadog/create_monitor'
|
||||
import { getMonitorTool } from '@/tools/datadog/get_monitor'
|
||||
import { listDowntimesTool } from '@/tools/datadog/list_downtimes'
|
||||
import { listMonitorsTool } from '@/tools/datadog/list_monitors'
|
||||
import { muteMonitorTool } from '@/tools/datadog/mute_monitor'
|
||||
import { queryLogsTool } from '@/tools/datadog/query_logs'
|
||||
import { queryTimeseriesTool } from '@/tools/datadog/query_timeseries'
|
||||
import { sendLogsTool } from '@/tools/datadog/send_logs'
|
||||
import { submitMetricsTool } from '@/tools/datadog/submit_metrics'
|
||||
|
||||
export const datadogSubmitMetricsTool = submitMetricsTool
|
||||
export const datadogQueryTimeseriesTool = queryTimeseriesTool
|
||||
export const datadogCreateEventTool = createEventTool
|
||||
export const datadogCreateMonitorTool = createMonitorTool
|
||||
export const datadogGetMonitorTool = getMonitorTool
|
||||
export const datadogListMonitorsTool = listMonitorsTool
|
||||
export const datadogMuteMonitorTool = muteMonitorTool
|
||||
export const datadogQueryLogsTool = queryLogsTool
|
||||
export const datadogSendLogsTool = sendLogsTool
|
||||
export const datadogCreateDowntimeTool = createDowntimeTool
|
||||
export const datadogListDowntimesTool = listDowntimesTool
|
||||
export const datadogCancelDowntimeTool = cancelDowntimeTool
|
||||
116
apps/sim/tools/datadog/list_downtimes.ts
Normal file
116
apps/sim/tools/datadog/list_downtimes.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import type { ListDowntimesParams, ListDowntimesResponse } from '@/tools/datadog/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listDowntimesTool: ToolConfig<ListDowntimesParams, ListDowntimesResponse> = {
|
||||
id: 'datadog_list_downtimes',
|
||||
name: 'Datadog List Downtimes',
|
||||
description: 'List all scheduled downtimes in Datadog.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
currentOnly: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only return currently active downtimes',
|
||||
},
|
||||
monitorId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by monitor ID',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog API key',
|
||||
},
|
||||
applicationKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog Application key',
|
||||
},
|
||||
site: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog site/region (default: datadoghq.com)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const site = params.site || 'datadoghq.com'
|
||||
const queryParams = new URLSearchParams()
|
||||
|
||||
if (params.currentOnly) queryParams.set('current_only', 'true')
|
||||
if (params.monitorId) queryParams.set('monitor_id', params.monitorId)
|
||||
|
||||
const queryString = queryParams.toString()
|
||||
return `https://api.${site}/api/v2/downtime${queryString ? `?${queryString}` : ''}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/json',
|
||||
'DD-API-KEY': params.apiKey,
|
||||
'DD-APPLICATION-KEY': params.applicationKey,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
downtimes: [],
|
||||
},
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const downtimes = (data.data || []).map((d: any) => {
|
||||
const attrs = d.attributes || {}
|
||||
return {
|
||||
id: d.id,
|
||||
scope: attrs.scope ? [attrs.scope] : [],
|
||||
message: attrs.message,
|
||||
start: attrs.schedule?.start ? new Date(attrs.schedule.start).getTime() / 1000 : undefined,
|
||||
end: attrs.schedule?.end ? new Date(attrs.schedule.end).getTime() / 1000 : undefined,
|
||||
timezone: attrs.schedule?.timezone,
|
||||
disabled: attrs.disabled,
|
||||
active: attrs.status === 'active',
|
||||
created: attrs.created ? new Date(attrs.created).getTime() / 1000 : undefined,
|
||||
modified: attrs.modified ? new Date(attrs.modified).getTime() / 1000 : undefined,
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
downtimes,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
downtimes: {
|
||||
type: 'array',
|
||||
description: 'List of downtimes',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'Downtime ID' },
|
||||
scope: { type: 'array', description: 'Downtime scope' },
|
||||
message: { type: 'string', description: 'Downtime message' },
|
||||
start: { type: 'number', description: 'Start time (Unix timestamp)' },
|
||||
end: { type: 'number', description: 'End time (Unix timestamp)' },
|
||||
active: { type: 'boolean', description: 'Whether downtime is currently active' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
180
apps/sim/tools/datadog/list_monitors.ts
Normal file
180
apps/sim/tools/datadog/list_monitors.ts
Normal file
@@ -0,0 +1,180 @@
|
||||
import type { ListMonitorsParams, ListMonitorsResponse } from '@/tools/datadog/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listMonitorsTool: ToolConfig<ListMonitorsParams, ListMonitorsResponse> = {
|
||||
id: 'datadog_list_monitors',
|
||||
name: 'Datadog List Monitors',
|
||||
description: 'List all monitors in Datadog with optional filtering by name, tags, or state.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
groupStates: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated group states to filter by: alert, warn, no data, ok',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter monitors by name (partial match)',
|
||||
},
|
||||
tags: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of tags to filter by',
|
||||
},
|
||||
monitorTags: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of monitor tags to filter by',
|
||||
},
|
||||
withDowntimes: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Include downtime data with monitors',
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Page number for pagination (0-indexed)',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Number of monitors per page (max 1000)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog API key',
|
||||
},
|
||||
applicationKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog Application key',
|
||||
},
|
||||
site: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog site/region (default: datadoghq.com)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const site = params.site || 'datadoghq.com'
|
||||
const queryParams = new URLSearchParams()
|
||||
|
||||
if (params.groupStates) queryParams.set('group_states', params.groupStates)
|
||||
if (params.name) queryParams.set('name', params.name)
|
||||
if (params.tags) queryParams.set('tags', params.tags)
|
||||
if (params.monitorTags) queryParams.set('monitor_tags', params.monitorTags)
|
||||
if (params.withDowntimes) queryParams.set('with_downtimes', 'true')
|
||||
if (params.page !== undefined) queryParams.set('page', String(params.page))
|
||||
if (params.pageSize) queryParams.set('page_size', String(params.pageSize))
|
||||
|
||||
const queryString = queryParams.toString()
|
||||
const url = `https://api.${site}/api/v1/monitor${queryString ? `?${queryString}` : ''}`
|
||||
console.log(
|
||||
'[Datadog List Monitors] URL:',
|
||||
url,
|
||||
'Site param:',
|
||||
params.site,
|
||||
'API Key present:',
|
||||
!!params.apiKey,
|
||||
'App Key present:',
|
||||
!!params.applicationKey
|
||||
)
|
||||
return url
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/json',
|
||||
'DD-API-KEY': params.apiKey,
|
||||
'DD-APPLICATION-KEY': params.applicationKey,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
monitors: [],
|
||||
},
|
||||
error: errorData.errors?.[0] || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const text = await response.text()
|
||||
let data: any
|
||||
try {
|
||||
data = JSON.parse(text)
|
||||
} catch (e) {
|
||||
return {
|
||||
success: false,
|
||||
output: { monitors: [] },
|
||||
error: `Failed to parse response: ${text.substring(0, 200)}`,
|
||||
}
|
||||
}
|
||||
|
||||
if (!Array.isArray(data)) {
|
||||
return {
|
||||
success: false,
|
||||
output: { monitors: [] },
|
||||
error: `Expected array but got: ${typeof data} - ${JSON.stringify(data).substring(0, 200)}`,
|
||||
}
|
||||
}
|
||||
|
||||
const monitors = data.map((m: any) => ({
|
||||
id: m.id,
|
||||
name: m.name,
|
||||
type: m.type,
|
||||
query: m.query,
|
||||
message: m.message,
|
||||
tags: m.tags,
|
||||
priority: m.priority,
|
||||
options: m.options,
|
||||
overall_state: m.overall_state,
|
||||
created: m.created,
|
||||
modified: m.modified,
|
||||
creator: m.creator,
|
||||
}))
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
monitors,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
monitors: {
|
||||
type: 'array',
|
||||
description: 'List of monitors',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'number', description: 'Monitor ID' },
|
||||
name: { type: 'string', description: 'Monitor name' },
|
||||
type: { type: 'string', description: 'Monitor type' },
|
||||
query: { type: 'string', description: 'Monitor query' },
|
||||
overall_state: { type: 'string', description: 'Current state' },
|
||||
tags: { type: 'array', description: 'Tags' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
94
apps/sim/tools/datadog/mute_monitor.ts
Normal file
94
apps/sim/tools/datadog/mute_monitor.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import type { MuteMonitorParams, MuteMonitorResponse } from '@/tools/datadog/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const muteMonitorTool: ToolConfig<MuteMonitorParams, MuteMonitorResponse> = {
|
||||
id: 'datadog_mute_monitor',
|
||||
name: 'Datadog Mute Monitor',
|
||||
description: 'Mute a monitor to temporarily suppress notifications.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
monitorId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The ID of the monitor to mute',
|
||||
},
|
||||
scope: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Scope to mute (e.g., "host:myhost"). If not specified, mutes all scopes.',
|
||||
},
|
||||
end: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unix timestamp when the mute should end. If not specified, mutes indefinitely.',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog API key',
|
||||
},
|
||||
applicationKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog Application key',
|
||||
},
|
||||
site: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog site/region (default: datadoghq.com)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const site = params.site || 'datadoghq.com'
|
||||
return `https://api.${site}/api/v1/monitor/${params.monitorId}/mute`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/json',
|
||||
'DD-API-KEY': params.apiKey,
|
||||
'DD-APPLICATION-KEY': params.applicationKey,
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, any> = {}
|
||||
if (params.scope) body.scope = params.scope
|
||||
if (params.end) body.end = params.end
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
success: false,
|
||||
},
|
||||
error: errorData.errors?.[0] || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the monitor was successfully muted',
|
||||
},
|
||||
},
|
||||
}
|
||||
169
apps/sim/tools/datadog/query_logs.ts
Normal file
169
apps/sim/tools/datadog/query_logs.ts
Normal file
@@ -0,0 +1,169 @@
|
||||
import type { QueryLogsParams, QueryLogsResponse } from '@/tools/datadog/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const queryLogsTool: ToolConfig<QueryLogsParams, QueryLogsResponse> = {
|
||||
id: 'datadog_query_logs',
|
||||
name: 'Datadog Query Logs',
|
||||
description:
|
||||
'Search and retrieve logs from Datadog. Use for troubleshooting, analysis, or monitoring.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
query: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Log search query (e.g., "service:web-app status:error")',
|
||||
},
|
||||
from: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Start time in ISO-8601 format or relative (e.g., "now-1h")',
|
||||
},
|
||||
to: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'End time in ISO-8601 format or relative (e.g., "now")',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of logs to return (default: 50, max: 1000)',
|
||||
},
|
||||
sort: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort order: timestamp (oldest first) or -timestamp (newest first)',
|
||||
},
|
||||
indexes: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Comma-separated list of log indexes to search',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog API key',
|
||||
},
|
||||
applicationKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog Application key',
|
||||
},
|
||||
site: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog site/region (default: datadoghq.com)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const site = params.site || 'datadoghq.com'
|
||||
return `https://api.${site}/api/v2/logs/events/search`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/json',
|
||||
'DD-API-KEY': params.apiKey,
|
||||
'DD-APPLICATION-KEY': params.applicationKey,
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, any> = {
|
||||
filter: {
|
||||
query: params.query,
|
||||
from: params.from,
|
||||
to: params.to,
|
||||
},
|
||||
page: {
|
||||
limit: params.limit || 50,
|
||||
},
|
||||
}
|
||||
|
||||
if (params.sort) {
|
||||
body.sort = params.sort
|
||||
}
|
||||
|
||||
if (params.indexes) {
|
||||
body.filter.indexes = params.indexes
|
||||
.split(',')
|
||||
.map((i: string) => i.trim())
|
||||
.filter((i: string) => i.length > 0)
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
logs: [],
|
||||
},
|
||||
error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const logs = (data.data || []).map((log: any) => ({
|
||||
id: log.id,
|
||||
content: {
|
||||
timestamp: log.attributes?.timestamp,
|
||||
host: log.attributes?.host,
|
||||
service: log.attributes?.service,
|
||||
message: log.attributes?.message,
|
||||
status: log.attributes?.status,
|
||||
attributes: log.attributes?.attributes,
|
||||
tags: log.attributes?.tags,
|
||||
},
|
||||
}))
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
logs,
|
||||
nextLogId: data.meta?.page?.after,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
logs: {
|
||||
type: 'array',
|
||||
description: 'List of log entries',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Log ID' },
|
||||
content: {
|
||||
type: 'object',
|
||||
description: 'Log content',
|
||||
properties: {
|
||||
timestamp: { type: 'string', description: 'Log timestamp' },
|
||||
host: { type: 'string', description: 'Host name' },
|
||||
service: { type: 'string', description: 'Service name' },
|
||||
message: { type: 'string', description: 'Log message' },
|
||||
status: { type: 'string', description: 'Log status/level' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
nextLogId: {
|
||||
type: 'string',
|
||||
description: 'Cursor for pagination',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
110
apps/sim/tools/datadog/query_timeseries.ts
Normal file
110
apps/sim/tools/datadog/query_timeseries.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import type { QueryTimeseriesParams, QueryTimeseriesResponse } from '@/tools/datadog/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const queryTimeseriesTool: ToolConfig<QueryTimeseriesParams, QueryTimeseriesResponse> = {
|
||||
id: 'datadog_query_timeseries',
|
||||
name: 'Datadog Query Timeseries',
|
||||
description:
|
||||
'Query metric timeseries data from Datadog. Use for analyzing trends, creating reports, or retrieving metric values.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
query: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Datadog metrics query (e.g., "avg:system.cpu.user{*}")',
|
||||
},
|
||||
from: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Start time as Unix timestamp in seconds',
|
||||
},
|
||||
to: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'End time as Unix timestamp in seconds',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog API key',
|
||||
},
|
||||
applicationKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog Application key',
|
||||
},
|
||||
site: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog site/region (default: datadoghq.com)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const site = params.site || 'datadoghq.com'
|
||||
const queryParams = new URLSearchParams({
|
||||
query: params.query,
|
||||
from: String(params.from),
|
||||
to: String(params.to),
|
||||
})
|
||||
return `https://api.${site}/api/v1/query?${queryParams.toString()}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/json',
|
||||
'DD-API-KEY': params.apiKey,
|
||||
'DD-APPLICATION-KEY': params.applicationKey,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
series: [],
|
||||
status: 'error',
|
||||
},
|
||||
error: errorData.errors?.[0] || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const series = (data.series || []).map((s: any) => ({
|
||||
metric: s.metric || s.expression,
|
||||
tags: s.tag_set || [],
|
||||
points: (s.pointlist || []).map((p: [number, number]) => ({
|
||||
timestamp: p[0] / 1000, // Convert from milliseconds to seconds
|
||||
value: p[1],
|
||||
})),
|
||||
}))
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
series,
|
||||
status: data.status || 'ok',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
series: {
|
||||
type: 'array',
|
||||
description: 'Array of timeseries data with metric name, tags, and data points',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Query status',
|
||||
},
|
||||
},
|
||||
}
|
||||
94
apps/sim/tools/datadog/send_logs.ts
Normal file
94
apps/sim/tools/datadog/send_logs.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import type { SendLogsParams, SendLogsResponse } from '@/tools/datadog/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const sendLogsTool: ToolConfig<SendLogsParams, SendLogsResponse> = {
|
||||
id: 'datadog_send_logs',
|
||||
name: 'Datadog Send Logs',
|
||||
description: 'Send log entries to Datadog for centralized logging and analysis.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
logs: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'JSON array of log entries. Each entry should have message and optionally ddsource, ddtags, hostname, service.',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog API key',
|
||||
},
|
||||
site: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog site/region (default: datadoghq.com)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const site = params.site || 'datadoghq.com'
|
||||
// Logs API uses a different subdomain
|
||||
const logsHost =
|
||||
site === 'datadoghq.com'
|
||||
? 'http-intake.logs.datadoghq.com'
|
||||
: site === 'datadoghq.eu'
|
||||
? 'http-intake.logs.datadoghq.eu'
|
||||
: `http-intake.logs.${site}`
|
||||
return `https://${logsHost}/api/v2/logs`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/json',
|
||||
'DD-API-KEY': params.apiKey,
|
||||
}),
|
||||
body: (params) => {
|
||||
let logs: any[]
|
||||
try {
|
||||
logs = typeof params.logs === 'string' ? JSON.parse(params.logs) : params.logs
|
||||
} catch {
|
||||
throw new Error('Invalid JSON in logs parameter')
|
||||
}
|
||||
|
||||
// Ensure each log entry has the required format
|
||||
return logs.map((log: any) => ({
|
||||
ddsource: log.ddsource || 'custom',
|
||||
ddtags: log.ddtags || '',
|
||||
hostname: log.hostname || '',
|
||||
message: log.message,
|
||||
service: log.service || '',
|
||||
}))
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
success: false,
|
||||
},
|
||||
error: errorData.errors?.[0] || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the logs were sent successfully',
|
||||
},
|
||||
},
|
||||
}
|
||||
101
apps/sim/tools/datadog/submit_metrics.ts
Normal file
101
apps/sim/tools/datadog/submit_metrics.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import type { SubmitMetricsParams, SubmitMetricsResponse } from '@/tools/datadog/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const submitMetricsTool: ToolConfig<SubmitMetricsParams, SubmitMetricsResponse> = {
|
||||
id: 'datadog_submit_metrics',
|
||||
name: 'Datadog Submit Metrics',
|
||||
description:
|
||||
'Submit custom metrics to Datadog. Use for tracking application performance, business metrics, or custom monitoring data.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
series: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'JSON array of metric series to submit. Each series should include metric name, type (gauge/rate/count), points (timestamp/value pairs), and optional tags.',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog API key',
|
||||
},
|
||||
site: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Datadog site/region (default: datadoghq.com)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const site = params.site || 'datadoghq.com'
|
||||
return `https://api.${site}/api/v2/series`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/json',
|
||||
'DD-API-KEY': params.apiKey,
|
||||
}),
|
||||
body: (params) => {
|
||||
let series: any[]
|
||||
try {
|
||||
series = typeof params.series === 'string' ? JSON.parse(params.series) : params.series
|
||||
} catch {
|
||||
throw new Error('Invalid JSON in series parameter')
|
||||
}
|
||||
|
||||
// Transform to Datadog API v2 format
|
||||
const formattedSeries = series.map((s: any) => ({
|
||||
metric: s.metric,
|
||||
type: s.type === 'gauge' ? 0 : s.type === 'rate' ? 1 : s.type === 'count' ? 2 : 3,
|
||||
points: s.points.map((p: any) => ({
|
||||
timestamp: p.timestamp,
|
||||
value: p.value,
|
||||
})),
|
||||
tags: s.tags || [],
|
||||
unit: s.unit,
|
||||
resources: s.resources || [{ name: 'host', type: 'host' }],
|
||||
}))
|
||||
|
||||
return { series: formattedSeries }
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
success: false,
|
||||
errors: [errorData.errors?.[0] || `HTTP ${response.status}: ${response.statusText}`],
|
||||
},
|
||||
error: errorData.errors?.[0] || `HTTP ${response.status}: ${response.statusText}`,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json().catch(() => ({}))
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
success: true,
|
||||
errors: data.errors || [],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the metrics were submitted successfully',
|
||||
},
|
||||
errors: {
|
||||
type: 'array',
|
||||
description: 'Any errors that occurred during submission',
|
||||
},
|
||||
},
|
||||
}
|
||||
782
apps/sim/tools/datadog/types.ts
Normal file
782
apps/sim/tools/datadog/types.ts
Normal file
@@ -0,0 +1,782 @@
|
||||
// Common types for Datadog tools
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
// Datadog Site/Region options
|
||||
export type DatadogSite =
|
||||
| 'datadoghq.com'
|
||||
| 'us3.datadoghq.com'
|
||||
| 'us5.datadoghq.com'
|
||||
| 'datadoghq.eu'
|
||||
| 'ap1.datadoghq.com'
|
||||
| 'ddog-gov.com'
|
||||
|
||||
// Base parameters for write-only operations (only need API key)
|
||||
export interface DatadogWriteOnlyParams {
|
||||
apiKey: string
|
||||
site?: DatadogSite
|
||||
}
|
||||
|
||||
// Base parameters for read/manage operations (need both API key and Application key)
|
||||
export interface DatadogBaseParams extends DatadogWriteOnlyParams {
|
||||
applicationKey: string
|
||||
}
|
||||
|
||||
// ========================
|
||||
// METRICS TYPES
|
||||
// ========================
|
||||
|
||||
export type MetricType = 'gauge' | 'rate' | 'count' | 'distribution'
|
||||
|
||||
export interface MetricPoint {
|
||||
timestamp: number
|
||||
value: number
|
||||
}
|
||||
|
||||
export interface MetricSeries {
|
||||
metric: string
|
||||
type?: MetricType
|
||||
points: MetricPoint[]
|
||||
tags?: string[]
|
||||
unit?: string
|
||||
resources?: { name: string; type: string }[]
|
||||
}
|
||||
|
||||
export interface SubmitMetricsParams extends DatadogWriteOnlyParams {
|
||||
series: string // JSON string of MetricSeries[]
|
||||
}
|
||||
|
||||
export interface SubmitMetricsOutput {
|
||||
success: boolean
|
||||
errors?: string[]
|
||||
}
|
||||
|
||||
export interface SubmitMetricsResponse extends ToolResponse {
|
||||
output: SubmitMetricsOutput
|
||||
}
|
||||
|
||||
export interface QueryTimeseriesParams extends DatadogBaseParams {
|
||||
query: string
|
||||
from: number // Unix timestamp in seconds
|
||||
to: number // Unix timestamp in seconds
|
||||
}
|
||||
|
||||
export interface TimeseriesPoint {
|
||||
timestamp: number
|
||||
value: number
|
||||
}
|
||||
|
||||
export interface TimeseriesResult {
|
||||
metric: string
|
||||
tags: string[]
|
||||
points: TimeseriesPoint[]
|
||||
}
|
||||
|
||||
export interface QueryTimeseriesOutput {
|
||||
series: TimeseriesResult[]
|
||||
status: string
|
||||
}
|
||||
|
||||
export interface QueryTimeseriesResponse extends ToolResponse {
|
||||
output: QueryTimeseriesOutput
|
||||
}
|
||||
|
||||
export interface ListMetricsParams extends DatadogBaseParams {
|
||||
from?: number // Unix timestamp - only return metrics active since this time
|
||||
host?: string // Filter by host name
|
||||
tags?: string // Filter by tags (comma-separated)
|
||||
}
|
||||
|
||||
export interface ListMetricsOutput {
|
||||
metrics: string[]
|
||||
}
|
||||
|
||||
export interface ListMetricsResponse extends ToolResponse {
|
||||
output: ListMetricsOutput
|
||||
}
|
||||
|
||||
export interface GetMetricMetadataParams extends DatadogBaseParams {
|
||||
metricName: string
|
||||
}
|
||||
|
||||
export interface MetricMetadata {
|
||||
description?: string
|
||||
short_name?: string
|
||||
unit?: string
|
||||
per_unit?: string
|
||||
type?: string
|
||||
integration?: string
|
||||
}
|
||||
|
||||
export interface GetMetricMetadataOutput {
|
||||
metadata: MetricMetadata
|
||||
}
|
||||
|
||||
export interface GetMetricMetadataResponse extends ToolResponse {
|
||||
output: GetMetricMetadataOutput
|
||||
}
|
||||
|
||||
// ========================
|
||||
// EVENTS TYPES
|
||||
// ========================
|
||||
|
||||
export type EventAlertType =
|
||||
| 'error'
|
||||
| 'warning'
|
||||
| 'info'
|
||||
| 'success'
|
||||
| 'user_update'
|
||||
| 'recommendation'
|
||||
| 'snapshot'
|
||||
export type EventPriority = 'normal' | 'low'
|
||||
|
||||
export interface CreateEventParams extends DatadogWriteOnlyParams {
|
||||
title: string
|
||||
text: string
|
||||
alertType?: EventAlertType
|
||||
priority?: EventPriority
|
||||
host?: string
|
||||
tags?: string // Comma-separated tags
|
||||
aggregationKey?: string
|
||||
sourceTypeName?: string
|
||||
dateHappened?: number // Unix timestamp
|
||||
}
|
||||
|
||||
export interface EventData {
|
||||
id: number
|
||||
title: string
|
||||
text: string
|
||||
date_happened: number
|
||||
priority: string
|
||||
alert_type: string
|
||||
host?: string
|
||||
tags?: string[]
|
||||
url?: string
|
||||
}
|
||||
|
||||
export interface CreateEventOutput {
|
||||
event: EventData
|
||||
}
|
||||
|
||||
export interface CreateEventResponse extends ToolResponse {
|
||||
output: CreateEventOutput
|
||||
}
|
||||
|
||||
export interface GetEventParams extends DatadogBaseParams {
|
||||
eventId: string
|
||||
}
|
||||
|
||||
export interface GetEventOutput {
|
||||
event: EventData
|
||||
}
|
||||
|
||||
export interface GetEventResponse extends ToolResponse {
|
||||
output: GetEventOutput
|
||||
}
|
||||
|
||||
export interface QueryEventsParams extends DatadogBaseParams {
|
||||
start: number // Unix timestamp
|
||||
end: number // Unix timestamp
|
||||
priority?: EventPriority
|
||||
sources?: string // Comma-separated source names
|
||||
tags?: string // Comma-separated tags
|
||||
unaggregated?: boolean
|
||||
excludeAggregate?: boolean
|
||||
page?: number
|
||||
}
|
||||
|
||||
export interface QueryEventsOutput {
|
||||
events: EventData[]
|
||||
}
|
||||
|
||||
export interface QueryEventsResponse extends ToolResponse {
|
||||
output: QueryEventsOutput
|
||||
}
|
||||
|
||||
// ========================
|
||||
// MONITORS TYPES
|
||||
// ========================
|
||||
|
||||
export type MonitorType =
|
||||
| 'metric alert'
|
||||
| 'service check'
|
||||
| 'event alert'
|
||||
| 'process alert'
|
||||
| 'log alert'
|
||||
| 'query alert'
|
||||
| 'composite'
|
||||
| 'synthetics alert'
|
||||
| 'trace-analytics alert'
|
||||
| 'slo alert'
|
||||
|
||||
export interface MonitorThresholds {
|
||||
critical?: number
|
||||
critical_recovery?: number
|
||||
warning?: number
|
||||
warning_recovery?: number
|
||||
ok?: number
|
||||
}
|
||||
|
||||
export interface MonitorOptions {
|
||||
notify_no_data?: boolean
|
||||
no_data_timeframe?: number
|
||||
notify_audit?: boolean
|
||||
renotify_interval?: number
|
||||
escalation_message?: string
|
||||
thresholds?: MonitorThresholds
|
||||
include_tags?: boolean
|
||||
require_full_window?: boolean
|
||||
timeout_h?: number
|
||||
evaluation_delay?: number
|
||||
new_group_delay?: number
|
||||
min_location_failed?: number
|
||||
}
|
||||
|
||||
export interface CreateMonitorParams extends DatadogBaseParams {
|
||||
name: string
|
||||
type: MonitorType
|
||||
query: string
|
||||
message?: string
|
||||
tags?: string // Comma-separated tags
|
||||
priority?: number // 1-5
|
||||
options?: string // JSON string of MonitorOptions
|
||||
}
|
||||
|
||||
export interface MonitorData {
|
||||
id: number
|
||||
name: string
|
||||
type: string
|
||||
query: string
|
||||
message?: string
|
||||
tags?: string[]
|
||||
priority?: number
|
||||
options?: MonitorOptions
|
||||
overall_state?: string
|
||||
created?: string
|
||||
modified?: string
|
||||
creator?: { email: string; handle: string; name: string }
|
||||
}
|
||||
|
||||
export interface CreateMonitorOutput {
|
||||
monitor: MonitorData
|
||||
}
|
||||
|
||||
export interface CreateMonitorResponse extends ToolResponse {
|
||||
output: CreateMonitorOutput
|
||||
}
|
||||
|
||||
export interface GetMonitorParams extends DatadogBaseParams {
|
||||
monitorId: string
|
||||
groupStates?: string // Comma-separated states: alert, warn, no data
|
||||
withDowntimes?: boolean
|
||||
}
|
||||
|
||||
export interface GetMonitorOutput {
|
||||
monitor: MonitorData
|
||||
}
|
||||
|
||||
export interface GetMonitorResponse extends ToolResponse {
|
||||
output: GetMonitorOutput
|
||||
}
|
||||
|
||||
export interface UpdateMonitorParams extends DatadogBaseParams {
|
||||
monitorId: string
|
||||
name?: string
|
||||
query?: string
|
||||
message?: string
|
||||
tags?: string // Comma-separated tags
|
||||
priority?: number
|
||||
options?: string // JSON string of MonitorOptions
|
||||
}
|
||||
|
||||
export interface UpdateMonitorOutput {
|
||||
monitor: MonitorData
|
||||
}
|
||||
|
||||
export interface UpdateMonitorResponse extends ToolResponse {
|
||||
output: UpdateMonitorOutput
|
||||
}
|
||||
|
||||
export interface DeleteMonitorParams extends DatadogBaseParams {
|
||||
monitorId: string
|
||||
force?: boolean
|
||||
}
|
||||
|
||||
export interface DeleteMonitorOutput {
|
||||
deleted_monitor_id: number
|
||||
}
|
||||
|
||||
export interface DeleteMonitorResponse extends ToolResponse {
|
||||
output: DeleteMonitorOutput
|
||||
}
|
||||
|
||||
export interface ListMonitorsParams extends DatadogBaseParams {
|
||||
groupStates?: string // Comma-separated states
|
||||
name?: string // Filter by name
|
||||
tags?: string // Filter by tags (comma-separated)
|
||||
monitorTags?: string // Filter by monitor tags
|
||||
withDowntimes?: boolean
|
||||
idOffset?: number
|
||||
page?: number
|
||||
pageSize?: number
|
||||
}
|
||||
|
||||
export interface ListMonitorsOutput {
|
||||
monitors: MonitorData[]
|
||||
}
|
||||
|
||||
export interface ListMonitorsResponse extends ToolResponse {
|
||||
output: ListMonitorsOutput
|
||||
}
|
||||
|
||||
export interface MuteMonitorParams extends DatadogBaseParams {
|
||||
monitorId: string
|
||||
scope?: string // Scope to mute (e.g., "host:myhost")
|
||||
end?: number // Unix timestamp when mute ends
|
||||
}
|
||||
|
||||
export interface MuteMonitorOutput {
|
||||
success: boolean
|
||||
}
|
||||
|
||||
export interface MuteMonitorResponse extends ToolResponse {
|
||||
output: MuteMonitorOutput
|
||||
}
|
||||
|
||||
export interface UnmuteMonitorParams extends DatadogBaseParams {
|
||||
monitorId: string
|
||||
scope?: string
|
||||
allScopes?: boolean
|
||||
}
|
||||
|
||||
export interface UnmuteMonitorOutput {
|
||||
success: boolean
|
||||
}
|
||||
|
||||
export interface UnmuteMonitorResponse extends ToolResponse {
|
||||
output: UnmuteMonitorOutput
|
||||
}
|
||||
|
||||
// ========================
|
||||
// LOGS TYPES
|
||||
// ========================
|
||||
|
||||
export interface LogEntry {
|
||||
ddsource?: string
|
||||
ddtags?: string
|
||||
hostname?: string
|
||||
message: string
|
||||
service?: string
|
||||
}
|
||||
|
||||
export interface SendLogsParams extends DatadogWriteOnlyParams {
|
||||
logs: string // JSON string of LogEntry[]
|
||||
}
|
||||
|
||||
export interface SendLogsOutput {
|
||||
success: boolean
|
||||
}
|
||||
|
||||
export interface SendLogsResponse extends ToolResponse {
|
||||
output: SendLogsOutput
|
||||
}
|
||||
|
||||
export interface QueryLogsParams extends DatadogBaseParams {
|
||||
query: string
|
||||
from: string // ISO-8601 or relative (now-1h)
|
||||
to: string // ISO-8601 or relative (now)
|
||||
limit?: number
|
||||
sort?: 'timestamp' | '-timestamp'
|
||||
indexes?: string // Comma-separated index names
|
||||
}
|
||||
|
||||
export interface LogData {
|
||||
id: string
|
||||
content: {
|
||||
timestamp: string
|
||||
host?: string
|
||||
service?: string
|
||||
message: string
|
||||
status?: string
|
||||
attributes?: Record<string, any>
|
||||
tags?: string[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface QueryLogsOutput {
|
||||
logs: LogData[]
|
||||
nextLogId?: string
|
||||
}
|
||||
|
||||
export interface QueryLogsResponse extends ToolResponse {
|
||||
output: QueryLogsOutput
|
||||
}
|
||||
|
||||
// ========================
|
||||
// DOWNTIME TYPES
|
||||
// ========================
|
||||
|
||||
export interface CreateDowntimeParams extends DatadogBaseParams {
|
||||
scope: string // Scope to apply downtime (e.g., "host:myhost" or "*")
|
||||
message?: string
|
||||
start?: number // Unix timestamp, defaults to now
|
||||
end?: number // Unix timestamp
|
||||
timezone?: string
|
||||
monitorId?: string // Monitor ID to mute
|
||||
monitorTags?: string // Comma-separated tags to match monitors
|
||||
muteFirstRecoveryNotification?: boolean
|
||||
notifyEndTypes?: string // Comma-separated: "canceled", "expired"
|
||||
recurrence?: string // JSON string of recurrence config
|
||||
}
|
||||
|
||||
export interface DowntimeData {
|
||||
id: number
|
||||
scope: string[]
|
||||
message?: string
|
||||
start?: number
|
||||
end?: number
|
||||
timezone?: string
|
||||
monitor_id?: number
|
||||
monitor_tags?: string[]
|
||||
mute_first_recovery_notification?: boolean
|
||||
disabled?: boolean
|
||||
created?: number
|
||||
modified?: number
|
||||
creator_id?: number
|
||||
canceled?: number
|
||||
active?: boolean
|
||||
}
|
||||
|
||||
export interface CreateDowntimeOutput {
|
||||
downtime: DowntimeData
|
||||
}
|
||||
|
||||
export interface CreateDowntimeResponse extends ToolResponse {
|
||||
output: CreateDowntimeOutput
|
||||
}
|
||||
|
||||
export interface ListDowntimesParams extends DatadogBaseParams {
|
||||
currentOnly?: boolean
|
||||
withCreator?: boolean
|
||||
monitorId?: string
|
||||
}
|
||||
|
||||
export interface ListDowntimesOutput {
|
||||
downtimes: DowntimeData[]
|
||||
}
|
||||
|
||||
export interface ListDowntimesResponse extends ToolResponse {
|
||||
output: ListDowntimesOutput
|
||||
}
|
||||
|
||||
export interface CancelDowntimeParams extends DatadogBaseParams {
|
||||
downtimeId: string
|
||||
}
|
||||
|
||||
export interface CancelDowntimeOutput {
|
||||
success: boolean
|
||||
}
|
||||
|
||||
export interface CancelDowntimeResponse extends ToolResponse {
|
||||
output: CancelDowntimeOutput
|
||||
}
|
||||
|
||||
// ========================
|
||||
// SLO TYPES
|
||||
// ========================
|
||||
|
||||
export type SloType = 'metric' | 'monitor' | 'time_slice'
|
||||
|
||||
export interface SloThreshold {
|
||||
timeframe: '7d' | '30d' | '90d' | 'custom'
|
||||
target: number // Target percentage (e.g., 99.9)
|
||||
target_display?: string
|
||||
warning?: number
|
||||
warning_display?: string
|
||||
}
|
||||
|
||||
export interface CreateSloParams extends DatadogBaseParams {
|
||||
name: string
|
||||
type: SloType
|
||||
description?: string
|
||||
tags?: string // Comma-separated tags
|
||||
thresholds: string // JSON string of SloThreshold[]
|
||||
// For metric-based SLO
|
||||
query?: string // JSON string of { numerator: string, denominator: string }
|
||||
// For monitor-based SLO
|
||||
monitorIds?: string // Comma-separated monitor IDs
|
||||
groups?: string // Comma-separated group names
|
||||
}
|
||||
|
||||
export interface SloData {
|
||||
id: string
|
||||
name: string
|
||||
type: string
|
||||
description?: string
|
||||
tags?: string[]
|
||||
thresholds: SloThreshold[]
|
||||
creator?: { email: string; handle: string; name: string }
|
||||
created_at?: number
|
||||
modified_at?: number
|
||||
}
|
||||
|
||||
export interface CreateSloOutput {
|
||||
slo: SloData
|
||||
}
|
||||
|
||||
export interface CreateSloResponse extends ToolResponse {
|
||||
output: CreateSloOutput
|
||||
}
|
||||
|
||||
export interface GetSloHistoryParams extends DatadogBaseParams {
|
||||
sloId: string
|
||||
fromTs: number // Unix timestamp
|
||||
toTs: number // Unix timestamp
|
||||
target?: number // Target SLO percentage
|
||||
}
|
||||
|
||||
export interface SloHistoryData {
|
||||
from_ts: number
|
||||
to_ts: number
|
||||
type: string
|
||||
type_id: number
|
||||
sli_value?: number
|
||||
overall: {
|
||||
name: string
|
||||
sli_value: number
|
||||
span_precision: number
|
||||
precision: { [key: string]: number }
|
||||
}
|
||||
series?: {
|
||||
times: number[]
|
||||
values: number[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface GetSloHistoryOutput {
|
||||
history: SloHistoryData
|
||||
}
|
||||
|
||||
export interface GetSloHistoryResponse extends ToolResponse {
|
||||
output: GetSloHistoryOutput
|
||||
}
|
||||
|
||||
// ========================
|
||||
// DASHBOARD TYPES
|
||||
// ========================
|
||||
|
||||
export type DashboardLayoutType = 'ordered' | 'free'
|
||||
|
||||
export interface CreateDashboardParams extends DatadogBaseParams {
|
||||
title: string
|
||||
layoutType: DashboardLayoutType
|
||||
description?: string
|
||||
widgets?: string // JSON string of widget definitions
|
||||
isReadOnly?: boolean
|
||||
notifyList?: string // Comma-separated user handles to notify
|
||||
templateVariables?: string // JSON string of template variable definitions
|
||||
tags?: string // Comma-separated tags
|
||||
}
|
||||
|
||||
export interface DashboardData {
|
||||
id: string
|
||||
title: string
|
||||
layout_type: string
|
||||
description?: string
|
||||
url?: string
|
||||
author_handle?: string
|
||||
created_at?: string
|
||||
modified_at?: string
|
||||
is_read_only?: boolean
|
||||
tags?: string[]
|
||||
}
|
||||
|
||||
export interface CreateDashboardOutput {
|
||||
dashboard: DashboardData
|
||||
}
|
||||
|
||||
export interface CreateDashboardResponse extends ToolResponse {
|
||||
output: CreateDashboardOutput
|
||||
}
|
||||
|
||||
export interface GetDashboardParams extends DatadogBaseParams {
|
||||
dashboardId: string
|
||||
}
|
||||
|
||||
export interface GetDashboardOutput {
|
||||
dashboard: DashboardData
|
||||
}
|
||||
|
||||
export interface GetDashboardResponse extends ToolResponse {
|
||||
output: GetDashboardOutput
|
||||
}
|
||||
|
||||
export interface ListDashboardsParams extends DatadogBaseParams {
|
||||
filterShared?: boolean
|
||||
filterDeleted?: boolean
|
||||
count?: number
|
||||
start?: number
|
||||
}
|
||||
|
||||
export interface DashboardSummary {
|
||||
id: string
|
||||
title: string
|
||||
description?: string
|
||||
layout_type: string
|
||||
url?: string
|
||||
author_handle?: string
|
||||
created_at?: string
|
||||
modified_at?: string
|
||||
is_read_only?: boolean
|
||||
popularity?: number
|
||||
}
|
||||
|
||||
export interface ListDashboardsOutput {
|
||||
dashboards: DashboardSummary[]
|
||||
total?: number
|
||||
}
|
||||
|
||||
export interface ListDashboardsResponse extends ToolResponse {
|
||||
output: ListDashboardsOutput
|
||||
}
|
||||
|
||||
// ========================
|
||||
// HOSTS TYPES
|
||||
// ========================
|
||||
|
||||
export interface ListHostsParams extends DatadogBaseParams {
|
||||
filter?: string // Filter hosts by name, alias, or tag
|
||||
sortField?: string // Field to sort by
|
||||
sortDir?: 'asc' | 'desc'
|
||||
start?: number // Starting offset
|
||||
count?: number // Max hosts to return
|
||||
from?: number // Unix timestamp - hosts seen in last N seconds
|
||||
includeMutedHostsData?: boolean
|
||||
includeHostsMetadata?: boolean
|
||||
}
|
||||
|
||||
export interface HostData {
|
||||
name: string
|
||||
id: number
|
||||
aliases?: string[]
|
||||
apps?: string[]
|
||||
aws_name?: string
|
||||
host_name?: string
|
||||
is_muted?: boolean
|
||||
last_reported_time?: number
|
||||
meta?: {
|
||||
agent_version?: string
|
||||
cpu_cores?: number
|
||||
gohai?: string
|
||||
machine?: string
|
||||
platform?: string
|
||||
}
|
||||
metrics?: {
|
||||
cpu?: number
|
||||
iowait?: number
|
||||
load?: number
|
||||
}
|
||||
mute_timeout?: number
|
||||
sources?: string[]
|
||||
tags_by_source?: Record<string, string[]>
|
||||
up?: boolean
|
||||
}
|
||||
|
||||
export interface ListHostsOutput {
|
||||
hosts: HostData[]
|
||||
total_matching?: number
|
||||
total_returned?: number
|
||||
}
|
||||
|
||||
export interface ListHostsResponse extends ToolResponse {
|
||||
output: ListHostsOutput
|
||||
}
|
||||
|
||||
// ========================
|
||||
// INCIDENTS TYPES
|
||||
// ========================
|
||||
|
||||
export type IncidentSeverity = 'SEV-1' | 'SEV-2' | 'SEV-3' | 'SEV-4' | 'SEV-5' | 'UNKNOWN'
|
||||
export type IncidentState = 'active' | 'stable' | 'resolved'
|
||||
|
||||
export interface CreateIncidentParams extends DatadogBaseParams {
|
||||
title: string
|
||||
customerImpacted: boolean
|
||||
severity?: IncidentSeverity
|
||||
fields?: string // JSON string of additional fields
|
||||
}
|
||||
|
||||
export interface IncidentData {
|
||||
id: string
|
||||
type: string
|
||||
attributes: {
|
||||
title: string
|
||||
customer_impacted: boolean
|
||||
severity?: IncidentSeverity
|
||||
state?: IncidentState
|
||||
created?: string
|
||||
modified?: string
|
||||
resolved?: string
|
||||
detected?: string
|
||||
customer_impact_scope?: string
|
||||
customer_impact_start?: string
|
||||
customer_impact_end?: string
|
||||
public_id?: number
|
||||
time_to_detect?: number
|
||||
time_to_internal_response?: number
|
||||
time_to_repair?: number
|
||||
time_to_resolve?: number
|
||||
}
|
||||
}
|
||||
|
||||
export interface CreateIncidentOutput {
|
||||
incident: IncidentData
|
||||
}
|
||||
|
||||
export interface CreateIncidentResponse extends ToolResponse {
|
||||
output: CreateIncidentOutput
|
||||
}
|
||||
|
||||
export interface ListIncidentsParams extends DatadogBaseParams {
|
||||
query?: string
|
||||
pageSize?: number
|
||||
pageOffset?: number
|
||||
include?: string // Comma-separated: users, attachments
|
||||
}
|
||||
|
||||
export interface ListIncidentsOutput {
|
||||
incidents: IncidentData[]
|
||||
}
|
||||
|
||||
export interface ListIncidentsResponse extends ToolResponse {
|
||||
output: ListIncidentsOutput
|
||||
}
|
||||
|
||||
// Union type for all Datadog responses
|
||||
export type DatadogResponse =
|
||||
| SubmitMetricsResponse
|
||||
| QueryTimeseriesResponse
|
||||
| ListMetricsResponse
|
||||
| GetMetricMetadataResponse
|
||||
| CreateEventResponse
|
||||
| GetEventResponse
|
||||
| QueryEventsResponse
|
||||
| CreateMonitorResponse
|
||||
| GetMonitorResponse
|
||||
| UpdateMonitorResponse
|
||||
| DeleteMonitorResponse
|
||||
| ListMonitorsResponse
|
||||
| MuteMonitorResponse
|
||||
| UnmuteMonitorResponse
|
||||
| SendLogsResponse
|
||||
| QueryLogsResponse
|
||||
| CreateDowntimeResponse
|
||||
| ListDowntimesResponse
|
||||
| CancelDowntimeResponse
|
||||
| CreateSloResponse
|
||||
| GetSloHistoryResponse
|
||||
| CreateDashboardResponse
|
||||
| GetDashboardResponse
|
||||
| ListDashboardsResponse
|
||||
| ListHostsResponse
|
||||
| CreateIncidentResponse
|
||||
| ListIncidentsResponse
|
||||
87
apps/sim/tools/dropbox/copy.ts
Normal file
87
apps/sim/tools/dropbox/copy.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import type { DropboxCopyParams, DropboxCopyResponse } from '@/tools/dropbox/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const dropboxCopyTool: ToolConfig<DropboxCopyParams, DropboxCopyResponse> = {
|
||||
id: 'dropbox_copy',
|
||||
name: 'Dropbox Copy',
|
||||
description: 'Copy a file or folder in Dropbox',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'dropbox',
|
||||
},
|
||||
|
||||
params: {
|
||||
fromPath: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The source path of the file or folder to copy',
|
||||
},
|
||||
toPath: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The destination path for the copied file or folder',
|
||||
},
|
||||
autorename: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'If true, rename the file if there is a conflict at destination',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.dropboxapi.com/2/files/copy_v2',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.accessToken) {
|
||||
throw new Error('Missing access token for Dropbox API request')
|
||||
}
|
||||
return {
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
},
|
||||
body: (params) => ({
|
||||
from_path: params.fromPath,
|
||||
to_path: params.toPath,
|
||||
autorename: params.autorename ?? false,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.error_summary || data.error?.message || 'Failed to copy file/folder',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
metadata: data.metadata,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Metadata of the copied item',
|
||||
properties: {
|
||||
'.tag': { type: 'string', description: 'Type: file or folder' },
|
||||
id: { type: 'string', description: 'Unique identifier' },
|
||||
name: { type: 'string', description: 'Name of the copied item' },
|
||||
path_display: { type: 'string', description: 'Display path' },
|
||||
size: { type: 'number', description: 'Size in bytes (files only)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
82
apps/sim/tools/dropbox/create_folder.ts
Normal file
82
apps/sim/tools/dropbox/create_folder.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import type { DropboxCreateFolderParams, DropboxCreateFolderResponse } from '@/tools/dropbox/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const dropboxCreateFolderTool: ToolConfig<
|
||||
DropboxCreateFolderParams,
|
||||
DropboxCreateFolderResponse
|
||||
> = {
|
||||
id: 'dropbox_create_folder',
|
||||
name: 'Dropbox Create Folder',
|
||||
description: 'Create a new folder in Dropbox',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'dropbox',
|
||||
},
|
||||
|
||||
params: {
|
||||
path: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The path where the folder should be created (e.g., /new-folder)',
|
||||
},
|
||||
autorename: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'If true, rename the folder if there is a conflict',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.dropboxapi.com/2/files/create_folder_v2',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.accessToken) {
|
||||
throw new Error('Missing access token for Dropbox API request')
|
||||
}
|
||||
return {
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
},
|
||||
body: (params) => ({
|
||||
path: params.path,
|
||||
autorename: params.autorename ?? false,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.error_summary || data.error?.message || 'Failed to create folder',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
folder: data.metadata,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
folder: {
|
||||
type: 'object',
|
||||
description: 'The created folder metadata',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique identifier for the folder' },
|
||||
name: { type: 'string', description: 'Name of the folder' },
|
||||
path_display: { type: 'string', description: 'Display path of the folder' },
|
||||
path_lower: { type: 'string', description: 'Lowercase path of the folder' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
131
apps/sim/tools/dropbox/create_shared_link.ts
Normal file
131
apps/sim/tools/dropbox/create_shared_link.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
import type {
|
||||
DropboxCreateSharedLinkParams,
|
||||
DropboxCreateSharedLinkResponse,
|
||||
} from '@/tools/dropbox/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const dropboxCreateSharedLinkTool: ToolConfig<
|
||||
DropboxCreateSharedLinkParams,
|
||||
DropboxCreateSharedLinkResponse
|
||||
> = {
|
||||
id: 'dropbox_create_shared_link',
|
||||
name: 'Dropbox Create Shared Link',
|
||||
description: 'Create a shareable link for a file or folder in Dropbox',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'dropbox',
|
||||
},
|
||||
|
||||
params: {
|
||||
path: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The path of the file or folder to share',
|
||||
},
|
||||
requestedVisibility: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Visibility: public, team_only, or password',
|
||||
},
|
||||
linkPassword: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Password for the shared link (only if visibility is password)',
|
||||
},
|
||||
expires: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Expiration date in ISO 8601 format (e.g., 2025-12-31T23:59:59Z)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.dropboxapi.com/2/sharing/create_shared_link_with_settings',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.accessToken) {
|
||||
throw new Error('Missing access token for Dropbox API request')
|
||||
}
|
||||
return {
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
},
|
||||
body: (params) => {
|
||||
const body: Record<string, any> = {
|
||||
path: params.path,
|
||||
}
|
||||
|
||||
const settings: Record<string, any> = {}
|
||||
|
||||
if (params.requestedVisibility) {
|
||||
settings.requested_visibility = { '.tag': params.requestedVisibility }
|
||||
}
|
||||
|
||||
if (params.linkPassword) {
|
||||
settings.link_password = params.linkPassword
|
||||
}
|
||||
|
||||
if (params.expires) {
|
||||
settings.expires = params.expires
|
||||
}
|
||||
|
||||
if (Object.keys(settings).length > 0) {
|
||||
body.settings = settings
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
// Check if a shared link already exists
|
||||
if (data.error_summary?.includes('shared_link_already_exists')) {
|
||||
return {
|
||||
success: false,
|
||||
error:
|
||||
'A shared link already exists for this path. Use list_shared_links to get the existing link.',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
return {
|
||||
success: false,
|
||||
error: data.error_summary || data.error?.message || 'Failed to create shared link',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
sharedLink: data,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
sharedLink: {
|
||||
type: 'object',
|
||||
description: 'The created shared link',
|
||||
properties: {
|
||||
url: { type: 'string', description: 'The shared link URL' },
|
||||
name: { type: 'string', description: 'Name of the shared item' },
|
||||
path_lower: { type: 'string', description: 'Lowercase path of the shared item' },
|
||||
expires: { type: 'string', description: 'Expiration date if set' },
|
||||
link_permissions: {
|
||||
type: 'object',
|
||||
description: 'Permissions for the shared link',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
76
apps/sim/tools/dropbox/delete.ts
Normal file
76
apps/sim/tools/dropbox/delete.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import type { DropboxDeleteParams, DropboxDeleteResponse } from '@/tools/dropbox/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const dropboxDeleteTool: ToolConfig<DropboxDeleteParams, DropboxDeleteResponse> = {
|
||||
id: 'dropbox_delete',
|
||||
name: 'Dropbox Delete',
|
||||
description: 'Delete a file or folder in Dropbox (moves to trash)',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'dropbox',
|
||||
},
|
||||
|
||||
params: {
|
||||
path: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The path of the file or folder to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.dropboxapi.com/2/files/delete_v2',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.accessToken) {
|
||||
throw new Error('Missing access token for Dropbox API request')
|
||||
}
|
||||
return {
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
},
|
||||
body: (params) => ({
|
||||
path: params.path,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.error_summary || data.error?.message || 'Failed to delete file/folder',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
metadata: data.metadata,
|
||||
deleted: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Metadata of the deleted item',
|
||||
properties: {
|
||||
'.tag': { type: 'string', description: 'Type: file, folder, or deleted' },
|
||||
name: { type: 'string', description: 'Name of the deleted item' },
|
||||
path_display: { type: 'string', description: 'Display path' },
|
||||
},
|
||||
},
|
||||
deleted: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the deletion was successful',
|
||||
},
|
||||
},
|
||||
}
|
||||
82
apps/sim/tools/dropbox/download.ts
Normal file
82
apps/sim/tools/dropbox/download.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import type { DropboxDownloadParams, DropboxDownloadResponse } from '@/tools/dropbox/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const dropboxDownloadTool: ToolConfig<DropboxDownloadParams, DropboxDownloadResponse> = {
|
||||
id: 'dropbox_download',
|
||||
name: 'Dropbox Download File',
|
||||
description: 'Download a file from Dropbox and get a temporary link',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'dropbox',
|
||||
},
|
||||
|
||||
params: {
|
||||
path: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The path of the file to download (e.g., /folder/document.pdf)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.dropboxapi.com/2/files/get_temporary_link',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.accessToken) {
|
||||
throw new Error('Missing access token for Dropbox API request')
|
||||
}
|
||||
return {
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
},
|
||||
body: (params) => ({
|
||||
path: params.path,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.error_summary || data.error?.message || 'Failed to download file',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
file: data.metadata,
|
||||
content: '', // Content will be available via the temporary link
|
||||
temporaryLink: data.link,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
file: {
|
||||
type: 'object',
|
||||
description: 'The file metadata',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique identifier for the file' },
|
||||
name: { type: 'string', description: 'Name of the file' },
|
||||
path_display: { type: 'string', description: 'Display path of the file' },
|
||||
size: { type: 'number', description: 'Size of the file in bytes' },
|
||||
},
|
||||
},
|
||||
temporaryLink: {
|
||||
type: 'string',
|
||||
description: 'Temporary link to download the file (valid for ~4 hours)',
|
||||
},
|
||||
content: {
|
||||
type: 'string',
|
||||
description: 'Base64 encoded file content (if fetched)',
|
||||
},
|
||||
},
|
||||
}
|
||||
95
apps/sim/tools/dropbox/get_metadata.ts
Normal file
95
apps/sim/tools/dropbox/get_metadata.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import type { DropboxGetMetadataParams, DropboxGetMetadataResponse } from '@/tools/dropbox/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const dropboxGetMetadataTool: ToolConfig<
|
||||
DropboxGetMetadataParams,
|
||||
DropboxGetMetadataResponse
|
||||
> = {
|
||||
id: 'dropbox_get_metadata',
|
||||
name: 'Dropbox Get Metadata',
|
||||
description: 'Get metadata for a file or folder in Dropbox',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'dropbox',
|
||||
},
|
||||
|
||||
params: {
|
||||
path: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The path of the file or folder to get metadata for',
|
||||
},
|
||||
includeMediaInfo: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'If true, include media info for photos/videos',
|
||||
},
|
||||
includeDeleted: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'If true, include deleted files in results',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.dropboxapi.com/2/files/get_metadata',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.accessToken) {
|
||||
throw new Error('Missing access token for Dropbox API request')
|
||||
}
|
||||
return {
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
},
|
||||
body: (params) => ({
|
||||
path: params.path,
|
||||
include_media_info: params.includeMediaInfo ?? false,
|
||||
include_deleted: params.includeDeleted ?? false,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.error_summary || data.error?.message || 'Failed to get metadata',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
metadata: data,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Metadata for the file or folder',
|
||||
properties: {
|
||||
'.tag': { type: 'string', description: 'Type: file, folder, or deleted' },
|
||||
id: { type: 'string', description: 'Unique identifier' },
|
||||
name: { type: 'string', description: 'Name of the item' },
|
||||
path_display: { type: 'string', description: 'Display path' },
|
||||
path_lower: { type: 'string', description: 'Lowercase path' },
|
||||
size: { type: 'number', description: 'Size in bytes (files only)' },
|
||||
client_modified: { type: 'string', description: 'Client modification time' },
|
||||
server_modified: { type: 'string', description: 'Server modification time' },
|
||||
rev: { type: 'string', description: 'Revision identifier' },
|
||||
content_hash: { type: 'string', description: 'Content hash' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
23
apps/sim/tools/dropbox/index.ts
Normal file
23
apps/sim/tools/dropbox/index.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { dropboxCopyTool } from '@/tools/dropbox/copy'
|
||||
import { dropboxCreateFolderTool } from '@/tools/dropbox/create_folder'
|
||||
import { dropboxCreateSharedLinkTool } from '@/tools/dropbox/create_shared_link'
|
||||
import { dropboxDeleteTool } from '@/tools/dropbox/delete'
|
||||
import { dropboxDownloadTool } from '@/tools/dropbox/download'
|
||||
import { dropboxGetMetadataTool } from '@/tools/dropbox/get_metadata'
|
||||
import { dropboxListFolderTool } from '@/tools/dropbox/list_folder'
|
||||
import { dropboxMoveTool } from '@/tools/dropbox/move'
|
||||
import { dropboxSearchTool } from '@/tools/dropbox/search'
|
||||
import { dropboxUploadTool } from '@/tools/dropbox/upload'
|
||||
|
||||
export {
|
||||
dropboxCopyTool,
|
||||
dropboxCreateFolderTool,
|
||||
dropboxCreateSharedLinkTool,
|
||||
dropboxDeleteTool,
|
||||
dropboxDownloadTool,
|
||||
dropboxGetMetadataTool,
|
||||
dropboxListFolderTool,
|
||||
dropboxMoveTool,
|
||||
dropboxSearchTool,
|
||||
dropboxUploadTool,
|
||||
}
|
||||
115
apps/sim/tools/dropbox/list_folder.ts
Normal file
115
apps/sim/tools/dropbox/list_folder.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import type { DropboxListFolderParams, DropboxListFolderResponse } from '@/tools/dropbox/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const dropboxListFolderTool: ToolConfig<DropboxListFolderParams, DropboxListFolderResponse> =
|
||||
{
|
||||
id: 'dropbox_list_folder',
|
||||
name: 'Dropbox List Folder',
|
||||
description: 'List the contents of a folder in Dropbox',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'dropbox',
|
||||
},
|
||||
|
||||
params: {
|
||||
path: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The path of the folder to list (use "" for root)',
|
||||
},
|
||||
recursive: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'If true, list contents recursively',
|
||||
},
|
||||
includeDeleted: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'If true, include deleted files/folders',
|
||||
},
|
||||
includeMediaInfo: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'If true, include media info for photos/videos',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Maximum number of results to return (default: 500)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.dropboxapi.com/2/files/list_folder',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.accessToken) {
|
||||
throw new Error('Missing access token for Dropbox API request')
|
||||
}
|
||||
return {
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
},
|
||||
body: (params) => ({
|
||||
path: params.path === '/' ? '' : params.path,
|
||||
recursive: params.recursive ?? false,
|
||||
include_deleted: params.includeDeleted ?? false,
|
||||
include_media_info: params.includeMediaInfo ?? false,
|
||||
limit: params.limit,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.error_summary || data.error?.message || 'Failed to list folder',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
entries: data.entries,
|
||||
cursor: data.cursor,
|
||||
hasMore: data.has_more,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
entries: {
|
||||
type: 'array',
|
||||
description: 'List of files and folders in the directory',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
'.tag': { type: 'string', description: 'Type: file, folder, or deleted' },
|
||||
id: { type: 'string', description: 'Unique identifier' },
|
||||
name: { type: 'string', description: 'Name of the file/folder' },
|
||||
path_display: { type: 'string', description: 'Display path' },
|
||||
size: { type: 'number', description: 'Size in bytes (files only)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for pagination',
|
||||
},
|
||||
hasMore: {
|
||||
type: 'boolean',
|
||||
description: 'Whether there are more results',
|
||||
},
|
||||
},
|
||||
}
|
||||
87
apps/sim/tools/dropbox/move.ts
Normal file
87
apps/sim/tools/dropbox/move.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import type { DropboxMoveParams, DropboxMoveResponse } from '@/tools/dropbox/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const dropboxMoveTool: ToolConfig<DropboxMoveParams, DropboxMoveResponse> = {
|
||||
id: 'dropbox_move',
|
||||
name: 'Dropbox Move',
|
||||
description: 'Move or rename a file or folder in Dropbox',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'dropbox',
|
||||
},
|
||||
|
||||
params: {
|
||||
fromPath: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The source path of the file or folder to move',
|
||||
},
|
||||
toPath: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The destination path for the moved file or folder',
|
||||
},
|
||||
autorename: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'If true, rename the file if there is a conflict at destination',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.dropboxapi.com/2/files/move_v2',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.accessToken) {
|
||||
throw new Error('Missing access token for Dropbox API request')
|
||||
}
|
||||
return {
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
},
|
||||
body: (params) => ({
|
||||
from_path: params.fromPath,
|
||||
to_path: params.toPath,
|
||||
autorename: params.autorename ?? false,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.error_summary || data.error?.message || 'Failed to move file/folder',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
metadata: data.metadata,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'Metadata of the moved item',
|
||||
properties: {
|
||||
'.tag': { type: 'string', description: 'Type: file or folder' },
|
||||
id: { type: 'string', description: 'Unique identifier' },
|
||||
name: { type: 'string', description: 'Name of the moved item' },
|
||||
path_display: { type: 'string', description: 'Display path' },
|
||||
size: { type: 'number', description: 'Size in bytes (files only)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
135
apps/sim/tools/dropbox/search.ts
Normal file
135
apps/sim/tools/dropbox/search.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import type { DropboxSearchParams, DropboxSearchResponse } from '@/tools/dropbox/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const dropboxSearchTool: ToolConfig<DropboxSearchParams, DropboxSearchResponse> = {
|
||||
id: 'dropbox_search',
|
||||
name: 'Dropbox Search',
|
||||
description: 'Search for files and folders in Dropbox',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'dropbox',
|
||||
},
|
||||
|
||||
params: {
|
||||
query: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The search query',
|
||||
},
|
||||
path: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Limit search to a specific folder path',
|
||||
},
|
||||
fileExtensions: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Comma-separated list of file extensions to filter by (e.g., pdf,xlsx)',
|
||||
},
|
||||
maxResults: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Maximum number of results to return (default: 100)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.dropboxapi.com/2/files/search_v2',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.accessToken) {
|
||||
throw new Error('Missing access token for Dropbox API request')
|
||||
}
|
||||
return {
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
},
|
||||
body: (params) => {
|
||||
const body: Record<string, any> = {
|
||||
query: params.query,
|
||||
}
|
||||
|
||||
const options: Record<string, any> = {}
|
||||
|
||||
if (params.path) {
|
||||
options.path = params.path
|
||||
}
|
||||
|
||||
if (params.fileExtensions) {
|
||||
const extensions = params.fileExtensions
|
||||
.split(',')
|
||||
.map((ext) => ext.trim())
|
||||
.filter((ext) => ext.length > 0)
|
||||
if (extensions.length > 0) {
|
||||
options.file_extensions = extensions
|
||||
}
|
||||
}
|
||||
|
||||
if (params.maxResults) {
|
||||
options.max_results = params.maxResults
|
||||
}
|
||||
|
||||
if (Object.keys(options).length > 0) {
|
||||
body.options = options
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.error_summary || data.error?.message || 'Failed to search files',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
matches: data.matches || [],
|
||||
hasMore: data.has_more || false,
|
||||
cursor: data.cursor,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
matches: {
|
||||
type: 'array',
|
||||
description: 'Search results',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
match_type: {
|
||||
type: 'object',
|
||||
description: 'Type of match: filename, content, or both',
|
||||
},
|
||||
metadata: {
|
||||
type: 'object',
|
||||
description: 'File or folder metadata',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
hasMore: {
|
||||
type: 'boolean',
|
||||
description: 'Whether there are more results',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for pagination',
|
||||
},
|
||||
},
|
||||
}
|
||||
244
apps/sim/tools/dropbox/types.ts
Normal file
244
apps/sim/tools/dropbox/types.ts
Normal file
@@ -0,0 +1,244 @@
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
// ===== Core Types =====
|
||||
|
||||
export interface DropboxFileMetadata {
|
||||
'.tag': 'file'
|
||||
id: string
|
||||
name: string
|
||||
path_display: string
|
||||
path_lower: string
|
||||
size: number
|
||||
client_modified: string
|
||||
server_modified: string
|
||||
rev: string
|
||||
content_hash?: string
|
||||
is_downloadable?: boolean
|
||||
}
|
||||
|
||||
export interface DropboxFolderMetadata {
|
||||
'.tag': 'folder'
|
||||
id: string
|
||||
name: string
|
||||
path_display: string
|
||||
path_lower: string
|
||||
}
|
||||
|
||||
export interface DropboxDeletedMetadata {
|
||||
'.tag': 'deleted'
|
||||
name: string
|
||||
path_display: string
|
||||
path_lower: string
|
||||
}
|
||||
|
||||
export type DropboxMetadata = DropboxFileMetadata | DropboxFolderMetadata | DropboxDeletedMetadata
|
||||
|
||||
export interface DropboxSharedLinkMetadata {
|
||||
url: string
|
||||
name: string
|
||||
path_lower: string
|
||||
link_permissions: {
|
||||
can_revoke: boolean
|
||||
resolved_visibility: {
|
||||
'.tag': 'public' | 'team_only' | 'password' | 'team_and_password' | 'shared_folder_only'
|
||||
}
|
||||
revoke_failure_reason?: {
|
||||
'.tag': string
|
||||
}
|
||||
}
|
||||
expires?: string
|
||||
id?: string
|
||||
}
|
||||
|
||||
export interface DropboxSearchMatch {
|
||||
match_type: {
|
||||
'.tag': 'filename' | 'content' | 'both'
|
||||
}
|
||||
metadata: {
|
||||
'.tag': 'metadata'
|
||||
metadata: DropboxMetadata
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Base Params =====
|
||||
|
||||
export interface DropboxBaseParams {
|
||||
accessToken?: string
|
||||
}
|
||||
|
||||
// ===== Upload Params =====
|
||||
|
||||
export interface DropboxUploadParams extends DropboxBaseParams {
|
||||
path: string
|
||||
fileContent: string // Base64 encoded file content
|
||||
fileName?: string
|
||||
mode?: 'add' | 'overwrite'
|
||||
autorename?: boolean
|
||||
mute?: boolean
|
||||
}
|
||||
|
||||
export interface DropboxUploadResponse extends ToolResponse {
|
||||
output: {
|
||||
file?: DropboxFileMetadata
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Download Params =====
|
||||
|
||||
export interface DropboxDownloadParams extends DropboxBaseParams {
|
||||
path: string
|
||||
}
|
||||
|
||||
export interface DropboxDownloadResponse extends ToolResponse {
|
||||
output: {
|
||||
file?: DropboxFileMetadata
|
||||
content?: string // Base64 encoded file content
|
||||
temporaryLink?: string
|
||||
}
|
||||
}
|
||||
|
||||
// ===== List Folder Params =====
|
||||
|
||||
export interface DropboxListFolderParams extends DropboxBaseParams {
|
||||
path: string
|
||||
recursive?: boolean
|
||||
includeDeleted?: boolean
|
||||
includeMediaInfo?: boolean
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface DropboxListFolderResponse extends ToolResponse {
|
||||
output: {
|
||||
entries?: DropboxMetadata[]
|
||||
cursor?: string
|
||||
hasMore?: boolean
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Create Folder Params =====
|
||||
|
||||
export interface DropboxCreateFolderParams extends DropboxBaseParams {
|
||||
path: string
|
||||
autorename?: boolean
|
||||
}
|
||||
|
||||
export interface DropboxCreateFolderResponse extends ToolResponse {
|
||||
output: {
|
||||
folder?: DropboxFolderMetadata
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Delete Params =====
|
||||
|
||||
export interface DropboxDeleteParams extends DropboxBaseParams {
|
||||
path: string
|
||||
}
|
||||
|
||||
export interface DropboxDeleteResponse extends ToolResponse {
|
||||
output: {
|
||||
metadata?: DropboxMetadata
|
||||
deleted?: boolean
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Copy Params =====
|
||||
|
||||
export interface DropboxCopyParams extends DropboxBaseParams {
|
||||
fromPath: string
|
||||
toPath: string
|
||||
autorename?: boolean
|
||||
}
|
||||
|
||||
export interface DropboxCopyResponse extends ToolResponse {
|
||||
output: {
|
||||
metadata?: DropboxMetadata
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Move Params =====
|
||||
|
||||
export interface DropboxMoveParams extends DropboxBaseParams {
|
||||
fromPath: string
|
||||
toPath: string
|
||||
autorename?: boolean
|
||||
}
|
||||
|
||||
export interface DropboxMoveResponse extends ToolResponse {
|
||||
output: {
|
||||
metadata?: DropboxMetadata
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Get Metadata Params =====
|
||||
|
||||
export interface DropboxGetMetadataParams extends DropboxBaseParams {
|
||||
path: string
|
||||
includeMediaInfo?: boolean
|
||||
includeDeleted?: boolean
|
||||
}
|
||||
|
||||
export interface DropboxGetMetadataResponse extends ToolResponse {
|
||||
output: {
|
||||
metadata?: DropboxMetadata
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Create Shared Link Params =====
|
||||
|
||||
export interface DropboxCreateSharedLinkParams extends DropboxBaseParams {
|
||||
path: string
|
||||
requestedVisibility?: 'public' | 'team_only' | 'password'
|
||||
linkPassword?: string
|
||||
expires?: string
|
||||
}
|
||||
|
||||
export interface DropboxCreateSharedLinkResponse extends ToolResponse {
|
||||
output: {
|
||||
sharedLink?: DropboxSharedLinkMetadata
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Search Params =====
|
||||
|
||||
export interface DropboxSearchParams extends DropboxBaseParams {
|
||||
query: string
|
||||
path?: string
|
||||
fileExtensions?: string
|
||||
maxResults?: number
|
||||
}
|
||||
|
||||
export interface DropboxSearchResponse extends ToolResponse {
|
||||
output: {
|
||||
matches?: DropboxSearchMatch[]
|
||||
hasMore?: boolean
|
||||
cursor?: string
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Get Temporary Link Params =====
|
||||
|
||||
export interface DropboxGetTemporaryLinkParams extends DropboxBaseParams {
|
||||
path: string
|
||||
}
|
||||
|
||||
export interface DropboxGetTemporaryLinkResponse extends ToolResponse {
|
||||
output: {
|
||||
metadata?: DropboxFileMetadata
|
||||
link?: string
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Combined Response Type =====
|
||||
|
||||
export type DropboxResponse =
|
||||
| DropboxUploadResponse
|
||||
| DropboxDownloadResponse
|
||||
| DropboxListFolderResponse
|
||||
| DropboxCreateFolderResponse
|
||||
| DropboxDeleteResponse
|
||||
| DropboxCopyResponse
|
||||
| DropboxMoveResponse
|
||||
| DropboxGetMetadataResponse
|
||||
| DropboxCreateSharedLinkResponse
|
||||
| DropboxSearchResponse
|
||||
| DropboxGetTemporaryLinkResponse
|
||||
119
apps/sim/tools/dropbox/upload.ts
Normal file
119
apps/sim/tools/dropbox/upload.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import type { DropboxUploadParams, DropboxUploadResponse } from '@/tools/dropbox/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const dropboxUploadTool: ToolConfig<DropboxUploadParams, DropboxUploadResponse> = {
|
||||
id: 'dropbox_upload',
|
||||
name: 'Dropbox Upload File',
|
||||
description: 'Upload a file to Dropbox',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'dropbox',
|
||||
},
|
||||
|
||||
params: {
|
||||
path: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'The path in Dropbox where the file should be saved (e.g., /folder/document.pdf)',
|
||||
},
|
||||
fileContent: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The base64 encoded content of the file to upload',
|
||||
},
|
||||
fileName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Optional filename (used if path is a folder)',
|
||||
},
|
||||
mode: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Write mode: add (default) or overwrite',
|
||||
},
|
||||
autorename: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'If true, rename the file if there is a conflict',
|
||||
},
|
||||
mute: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: "If true, don't notify the user about this upload",
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://content.dropboxapi.com/2/files/upload',
|
||||
method: 'POST',
|
||||
headers: (params) => {
|
||||
if (!params.accessToken) {
|
||||
throw new Error('Missing access token for Dropbox API request')
|
||||
}
|
||||
|
||||
const dropboxApiArg = {
|
||||
path: params.path,
|
||||
mode: params.mode || 'add',
|
||||
autorename: params.autorename ?? true,
|
||||
mute: params.mute ?? false,
|
||||
}
|
||||
|
||||
return {
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/octet-stream',
|
||||
'Dropbox-API-Arg': JSON.stringify(dropboxApiArg),
|
||||
}
|
||||
},
|
||||
body: (params) => {
|
||||
// The body should be the raw binary data
|
||||
// In this case we're passing the base64 content which will be decoded
|
||||
return params.fileContent
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response, params) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false,
|
||||
error: data.error_summary || data.error?.message || 'Failed to upload file',
|
||||
output: {},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
file: data,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
file: {
|
||||
type: 'object',
|
||||
description: 'The uploaded file metadata',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique identifier for the file' },
|
||||
name: { type: 'string', description: 'Name of the file' },
|
||||
path_display: { type: 'string', description: 'Display path of the file' },
|
||||
path_lower: { type: 'string', description: 'Lowercase path of the file' },
|
||||
size: { type: 'number', description: 'Size of the file in bytes' },
|
||||
client_modified: { type: 'string', description: 'Client modification time' },
|
||||
server_modified: { type: 'string', description: 'Server modification time' },
|
||||
rev: { type: 'string', description: 'Revision identifier' },
|
||||
content_hash: { type: 'string', description: 'Content hash for the file' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
181
apps/sim/tools/elasticsearch/bulk.ts
Normal file
181
apps/sim/tools/elasticsearch/bulk.ts
Normal file
@@ -0,0 +1,181 @@
|
||||
import type {
|
||||
ElasticsearchBulkParams,
|
||||
ElasticsearchBulkResponse,
|
||||
} from '@/tools/elasticsearch/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
function buildBaseUrl(params: ElasticsearchBulkParams): string {
|
||||
if (params.deploymentType === 'cloud' && params.cloudId) {
|
||||
const parts = params.cloudId.split(':')
|
||||
if (parts.length >= 2) {
|
||||
try {
|
||||
const decoded = Buffer.from(parts[1], 'base64').toString('utf-8')
|
||||
const [esHost] = decoded.split('$')
|
||||
if (esHost) {
|
||||
return `https://${parts[0]}.${esHost}`
|
||||
}
|
||||
} catch {
|
||||
// Fallback
|
||||
}
|
||||
}
|
||||
throw new Error('Invalid Cloud ID format')
|
||||
}
|
||||
|
||||
if (!params.host) {
|
||||
throw new Error('Host is required for self-hosted deployments')
|
||||
}
|
||||
|
||||
return params.host.replace(/\/$/, '')
|
||||
}
|
||||
|
||||
function buildAuthHeaders(params: ElasticsearchBulkParams): Record<string, string> {
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/x-ndjson',
|
||||
}
|
||||
|
||||
if (params.authMethod === 'api_key' && params.apiKey) {
|
||||
headers.Authorization = `ApiKey ${params.apiKey}`
|
||||
} else if (params.authMethod === 'basic_auth' && params.username && params.password) {
|
||||
const credentials = Buffer.from(`${params.username}:${params.password}`).toString('base64')
|
||||
headers.Authorization = `Basic ${credentials}`
|
||||
} else {
|
||||
throw new Error('Invalid authentication configuration')
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
export const bulkTool: ToolConfig<ElasticsearchBulkParams, ElasticsearchBulkResponse> = {
|
||||
id: 'elasticsearch_bulk',
|
||||
name: 'Elasticsearch Bulk Operations',
|
||||
description:
|
||||
'Perform multiple index, create, delete, or update operations in a single request for high performance.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
deploymentType: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'Deployment type: self_hosted or cloud',
|
||||
},
|
||||
host: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
description: 'Elasticsearch host URL (for self-hosted)',
|
||||
},
|
||||
cloudId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
description: 'Elastic Cloud ID (for cloud deployments)',
|
||||
},
|
||||
authMethod: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'Authentication method: api_key or basic_auth',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Elasticsearch API key',
|
||||
},
|
||||
username: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
description: 'Username for basic auth',
|
||||
},
|
||||
password: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Password for basic auth',
|
||||
},
|
||||
index: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
description: 'Default index for operations that do not specify one',
|
||||
},
|
||||
operations: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'Bulk operations as NDJSON string (newline-delimited JSON)',
|
||||
},
|
||||
refresh: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
description: 'Refresh policy: true, false, or wait_for',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = buildBaseUrl(params)
|
||||
let url = params.index
|
||||
? `${baseUrl}/${encodeURIComponent(params.index)}/_bulk`
|
||||
: `${baseUrl}/_bulk`
|
||||
|
||||
if (params.refresh) {
|
||||
url += `?refresh=${params.refresh}`
|
||||
}
|
||||
|
||||
return url
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => buildAuthHeaders(params),
|
||||
body: (params) => {
|
||||
// The body should be NDJSON format - we pass it as raw string
|
||||
// Ensure it ends with a newline
|
||||
// Note: The executor in tools/utils.ts handles NDJSON content-type specially
|
||||
// and accepts string bodies directly
|
||||
let operations = params.operations.trim()
|
||||
if (!operations.endsWith('\n')) {
|
||||
operations += '\n'
|
||||
}
|
||||
return operations as unknown as Record<string, any>
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text()
|
||||
let errorMessage = `Elasticsearch error: ${response.status}`
|
||||
try {
|
||||
const errorJson = JSON.parse(errorText)
|
||||
errorMessage = errorJson.error?.reason || errorJson.error?.type || errorMessage
|
||||
} catch {
|
||||
errorMessage = errorText || errorMessage
|
||||
}
|
||||
return {
|
||||
success: false,
|
||||
output: { took: 0, errors: true, items: [] },
|
||||
error: errorMessage,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
took: data.took,
|
||||
errors: data.errors,
|
||||
items: data.items,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
took: {
|
||||
type: 'number',
|
||||
description: 'Time in milliseconds the bulk operation took',
|
||||
},
|
||||
errors: {
|
||||
type: 'boolean',
|
||||
description: 'Whether any operation had an error',
|
||||
},
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Results for each operation',
|
||||
},
|
||||
},
|
||||
}
|
||||
212
apps/sim/tools/elasticsearch/cluster_health.ts
Normal file
212
apps/sim/tools/elasticsearch/cluster_health.ts
Normal file
@@ -0,0 +1,212 @@
|
||||
import type {
|
||||
ElasticsearchClusterHealthParams,
|
||||
ElasticsearchClusterHealthResponse,
|
||||
} from '@/tools/elasticsearch/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
function buildBaseUrl(params: ElasticsearchClusterHealthParams): string {
|
||||
if (params.deploymentType === 'cloud' && params.cloudId) {
|
||||
const parts = params.cloudId.split(':')
|
||||
if (parts.length >= 2) {
|
||||
try {
|
||||
const decoded = Buffer.from(parts[1], 'base64').toString('utf-8')
|
||||
const [esHost] = decoded.split('$')
|
||||
if (esHost) {
|
||||
return `https://${parts[0]}.${esHost}`
|
||||
}
|
||||
} catch {
|
||||
// Fallback
|
||||
}
|
||||
}
|
||||
throw new Error('Invalid Cloud ID format')
|
||||
}
|
||||
|
||||
if (!params.host) {
|
||||
throw new Error('Host is required for self-hosted deployments')
|
||||
}
|
||||
|
||||
return params.host.replace(/\/$/, '')
|
||||
}
|
||||
|
||||
function buildAuthHeaders(params: ElasticsearchClusterHealthParams): Record<string, string> {
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
|
||||
if (params.authMethod === 'api_key' && params.apiKey) {
|
||||
headers.Authorization = `ApiKey ${params.apiKey}`
|
||||
} else if (params.authMethod === 'basic_auth' && params.username && params.password) {
|
||||
const credentials = Buffer.from(`${params.username}:${params.password}`).toString('base64')
|
||||
headers.Authorization = `Basic ${credentials}`
|
||||
} else {
|
||||
throw new Error('Invalid authentication configuration')
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
export const clusterHealthTool: ToolConfig<
|
||||
ElasticsearchClusterHealthParams,
|
||||
ElasticsearchClusterHealthResponse
|
||||
> = {
|
||||
id: 'elasticsearch_cluster_health',
|
||||
name: 'Elasticsearch Cluster Health',
|
||||
description: 'Get the health status of the Elasticsearch cluster.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
deploymentType: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'Deployment type: self_hosted or cloud',
|
||||
},
|
||||
host: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
description: 'Elasticsearch host URL (for self-hosted)',
|
||||
},
|
||||
cloudId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
description: 'Elastic Cloud ID (for cloud deployments)',
|
||||
},
|
||||
authMethod: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'Authentication method: api_key or basic_auth',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Elasticsearch API key',
|
||||
},
|
||||
username: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
description: 'Username for basic auth',
|
||||
},
|
||||
password: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Password for basic auth',
|
||||
},
|
||||
waitForStatus: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
description: 'Wait until cluster reaches this status: green, yellow, or red',
|
||||
},
|
||||
timeout: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
description: 'Timeout for the wait operation (e.g., 30s, 1m)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = buildBaseUrl(params)
|
||||
let url = `${baseUrl}/_cluster/health`
|
||||
|
||||
const queryParams: string[] = []
|
||||
if (params.waitForStatus) {
|
||||
queryParams.push(`wait_for_status=${params.waitForStatus}`)
|
||||
}
|
||||
if (params.timeout) {
|
||||
queryParams.push(`timeout=${encodeURIComponent(params.timeout)}`)
|
||||
}
|
||||
if (queryParams.length > 0) {
|
||||
url += `?${queryParams.join('&')}`
|
||||
}
|
||||
|
||||
return url
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => buildAuthHeaders(params),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text()
|
||||
let errorMessage = `Elasticsearch error: ${response.status}`
|
||||
try {
|
||||
const errorJson = JSON.parse(errorText)
|
||||
errorMessage = errorJson.error?.reason || errorJson.error?.type || errorMessage
|
||||
} catch {
|
||||
errorMessage = errorText || errorMessage
|
||||
}
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
cluster_name: '',
|
||||
status: 'red' as const,
|
||||
timed_out: true,
|
||||
number_of_nodes: 0,
|
||||
number_of_data_nodes: 0,
|
||||
active_primary_shards: 0,
|
||||
active_shards: 0,
|
||||
relocating_shards: 0,
|
||||
initializing_shards: 0,
|
||||
unassigned_shards: 0,
|
||||
delayed_unassigned_shards: 0,
|
||||
number_of_pending_tasks: 0,
|
||||
number_of_in_flight_fetch: 0,
|
||||
task_max_waiting_in_queue_millis: 0,
|
||||
active_shards_percent_as_number: 0,
|
||||
},
|
||||
error: errorMessage,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
cluster_name: data.cluster_name,
|
||||
status: data.status,
|
||||
timed_out: data.timed_out,
|
||||
number_of_nodes: data.number_of_nodes,
|
||||
number_of_data_nodes: data.number_of_data_nodes,
|
||||
active_primary_shards: data.active_primary_shards,
|
||||
active_shards: data.active_shards,
|
||||
relocating_shards: data.relocating_shards,
|
||||
initializing_shards: data.initializing_shards,
|
||||
unassigned_shards: data.unassigned_shards,
|
||||
delayed_unassigned_shards: data.delayed_unassigned_shards,
|
||||
number_of_pending_tasks: data.number_of_pending_tasks,
|
||||
number_of_in_flight_fetch: data.number_of_in_flight_fetch,
|
||||
task_max_waiting_in_queue_millis: data.task_max_waiting_in_queue_millis,
|
||||
active_shards_percent_as_number: data.active_shards_percent_as_number,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
cluster_name: {
|
||||
type: 'string',
|
||||
description: 'Name of the cluster',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Cluster health status: green, yellow, or red',
|
||||
},
|
||||
number_of_nodes: {
|
||||
type: 'number',
|
||||
description: 'Total number of nodes in the cluster',
|
||||
},
|
||||
number_of_data_nodes: {
|
||||
type: 'number',
|
||||
description: 'Number of data nodes',
|
||||
},
|
||||
active_shards: {
|
||||
type: 'number',
|
||||
description: 'Number of active shards',
|
||||
},
|
||||
unassigned_shards: {
|
||||
type: 'number',
|
||||
description: 'Number of unassigned shards',
|
||||
},
|
||||
},
|
||||
}
|
||||
177
apps/sim/tools/elasticsearch/cluster_stats.ts
Normal file
177
apps/sim/tools/elasticsearch/cluster_stats.ts
Normal file
@@ -0,0 +1,177 @@
|
||||
import type {
|
||||
ElasticsearchClusterStatsParams,
|
||||
ElasticsearchClusterStatsResponse,
|
||||
} from '@/tools/elasticsearch/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
function buildBaseUrl(params: ElasticsearchClusterStatsParams): string {
|
||||
if (params.deploymentType === 'cloud' && params.cloudId) {
|
||||
const parts = params.cloudId.split(':')
|
||||
if (parts.length >= 2) {
|
||||
try {
|
||||
const decoded = Buffer.from(parts[1], 'base64').toString('utf-8')
|
||||
const [esHost] = decoded.split('$')
|
||||
if (esHost) {
|
||||
return `https://${parts[0]}.${esHost}`
|
||||
}
|
||||
} catch {
|
||||
// Fallback
|
||||
}
|
||||
}
|
||||
throw new Error('Invalid Cloud ID format')
|
||||
}
|
||||
|
||||
if (!params.host) {
|
||||
throw new Error('Host is required for self-hosted deployments')
|
||||
}
|
||||
|
||||
return params.host.replace(/\/$/, '')
|
||||
}
|
||||
|
||||
function buildAuthHeaders(params: ElasticsearchClusterStatsParams): Record<string, string> {
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
|
||||
if (params.authMethod === 'api_key' && params.apiKey) {
|
||||
headers.Authorization = `ApiKey ${params.apiKey}`
|
||||
} else if (params.authMethod === 'basic_auth' && params.username && params.password) {
|
||||
const credentials = Buffer.from(`${params.username}:${params.password}`).toString('base64')
|
||||
headers.Authorization = `Basic ${credentials}`
|
||||
} else {
|
||||
throw new Error('Invalid authentication configuration')
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
export const clusterStatsTool: ToolConfig<
|
||||
ElasticsearchClusterStatsParams,
|
||||
ElasticsearchClusterStatsResponse
|
||||
> = {
|
||||
id: 'elasticsearch_cluster_stats',
|
||||
name: 'Elasticsearch Cluster Stats',
|
||||
description: 'Get comprehensive statistics about the Elasticsearch cluster.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
deploymentType: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'Deployment type: self_hosted or cloud',
|
||||
},
|
||||
host: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
description: 'Elasticsearch host URL (for self-hosted)',
|
||||
},
|
||||
cloudId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
description: 'Elastic Cloud ID (for cloud deployments)',
|
||||
},
|
||||
authMethod: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'Authentication method: api_key or basic_auth',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Elasticsearch API key',
|
||||
},
|
||||
username: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
description: 'Username for basic auth',
|
||||
},
|
||||
password: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Password for basic auth',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = buildBaseUrl(params)
|
||||
return `${baseUrl}/_cluster/stats`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => buildAuthHeaders(params),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text()
|
||||
let errorMessage = `Elasticsearch error: ${response.status}`
|
||||
try {
|
||||
const errorJson = JSON.parse(errorText)
|
||||
errorMessage = errorJson.error?.reason || errorJson.error?.type || errorMessage
|
||||
} catch {
|
||||
errorMessage = errorText || errorMessage
|
||||
}
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
cluster_name: '',
|
||||
cluster_uuid: '',
|
||||
status: 'red',
|
||||
nodes: {
|
||||
count: { total: 0, data: 0, master: 0 },
|
||||
versions: [],
|
||||
},
|
||||
indices: {
|
||||
count: 0,
|
||||
docs: { count: 0, deleted: 0 },
|
||||
store: { size_in_bytes: 0 },
|
||||
shards: { total: 0, primaries: 0 },
|
||||
},
|
||||
},
|
||||
error: errorMessage,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
cluster_name: data.cluster_name,
|
||||
cluster_uuid: data.cluster_uuid,
|
||||
status: data.status,
|
||||
nodes: {
|
||||
count: data.nodes?.count || { total: 0, data: 0, master: 0 },
|
||||
versions: data.nodes?.versions || [],
|
||||
},
|
||||
indices: {
|
||||
count: data.indices?.count || 0,
|
||||
docs: data.indices?.docs || { count: 0, deleted: 0 },
|
||||
store: data.indices?.store || { size_in_bytes: 0 },
|
||||
shards: data.indices?.shards || { total: 0, primaries: 0 },
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
cluster_name: {
|
||||
type: 'string',
|
||||
description: 'Name of the cluster',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Cluster health status',
|
||||
},
|
||||
nodes: {
|
||||
type: 'object',
|
||||
description: 'Node statistics including count and versions',
|
||||
},
|
||||
indices: {
|
||||
type: 'object',
|
||||
description: 'Index statistics including document count and store size',
|
||||
},
|
||||
},
|
||||
}
|
||||
164
apps/sim/tools/elasticsearch/count.ts
Normal file
164
apps/sim/tools/elasticsearch/count.ts
Normal file
@@ -0,0 +1,164 @@
|
||||
import type {
|
||||
ElasticsearchCountParams,
|
||||
ElasticsearchCountResponse,
|
||||
} from '@/tools/elasticsearch/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
function buildBaseUrl(params: ElasticsearchCountParams): string {
|
||||
if (params.deploymentType === 'cloud' && params.cloudId) {
|
||||
const parts = params.cloudId.split(':')
|
||||
if (parts.length >= 2) {
|
||||
try {
|
||||
const decoded = Buffer.from(parts[1], 'base64').toString('utf-8')
|
||||
const [esHost] = decoded.split('$')
|
||||
if (esHost) {
|
||||
return `https://${parts[0]}.${esHost}`
|
||||
}
|
||||
} catch {
|
||||
// Fallback
|
||||
}
|
||||
}
|
||||
throw new Error('Invalid Cloud ID format')
|
||||
}
|
||||
|
||||
if (!params.host) {
|
||||
throw new Error('Host is required for self-hosted deployments')
|
||||
}
|
||||
|
||||
return params.host.replace(/\/$/, '')
|
||||
}
|
||||
|
||||
function buildAuthHeaders(params: ElasticsearchCountParams): Record<string, string> {
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
|
||||
if (params.authMethod === 'api_key' && params.apiKey) {
|
||||
headers.Authorization = `ApiKey ${params.apiKey}`
|
||||
} else if (params.authMethod === 'basic_auth' && params.username && params.password) {
|
||||
const credentials = Buffer.from(`${params.username}:${params.password}`).toString('base64')
|
||||
headers.Authorization = `Basic ${credentials}`
|
||||
} else {
|
||||
throw new Error('Invalid authentication configuration')
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
export const countTool: ToolConfig<ElasticsearchCountParams, ElasticsearchCountResponse> = {
|
||||
id: 'elasticsearch_count',
|
||||
name: 'Elasticsearch Count',
|
||||
description: 'Count documents matching a query in Elasticsearch.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
deploymentType: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'Deployment type: self_hosted or cloud',
|
||||
},
|
||||
host: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
description: 'Elasticsearch host URL (for self-hosted)',
|
||||
},
|
||||
cloudId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
description: 'Elastic Cloud ID (for cloud deployments)',
|
||||
},
|
||||
authMethod: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'Authentication method: api_key or basic_auth',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Elasticsearch API key',
|
||||
},
|
||||
username: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
description: 'Username for basic auth',
|
||||
},
|
||||
password: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Password for basic auth',
|
||||
},
|
||||
index: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'Index name to count documents in',
|
||||
},
|
||||
query: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
description: 'Optional query to filter documents (JSON string)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = buildBaseUrl(params)
|
||||
return `${baseUrl}/${encodeURIComponent(params.index)}/_count`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => buildAuthHeaders(params),
|
||||
body: (params) => {
|
||||
if (params.query) {
|
||||
try {
|
||||
return { query: JSON.parse(params.query) }
|
||||
} catch {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
return {}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text()
|
||||
let errorMessage = `Elasticsearch error: ${response.status}`
|
||||
try {
|
||||
const errorJson = JSON.parse(errorText)
|
||||
errorMessage = errorJson.error?.reason || errorJson.error?.type || errorMessage
|
||||
} catch {
|
||||
errorMessage = errorText || errorMessage
|
||||
}
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
count: 0,
|
||||
_shards: { total: 0, successful: 0, skipped: 0, failed: 0 },
|
||||
},
|
||||
error: errorMessage,
|
||||
}
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
count: data.count,
|
||||
_shards: data._shards,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
count: {
|
||||
type: 'number',
|
||||
description: 'Number of documents matching the query',
|
||||
},
|
||||
_shards: {
|
||||
type: 'object',
|
||||
description: 'Shard statistics',
|
||||
},
|
||||
},
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user