mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-09 23:17:59 -05:00
Compare commits
104 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4827866f9a | ||
|
|
214632604d | ||
|
|
1ddbac1d2e | ||
|
|
35a57bfad4 | ||
|
|
f8678b179a | ||
|
|
0ebb45b2db | ||
|
|
6247f421bc | ||
|
|
3e697d9ed9 | ||
|
|
6385d82b85 | ||
|
|
f91beb324e | ||
|
|
4f69b171f2 | ||
|
|
a1a189f328 | ||
|
|
7dc48510dc | ||
|
|
4431a1a484 | ||
|
|
93fe68785e | ||
|
|
50c1c6775b | ||
|
|
df5f823d1c | ||
|
|
094f87fa1f | ||
|
|
65efa039da | ||
|
|
6b15a50311 | ||
|
|
65787d7cc3 | ||
|
|
4d1a9a3f22 | ||
|
|
656a6b8abd | ||
|
|
889b44c90a | ||
|
|
3a33ec929f | ||
|
|
24356d99ec | ||
|
|
6de1c04517 | ||
|
|
38be2b76c4 | ||
|
|
a2f14cab54 | ||
|
|
474762d6fb | ||
|
|
0005c3e465 | ||
|
|
fc40b4f7af | ||
|
|
eb07a080fb | ||
|
|
2a7f51a2f6 | ||
|
|
90c3c43607 | ||
|
|
83d813a7cc | ||
|
|
811c736705 | ||
|
|
c6757311af | ||
|
|
b5b12ba2d1 | ||
|
|
0d30676e34 | ||
|
|
36bdccb449 | ||
|
|
f45730a89e | ||
|
|
04cd837e9c | ||
|
|
c23130a26e | ||
|
|
7575cd6f27 | ||
|
|
fbde64f0b0 | ||
|
|
25f7ed20f6 | ||
|
|
261aa3d72d | ||
|
|
9da19e84b7 | ||
|
|
e83afc0a62 | ||
|
|
1720fa8749 | ||
|
|
f3ad7750af | ||
|
|
78b7643e65 | ||
|
|
7ef1150383 | ||
|
|
67cfb21d08 | ||
|
|
a337af92bc | ||
|
|
b4a99779eb | ||
|
|
471cb4747c | ||
|
|
491bd783b5 | ||
|
|
5516fa39c3 | ||
|
|
21fa92bc41 | ||
|
|
26ca37328a | ||
|
|
731997f768 | ||
|
|
1d6975db49 | ||
|
|
c4a6d11cc0 | ||
|
|
7b5405e968 | ||
|
|
1ae3b47f5c | ||
|
|
3120a785df | ||
|
|
8775e76c32 | ||
|
|
9a6c68789d | ||
|
|
08bc1125bd | ||
|
|
f4f74da1dc | ||
|
|
de330d80f5 | ||
|
|
b7228d57f7 | ||
|
|
dcbeca1abe | ||
|
|
27ea333974 | ||
|
|
9861d3a0ac | ||
|
|
fdbf8be79b | ||
|
|
6f4f4e22f0 | ||
|
|
837aabca5e | ||
|
|
f7d2c9667f | ||
|
|
29befbc5f6 | ||
|
|
f9cfca92bf | ||
|
|
9cf8aaee1b | ||
|
|
25afacb25e | ||
|
|
0e6a1315d0 | ||
|
|
f0dc8e81d9 | ||
|
|
ab30d37020 | ||
|
|
12d42e29ac | ||
|
|
a5b7148375 | ||
|
|
57e6a0b621 | ||
|
|
b72e111e22 | ||
|
|
300aaa5368 | ||
|
|
bdcc42e566 | ||
|
|
a45bb1bf3b | ||
|
|
3a5363ac54 | ||
|
|
17b2e58c32 | ||
|
|
8d38c2f15e | ||
|
|
9762bbc451 | ||
|
|
e43afc8b6c | ||
|
|
fcf52ac4d5 | ||
|
|
842200bcf2 | ||
|
|
6009a7359f | ||
|
|
0acd86023c |
13
.github/workflows/test-build.yml
vendored
13
.github/workflows/test-build.yml
vendored
@@ -48,6 +48,19 @@ jobs:
|
||||
ENCRYPTION_KEY: '7cf672e460e430c1fba707575c2b0e2ad5a99dddf9b7b7e3b5646e630861db1c' # dummy key for CI only
|
||||
run: bun run test
|
||||
|
||||
- name: Check schema and migrations are in sync
|
||||
working-directory: packages/db
|
||||
run: |
|
||||
bunx drizzle-kit generate --config=./drizzle.config.ts
|
||||
if [ -n "$(git status --porcelain ./migrations)" ]; then
|
||||
echo "❌ Schema and migrations are out of sync!"
|
||||
echo "Run 'cd packages/db && bunx drizzle-kit generate' and commit the new migrations."
|
||||
git status --porcelain ./migrations
|
||||
git diff ./migrations
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ Schema and migrations are in sync"
|
||||
|
||||
- name: Build application
|
||||
env:
|
||||
NODE_OPTIONS: '--no-warnings'
|
||||
|
||||
@@ -130,6 +130,7 @@ When running with Docker, use `host.docker.internal` if vLLM is on your host mac
|
||||
|
||||
**Requirements:**
|
||||
- [Bun](https://bun.sh/) runtime
|
||||
- [Node.js](https://nodejs.org/) v20+ (required for sandboxed code execution)
|
||||
- PostgreSQL 12+ with [pgvector extension](https://github.com/pgvector/pgvector) (required for AI embeddings)
|
||||
|
||||
**Note:** Sim uses vector embeddings for AI features like knowledge bases and semantic search, which requires the `pgvector` PostgreSQL extension.
|
||||
@@ -187,6 +188,7 @@ DATABASE_URL="postgresql://postgres:your_password@localhost:5432/simstudio"
|
||||
|
||||
Then run the migrations:
|
||||
```bash
|
||||
cd packages/db # Required so drizzle picks correct .env file
|
||||
bunx drizzle-kit migrate --config=./drizzle.config.ts
|
||||
```
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type React from 'react'
|
||||
import { findNeighbour } from 'fumadocs-core/page-tree'
|
||||
import defaultMdxComponents from 'fumadocs-ui/mdx'
|
||||
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/page'
|
||||
@@ -10,14 +11,15 @@ import { LLMCopyButton } from '@/components/page-actions'
|
||||
import { StructuredData } from '@/components/structured-data'
|
||||
import { CodeBlock } from '@/components/ui/code-block'
|
||||
import { Heading } from '@/components/ui/heading'
|
||||
import { source } from '@/lib/source'
|
||||
import { type PageData, source } from '@/lib/source'
|
||||
|
||||
export default async function Page(props: { params: Promise<{ slug?: string[]; lang: string }> }) {
|
||||
const params = await props.params
|
||||
const page = source.getPage(params.slug, params.lang)
|
||||
if (!page) notFound()
|
||||
|
||||
const MDX = page.data.body
|
||||
const data = page.data as PageData
|
||||
const MDX = data.body
|
||||
const baseUrl = 'https://docs.sim.ai'
|
||||
|
||||
const pageTreeRecord = source.pageTree as Record<string, any>
|
||||
@@ -51,7 +53,7 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l
|
||||
|
||||
if (index === urlParts.length - 1) {
|
||||
breadcrumbs.push({
|
||||
name: page.data.title,
|
||||
name: data.title,
|
||||
url: `${baseUrl}${page.url}`,
|
||||
})
|
||||
} else {
|
||||
@@ -168,15 +170,15 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l
|
||||
return (
|
||||
<>
|
||||
<StructuredData
|
||||
title={page.data.title}
|
||||
description={page.data.description || ''}
|
||||
title={data.title}
|
||||
description={data.description || ''}
|
||||
url={`${baseUrl}${page.url}`}
|
||||
lang={params.lang}
|
||||
breadcrumb={breadcrumbs}
|
||||
/>
|
||||
<DocsPage
|
||||
toc={page.data.toc}
|
||||
full={page.data.full}
|
||||
toc={data.toc}
|
||||
full={data.full}
|
||||
breadcrumb={{
|
||||
enabled: false,
|
||||
}}
|
||||
@@ -207,20 +209,32 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l
|
||||
</div>
|
||||
<PageNavigationArrows previous={neighbours?.previous} next={neighbours?.next} />
|
||||
</div>
|
||||
<DocsTitle>{page.data.title}</DocsTitle>
|
||||
<DocsDescription>{page.data.description}</DocsDescription>
|
||||
<DocsTitle>{data.title}</DocsTitle>
|
||||
<DocsDescription>{data.description}</DocsDescription>
|
||||
</div>
|
||||
<DocsBody>
|
||||
<MDX
|
||||
components={{
|
||||
...defaultMdxComponents,
|
||||
CodeBlock,
|
||||
h1: (props) => <Heading as='h1' {...props} />,
|
||||
h2: (props) => <Heading as='h2' {...props} />,
|
||||
h3: (props) => <Heading as='h3' {...props} />,
|
||||
h4: (props) => <Heading as='h4' {...props} />,
|
||||
h5: (props) => <Heading as='h5' {...props} />,
|
||||
h6: (props) => <Heading as='h6' {...props} />,
|
||||
h1: (props: React.HTMLAttributes<HTMLHeadingElement>) => (
|
||||
<Heading as='h1' {...props} />
|
||||
),
|
||||
h2: (props: React.HTMLAttributes<HTMLHeadingElement>) => (
|
||||
<Heading as='h2' {...props} />
|
||||
),
|
||||
h3: (props: React.HTMLAttributes<HTMLHeadingElement>) => (
|
||||
<Heading as='h3' {...props} />
|
||||
),
|
||||
h4: (props: React.HTMLAttributes<HTMLHeadingElement>) => (
|
||||
<Heading as='h4' {...props} />
|
||||
),
|
||||
h5: (props: React.HTMLAttributes<HTMLHeadingElement>) => (
|
||||
<Heading as='h5' {...props} />
|
||||
),
|
||||
h6: (props: React.HTMLAttributes<HTMLHeadingElement>) => (
|
||||
<Heading as='h6' {...props} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</DocsBody>
|
||||
@@ -240,16 +254,17 @@ export async function generateMetadata(props: {
|
||||
const page = source.getPage(params.slug, params.lang)
|
||||
if (!page) notFound()
|
||||
|
||||
const data = page.data as PageData
|
||||
const baseUrl = 'https://docs.sim.ai'
|
||||
const fullUrl = `${baseUrl}${page.url}`
|
||||
|
||||
const description = page.data.description || ''
|
||||
const ogImageUrl = `${baseUrl}/api/og?title=${encodeURIComponent(page.data.title)}&category=DOCUMENTATION${description ? `&description=${encodeURIComponent(description)}` : ''}`
|
||||
const description = data.description || ''
|
||||
const ogImageUrl = `${baseUrl}/api/og?title=${encodeURIComponent(data.title)}&category=DOCUMENTATION${description ? `&description=${encodeURIComponent(description)}` : ''}`
|
||||
|
||||
return {
|
||||
title: page.data.title,
|
||||
title: data.title,
|
||||
description:
|
||||
page.data.description || 'Sim visual workflow builder for AI applications documentation',
|
||||
data.description || 'Sim visual workflow builder for AI applications documentation',
|
||||
keywords: [
|
||||
'AI workflow builder',
|
||||
'visual workflow editor',
|
||||
@@ -258,16 +273,16 @@ export async function generateMetadata(props: {
|
||||
'AI agents',
|
||||
'no-code AI',
|
||||
'drag and drop workflows',
|
||||
page.data.title?.toLowerCase().split(' '),
|
||||
data.title?.toLowerCase().split(' '),
|
||||
]
|
||||
.flat()
|
||||
.filter(Boolean),
|
||||
authors: [{ name: 'Sim Team' }],
|
||||
category: 'Developer Tools',
|
||||
openGraph: {
|
||||
title: page.data.title,
|
||||
title: data.title,
|
||||
description:
|
||||
page.data.description || 'Sim visual workflow builder for AI applications documentation',
|
||||
data.description || 'Sim visual workflow builder for AI applications documentation',
|
||||
url: fullUrl,
|
||||
siteName: 'Sim Documentation',
|
||||
type: 'article',
|
||||
@@ -280,15 +295,15 @@ export async function generateMetadata(props: {
|
||||
url: ogImageUrl,
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: page.data.title,
|
||||
alt: data.title,
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: page.data.title,
|
||||
title: data.title,
|
||||
description:
|
||||
page.data.description || 'Sim visual workflow builder for AI applications documentation',
|
||||
data.description || 'Sim visual workflow builder for AI applications documentation',
|
||||
images: [ogImageUrl],
|
||||
creator: '@simdotai',
|
||||
site: '@simdotai',
|
||||
|
||||
23
apps/docs/app/[lang]/not-found.tsx
Normal file
23
apps/docs/app/[lang]/not-found.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { DocsBody, DocsPage } from 'fumadocs-ui/page'
|
||||
|
||||
export const metadata = {
|
||||
title: 'Page Not Found',
|
||||
}
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<DocsPage>
|
||||
<DocsBody>
|
||||
<div className='flex min-h-[60vh] flex-col items-center justify-center text-center'>
|
||||
<h1 className='mb-4 bg-gradient-to-b from-[#8357FF] to-[#6F3DFA] bg-clip-text font-bold text-8xl text-transparent'>
|
||||
404
|
||||
</h1>
|
||||
<h2 className='mb-2 font-semibold text-2xl text-foreground'>Page Not Found</h2>
|
||||
<p className='text-muted-foreground'>
|
||||
The page you're looking for doesn't exist or has been moved.
|
||||
</p>
|
||||
</div>
|
||||
</DocsBody>
|
||||
</DocsPage>
|
||||
)
|
||||
}
|
||||
@@ -43,7 +43,6 @@ export async function GET(request: NextRequest) {
|
||||
const description = searchParams.get('description') || ''
|
||||
|
||||
const baseUrl = new URL(request.url).origin
|
||||
const backgroundImageUrl = `${baseUrl}/static/og-background.png`
|
||||
|
||||
const allText = `${title}${category}${description}docs.sim.ai`
|
||||
const fontData = await loadGoogleFont('Geist', '400;500;600', allText)
|
||||
@@ -55,36 +54,49 @@ export async function GET(request: NextRequest) {
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
background: 'linear-gradient(315deg, #1e1e3f 0%, #1a1a2e 40%, #0f0f0f 100%)',
|
||||
background: '#0c0c0c',
|
||||
position: 'relative',
|
||||
fontFamily: 'Geist',
|
||||
}}
|
||||
>
|
||||
{/* Background texture */}
|
||||
<img
|
||||
src={backgroundImageUrl}
|
||||
alt=''
|
||||
{/* Base gradient layer - very subtle purple tint across the entire image */}
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
objectFit: 'cover',
|
||||
opacity: 0.04,
|
||||
background:
|
||||
'radial-gradient(ellipse 150% 100% at 50% 100%, rgba(88, 28, 135, 0.15) 0%, rgba(88, 28, 135, 0.08) 25%, rgba(88, 28, 135, 0.03) 50%, transparent 80%)',
|
||||
display: 'flex',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Subtle purple glow from bottom right */}
|
||||
{/* Secondary glow - adds depth without harsh edges */}
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
width: '50%',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
background:
|
||||
'radial-gradient(ellipse at bottom right, rgba(112, 31, 252, 0.1) 0%, transparent 50%)',
|
||||
'radial-gradient(ellipse 100% 80% at 80% 90%, rgba(112, 31, 252, 0.12) 0%, rgba(112, 31, 252, 0.04) 40%, transparent 70%)',
|
||||
display: 'flex',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Top darkening - creates natural vignette */}
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
background:
|
||||
'linear-gradient(180deg, rgba(0, 0, 0, 0.3) 0%, transparent 40%, transparent 100%)',
|
||||
display: 'flex',
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -6,7 +6,10 @@ import { source } from '@/lib/source'
|
||||
|
||||
export const revalidate = false
|
||||
|
||||
export async function GET(_req: NextRequest, { params }: { params: Promise<{ slug?: string[] }> }) {
|
||||
export async function GET(
|
||||
_request: NextRequest,
|
||||
{ params }: { params: Promise<{ slug?: string[] }> }
|
||||
) {
|
||||
const { slug } = await params
|
||||
|
||||
let lang: (typeof i18n.languages)[number] = i18n.defaultLanguage
|
||||
|
||||
@@ -2452,6 +2452,56 @@ export const GeminiIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
</svg>
|
||||
)
|
||||
|
||||
export const VertexIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
{...props}
|
||||
id='standard_product_icon'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
version='1.1'
|
||||
viewBox='0 0 512 512'
|
||||
>
|
||||
<g id='bounding_box'>
|
||||
<rect width='512' height='512' fill='none' />
|
||||
</g>
|
||||
<g id='art'>
|
||||
<path
|
||||
d='M128,244.99c-8.84,0-16-7.16-16-16v-95.97c0-8.84,7.16-16,16-16s16,7.16,16,16v95.97c0,8.84-7.16,16-16,16Z'
|
||||
fill='#ea4335'
|
||||
/>
|
||||
<path
|
||||
d='M256,458c-2.98,0-5.97-.83-8.59-2.5l-186-122c-7.46-4.74-9.65-14.63-4.91-22.09,4.75-7.46,14.64-9.65,22.09-4.91l177.41,116.53,177.41-116.53c7.45-4.74,17.34-2.55,22.09,4.91,4.74,7.46,2.55,17.34-4.91,22.09l-186,122c-2.62,1.67-5.61,2.5-8.59,2.5Z'
|
||||
fill='#fbbc04'
|
||||
/>
|
||||
<path
|
||||
d='M256,388.03c-8.84,0-16-7.16-16-16v-73.06c0-8.84,7.16-16,16-16s16,7.16,16,16v73.06c0,8.84-7.16,16-16,16Z'
|
||||
fill='#34a853'
|
||||
/>
|
||||
<circle cx='128' cy='70' r='16' fill='#ea4335' />
|
||||
<circle cx='128' cy='292' r='16' fill='#ea4335' />
|
||||
<path
|
||||
d='M384.23,308.01c-8.82,0-15.98-7.14-16-15.97l-.23-94.01c-.02-8.84,7.13-16.02,15.97-16.03h.04c8.82,0,15.98,7.14,16,15.97l.23,94.01c.02,8.84-7.13,16.02-15.97,16.03h-.04Z'
|
||||
fill='#4285f4'
|
||||
/>
|
||||
<circle cx='384' cy='70' r='16' fill='#4285f4' />
|
||||
<circle cx='384' cy='134' r='16' fill='#4285f4' />
|
||||
<path
|
||||
d='M320,220.36c-8.84,0-16-7.16-16-16v-103.02c0-8.84,7.16-16,16-16s16,7.16,16,16v103.02c0,8.84-7.16,16-16,16Z'
|
||||
fill='#fbbc04'
|
||||
/>
|
||||
<circle cx='256' cy='171' r='16' fill='#34a853' />
|
||||
<circle cx='256' cy='235' r='16' fill='#34a853' />
|
||||
<circle cx='320' cy='265' r='16' fill='#fbbc04' />
|
||||
<circle cx='320' cy='329' r='16' fill='#fbbc04' />
|
||||
<path
|
||||
d='M192,217.36c-8.84,0-16-7.16-16-16v-100.02c0-8.84,7.16-16,16-16s16,7.16,16,16v100.02c0,8.84-7.16,16-16,16Z'
|
||||
fill='#fbbc04'
|
||||
/>
|
||||
<circle cx='192' cy='265' r='16' fill='#fbbc04' />
|
||||
<circle cx='192' cy='329' r='16' fill='#fbbc04' />
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export const CerebrasIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
{...props}
|
||||
@@ -3335,6 +3385,21 @@ export function SalesforceIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function ServiceNowIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 71.1 63.6'>
|
||||
<path
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
fill='#62D84E'
|
||||
d='M35.8,0C16.1,0,0,15.9,0,35.6c0,9.8,4,19.3,11.2,26c2.5,2.4,6.4,2.6,9.2,0.5c9-6.7,21.4-6.7,30.4,0
|
||||
c2.8,2.1,6.7,1.9,9.2-0.5C74.3,48,74.9,25.4,61.3,11.1C54.7,4.1,45.4,0.1,35.8,0 M35.6,53.5C26,53.8,18,46.2,17.8,36.7
|
||||
c0-0.3,0-0.6,0-0.9c0-9.8,8-17.8,17.8-17.8s17.8,8,17.8,17.8c0.3,9.6-7.3,17.5-16.8,17.8C36.2,53.5,35.9,53.5,35.6,53.5'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function ApolloIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
|
||||
@@ -85,6 +85,7 @@ import {
|
||||
SendgridIcon,
|
||||
SentryIcon,
|
||||
SerperIcon,
|
||||
ServiceNowIcon,
|
||||
SftpIcon,
|
||||
ShopifyIcon,
|
||||
SlackIcon,
|
||||
@@ -119,116 +120,117 @@ import {
|
||||
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,
|
||||
wealthbox: WealthboxIcon,
|
||||
vision: EyeIcon,
|
||||
video_generator: VideoIcon,
|
||||
typeform: TypeformIcon,
|
||||
twilio_voice: TwilioIcon,
|
||||
twilio_sms: TwilioIcon,
|
||||
tts: TTSIcon,
|
||||
trello: TrelloIcon,
|
||||
translate: TranslateIcon,
|
||||
thinking: BrainIcon,
|
||||
telegram: TelegramIcon,
|
||||
tavily: TavilyIcon,
|
||||
supabase: SupabaseIcon,
|
||||
stt: STTIcon,
|
||||
stripe: StripeIcon,
|
||||
stagehand: StagehandIcon,
|
||||
ssh: SshIcon,
|
||||
sqs: SQSIcon,
|
||||
spotify: SpotifyIcon,
|
||||
smtp: SmtpIcon,
|
||||
slack: SlackIcon,
|
||||
shopify: ShopifyIcon,
|
||||
sharepoint: MicrosoftSharepointIcon,
|
||||
sftp: SftpIcon,
|
||||
serper: SerperIcon,
|
||||
sentry: SentryIcon,
|
||||
sendgrid: SendgridIcon,
|
||||
search: SearchIcon,
|
||||
salesforce: SalesforceIcon,
|
||||
s3: S3Icon,
|
||||
resend: ResendIcon,
|
||||
reddit: RedditIcon,
|
||||
rds: RDSIcon,
|
||||
qdrant: QdrantIcon,
|
||||
posthog: PosthogIcon,
|
||||
postgresql: PostgresIcon,
|
||||
polymarket: PolymarketIcon,
|
||||
pipedrive: PipedriveIcon,
|
||||
pinecone: PineconeIcon,
|
||||
perplexity: PerplexityIcon,
|
||||
parallel_ai: ParallelIcon,
|
||||
outlook: OutlookIcon,
|
||||
openai: OpenAIIcon,
|
||||
onedrive: MicrosoftOneDriveIcon,
|
||||
notion: NotionIcon,
|
||||
neo4j: Neo4jIcon,
|
||||
mysql: MySQLIcon,
|
||||
mongodb: MongoDBIcon,
|
||||
mistral_parse: MistralIcon,
|
||||
microsoft_teams: MicrosoftTeamsIcon,
|
||||
microsoft_planner: MicrosoftPlannerIcon,
|
||||
microsoft_excel: MicrosoftExcelIcon,
|
||||
memory: BrainIcon,
|
||||
mem0: Mem0Icon,
|
||||
mailgun: MailgunIcon,
|
||||
mailchimp: MailchimpIcon,
|
||||
linkup: LinkupIcon,
|
||||
linkedin: LinkedInIcon,
|
||||
linear: LinearIcon,
|
||||
knowledge: PackageSearchIcon,
|
||||
kalshi: KalshiIcon,
|
||||
jira: JiraIcon,
|
||||
jina: JinaAIIcon,
|
||||
intercom: IntercomIcon,
|
||||
incidentio: IncidentioIcon,
|
||||
image_generator: ImageIcon,
|
||||
hunter: HunterIOIcon,
|
||||
huggingface: HuggingFaceIcon,
|
||||
hubspot: HubspotIcon,
|
||||
grafana: GrafanaIcon,
|
||||
google_vault: GoogleVaultIcon,
|
||||
google_slides: GoogleSlidesIcon,
|
||||
google_sheets: GoogleSheetsIcon,
|
||||
google_groups: GoogleGroupsIcon,
|
||||
google_forms: GoogleFormsIcon,
|
||||
google_drive: GoogleDriveIcon,
|
||||
google_docs: GoogleDocsIcon,
|
||||
google_calendar: GoogleCalendarIcon,
|
||||
google_search: GoogleIcon,
|
||||
gmail: GmailIcon,
|
||||
gitlab: GitLabIcon,
|
||||
github: GithubIcon,
|
||||
firecrawl: FirecrawlIcon,
|
||||
file: DocumentIcon,
|
||||
exa: ExaAIIcon,
|
||||
elevenlabs: ElevenLabsIcon,
|
||||
elasticsearch: ElasticsearchIcon,
|
||||
dynamodb: DynamoDBIcon,
|
||||
duckduckgo: DuckDuckGoIcon,
|
||||
dropbox: DropboxIcon,
|
||||
discord: DiscordIcon,
|
||||
datadog: DatadogIcon,
|
||||
cursor: CursorIcon,
|
||||
confluence: ConfluenceIcon,
|
||||
clay: ClayIcon,
|
||||
calendly: CalendlyIcon,
|
||||
browser_use: BrowserUseIcon,
|
||||
asana: AsanaIcon,
|
||||
arxiv: ArxivIcon,
|
||||
apollo: ApolloIcon,
|
||||
apify: ApifyIcon,
|
||||
airtable: AirtableIcon,
|
||||
ahrefs: AhrefsIcon,
|
||||
airtable: AirtableIcon,
|
||||
apify: ApifyIcon,
|
||||
apollo: ApolloIcon,
|
||||
arxiv: ArxivIcon,
|
||||
asana: AsanaIcon,
|
||||
browser_use: BrowserUseIcon,
|
||||
calendly: CalendlyIcon,
|
||||
clay: ClayIcon,
|
||||
confluence: ConfluenceIcon,
|
||||
cursor: CursorIcon,
|
||||
datadog: DatadogIcon,
|
||||
discord: DiscordIcon,
|
||||
dropbox: DropboxIcon,
|
||||
duckduckgo: DuckDuckGoIcon,
|
||||
dynamodb: DynamoDBIcon,
|
||||
elasticsearch: ElasticsearchIcon,
|
||||
elevenlabs: ElevenLabsIcon,
|
||||
exa: ExaAIIcon,
|
||||
file: DocumentIcon,
|
||||
firecrawl: FirecrawlIcon,
|
||||
github: GithubIcon,
|
||||
gitlab: GitLabIcon,
|
||||
gmail: GmailIcon,
|
||||
google_calendar: GoogleCalendarIcon,
|
||||
google_docs: GoogleDocsIcon,
|
||||
google_drive: GoogleDriveIcon,
|
||||
google_forms: GoogleFormsIcon,
|
||||
google_groups: GoogleGroupsIcon,
|
||||
google_search: GoogleIcon,
|
||||
google_sheets: GoogleSheetsIcon,
|
||||
google_slides: GoogleSlidesIcon,
|
||||
google_vault: GoogleVaultIcon,
|
||||
grafana: GrafanaIcon,
|
||||
hubspot: HubspotIcon,
|
||||
huggingface: HuggingFaceIcon,
|
||||
hunter: HunterIOIcon,
|
||||
image_generator: ImageIcon,
|
||||
incidentio: IncidentioIcon,
|
||||
intercom: IntercomIcon,
|
||||
jina: JinaAIIcon,
|
||||
jira: JiraIcon,
|
||||
kalshi: KalshiIcon,
|
||||
knowledge: PackageSearchIcon,
|
||||
linear: LinearIcon,
|
||||
linkedin: LinkedInIcon,
|
||||
linkup: LinkupIcon,
|
||||
mailchimp: MailchimpIcon,
|
||||
mailgun: MailgunIcon,
|
||||
mem0: Mem0Icon,
|
||||
memory: BrainIcon,
|
||||
microsoft_excel: MicrosoftExcelIcon,
|
||||
microsoft_planner: MicrosoftPlannerIcon,
|
||||
microsoft_teams: MicrosoftTeamsIcon,
|
||||
mistral_parse: MistralIcon,
|
||||
mongodb: MongoDBIcon,
|
||||
mysql: MySQLIcon,
|
||||
neo4j: Neo4jIcon,
|
||||
notion: NotionIcon,
|
||||
onedrive: MicrosoftOneDriveIcon,
|
||||
openai: OpenAIIcon,
|
||||
outlook: OutlookIcon,
|
||||
parallel_ai: ParallelIcon,
|
||||
perplexity: PerplexityIcon,
|
||||
pinecone: PineconeIcon,
|
||||
pipedrive: PipedriveIcon,
|
||||
polymarket: PolymarketIcon,
|
||||
postgresql: PostgresIcon,
|
||||
posthog: PosthogIcon,
|
||||
qdrant: QdrantIcon,
|
||||
rds: RDSIcon,
|
||||
reddit: RedditIcon,
|
||||
resend: ResendIcon,
|
||||
s3: S3Icon,
|
||||
salesforce: SalesforceIcon,
|
||||
search: SearchIcon,
|
||||
sendgrid: SendgridIcon,
|
||||
sentry: SentryIcon,
|
||||
serper: SerperIcon,
|
||||
servicenow: ServiceNowIcon,
|
||||
sftp: SftpIcon,
|
||||
sharepoint: MicrosoftSharepointIcon,
|
||||
shopify: ShopifyIcon,
|
||||
slack: SlackIcon,
|
||||
smtp: SmtpIcon,
|
||||
spotify: SpotifyIcon,
|
||||
sqs: SQSIcon,
|
||||
ssh: SshIcon,
|
||||
stagehand: StagehandIcon,
|
||||
stripe: StripeIcon,
|
||||
stt: STTIcon,
|
||||
supabase: SupabaseIcon,
|
||||
tavily: TavilyIcon,
|
||||
telegram: TelegramIcon,
|
||||
thinking: BrainIcon,
|
||||
translate: TranslateIcon,
|
||||
trello: TrelloIcon,
|
||||
tts: TTSIcon,
|
||||
twilio_sms: TwilioIcon,
|
||||
twilio_voice: TwilioIcon,
|
||||
typeform: TypeformIcon,
|
||||
video_generator: VideoIcon,
|
||||
vision: EyeIcon,
|
||||
wealthbox: WealthboxIcon,
|
||||
webflow: WebflowIcon,
|
||||
whatsapp: WhatsAppIcon,
|
||||
wikipedia: WikipediaIcon,
|
||||
wordpress: WordpressIcon,
|
||||
x: xIcon,
|
||||
youtube: YouTubeIcon,
|
||||
zendesk: ZendeskIcon,
|
||||
zep: ZepIcon,
|
||||
zoom: ZoomIcon,
|
||||
}
|
||||
|
||||
@@ -111,26 +111,24 @@ Verschiedene Blocktypen erzeugen unterschiedliche Ausgabestrukturen. Hier ist, w
|
||||
|
||||
```json
|
||||
{
|
||||
"content": "Original content passed through",
|
||||
"conditionResult": true,
|
||||
"selectedPath": {
|
||||
"blockId": "2acd9007-27e8-4510-a487-73d3b825e7c1",
|
||||
"blockType": "agent",
|
||||
"blockTitle": "Follow-up Agent"
|
||||
},
|
||||
"selectedConditionId": "condition-1"
|
||||
"selectedOption": "condition-1"
|
||||
}
|
||||
```
|
||||
|
||||
### Ausgabefelder des Condition-Blocks
|
||||
|
||||
- **content**: Der ursprüngliche, durchgeleitete Inhalt
|
||||
- **conditionResult**: Boolesches Ergebnis der Bedingungsauswertung
|
||||
- **selectedPath**: Informationen über den ausgewählten Pfad
|
||||
- **blockId**: ID des nächsten Blocks im ausgewählten Pfad
|
||||
- **blockType**: Typ des nächsten Blocks
|
||||
- **blockTitle**: Titel des nächsten Blocks
|
||||
- **selectedConditionId**: ID der ausgewählten Bedingung
|
||||
- **selectedOption**: ID der ausgewählten Bedingung
|
||||
|
||||
</Tab>
|
||||
<Tab>
|
||||
|
||||
@@ -48,8 +48,13 @@ Ruft detaillierte Informationen zu einem bestimmten Jira-Issue ab
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Status des Operationserfolgs |
|
||||
| `output` | object | Jira-Issue-Details mit Issue-Key, Zusammenfassung, Beschreibung, Erstellungs- und Aktualisierungszeitstempeln |
|
||||
| `ts` | string | Zeitstempel der Operation |
|
||||
| `issueKey` | string | Issue-Key \(z.B. PROJ-123\) |
|
||||
| `summary` | string | Issue-Zusammenfassung |
|
||||
| `description` | json | Inhalt der Issue-Beschreibung |
|
||||
| `created` | string | Zeitstempel der Issue-Erstellung |
|
||||
| `updated` | string | Zeitstempel der letzten Issue-Aktualisierung |
|
||||
| `issue` | json | Vollständiges Issue-Objekt mit allen Feldern |
|
||||
|
||||
### `jira_update`
|
||||
|
||||
@@ -73,8 +78,9 @@ Ein Jira-Issue aktualisieren
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Aktualisierte Jira-Issue-Details mit Zeitstempel, Issue-Key, Zusammenfassung und Erfolgsstatus |
|
||||
| `ts` | string | Zeitstempel der Operation |
|
||||
| `issueKey` | string | Aktualisierter Issue-Key \(z.B. PROJ-123\) |
|
||||
| `summary` | string | Issue-Zusammenfassung nach der Aktualisierung |
|
||||
|
||||
### `jira_write`
|
||||
|
||||
@@ -84,21 +90,30 @@ Ein Jira-Issue erstellen
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Ja | Ihre Jira-Domain (z.B. ihrfirma.atlassian.net) |
|
||||
| `domain` | string | Ja | Ihre Jira-Domain \(z.B. ihrfirma.atlassian.net\) |
|
||||
| `projectId` | string | Ja | Projekt-ID für das Issue |
|
||||
| `summary` | string | Ja | Zusammenfassung für das Issue |
|
||||
| `description` | string | Nein | Beschreibung für das Issue |
|
||||
| `priority` | string | Nein | Priorität für das Issue |
|
||||
| `assignee` | string | Nein | Bearbeiter für das Issue |
|
||||
| `cloudId` | string | Nein | Jira Cloud-ID für die Instanz. Wenn nicht angegeben, wird sie anhand der Domain abgerufen. |
|
||||
| `issueType` | string | Ja | Art des zu erstellenden Issues (z.B. Task, Story) |
|
||||
| `priority` | string | Nein | Prioritäts-ID oder -Name für das Issue \(z.B. "10000" oder "High"\) |
|
||||
| `assignee` | string | Nein | Account-ID des Bearbeiters für das Issue |
|
||||
| `cloudId` | string | Nein | Jira Cloud-ID für die Instanz. Wenn nicht angegeben, wird sie über die Domain abgerufen. |
|
||||
| `issueType` | string | Ja | Typ des zu erstellenden Issues \(z.B. Task, Story\) |
|
||||
| `labels` | array | Nein | Labels für das Issue \(Array von Label-Namen\) |
|
||||
| `duedate` | string | Nein | Fälligkeitsdatum für das Issue \(Format: YYYY-MM-DD\) |
|
||||
| `reporter` | string | Nein | Account-ID des Melders für das Issue |
|
||||
| `environment` | string | Nein | Umgebungsinformationen für das Issue |
|
||||
| `customFieldId` | string | Nein | Benutzerdefinierte Feld-ID \(z.B. customfield_10001\) |
|
||||
| `customFieldValue` | string | Nein | Wert für das benutzerdefinierte Feld |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Erstellte Jira-Issue-Details mit Zeitstempel, Issue-Key, Zusammenfassung, Erfolgsstatus und URL |
|
||||
| `ts` | string | Zeitstempel der Operation |
|
||||
| `issueKey` | string | Erstellter Issue-Key \(z.B. PROJ-123\) |
|
||||
| `summary` | string | Issue-Zusammenfassung |
|
||||
| `url` | string | URL zum erstellten Issue |
|
||||
| `assigneeId` | string | Account-ID des zugewiesenen Benutzers \(falls zugewiesen\) |
|
||||
|
||||
### `jira_bulk_read`
|
||||
|
||||
@@ -116,8 +131,7 @@ Mehrere Jira-Issues in Masse abrufen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | array | Array von Jira-Issues mit Zusammenfassung, Beschreibung, Erstellungs- und Aktualisierungszeitstempeln |
|
||||
| `issues` | array | Array von Jira-Issues mit Zeitstempel, Zusammenfassung, Beschreibung, Erstellungs- und Aktualisierungszeitstempeln |
|
||||
|
||||
### `jira_delete_issue`
|
||||
|
||||
@@ -136,8 +150,8 @@ Ein Jira-Issue löschen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Details zum gelöschten Issue mit Zeitstempel, Issue-Key und Erfolgsstatus |
|
||||
| `ts` | string | Zeitstempel der Operation |
|
||||
| `issueKey` | string | Gelöschter Issue-Key |
|
||||
|
||||
### `jira_assign_issue`
|
||||
|
||||
@@ -156,8 +170,9 @@ Ein Jira-Issue einem Benutzer zuweisen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Zuweisungsdetails mit Zeitstempel, Issue-Key, Bearbeiter-ID und Erfolgsstatus |
|
||||
| `ts` | string | Zeitstempel der Operation |
|
||||
| `issueKey` | string | Issue-Key, der zugewiesen wurde |
|
||||
| `assigneeId` | string | Konto-ID des Bearbeiters |
|
||||
|
||||
### `jira_transition_issue`
|
||||
|
||||
@@ -177,8 +192,9 @@ Ein Jira-Issue zwischen Workflow-Status verschieben (z.B. To Do -> In Progress)
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Übergangsdetails mit Zeitstempel, Issue-Key, Übergangs-ID und Erfolgsstatus |
|
||||
| `ts` | string | Zeitstempel der Operation |
|
||||
| `issueKey` | string | Issue-Key, der übergangen wurde |
|
||||
| `transitionId` | string | Angewendete Übergangs-ID |
|
||||
|
||||
### `jira_search_issues`
|
||||
|
||||
@@ -199,8 +215,11 @@ Nach Jira-Issues mit JQL (Jira Query Language) suchen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Suchergebnisse mit Zeitstempel, Gesamtanzahl, Paginierungsdetails und Array der übereinstimmenden Issues |
|
||||
| `ts` | string | Zeitstempel der Operation |
|
||||
| `total` | number | Gesamtanzahl der übereinstimmenden Issues |
|
||||
| `startAt` | number | Paginierungsstartindex |
|
||||
| `maxResults` | number | Maximale Ergebnisse pro Seite |
|
||||
| `issues` | array | Array übereinstimmender Issues mit Key, Zusammenfassung, Status, Bearbeiter, Erstellungs- und Aktualisierungsdatum |
|
||||
|
||||
### `jira_add_comment`
|
||||
|
||||
@@ -219,8 +238,10 @@ Einen Kommentar zu einem Jira-Issue hinzufügen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Kommentardetails mit Zeitstempel, Issue-Key, Kommentar-ID, Inhalt und Erfolgsstatus |
|
||||
| `ts` | string | Zeitstempel der Operation |
|
||||
| `issueKey` | string | Issue-Key, zu dem der Kommentar hinzugefügt wurde |
|
||||
| `commentId` | string | Erstellte Kommentar-ID |
|
||||
| `body` | string | Kommentartextinhalt |
|
||||
|
||||
### `jira_get_comments`
|
||||
|
||||
@@ -240,8 +261,10 @@ Alle Kommentare eines Jira-Issues abrufen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Kommentardaten mit Zeitstempel, Issue-Key, Gesamtanzahl und Array von Kommentaren |
|
||||
| `ts` | string | Zeitstempel der Operation |
|
||||
| `issueKey` | string | Issue-Key |
|
||||
| `total` | number | Gesamtanzahl der Kommentare |
|
||||
| `comments` | array | Array von Kommentaren mit ID, Autor, Inhalt, Erstellungs- und Aktualisierungsdatum |
|
||||
|
||||
### `jira_update_comment`
|
||||
|
||||
@@ -261,8 +284,10 @@ Einen bestehenden Kommentar zu einem Jira-Issue aktualisieren
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Aktualisierte Kommentardetails mit Zeitstempel, Issue-Key, Kommentar-ID, Textinhalt und Erfolgsstatus |
|
||||
| `ts` | string | Zeitstempel der Operation |
|
||||
| `issueKey` | string | Issue-Key |
|
||||
| `commentId` | string | Aktualisierte Kommentar-ID |
|
||||
| `body` | string | Aktualisierter Kommentartext |
|
||||
|
||||
### `jira_delete_comment`
|
||||
|
||||
@@ -281,8 +306,9 @@ Einen Kommentar aus einem Jira-Issue löschen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Löschdetails mit Zeitstempel, Issue-Key, Kommentar-ID und Erfolgsstatus |
|
||||
| `ts` | string | Zeitstempel der Operation |
|
||||
| `issueKey` | string | Issue-Key |
|
||||
| `commentId` | string | ID des gelöschten Kommentars |
|
||||
|
||||
### `jira_get_attachments`
|
||||
|
||||
@@ -300,8 +326,9 @@ Alle Anhänge eines Jira-Issues abrufen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Anhangsdaten mit Zeitstempel, Issue-Key und Array von Anhängen |
|
||||
| `ts` | string | Zeitstempel der Operation |
|
||||
| `issueKey` | string | Issue-Key |
|
||||
| `attachments` | array | Array von Anhängen mit ID, Dateiname, Größe, MIME-Typ, Erstellungsdatum und Autor |
|
||||
|
||||
### `jira_delete_attachment`
|
||||
|
||||
@@ -319,8 +346,8 @@ Einen Anhang von einem Jira-Issue löschen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Löschdetails mit Zeitstempel, Anhangs-ID und Erfolgsstatus |
|
||||
| `ts` | string | Zeitstempel der Operation |
|
||||
| `attachmentId` | string | ID des gelöschten Anhangs |
|
||||
|
||||
### `jira_add_worklog`
|
||||
|
||||
@@ -341,8 +368,10 @@ Einen Zeiterfassungseintrag zu einem Jira-Issue hinzufügen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Worklog-Details mit Zeitstempel, Issue-Key, Worklog-ID, aufgewendeter Zeit in Sekunden und Erfolgsstatus |
|
||||
| `ts` | string | Zeitstempel der Operation |
|
||||
| `issueKey` | string | Issue-Key, zu dem der Worklog hinzugefügt wurde |
|
||||
| `worklogId` | string | ID des erstellten Worklogs |
|
||||
| `timeSpentSeconds` | number | Aufgewendete Zeit in Sekunden |
|
||||
|
||||
### `jira_get_worklogs`
|
||||
|
||||
@@ -362,8 +391,10 @@ Alle Worklog-Einträge eines Jira-Issues abrufen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Worklog-Daten mit Zeitstempel, Issue-Key, Gesamtanzahl und Array von Worklogs |
|
||||
| `ts` | string | Zeitstempel der Operation |
|
||||
| `issueKey` | string | Issue-Key |
|
||||
| `total` | number | Gesamtanzahl der Worklogs |
|
||||
| `worklogs` | array | Array von Worklogs mit ID, Autor, aufgewendeter Zeit in Sekunden, aufgewendeter Zeit, Kommentar, Erstellungs-, Aktualisierungs- und Startdatum |
|
||||
|
||||
### `jira_update_worklog`
|
||||
|
||||
@@ -385,8 +416,9 @@ Aktualisieren eines vorhandenen Worklog-Eintrags in einem Jira-Issue
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Worklog-Aktualisierungsdetails mit Zeitstempel, Issue-Key, Worklog-ID und Erfolgsstatus |
|
||||
| `ts` | string | Zeitstempel der Operation |
|
||||
| `issueKey` | string | Issue-Key |
|
||||
| `worklogId` | string | ID des aktualisierten Worklogs |
|
||||
|
||||
### `jira_delete_worklog`
|
||||
|
||||
@@ -405,8 +437,9 @@ Löschen eines Worklog-Eintrags aus einem Jira-Issue
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Löschdetails mit Zeitstempel, Issue-Key, Worklog-ID und Erfolgsstatus |
|
||||
| `ts` | string | Zeitstempel der Operation |
|
||||
| `issueKey` | string | Issue-Key |
|
||||
| `worklogId` | string | ID des gelöschten Worklogs |
|
||||
|
||||
### `jira_create_issue_link`
|
||||
|
||||
@@ -427,8 +460,11 @@ Eine Verknüpfungsbeziehung zwischen zwei Jira-Issues erstellen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Issue-Verknüpfungsdetails mit Zeitstempel, eingehendem Issue-Key, ausgehendem Issue-Key, Verknüpfungstyp und Erfolgsstatus |
|
||||
| `ts` | string | Zeitstempel der Operation |
|
||||
| `inwardIssue` | string | Key des eingehenden Issues |
|
||||
| `outwardIssue` | string | Key des ausgehenden Issues |
|
||||
| `linkType` | string | Art der Issue-Verknüpfung |
|
||||
| `linkId` | string | ID der erstellten Verknüpfung |
|
||||
|
||||
### `jira_delete_issue_link`
|
||||
|
||||
@@ -446,8 +482,8 @@ Eine Verknüpfung zwischen zwei Jira-Issues löschen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Löschdetails mit Zeitstempel, Link-ID und Erfolgsstatus |
|
||||
| `ts` | string | Zeitstempel der Operation |
|
||||
| `linkId` | string | ID der gelöschten Verknüpfung |
|
||||
|
||||
### `jira_add_watcher`
|
||||
|
||||
@@ -466,8 +502,9 @@ Einen Beobachter zu einem Jira-Issue hinzufügen, um Benachrichtigungen über Ak
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Beobachterdetails mit Zeitstempel, Issue-Key, Beobachter-Account-ID und Erfolgsstatus |
|
||||
| `ts` | string | Zeitstempel der Operation |
|
||||
| `issueKey` | string | Issue-Key |
|
||||
| `watcherAccountId` | string | Account-ID des hinzugefügten Beobachters |
|
||||
|
||||
### `jira_remove_watcher`
|
||||
|
||||
@@ -486,8 +523,33 @@ Einen Beobachter von einem Jira-Issue entfernen
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Erfolgsstatus der Operation |
|
||||
| `output` | object | Entfernungsdetails mit Zeitstempel, Issue-Key, Beobachter-Konto-ID und Erfolgsstatus |
|
||||
| `ts` | string | Zeitstempel der Operation |
|
||||
| `issueKey` | string | Issue-Key |
|
||||
| `watcherAccountId` | string | Account-ID des entfernten Beobachters |
|
||||
|
||||
### `jira_get_users`
|
||||
|
||||
Jira-Benutzer abrufen. Wenn eine Account-ID angegeben wird, wird ein einzelner Benutzer zurückgegeben. Andernfalls wird eine Liste aller Benutzer zurückgegeben.
|
||||
|
||||
#### Eingabe
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Ja | Ihre Jira-Domain \(z.B. ihrfirma.atlassian.net\) |
|
||||
| `accountId` | string | Nein | Optionale Account-ID, um einen bestimmten Benutzer abzurufen. Wenn nicht angegeben, werden alle Benutzer zurückgegeben. |
|
||||
| `startAt` | number | Nein | Der Index des ersten zurückzugebenden Benutzers \(für Paginierung, Standard: 0\) |
|
||||
| `maxResults` | number | Nein | Maximale Anzahl der zurückzugebenden Benutzer \(Standard: 50\) |
|
||||
| `cloudId` | string | Nein | Jira Cloud-ID für die Instanz. Wenn nicht angegeben, wird sie anhand der Domain abgerufen. |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Zeitstempel der Operation |
|
||||
| `users` | json | Array von Benutzern mit accountId, displayName, emailAddress, active-Status und avatarUrls |
|
||||
| `total` | number | Gesamtanzahl der zurückgegebenen Benutzer |
|
||||
| `startAt` | number | Startindex für Paginierung |
|
||||
| `maxResults` | number | Maximale Ergebnisse pro Seite |
|
||||
|
||||
## Hinweise
|
||||
|
||||
|
||||
124
apps/docs/content/docs/de/tools/servicenow.mdx
Normal file
124
apps/docs/content/docs/de/tools/servicenow.mdx
Normal file
@@ -0,0 +1,124 @@
|
||||
---
|
||||
title: ServiceNow
|
||||
description: ServiceNow-Datensätze erstellen, lesen, aktualisieren und löschen
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="servicenow"
|
||||
color="#032D42"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[ServiceNow](https://www.servicenow.com/) ist eine leistungsstarke Cloud-Plattform zur Optimierung und Automatisierung von IT-Service-Management (ITSM), Workflows und Geschäftsprozessen in Ihrem Unternehmen. ServiceNow ermöglicht Ihnen die Verwaltung von Vorfällen, Anfragen, Aufgaben, Benutzern und mehr über seine umfangreiche API.
|
||||
|
||||
Mit ServiceNow können Sie:
|
||||
|
||||
- **IT-Workflows automatisieren**: Datensätze in jeder ServiceNow-Tabelle erstellen, lesen, aktualisieren und löschen, z. B. Vorfälle, Aufgaben, Änderungsanfragen und Benutzer.
|
||||
- **Systeme integrieren**: ServiceNow mit Ihren anderen Tools und Prozessen für nahtlose Automatisierung verbinden.
|
||||
- **Eine einzige Informationsquelle pflegen**: Alle Ihre Service- und Betriebsdaten organisiert und zugänglich halten.
|
||||
- **Betriebliche Effizienz steigern**: Manuelle Arbeit reduzieren und Servicequalität mit anpassbaren Workflows und Automatisierung verbessern.
|
||||
|
||||
In Sim ermöglicht die ServiceNow-Integration Ihren Agenten, direkt mit Ihrer ServiceNow-Instanz als Teil ihrer Workflows zu interagieren. Agenten können Datensätze in jeder ServiceNow-Tabelle erstellen, lesen, aktualisieren oder löschen und Ticket- oder Benutzerdaten für ausgefeilte Automatisierung und Entscheidungsfindung nutzen. Diese Integration verbindet Ihre Workflow-Automatisierung und IT-Betrieb und befähigt Ihre Agenten, Serviceanfragen, Vorfälle, Benutzer und Assets ohne manuelle Eingriffe zu verwalten. Durch die Verbindung von Sim mit ServiceNow können Sie Service-Management-Aufgaben automatisieren, Reaktionszeiten verbessern und konsistenten, sicheren Zugriff auf die wichtigen Servicedaten Ihres Unternehmens gewährleisten.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
## Nutzungsanweisungen
|
||||
|
||||
Integrieren Sie ServiceNow in Ihren Workflow. Erstellen, lesen, aktualisieren und löschen Sie Datensätze in jeder ServiceNow-Tabelle, einschließlich Vorfälle, Aufgaben, Änderungsanfragen, Benutzer und mehr.
|
||||
|
||||
## Tools
|
||||
|
||||
### `servicenow_create_record`
|
||||
|
||||
Einen neuen Datensatz in einer ServiceNow-Tabelle erstellen
|
||||
|
||||
#### Eingabe
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `instanceUrl` | string | Ja | ServiceNow-Instanz-URL \(z. B. https://instance.service-now.com\) |
|
||||
| `username` | string | Ja | ServiceNow-Benutzername |
|
||||
| `password` | string | Ja | ServiceNow-Passwort |
|
||||
| `tableName` | string | Ja | Tabellenname \(z. B. incident, task, sys_user\) |
|
||||
| `fields` | json | Ja | Felder, die für den Datensatz festgelegt werden sollen \(JSON-Objekt\) |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `record` | json | Erstellter ServiceNow-Datensatz mit sys_id und anderen Feldern |
|
||||
| `metadata` | json | Metadaten der Operation |
|
||||
|
||||
### `servicenow_read_record`
|
||||
|
||||
Datensätze aus einer ServiceNow-Tabelle lesen
|
||||
|
||||
#### Eingabe
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `instanceUrl` | string | Ja | ServiceNow-Instanz-URL \(z. B. https://instance.service-now.com\) |
|
||||
| `username` | string | Ja | ServiceNow-Benutzername |
|
||||
| `password` | string | Ja | ServiceNow-Passwort |
|
||||
| `tableName` | string | Ja | Tabellenname |
|
||||
| `sysId` | string | Nein | Spezifische Datensatz-sys_id |
|
||||
| `number` | string | Nein | Datensatznummer \(z. B. INC0010001\) |
|
||||
| `query` | string | Nein | Kodierte Abfragezeichenfolge \(z. B. "active=true^priority=1"\) |
|
||||
| `limit` | number | Nein | Maximale Anzahl der zurückzugebenden Datensätze |
|
||||
| `fields` | string | Nein | Durch Kommas getrennte Liste der zurückzugebenden Felder |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `records` | array | Array von ServiceNow-Datensätzen |
|
||||
| `metadata` | json | Metadaten der Operation |
|
||||
|
||||
### `servicenow_update_record`
|
||||
|
||||
Einen bestehenden Datensatz in einer ServiceNow-Tabelle aktualisieren
|
||||
|
||||
#### Eingabe
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `instanceUrl` | string | Ja | ServiceNow-Instanz-URL \(z. B. https://instance.service-now.com\) |
|
||||
| `username` | string | Ja | ServiceNow-Benutzername |
|
||||
| `password` | string | Ja | ServiceNow-Passwort |
|
||||
| `tableName` | string | Ja | Tabellenname |
|
||||
| `sysId` | string | Ja | Datensatz-sys_id zum Aktualisieren |
|
||||
| `fields` | json | Ja | Zu aktualisierende Felder \(JSON-Objekt\) |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `record` | json | Aktualisierter ServiceNow-Datensatz |
|
||||
| `metadata` | json | Metadaten der Operation |
|
||||
|
||||
### `servicenow_delete_record`
|
||||
|
||||
Einen Datensatz aus einer ServiceNow-Tabelle löschen
|
||||
|
||||
#### Eingabe
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `instanceUrl` | string | Ja | ServiceNow-Instanz-URL \(z. B. https://instance.service-now.com\) |
|
||||
| `username` | string | Ja | ServiceNow-Benutzername |
|
||||
| `password` | string | Ja | ServiceNow-Passwort |
|
||||
| `tableName` | string | Ja | Tabellenname |
|
||||
| `sysId` | string | Ja | Datensatz-sys_id zum Löschen |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Ob das Löschen erfolgreich war |
|
||||
| `metadata` | json | Metadaten der Operation |
|
||||
|
||||
## Hinweise
|
||||
|
||||
- Kategorie: `tools`
|
||||
- Typ: `servicenow`
|
||||
@@ -54,7 +54,7 @@ Integriert Slack in den Workflow. Kann Nachrichten senden, aktualisieren und lö
|
||||
|
||||
### `slack_message`
|
||||
|
||||
Senden Sie Nachrichten an Slack-Kanäle oder Benutzer über die Slack-API. Unterstützt Slack mrkdwn-Formatierung.
|
||||
Sende Nachrichten an Slack-Kanäle oder Direktnachrichten. Unterstützt Slack mrkdwn-Formatierung.
|
||||
|
||||
#### Eingabe
|
||||
|
||||
@@ -62,8 +62,9 @@ Senden Sie Nachrichten an Slack-Kanäle oder Benutzer über die Slack-API. Unter
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authMethod` | string | Nein | Authentifizierungsmethode: oauth oder bot_token |
|
||||
| `botToken` | string | Nein | Bot-Token für benutzerdefinierten Bot |
|
||||
| `channel` | string | Ja | Ziel-Slack-Kanal \(z.B. #general\) |
|
||||
| `text` | string | Ja | Nachrichtentext zum Senden \(unterstützt Slack mrkdwn-Formatierung\) |
|
||||
| `channel` | string | Nein | Ziel-Slack-Kanal \(z.B. #general\) |
|
||||
| `userId` | string | Nein | Ziel-Slack-Benutzer-ID für Direktnachrichten \(z.B. U1234567890\) |
|
||||
| `text` | string | Ja | Zu sendender Nachrichtentext \(unterstützt Slack mrkdwn-Formatierung\) |
|
||||
| `thread_ts` | string | Nein | Thread-Zeitstempel für Antworten \(erstellt Thread-Antwort\) |
|
||||
| `files` | file[] | Nein | Dateien, die an die Nachricht angehängt werden sollen |
|
||||
|
||||
@@ -108,11 +109,12 @@ Lesen Sie die neuesten Nachrichten aus Slack-Kanälen. Rufen Sie den Konversatio
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authMethod` | string | Nein | Authentifizierungsmethode: oauth oder bot_token |
|
||||
| `botToken` | string | Nein | Bot-Token für benutzerdefinierten Bot |
|
||||
| `channel` | string | Ja | Slack-Kanal, aus dem Nachrichten gelesen werden sollen (z.B. #general) |
|
||||
| `limit` | number | Nein | Anzahl der abzurufenden Nachrichten (Standard: 10, max: 100) |
|
||||
| `oldest` | string | Nein | Beginn des Zeitraums (Zeitstempel) |
|
||||
| `latest` | string | Nein | Ende des Zeitraums (Zeitstempel) |
|
||||
| `botToken` | string | Nein | Bot-Token für Custom Bot |
|
||||
| `channel` | string | Nein | Slack-Kanal, aus dem Nachrichten gelesen werden sollen \(z.B. #general\) |
|
||||
| `userId` | string | Nein | Benutzer-ID für DM-Konversation \(z.B. U1234567890\) |
|
||||
| `limit` | number | Nein | Anzahl der abzurufenden Nachrichten \(Standard: 10, max: 15\) |
|
||||
| `oldest` | string | Nein | Beginn des Zeitbereichs \(Zeitstempel\) |
|
||||
| `latest` | string | Nein | Ende des Zeitbereichs \(Zeitstempel\) |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
|
||||
@@ -39,14 +39,16 @@ Senden Sie eine Chat-Completion-Anfrage an jeden unterstützten LLM-Anbieter
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `model` | string | Ja | Das zu verwendende Modell (z.B. gpt-4o, claude-sonnet-4-5, gemini-2.0-flash) |
|
||||
| `systemPrompt` | string | Nein | System-Prompt zur Festlegung des Assistentenverhaltens |
|
||||
| `context` | string | Ja | Die Benutzernachricht oder der Kontext, der an das Modell gesendet wird |
|
||||
| `apiKey` | string | Nein | API-Schlüssel für den Anbieter (verwendet den Plattformschlüssel, wenn für gehostete Modelle nicht angegeben) |
|
||||
| `temperature` | number | Nein | Temperatur für die Antwortgenerierung (0-2) |
|
||||
| `maxTokens` | number | Nein | Maximale Tokens in der Antwort |
|
||||
| `model` | string | Ja | Das zu verwendende Modell \(z. B. gpt-4o, claude-sonnet-4-5, gemini-2.0-flash\) |
|
||||
| `systemPrompt` | string | Nein | System-Prompt zur Festlegung des Verhaltens des Assistenten |
|
||||
| `context` | string | Ja | Die Benutzernachricht oder der Kontext, der an das Modell gesendet werden soll |
|
||||
| `apiKey` | string | Nein | API-Schlüssel für den Anbieter \(verwendet Plattform-Schlüssel, falls nicht für gehostete Modelle angegeben\) |
|
||||
| `temperature` | number | Nein | Temperatur für die Antwortgenerierung \(0-2\) |
|
||||
| `maxTokens` | number | Nein | Maximale Anzahl von Tokens in der Antwort |
|
||||
| `azureEndpoint` | string | Nein | Azure OpenAI-Endpunkt-URL |
|
||||
| `azureApiVersion` | string | Nein | Azure OpenAI API-Version |
|
||||
| `azureApiVersion` | string | Nein | Azure OpenAI-API-Version |
|
||||
| `vertexProject` | string | Nein | Google Cloud-Projekt-ID für Vertex AI |
|
||||
| `vertexLocation` | string | Nein | Google Cloud-Standort für Vertex AI \(Standard: us-central1\) |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
|
||||
@@ -27,12 +27,14 @@ In Sim ermöglicht die Zoom-Integration Ihren Agenten die Automatisierung der Pl
|
||||
- Details oder Einladungen für jedes Meeting abzurufen
|
||||
- Bestehende Meetings direkt aus Ihren Automatisierungen zu aktualisieren oder zu löschen
|
||||
|
||||
Diese Funktionen ermöglichen es Ihnen, die Remote-Zusammenarbeit zu optimieren, wiederkehrende Videositzungen zu automatisieren und die Zoom-Umgebung Ihrer Organisation als Teil Ihrer Workflows zu verwalten.
|
||||
Um eine Verbindung zu Zoom herzustellen, fügen Sie den Zoom-Block ein und klicken Sie auf `Connect`, um sich mit Ihrem Zoom-Konto zu authentifizieren. Nach der Verbindung können Sie die Zoom-Tools verwenden, um Zoom-Meetings zu erstellen, aufzulisten, zu aktualisieren und zu löschen. Sie können Ihr Zoom-Konto jederzeit trennen, indem Sie unter Einstellungen > Integrationen auf `Disconnect` klicken, und der Zugriff auf Ihr Zoom-Konto wird sofort widerrufen.
|
||||
|
||||
Diese Funktionen ermöglichen es Ihnen, die Zusammenarbeit aus der Ferne zu optimieren, wiederkehrende Videositzungen zu automatisieren und die Zoom-Umgebung Ihrer Organisation als Teil Ihrer Workflows zu verwalten.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
## Nutzungsanleitung
|
||||
## Gebrauchsanweisung
|
||||
|
||||
Integrieren Sie Zoom in Workflows. Erstellen, listen, aktualisieren und löschen Sie Zoom-Meetings. Rufen Sie Meeting-Details, Einladungen, Aufzeichnungen und Teilnehmer ab. Verwalten Sie Cloud-Aufzeichnungen programmgesteuert.
|
||||
Integrieren Sie Zoom in Workflows. Erstellen, listen, aktualisieren und löschen Sie Zoom-Meetings. Erhalten Sie Meeting-Details, Einladungen, Aufzeichnungen und Teilnehmerinformationen. Verwalten Sie Cloud-Aufzeichnungen programmatisch.
|
||||
|
||||
## Tools
|
||||
|
||||
@@ -49,7 +51,7 @@ Ein neues Zoom-Meeting erstellen
|
||||
| `type` | number | Nein | Meeting-Typ: 1=sofort, 2=geplant, 3=wiederkehrend ohne feste Zeit, 8=wiederkehrend mit fester Zeit |
|
||||
| `startTime` | string | Nein | Meeting-Startzeit im ISO 8601-Format \(z.B. 2025-06-03T10:00:00Z\) |
|
||||
| `duration` | number | Nein | Meeting-Dauer in Minuten |
|
||||
| `timezone` | string | Nein | Zeitzone für das Meeting \(z.B. Europe/Berlin\) |
|
||||
| `timezone` | string | Nein | Zeitzone für das Meeting \(z.B. America/Los_Angeles\) |
|
||||
| `password` | string | Nein | Meeting-Passwort |
|
||||
| `agenda` | string | Nein | Meeting-Agenda |
|
||||
| `hostVideo` | boolean | Nein | Mit eingeschaltetem Host-Video starten |
|
||||
@@ -59,7 +61,7 @@ Ein neues Zoom-Meeting erstellen
|
||||
| `waitingRoom` | boolean | Nein | Warteraum aktivieren |
|
||||
| `autoRecording` | string | Nein | Automatische Aufzeichnungseinstellung: local, cloud oder none |
|
||||
|
||||
#### Output
|
||||
#### Ausgabe
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
@@ -67,18 +69,18 @@ Ein neues Zoom-Meeting erstellen
|
||||
|
||||
### `zoom_list_meetings`
|
||||
|
||||
Alle Meetings eines Zoom-Benutzers auflisten
|
||||
Alle Meetings für einen Zoom-Benutzer auflisten
|
||||
|
||||
#### Input
|
||||
#### Eingabe
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `userId` | string | Ja | Die Benutzer-ID oder E-Mail-Adresse. Verwenden Sie "me" für den authentifizierten Benutzer. |
|
||||
| `type` | string | Nein | Meeting-Typ-Filter: scheduled, live, upcoming, upcoming_meetings oder previous_meetings |
|
||||
| `pageSize` | number | Nein | Anzahl der Datensätze pro Seite (max. 300) |
|
||||
| `pageSize` | number | Nein | Anzahl der Datensätze pro Seite \(max. 300\) |
|
||||
| `nextPageToken` | string | Nein | Token für Paginierung, um die nächste Ergebnisseite zu erhalten |
|
||||
|
||||
#### Output
|
||||
#### Ausgabe
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
@@ -89,7 +91,7 @@ Alle Meetings eines Zoom-Benutzers auflisten
|
||||
|
||||
Details eines bestimmten Zoom-Meetings abrufen
|
||||
|
||||
#### Input
|
||||
#### Eingabe
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
@@ -97,13 +99,13 @@ Details eines bestimmten Zoom-Meetings abrufen
|
||||
| `occurrenceId` | string | Nein | Vorkommnis-ID für wiederkehrende Meetings |
|
||||
| `showPreviousOccurrences` | boolean | Nein | Frühere Vorkommnisse für wiederkehrende Meetings anzeigen |
|
||||
|
||||
#### Output
|
||||
#### Ausgabe
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `meeting` | object | Die Meeting-Details |
|
||||
|
||||
### `zoom_update_meeting`
|
||||
Details eines bestimmten Zoom-Meetings abrufen
|
||||
|
||||
Ein bestehendes Zoom-Meeting aktualisieren
|
||||
|
||||
@@ -116,11 +118,11 @@ Ein bestehendes Zoom-Meeting aktualisieren
|
||||
| `type` | number | Nein | Meeting-Typ: 1=sofort, 2=geplant, 3=wiederkehrend ohne feste Zeit, 8=wiederkehrend mit fester Zeit |
|
||||
| `startTime` | string | Nein | Meeting-Startzeit im ISO 8601-Format \(z.B. 2025-06-03T10:00:00Z\) |
|
||||
| `duration` | number | Nein | Meeting-Dauer in Minuten |
|
||||
| `timezone` | string | Nein | Zeitzone für das Meeting \(z.B. America/Los_Angeles\) |
|
||||
| `timezone` | string | Nein | Zeitzone für das Meeting \(z.B. Europe/Berlin\) |
|
||||
| `password` | string | Nein | Meeting-Passwort |
|
||||
| `agenda` | string | Nein | Meeting-Agenda |
|
||||
| `hostVideo` | boolean | Nein | Mit eingeschalteter Host-Kamera starten |
|
||||
| `participantVideo` | boolean | Nein | Mit eingeschalteter Teilnehmer-Kamera starten |
|
||||
| `hostVideo` | boolean | Nein | Mit eingeschaltetem Host-Video starten |
|
||||
| `participantVideo` | boolean | Nein | Mit eingeschaltetem Teilnehmer-Video starten |
|
||||
| `joinBeforeHost` | boolean | Nein | Teilnehmern erlauben, vor dem Host beizutreten |
|
||||
| `muteUponEntry` | boolean | Nein | Teilnehmer beim Betreten stummschalten |
|
||||
| `waitingRoom` | boolean | Nein | Warteraum aktivieren |
|
||||
@@ -132,7 +134,7 @@ Ein bestehendes Zoom-Meeting aktualisieren
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Ob das Meeting erfolgreich aktualisiert wurde |
|
||||
|
||||
### `zoom_delete_meeting`
|
||||
Ein Zoom-Meeting löschen
|
||||
|
||||
Ein Zoom-Meeting löschen oder abbrechen
|
||||
|
||||
@@ -141,9 +143,9 @@ Ein Zoom-Meeting löschen oder abbrechen
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `meetingId` | string | Ja | Die zu löschende Meeting-ID |
|
||||
| `occurrenceId` | string | Nein | Vorkommnis-ID zum Löschen eines bestimmten Vorkommnisses eines wiederkehrenden Meetings |
|
||||
| `occurrenceId` | string | Nein | Occurrence-ID zum Löschen eines bestimmten Termins eines wiederkehrenden Meetings |
|
||||
| `scheduleForReminder` | boolean | Nein | Erinnerungs-E-Mail zur Stornierung an Teilnehmer senden |
|
||||
| `cancelMeetingReminder` | boolean | Nein | Stornierungsmail an Teilnehmer und alternative Hosts senden |
|
||||
| `cancelMeetingReminder` | boolean | Nein | Stornierungs-E-Mail an Teilnehmer und alternative Hosts senden |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
@@ -153,7 +155,7 @@ Ein Zoom-Meeting löschen oder abbrechen
|
||||
|
||||
### `zoom_get_meeting_invitation`
|
||||
|
||||
Abrufen des Einladungstextes für ein Zoom-Meeting
|
||||
Den Einladungstext für ein Zoom-Meeting abrufen
|
||||
|
||||
#### Eingabe
|
||||
|
||||
@@ -165,20 +167,20 @@ Abrufen des Einladungstextes für ein Zoom-Meeting
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `invitation` | string | Der Einladungstext für das Meeting |
|
||||
| `invitation` | string | Der Meeting-Einladungstext |
|
||||
|
||||
### `zoom_list_recordings`
|
||||
|
||||
Alle Cloud-Aufzeichnungen für einen Zoom-Benutzer auflisten
|
||||
Alle Cloud-Aufzeichnungen eines Zoom-Benutzers auflisten
|
||||
|
||||
#### Eingabe
|
||||
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `userId` | string | Ja | Die Benutzer-ID oder E-Mail-Adresse. Verwenden Sie "me" für den authentifizierten Benutzer. |
|
||||
| `from` | string | Nein | Startdatum im Format jjjj-mm-tt \(innerhalb der letzten 6 Monate\) |
|
||||
| `to` | string | Nein | Enddatum im Format jjjj-mm-tt |
|
||||
| `pageSize` | number | Nein | Anzahl der Datensätze pro Seite \(max. 300\) |
|
||||
| `from` | string | Nein | Startdatum im Format yyyy-mm-dd (innerhalb der letzten 6 Monate) |
|
||||
| `to` | string | Nein | Enddatum im Format yyyy-mm-dd |
|
||||
| `pageSize` | number | Nein | Anzahl der Datensätze pro Seite (max. 300) |
|
||||
| `nextPageToken` | string | Nein | Token für die Paginierung, um die nächste Ergebnisseite zu erhalten |
|
||||
| `trash` | boolean | Nein | Auf true setzen, um Aufzeichnungen aus dem Papierkorb aufzulisten |
|
||||
|
||||
@@ -189,7 +191,7 @@ Alle Cloud-Aufzeichnungen für einen Zoom-Benutzer auflisten
|
||||
| `recordings` | array | Liste der Aufzeichnungen |
|
||||
| `pageInfo` | object | Paginierungsinformationen |
|
||||
|
||||
### `zoom_get_meeting_recordings`
|
||||
Alle Aufzeichnungen für ein bestimmtes Zoom-Meeting abrufen
|
||||
|
||||
Alle Aufzeichnungen für ein bestimmtes Zoom-Meeting abrufen
|
||||
|
||||
@@ -198,8 +200,8 @@ Alle Aufzeichnungen für ein bestimmtes Zoom-Meeting abrufen
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `meetingId` | string | Ja | Die Meeting-ID oder Meeting-UUID |
|
||||
| `includeFolderItems` | boolean | Nein | Elemente innerhalb eines Ordners einschließen |
|
||||
| `ttl` | number | Nein | Gültigkeitsdauer für Download-URLs in Sekunden \(max 604800\) |
|
||||
| `includeFolderItems` | boolean | Nein | Elemente innerhalb eines Ordners einbeziehen |
|
||||
| `ttl` | number | Nein | Gültigkeitsdauer für Download-URLs in Sekunden \(max. 604800\) |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
@@ -207,7 +209,7 @@ Alle Aufzeichnungen für ein bestimmtes Zoom-Meeting abrufen
|
||||
| --------- | ---- | ----------- |
|
||||
| `recording` | object | Die Meeting-Aufzeichnung mit allen Dateien |
|
||||
|
||||
### `zoom_delete_recording`
|
||||
Cloud-Aufzeichnungen für ein Zoom-Meeting löschen
|
||||
|
||||
Cloud-Aufzeichnungen für ein Zoom-Meeting löschen
|
||||
|
||||
@@ -225,7 +227,7 @@ Cloud-Aufzeichnungen für ein Zoom-Meeting löschen
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Ob die Aufzeichnung erfolgreich gelöscht wurde |
|
||||
|
||||
### `zoom_list_past_participants`
|
||||
Teilnehmer eines vergangenen Zoom-Meetings auflisten
|
||||
|
||||
Teilnehmer eines vergangenen Zoom-Meetings auflisten
|
||||
|
||||
@@ -234,14 +236,14 @@ Teilnehmer eines vergangenen Zoom-Meetings auflisten
|
||||
| Parameter | Typ | Erforderlich | Beschreibung |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `meetingId` | string | Ja | Die vergangene Meeting-ID oder UUID |
|
||||
| `pageSize` | number | Nein | Anzahl der Datensätze pro Seite \(max 300\) |
|
||||
| `pageSize` | number | Nein | Anzahl der Datensätze pro Seite \(max. 300\) |
|
||||
| `nextPageToken` | string | Nein | Token für Paginierung, um die nächste Ergebnisseite zu erhalten |
|
||||
|
||||
#### Ausgabe
|
||||
|
||||
| Parameter | Typ | Beschreibung |
|
||||
| --------- | ---- | ----------- |
|
||||
| `participants` | array | Liste der Besprechungsteilnehmer |
|
||||
| `participants` | array | Liste der Meeting-Teilnehmer |
|
||||
| `pageInfo` | object | Paginierungsinformationen |
|
||||
|
||||
## Hinweise
|
||||
|
||||
@@ -106,26 +106,24 @@ Different block types produce different output structures. Here's what you can e
|
||||
<Tab>
|
||||
```json
|
||||
{
|
||||
"content": "Original content passed through",
|
||||
"conditionResult": true,
|
||||
"selectedPath": {
|
||||
"blockId": "2acd9007-27e8-4510-a487-73d3b825e7c1",
|
||||
"blockType": "agent",
|
||||
"blockTitle": "Follow-up Agent"
|
||||
},
|
||||
"selectedConditionId": "condition-1"
|
||||
"selectedOption": "condition-1"
|
||||
}
|
||||
```
|
||||
|
||||
### Condition Block Output Fields
|
||||
|
||||
- **content**: The original content passed through
|
||||
- **conditionResult**: Boolean result of the condition evaluation
|
||||
- **selectedPath**: Information about the selected path
|
||||
- **blockId**: ID of the next block in the selected path
|
||||
- **blockType**: Type of the next block
|
||||
- **blockTitle**: Title of the next block
|
||||
- **selectedConditionId**: ID of the selected condition
|
||||
- **selectedOption**: ID of the selected condition
|
||||
|
||||
</Tab>
|
||||
<Tab>
|
||||
|
||||
@@ -51,8 +51,13 @@ Retrieve detailed information about a specific Jira issue
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Jira issue details with issue key, summary, description, created and updated timestamps |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `issueKey` | string | Issue key \(e.g., PROJ-123\) |
|
||||
| `summary` | string | Issue summary |
|
||||
| `description` | json | Issue description content |
|
||||
| `created` | string | Issue creation timestamp |
|
||||
| `updated` | string | Issue last updated timestamp |
|
||||
| `issue` | json | Complete issue object with all fields |
|
||||
|
||||
### `jira_update`
|
||||
|
||||
@@ -76,8 +81,9 @@ Update a Jira issue
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Updated Jira issue details with timestamp, issue key, summary, and success status |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `issueKey` | string | Updated issue key \(e.g., PROJ-123\) |
|
||||
| `summary` | string | Issue summary after update |
|
||||
|
||||
### `jira_write`
|
||||
|
||||
@@ -91,17 +97,26 @@ Write a Jira issue
|
||||
| `projectId` | string | Yes | Project ID for the issue |
|
||||
| `summary` | string | Yes | Summary for the issue |
|
||||
| `description` | string | No | Description for the issue |
|
||||
| `priority` | string | No | Priority for the issue |
|
||||
| `assignee` | string | No | Assignee for the issue |
|
||||
| `priority` | string | No | Priority ID or name for the issue \(e.g., "10000" or "High"\) |
|
||||
| `assignee` | string | No | Assignee account ID for the issue |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
| `issueType` | string | Yes | Type of issue to create \(e.g., Task, Story\) |
|
||||
| `labels` | array | No | Labels for the issue \(array of label names\) |
|
||||
| `duedate` | string | No | Due date for the issue \(format: YYYY-MM-DD\) |
|
||||
| `reporter` | string | No | Reporter account ID for the issue |
|
||||
| `environment` | string | No | Environment information for the issue |
|
||||
| `customFieldId` | string | No | Custom field ID \(e.g., customfield_10001\) |
|
||||
| `customFieldValue` | string | No | Value for the custom field |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Created Jira issue details with timestamp, issue key, summary, success status, and URL |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `issueKey` | string | Created issue key \(e.g., PROJ-123\) |
|
||||
| `summary` | string | Issue summary |
|
||||
| `url` | string | URL to the created issue |
|
||||
| `assigneeId` | string | Account ID of the assigned user \(if assigned\) |
|
||||
|
||||
### `jira_bulk_read`
|
||||
|
||||
@@ -119,8 +134,7 @@ Retrieve multiple Jira issues in bulk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | array | Array of Jira issues with summary, description, created and updated timestamps |
|
||||
| `issues` | array | Array of Jira issues with ts, summary, description, created, and updated timestamps |
|
||||
|
||||
### `jira_delete_issue`
|
||||
|
||||
@@ -139,8 +153,8 @@ Delete a Jira issue
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Deleted issue details with timestamp, issue key, and success status |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `issueKey` | string | Deleted issue key |
|
||||
|
||||
### `jira_assign_issue`
|
||||
|
||||
@@ -159,8 +173,9 @@ Assign a Jira issue to a user
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Assignment details with timestamp, issue key, assignee ID, and success status |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `issueKey` | string | Issue key that was assigned |
|
||||
| `assigneeId` | string | Account ID of the assignee |
|
||||
|
||||
### `jira_transition_issue`
|
||||
|
||||
@@ -180,8 +195,9 @@ Move a Jira issue between workflow statuses (e.g., To Do -> In Progress)
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Transition details with timestamp, issue key, transition ID, and success status |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `issueKey` | string | Issue key that was transitioned |
|
||||
| `transitionId` | string | Applied transition ID |
|
||||
|
||||
### `jira_search_issues`
|
||||
|
||||
@@ -202,8 +218,11 @@ Search for Jira issues using JQL (Jira Query Language)
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Search results with timestamp, total count, pagination details, and array of matching issues |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `total` | number | Total number of matching issues |
|
||||
| `startAt` | number | Pagination start index |
|
||||
| `maxResults` | number | Maximum results per page |
|
||||
| `issues` | array | Array of matching issues with key, summary, status, assignee, created, updated |
|
||||
|
||||
### `jira_add_comment`
|
||||
|
||||
@@ -222,8 +241,10 @@ Add a comment to a Jira issue
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Comment details with timestamp, issue key, comment ID, body, and success status |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `issueKey` | string | Issue key the comment was added to |
|
||||
| `commentId` | string | Created comment ID |
|
||||
| `body` | string | Comment text content |
|
||||
|
||||
### `jira_get_comments`
|
||||
|
||||
@@ -243,8 +264,10 @@ Get all comments from a Jira issue
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Comments data with timestamp, issue key, total count, and array of comments |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `issueKey` | string | Issue key |
|
||||
| `total` | number | Total number of comments |
|
||||
| `comments` | array | Array of comments with id, author, body, created, updated |
|
||||
|
||||
### `jira_update_comment`
|
||||
|
||||
@@ -264,8 +287,10 @@ Update an existing comment on a Jira issue
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Updated comment details with timestamp, issue key, comment ID, body text, and success status |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `issueKey` | string | Issue key |
|
||||
| `commentId` | string | Updated comment ID |
|
||||
| `body` | string | Updated comment text |
|
||||
|
||||
### `jira_delete_comment`
|
||||
|
||||
@@ -284,8 +309,9 @@ Delete a comment from a Jira issue
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Deletion details with timestamp, issue key, comment ID, and success status |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `issueKey` | string | Issue key |
|
||||
| `commentId` | string | Deleted comment ID |
|
||||
|
||||
### `jira_get_attachments`
|
||||
|
||||
@@ -303,8 +329,9 @@ Get all attachments from a Jira issue
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Attachments data with timestamp, issue key, and array of attachments |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `issueKey` | string | Issue key |
|
||||
| `attachments` | array | Array of attachments with id, filename, size, mimeType, created, author |
|
||||
|
||||
### `jira_delete_attachment`
|
||||
|
||||
@@ -322,8 +349,8 @@ Delete an attachment from a Jira issue
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Deletion details with timestamp, attachment ID, and success status |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `attachmentId` | string | Deleted attachment ID |
|
||||
|
||||
### `jira_add_worklog`
|
||||
|
||||
@@ -344,8 +371,10 @@ Add a time tracking worklog entry to a Jira issue
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Worklog details with timestamp, issue key, worklog ID, time spent in seconds, and success status |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `issueKey` | string | Issue key the worklog was added to |
|
||||
| `worklogId` | string | Created worklog ID |
|
||||
| `timeSpentSeconds` | number | Time spent in seconds |
|
||||
|
||||
### `jira_get_worklogs`
|
||||
|
||||
@@ -365,8 +394,10 @@ Get all worklog entries from a Jira issue
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Worklogs data with timestamp, issue key, total count, and array of worklogs |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `issueKey` | string | Issue key |
|
||||
| `total` | number | Total number of worklogs |
|
||||
| `worklogs` | array | Array of worklogs with id, author, timeSpentSeconds, timeSpent, comment, created, updated, started |
|
||||
|
||||
### `jira_update_worklog`
|
||||
|
||||
@@ -388,8 +419,9 @@ Update an existing worklog entry on a Jira issue
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Worklog update details with timestamp, issue key, worklog ID, and success status |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `issueKey` | string | Issue key |
|
||||
| `worklogId` | string | Updated worklog ID |
|
||||
|
||||
### `jira_delete_worklog`
|
||||
|
||||
@@ -408,8 +440,9 @@ Delete a worklog entry from a Jira issue
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Deletion details with timestamp, issue key, worklog ID, and success status |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `issueKey` | string | Issue key |
|
||||
| `worklogId` | string | Deleted worklog ID |
|
||||
|
||||
### `jira_create_issue_link`
|
||||
|
||||
@@ -430,8 +463,11 @@ Create a link relationship between two Jira issues
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Issue link details with timestamp, inward issue key, outward issue key, link type, and success status |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `inwardIssue` | string | Inward issue key |
|
||||
| `outwardIssue` | string | Outward issue key |
|
||||
| `linkType` | string | Type of issue link |
|
||||
| `linkId` | string | Created link ID |
|
||||
|
||||
### `jira_delete_issue_link`
|
||||
|
||||
@@ -449,8 +485,8 @@ Delete a link between two Jira issues
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Deletion details with timestamp, link ID, and success status |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `linkId` | string | Deleted link ID |
|
||||
|
||||
### `jira_add_watcher`
|
||||
|
||||
@@ -469,8 +505,9 @@ Add a watcher to a Jira issue to receive notifications about updates
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Watcher details with timestamp, issue key, watcher account ID, and success status |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `issueKey` | string | Issue key |
|
||||
| `watcherAccountId` | string | Added watcher account ID |
|
||||
|
||||
### `jira_remove_watcher`
|
||||
|
||||
@@ -489,8 +526,33 @@ Remove a watcher from a Jira issue
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Operation success status |
|
||||
| `output` | object | Removal details with timestamp, issue key, watcher account ID, and success status |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `issueKey` | string | Issue key |
|
||||
| `watcherAccountId` | string | Removed watcher account ID |
|
||||
|
||||
### `jira_get_users`
|
||||
|
||||
Get Jira users. If an account ID is provided, returns a single user. Otherwise, returns a list of all users.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `accountId` | string | No | Optional account ID to get a specific user. If not provided, returns all users. |
|
||||
| `startAt` | number | No | The index of the first user to return \(for pagination, default: 0\) |
|
||||
| `maxResults` | number | No | Maximum number of users to return \(default: 50\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `users` | json | Array of users with accountId, displayName, emailAddress, active status, and avatarUrls |
|
||||
| `total` | number | Total number of users returned |
|
||||
| `startAt` | number | Pagination start index |
|
||||
| `maxResults` | number | Maximum results per page |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -80,6 +80,7 @@
|
||||
"sendgrid",
|
||||
"sentry",
|
||||
"serper",
|
||||
"servicenow",
|
||||
"sftp",
|
||||
"sharepoint",
|
||||
"shopify",
|
||||
|
||||
129
apps/docs/content/docs/en/tools/servicenow.mdx
Normal file
129
apps/docs/content/docs/en/tools/servicenow.mdx
Normal file
@@ -0,0 +1,129 @@
|
||||
---
|
||||
title: ServiceNow
|
||||
description: Create, read, update, and delete ServiceNow records
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="servicenow"
|
||||
color="#032D42"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[ServiceNow](https://www.servicenow.com/) is a powerful cloud platform designed to streamline and automate IT service management (ITSM), workflows, and business processes across your organization. ServiceNow enables you to manage incidents, requests, tasks, users, and more using its extensive API.
|
||||
|
||||
With ServiceNow, you can:
|
||||
|
||||
- **Automate IT workflows**: Create, read, update, and delete records in any ServiceNow table, such as incidents, tasks, change requests, and users.
|
||||
- **Integrate systems**: Connect ServiceNow with your other tools and processes for seamless automation.
|
||||
- **Maintain a single source of truth**: Keep all your service and operations data organized and accessible.
|
||||
- **Drive operational efficiency**: Reduce manual work and improve service quality with customizable workflows and automation.
|
||||
|
||||
In Sim, the ServiceNow integration enables your agents to interact directly with your ServiceNow instance as part of their workflows. Agents can create, read, update, or delete records in any ServiceNow table and leverage ticket or user data for sophisticated automation and decision-making. This integration bridges your workflow automation and IT operations, empowering your agents to manage service requests, incidents, users, and assets without manual intervention. By connecting Sim with ServiceNow, you can automate service management tasks, improve response times, and ensure consistent, secure access to your organization's vital service data.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate ServiceNow into your workflow. Create, read, update, and delete records in any ServiceNow table including incidents, tasks, change requests, users, and more.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `servicenow_create_record`
|
||||
|
||||
Create a new record in a ServiceNow table
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `instanceUrl` | string | Yes | ServiceNow instance URL \(e.g., https://instance.service-now.com\) |
|
||||
| `username` | string | Yes | ServiceNow username |
|
||||
| `password` | string | Yes | ServiceNow password |
|
||||
| `tableName` | string | Yes | Table name \(e.g., incident, task, sys_user\) |
|
||||
| `fields` | json | Yes | Fields to set on the record \(JSON object\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `record` | json | Created ServiceNow record with sys_id and other fields |
|
||||
| `metadata` | json | Operation metadata |
|
||||
|
||||
### `servicenow_read_record`
|
||||
|
||||
Read records from a ServiceNow table
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `instanceUrl` | string | Yes | ServiceNow instance URL \(e.g., https://instance.service-now.com\) |
|
||||
| `username` | string | Yes | ServiceNow username |
|
||||
| `password` | string | Yes | ServiceNow password |
|
||||
| `tableName` | string | Yes | Table name |
|
||||
| `sysId` | string | No | Specific record sys_id |
|
||||
| `number` | string | No | Record number \(e.g., INC0010001\) |
|
||||
| `query` | string | No | Encoded query string \(e.g., "active=true^priority=1"\) |
|
||||
| `limit` | number | No | Maximum number of records to return |
|
||||
| `fields` | string | No | Comma-separated list of fields to return |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `records` | array | Array of ServiceNow records |
|
||||
| `metadata` | json | Operation metadata |
|
||||
|
||||
### `servicenow_update_record`
|
||||
|
||||
Update an existing record in a ServiceNow table
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `instanceUrl` | string | Yes | ServiceNow instance URL \(e.g., https://instance.service-now.com\) |
|
||||
| `username` | string | Yes | ServiceNow username |
|
||||
| `password` | string | Yes | ServiceNow password |
|
||||
| `tableName` | string | Yes | Table name |
|
||||
| `sysId` | string | Yes | Record sys_id to update |
|
||||
| `fields` | json | Yes | Fields to update \(JSON object\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `record` | json | Updated ServiceNow record |
|
||||
| `metadata` | json | Operation metadata |
|
||||
|
||||
### `servicenow_delete_record`
|
||||
|
||||
Delete a record from a ServiceNow table
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `instanceUrl` | string | Yes | ServiceNow instance URL \(e.g., https://instance.service-now.com\) |
|
||||
| `username` | string | Yes | ServiceNow username |
|
||||
| `password` | string | Yes | ServiceNow password |
|
||||
| `tableName` | string | Yes | Table name |
|
||||
| `sysId` | string | Yes | Record sys_id to delete |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the deletion was successful |
|
||||
| `metadata` | json | Operation metadata |
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- Category: `tools`
|
||||
- Type: `servicenow`
|
||||
@@ -56,7 +56,7 @@ Integrate Slack into the workflow. Can send, update, and delete messages, create
|
||||
|
||||
### `slack_message`
|
||||
|
||||
Send messages to Slack channels or users through the Slack API. Supports Slack mrkdwn formatting.
|
||||
Send messages to Slack channels or direct messages. Supports Slack mrkdwn formatting.
|
||||
|
||||
#### Input
|
||||
|
||||
@@ -64,7 +64,8 @@ Send messages to Slack channels or users through the Slack API. Supports Slack m
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authMethod` | string | No | Authentication method: oauth or bot_token |
|
||||
| `botToken` | string | No | Bot token for Custom Bot |
|
||||
| `channel` | string | Yes | Target Slack channel \(e.g., #general\) |
|
||||
| `channel` | string | No | Target Slack channel \(e.g., #general\) |
|
||||
| `userId` | string | No | Target Slack user ID for direct messages \(e.g., U1234567890\) |
|
||||
| `text` | string | Yes | Message text to send \(supports Slack mrkdwn formatting\) |
|
||||
| `thread_ts` | string | No | Thread timestamp to reply to \(creates thread reply\) |
|
||||
| `files` | file[] | No | Files to attach to the message |
|
||||
@@ -111,8 +112,9 @@ Read the latest messages from Slack channels. Retrieve conversation history with
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authMethod` | string | No | Authentication method: oauth or bot_token |
|
||||
| `botToken` | string | No | Bot token for Custom Bot |
|
||||
| `channel` | string | Yes | Slack channel to read messages from \(e.g., #general\) |
|
||||
| `limit` | number | No | Number of messages to retrieve \(default: 10, max: 100\) |
|
||||
| `channel` | string | No | Slack channel to read messages from \(e.g., #general\) |
|
||||
| `userId` | string | No | User ID for DM conversation \(e.g., U1234567890\) |
|
||||
| `limit` | number | No | Number of messages to retrieve \(default: 10, max: 15\) |
|
||||
| `oldest` | string | No | Start of time range \(timestamp\) |
|
||||
| `latest` | string | No | End of time range \(timestamp\) |
|
||||
|
||||
|
||||
@@ -50,6 +50,8 @@ Send a chat completion request to any supported LLM provider
|
||||
| `maxTokens` | number | No | Maximum tokens in the response |
|
||||
| `azureEndpoint` | string | No | Azure OpenAI endpoint URL |
|
||||
| `azureApiVersion` | string | No | Azure OpenAI API version |
|
||||
| `vertexProject` | string | No | Google Cloud project ID for Vertex AI |
|
||||
| `vertexLocation` | string | No | Google Cloud location for Vertex AI \(defaults to us-central1\) |
|
||||
|
||||
#### Output
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ In Sim, the Zoom integration empowers your agents to automate scheduling and mee
|
||||
- Retrieve details or invitations for any meeting
|
||||
- Update or delete existing meetings directly from your automations
|
||||
|
||||
To connect to Zoom, drop the Zoom block and click `Connect` to authenticate with your Zoom account. Once connected, you can use the Zoom tools to create, list, update, and delete Zoom meetings. At any given time, you can disconnect your Zoom account by clicking `Disconnect` in Settings > Integrations, and access to your Zoom account will be revoked immediatley.
|
||||
|
||||
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 */}
|
||||
|
||||
|
||||
@@ -111,26 +111,24 @@ Diferentes tipos de bloques producen diferentes estructuras de salida. Esto es l
|
||||
|
||||
```json
|
||||
{
|
||||
"content": "Original content passed through",
|
||||
"conditionResult": true,
|
||||
"selectedPath": {
|
||||
"blockId": "2acd9007-27e8-4510-a487-73d3b825e7c1",
|
||||
"blockType": "agent",
|
||||
"blockTitle": "Follow-up Agent"
|
||||
},
|
||||
"selectedConditionId": "condition-1"
|
||||
"selectedOption": "condition-1"
|
||||
}
|
||||
```
|
||||
|
||||
### Campos de salida del bloque de condición
|
||||
|
||||
- **content**: El contenido original que se transmite
|
||||
- **conditionResult**: Resultado booleano de la evaluación de la condición
|
||||
- **selectedPath**: Información sobre la ruta seleccionada
|
||||
- **conditionResult**: resultado booleano de la evaluación de la condición
|
||||
- **selectedPath**: información sobre la ruta seleccionada
|
||||
- **blockId**: ID del siguiente bloque en la ruta seleccionada
|
||||
- **blockType**: Tipo del siguiente bloque
|
||||
- **blockTitle**: Título del siguiente bloque
|
||||
- **selectedConditionId**: ID de la condición seleccionada
|
||||
- **blockType**: tipo del siguiente bloque
|
||||
- **blockTitle**: título del siguiente bloque
|
||||
- **selectedOption**: ID de la condición seleccionada
|
||||
|
||||
</Tab>
|
||||
<Tab>
|
||||
|
||||
@@ -48,8 +48,13 @@ Recupera información detallada sobre una incidencia específica de Jira
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Detalles de la incidencia de Jira con clave de incidencia, resumen, descripción, marcas de tiempo de creación y actualización |
|
||||
| `ts` | string | Marca de tiempo de la operación |
|
||||
| `issueKey` | string | Clave de la incidencia (p. ej., PROJ-123) |
|
||||
| `summary` | string | Resumen de la incidencia |
|
||||
| `description` | json | Contenido de la descripción de la incidencia |
|
||||
| `created` | string | Marca de tiempo de creación de la incidencia |
|
||||
| `updated` | string | Marca de tiempo de última actualización de la incidencia |
|
||||
| `issue` | json | Objeto completo de la incidencia con todos los campos |
|
||||
|
||||
### `jira_update`
|
||||
|
||||
@@ -73,8 +78,9 @@ Actualizar una incidencia de Jira
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Detalles actualizados de la incidencia de Jira con marca de tiempo, clave de incidencia, resumen y estado de éxito |
|
||||
| `ts` | string | Marca de tiempo de la operación |
|
||||
| `issueKey` | string | Clave de la incidencia actualizada (p. ej., PROJ-123) |
|
||||
| `summary` | string | Resumen de la incidencia después de la actualización |
|
||||
|
||||
### `jira_write`
|
||||
|
||||
@@ -83,22 +89,31 @@ Escribir una incidencia de Jira
|
||||
#### Entrada
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
| `domain` | string | Sí | Tu dominio de Jira \(p. ej., tuempresa.atlassian.net\) |
|
||||
| `projectId` | string | Sí | ID del proyecto para la incidencia |
|
||||
| `summary` | string | Sí | Resumen de la incidencia |
|
||||
| `description` | string | No | Descripción de la incidencia |
|
||||
| `priority` | string | No | Prioridad de la incidencia |
|
||||
| `assignee` | string | No | Asignado para la incidencia |
|
||||
| `cloudId` | string | No | ID de Jira Cloud para la instancia. Si no se proporciona, se obtendrá utilizando el dominio. |
|
||||
| `priority` | string | No | ID o nombre de prioridad para la incidencia \(p. ej., "10000" o "Alta"\) |
|
||||
| `assignee` | string | No | ID de cuenta del asignado para la incidencia |
|
||||
| `cloudId` | string | No | ID de Jira Cloud para la instancia. Si no se proporciona, se obtendrá usando el dominio. |
|
||||
| `issueType` | string | Sí | Tipo de incidencia a crear \(p. ej., Tarea, Historia\) |
|
||||
| `labels` | array | No | Etiquetas para la incidencia \(array de nombres de etiquetas\) |
|
||||
| `duedate` | string | No | Fecha de vencimiento para la incidencia \(formato: AAAA-MM-DD\) |
|
||||
| `reporter` | string | No | ID de cuenta del informador para la incidencia |
|
||||
| `environment` | string | No | Información del entorno para la incidencia |
|
||||
| `customFieldId` | string | No | ID del campo personalizado \(p. ej., customfield_10001\) |
|
||||
| `customFieldValue` | string | No | Valor para el campo personalizado |
|
||||
|
||||
#### Salida
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Detalles de la incidencia de Jira creada con marca de tiempo, clave de incidencia, resumen, estado de éxito y URL |
|
||||
| `ts` | string | Marca de tiempo de la operación |
|
||||
| `issueKey` | string | Clave de la incidencia creada \(p. ej., PROJ-123\) |
|
||||
| `summary` | string | Resumen de la incidencia |
|
||||
| `url` | string | URL de la incidencia creada |
|
||||
| `assigneeId` | string | ID de cuenta del usuario asignado \(si está asignado\) |
|
||||
|
||||
### `jira_bulk_read`
|
||||
|
||||
@@ -116,8 +131,7 @@ Recuperar múltiples incidencias de Jira en bloque
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | array | Array de incidencias de Jira con resumen, descripción, marcas de tiempo de creación y actualización |
|
||||
| `issues` | array | Array de incidencias de Jira con marca de tiempo, resumen, descripción, y marcas de tiempo de creación y actualización |
|
||||
|
||||
### `jira_delete_issue`
|
||||
|
||||
@@ -136,8 +150,8 @@ Eliminar una incidencia de Jira
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Detalles de la incidencia eliminada con marca de tiempo, clave de incidencia y estado de éxito |
|
||||
| `ts` | string | Marca de tiempo de la operación |
|
||||
| `issueKey` | string | Clave de la incidencia eliminada |
|
||||
|
||||
### `jira_assign_issue`
|
||||
|
||||
@@ -156,8 +170,9 @@ Asignar una incidencia de Jira a un usuario
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Detalles de la asignación con marca de tiempo, clave de incidencia, ID del asignado y estado de éxito |
|
||||
| `ts` | string | Marca de tiempo de la operación |
|
||||
| `issueKey` | string | Clave de la incidencia que fue asignada |
|
||||
| `assigneeId` | string | ID de cuenta del asignado |
|
||||
|
||||
### `jira_transition_issue`
|
||||
|
||||
@@ -177,8 +192,9 @@ Mover una incidencia de Jira entre estados de flujo de trabajo (p. ej., Pendient
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Detalles de la transición con marca de tiempo, clave de incidencia, ID de transición y estado de éxito |
|
||||
| `ts` | string | Marca de tiempo de la operación |
|
||||
| `issueKey` | string | Clave de incidencia que fue transicionada |
|
||||
| `transitionId` | string | ID de transición aplicada |
|
||||
|
||||
### `jira_search_issues`
|
||||
|
||||
@@ -199,8 +215,11 @@ Buscar incidencias de Jira usando JQL (Jira Query Language)
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Resultados de búsqueda con marca de tiempo, recuento total, detalles de paginación y array de incidencias coincidentes |
|
||||
| `ts` | string | Marca de tiempo de la operación |
|
||||
| `total` | number | Número total de incidencias coincidentes |
|
||||
| `startAt` | number | Índice de inicio de paginación |
|
||||
| `maxResults` | number | Máximo de resultados por página |
|
||||
| `issues` | array | Array de incidencias coincidentes con clave, resumen, estado, asignado, creado, actualizado |
|
||||
|
||||
### `jira_add_comment`
|
||||
|
||||
@@ -219,8 +238,10 @@ Añadir un comentario a una incidencia de Jira
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Detalles del comentario con marca de tiempo, clave de incidencia, ID del comentario, cuerpo y estado de éxito |
|
||||
| `ts` | string | Marca de tiempo de la operación |
|
||||
| `issueKey` | string | Clave de incidencia a la que se añadió el comentario |
|
||||
| `commentId` | string | ID del comentario creado |
|
||||
| `body` | string | Contenido de texto del comentario |
|
||||
|
||||
### `jira_get_comments`
|
||||
|
||||
@@ -240,8 +261,10 @@ Obtener todos los comentarios de una incidencia de Jira
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Datos de comentarios con marca de tiempo, clave de incidencia, recuento total y array de comentarios |
|
||||
| `ts` | string | Marca de tiempo de la operación |
|
||||
| `issueKey` | string | Clave de incidencia |
|
||||
| `total` | number | Número total de comentarios |
|
||||
| `comments` | array | Array de comentarios con id, autor, cuerpo, creado, actualizado |
|
||||
|
||||
### `jira_update_comment`
|
||||
|
||||
@@ -261,8 +284,10 @@ Actualizar un comentario existente en una incidencia de Jira
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Detalles del comentario actualizado con marca de tiempo, clave de incidencia, ID de comentario, texto del cuerpo y estado de éxito |
|
||||
| `ts` | string | Marca de tiempo de la operación |
|
||||
| `issueKey` | string | Clave de incidencia |
|
||||
| `commentId` | string | ID del comentario actualizado |
|
||||
| `body` | string | Texto actualizado del comentario |
|
||||
|
||||
### `jira_delete_comment`
|
||||
|
||||
@@ -281,8 +306,9 @@ Eliminar un comentario de una incidencia de Jira
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Detalles de eliminación con marca de tiempo, clave de incidencia, ID de comentario y estado de éxito |
|
||||
| `ts` | string | Marca de tiempo de la operación |
|
||||
| `issueKey` | string | Clave de incidencia |
|
||||
| `commentId` | string | ID del comentario eliminado |
|
||||
|
||||
### `jira_get_attachments`
|
||||
|
||||
@@ -300,8 +326,9 @@ Obtener todos los adjuntos de una incidencia de Jira
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Datos de adjuntos con marca de tiempo, clave de incidencia y array de adjuntos |
|
||||
| `ts` | string | Marca de tiempo de la operación |
|
||||
| `issueKey` | string | Clave de incidencia |
|
||||
| `attachments` | array | Array de adjuntos con id, nombre de archivo, tamaño, tipo MIME, fecha de creación y autor |
|
||||
|
||||
### `jira_delete_attachment`
|
||||
|
||||
@@ -319,8 +346,8 @@ Eliminar un adjunto de una incidencia de Jira
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Detalles de eliminación con marca de tiempo, ID de adjunto y estado de éxito |
|
||||
| `ts` | string | Marca de tiempo de la operación |
|
||||
| `attachmentId` | string | ID del adjunto eliminado |
|
||||
|
||||
### `jira_add_worklog`
|
||||
|
||||
@@ -341,8 +368,10 @@ Añadir una entrada de registro de trabajo de seguimiento de tiempo a una incide
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Detalles del registro de trabajo con marca de tiempo, clave de incidencia, ID del registro de trabajo, tiempo dedicado en segundos y estado de éxito |
|
||||
| `ts` | string | Marca de tiempo de la operación |
|
||||
| `issueKey` | string | Clave de incidencia a la que se añadió el registro de trabajo |
|
||||
| `worklogId` | string | ID del registro de trabajo creado |
|
||||
| `timeSpentSeconds` | number | Tiempo empleado en segundos |
|
||||
|
||||
### `jira_get_worklogs`
|
||||
|
||||
@@ -362,8 +391,10 @@ Obtener todas las entradas de registro de trabajo de una incidencia de Jira
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Datos de registros de trabajo con marca de tiempo, clave de incidencia, recuento total y array de registros de trabajo |
|
||||
| `ts` | string | Marca de tiempo de la operación |
|
||||
| `issueKey` | string | Clave de incidencia |
|
||||
| `total` | number | Número total de registros de trabajo |
|
||||
| `worklogs` | array | Array de registros de trabajo con id, autor, segundos empleados, tiempo empleado, comentario, fecha de creación, actualización e inicio |
|
||||
|
||||
### `jira_update_worklog`
|
||||
|
||||
@@ -385,8 +416,9 @@ Actualizar una entrada existente de registro de trabajo en una incidencia de Jir
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Detalles de actualización del registro de trabajo con marca de tiempo, clave de incidencia, ID de registro de trabajo y estado de éxito |
|
||||
| `ts` | string | Marca de tiempo de la operación |
|
||||
| `issueKey` | string | Clave de incidencia |
|
||||
| `worklogId` | string | ID del registro de trabajo actualizado |
|
||||
|
||||
### `jira_delete_worklog`
|
||||
|
||||
@@ -405,8 +437,9 @@ Eliminar una entrada de registro de trabajo de una incidencia de Jira
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Detalles de eliminación con marca de tiempo, clave de incidencia, ID de registro de trabajo y estado de éxito |
|
||||
| `ts` | string | Marca de tiempo de la operación |
|
||||
| `issueKey` | string | Clave de incidencia |
|
||||
| `worklogId` | string | ID del registro de trabajo eliminado |
|
||||
|
||||
### `jira_create_issue_link`
|
||||
|
||||
@@ -427,8 +460,11 @@ Crear una relación de enlace entre dos incidencias de Jira
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Detalles del enlace de incidencia con marca de tiempo, clave de incidencia de entrada, clave de incidencia de salida, tipo de enlace y estado de éxito |
|
||||
| `ts` | string | Marca de tiempo de la operación |
|
||||
| `inwardIssue` | string | Clave de incidencia de entrada |
|
||||
| `outwardIssue` | string | Clave de incidencia de salida |
|
||||
| `linkType` | string | Tipo de enlace de incidencia |
|
||||
| `linkId` | string | ID del enlace creado |
|
||||
|
||||
### `jira_delete_issue_link`
|
||||
|
||||
@@ -446,8 +482,8 @@ Eliminar un enlace entre dos incidencias de Jira
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Detalles de eliminación con marca de tiempo, ID del enlace y estado de éxito |
|
||||
| `ts` | string | Marca de tiempo de la operación |
|
||||
| `linkId` | string | ID del enlace eliminado |
|
||||
|
||||
### `jira_add_watcher`
|
||||
|
||||
@@ -466,8 +502,9 @@ Añadir un observador a una incidencia de Jira para recibir notificaciones sobre
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Detalles del observador con marca de tiempo, clave de incidencia, ID de cuenta del observador y estado de éxito |
|
||||
| `ts` | string | Marca de tiempo de la operación |
|
||||
| `issueKey` | string | Clave de incidencia |
|
||||
| `watcherAccountId` | string | ID de cuenta del observador añadido |
|
||||
|
||||
### `jira_remove_watcher`
|
||||
|
||||
@@ -486,8 +523,33 @@ Eliminar un observador de una incidencia de Jira
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Estado de éxito de la operación |
|
||||
| `output` | object | Detalles de eliminación con marca de tiempo, clave de incidencia, ID de cuenta del observador y estado de éxito |
|
||||
| `ts` | string | Marca de tiempo de la operación |
|
||||
| `issueKey` | string | Clave de incidencia |
|
||||
| `watcherAccountId` | string | ID de cuenta del observador eliminado |
|
||||
|
||||
### `jira_get_users`
|
||||
|
||||
Obtener usuarios de Jira. Si se proporciona un ID de cuenta, devuelve un solo usuario. De lo contrario, devuelve una lista de todos los usuarios.
|
||||
|
||||
#### Entrada
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
| `domain` | string | Sí | Tu dominio de Jira \(p. ej., tuempresa.atlassian.net\) |
|
||||
| `accountId` | string | No | ID de cuenta opcional para obtener un usuario específico. Si no se proporciona, devuelve todos los usuarios. |
|
||||
| `startAt` | number | No | El índice del primer usuario a devolver \(para paginación, predeterminado: 0\) |
|
||||
| `maxResults` | number | No | Número máximo de usuarios a devolver \(predeterminado: 50\) |
|
||||
| `cloudId` | string | No | ID de Jira Cloud para la instancia. Si no se proporciona, se obtendrá usando el dominio. |
|
||||
|
||||
#### Salida
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Marca de tiempo de la operación |
|
||||
| `users` | json | Array de usuarios con accountId, displayName, emailAddress, estado activo y avatarUrls |
|
||||
| `total` | number | Número total de usuarios devueltos |
|
||||
| `startAt` | number | Índice de inicio de paginación |
|
||||
| `maxResults` | number | Máximo de resultados por página |
|
||||
|
||||
## Notas
|
||||
|
||||
|
||||
124
apps/docs/content/docs/es/tools/servicenow.mdx
Normal file
124
apps/docs/content/docs/es/tools/servicenow.mdx
Normal file
@@ -0,0 +1,124 @@
|
||||
---
|
||||
title: ServiceNow
|
||||
description: Crear, leer, actualizar y eliminar registros de ServiceNow
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="servicenow"
|
||||
color="#032D42"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[ServiceNow](https://www.servicenow.com/) es una potente plataforma en la nube diseñada para optimizar y automatizar la gestión de servicios de TI (ITSM), flujos de trabajo y procesos empresariales en toda tu organización. ServiceNow te permite gestionar incidencias, solicitudes, tareas, usuarios y más utilizando su amplia API.
|
||||
|
||||
Con ServiceNow, puedes:
|
||||
|
||||
- **Automatizar flujos de trabajo de TI**: crear, leer, actualizar y eliminar registros en cualquier tabla de ServiceNow, como incidencias, tareas, solicitudes de cambio y usuarios.
|
||||
- **Integrar sistemas**: conectar ServiceNow con tus otras herramientas y procesos para una automatización fluida.
|
||||
- **Mantener una única fuente de verdad**: mantener todos tus datos de servicio y operaciones organizados y accesibles.
|
||||
- **Impulsar la eficiencia operativa**: reducir el trabajo manual y mejorar la calidad del servicio con flujos de trabajo personalizables y automatización.
|
||||
|
||||
En Sim, la integración de ServiceNow permite que tus agentes interactúen directamente con tu instancia de ServiceNow como parte de sus flujos de trabajo. Los agentes pueden crear, leer, actualizar o eliminar registros en cualquier tabla de ServiceNow y aprovechar datos de tickets o usuarios para automatización y toma de decisiones sofisticadas. Esta integración conecta tu automatización de flujos de trabajo y operaciones de TI, permitiendo que tus agentes gestionen solicitudes de servicio, incidencias, usuarios y activos sin intervención manual. Al conectar Sim con ServiceNow, puedes automatizar tareas de gestión de servicios, mejorar los tiempos de respuesta y garantizar un acceso consistente y seguro a los datos de servicio vitales de tu organización.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
## Instrucciones de uso
|
||||
|
||||
Integra ServiceNow en tu flujo de trabajo. Crea, lee, actualiza y elimina registros en cualquier tabla de ServiceNow, incluyendo incidencias, tareas, solicitudes de cambio, usuarios y más.
|
||||
|
||||
## Herramientas
|
||||
|
||||
### `servicenow_create_record`
|
||||
|
||||
Crear un nuevo registro en una tabla de ServiceNow
|
||||
|
||||
#### Entrada
|
||||
|
||||
| Parámetro | Tipo | Requerido | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `instanceUrl` | string | Sí | URL de la instancia de ServiceNow \(p. ej., https://instance.service-now.com\) |
|
||||
| `username` | string | Sí | Nombre de usuario de ServiceNow |
|
||||
| `password` | string | Sí | Contraseña de ServiceNow |
|
||||
| `tableName` | string | Sí | Nombre de la tabla \(p. ej., incident, task, sys_user\) |
|
||||
| `fields` | json | Sí | Campos a establecer en el registro \(objeto JSON\) |
|
||||
|
||||
#### Salida
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `record` | json | Registro de ServiceNow creado con sys_id y otros campos |
|
||||
| `metadata` | json | Metadatos de la operación |
|
||||
|
||||
### `servicenow_read_record`
|
||||
|
||||
Leer registros de una tabla de ServiceNow
|
||||
|
||||
#### Entrada
|
||||
|
||||
| Parámetro | Tipo | Requerido | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `instanceUrl` | string | Sí | URL de la instancia de ServiceNow \(p. ej., https://instance.service-now.com\) |
|
||||
| `username` | string | Sí | Nombre de usuario de ServiceNow |
|
||||
| `password` | string | Sí | Contraseña de ServiceNow |
|
||||
| `tableName` | string | Sí | Nombre de la tabla |
|
||||
| `sysId` | string | No | sys_id del registro específico |
|
||||
| `number` | string | No | Número de registro \(p. ej., INC0010001\) |
|
||||
| `query` | string | No | Cadena de consulta codificada \(p. ej., "active=true^priority=1"\) |
|
||||
| `limit` | number | No | Número máximo de registros a devolver |
|
||||
| `fields` | string | No | Lista de campos separados por comas a devolver |
|
||||
|
||||
#### Salida
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `records` | array | Array de registros de ServiceNow |
|
||||
| `metadata` | json | Metadatos de la operación |
|
||||
|
||||
### `servicenow_update_record`
|
||||
|
||||
Actualiza un registro existente en una tabla de ServiceNow
|
||||
|
||||
#### Entrada
|
||||
|
||||
| Parámetro | Tipo | Requerido | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `instanceUrl` | string | Sí | URL de la instancia de ServiceNow \(ej., https://instance.service-now.com\) |
|
||||
| `username` | string | Sí | Nombre de usuario de ServiceNow |
|
||||
| `password` | string | Sí | Contraseña de ServiceNow |
|
||||
| `tableName` | string | Sí | Nombre de la tabla |
|
||||
| `sysId` | string | Sí | sys_id del registro a actualizar |
|
||||
| `fields` | json | Sí | Campos a actualizar \(objeto JSON\) |
|
||||
|
||||
#### Salida
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `record` | json | Registro de ServiceNow actualizado |
|
||||
| `metadata` | json | Metadatos de la operación |
|
||||
|
||||
### `servicenow_delete_record`
|
||||
|
||||
Elimina un registro de una tabla de ServiceNow
|
||||
|
||||
#### Entrada
|
||||
|
||||
| Parámetro | Tipo | Requerido | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `instanceUrl` | string | Sí | URL de la instancia de ServiceNow \(ej., https://instance.service-now.com\) |
|
||||
| `username` | string | Sí | Nombre de usuario de ServiceNow |
|
||||
| `password` | string | Sí | Contraseña de ServiceNow |
|
||||
| `tableName` | string | Sí | Nombre de la tabla |
|
||||
| `sysId` | string | Sí | sys_id del registro a eliminar |
|
||||
|
||||
#### Salida
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Si la eliminación fue exitosa |
|
||||
| `metadata` | json | Metadatos de la operación |
|
||||
|
||||
## Notas
|
||||
|
||||
- Categoría: `tools`
|
||||
- Tipo: `servicenow`
|
||||
@@ -54,7 +54,7 @@ Integra Slack en el flujo de trabajo. Puede enviar, actualizar y eliminar mensaj
|
||||
|
||||
### `slack_message`
|
||||
|
||||
Envía mensajes a canales o usuarios de Slack a través de la API de Slack. Compatible con el formato mrkdwn de Slack.
|
||||
Envía mensajes a canales de Slack o mensajes directos. Compatible con el formato mrkdwn de Slack.
|
||||
|
||||
#### Entrada
|
||||
|
||||
@@ -62,9 +62,10 @@ Envía mensajes a canales o usuarios de Slack a través de la API de Slack. Comp
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authMethod` | string | No | Método de autenticación: oauth o bot_token |
|
||||
| `botToken` | string | No | Token del bot para Bot personalizado |
|
||||
| `channel` | string | Sí | Canal de Slack objetivo (p. ej., #general) |
|
||||
| `channel` | string | No | Canal de Slack objetivo (p. ej., #general) |
|
||||
| `userId` | string | No | ID de usuario de Slack objetivo para mensajes directos (p. ej., U1234567890) |
|
||||
| `text` | string | Sí | Texto del mensaje a enviar (admite formato mrkdwn de Slack) |
|
||||
| `thread_ts` | string | No | Marca de tiempo del hilo para responder (crea respuesta en hilo) |
|
||||
| `thread_ts` | string | No | Marca de tiempo del hilo al que responder (crea respuesta en hilo) |
|
||||
| `files` | file[] | No | Archivos para adjuntar al mensaje |
|
||||
|
||||
#### Salida
|
||||
@@ -109,8 +110,9 @@ Lee los últimos mensajes de los canales de Slack. Recupera el historial de conv
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authMethod` | string | No | Método de autenticación: oauth o bot_token |
|
||||
| `botToken` | string | No | Token del bot para Bot personalizado |
|
||||
| `channel` | string | Sí | Canal de Slack del que leer mensajes (p. ej., #general) |
|
||||
| `limit` | number | No | Número de mensajes a recuperar (predeterminado: 10, máx: 100) |
|
||||
| `channel` | string | No | Canal de Slack del que leer mensajes (p. ej., #general) |
|
||||
| `userId` | string | No | ID de usuario para conversación de mensaje directo (p. ej., U1234567890) |
|
||||
| `limit` | number | No | Número de mensajes a recuperar (predeterminado: 10, máx: 15) |
|
||||
| `oldest` | string | No | Inicio del rango de tiempo (marca de tiempo) |
|
||||
| `latest` | string | No | Fin del rango de tiempo (marca de tiempo) |
|
||||
|
||||
|
||||
@@ -37,16 +37,18 @@ Envía una solicitud de completado de chat a cualquier proveedor de LLM compatib
|
||||
|
||||
#### Entrada
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| Parámetro | Tipo | Requerido | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `model` | string | Sí | El modelo a utilizar \(p. ej., gpt-4o, claude-sonnet-4-5, gemini-2.0-flash\) |
|
||||
| `model` | string | Sí | El modelo a utilizar \(ej., gpt-4o, claude-sonnet-4-5, gemini-2.0-flash\) |
|
||||
| `systemPrompt` | string | No | Prompt del sistema para establecer el comportamiento del asistente |
|
||||
| `context` | string | Sí | El mensaje del usuario o contexto para enviar al modelo |
|
||||
| `apiKey` | string | No | Clave API para el proveedor \(usa la clave de la plataforma si no se proporciona para modelos alojados\) |
|
||||
| `context` | string | Sí | El mensaje del usuario o contexto a enviar al modelo |
|
||||
| `apiKey` | string | No | Clave API del proveedor \(usa la clave de la plataforma si no se proporciona para modelos alojados\) |
|
||||
| `temperature` | number | No | Temperatura para la generación de respuestas \(0-2\) |
|
||||
| `maxTokens` | number | No | Tokens máximos en la respuesta |
|
||||
| `azureEndpoint` | string | No | URL del endpoint de Azure OpenAI |
|
||||
| `azureApiVersion` | string | No | Versión de la API de Azure OpenAI |
|
||||
| `vertexProject` | string | No | ID del proyecto de Google Cloud para Vertex AI |
|
||||
| `vertexLocation` | string | No | Ubicación de Google Cloud para Vertex AI \(por defecto us-central1\) |
|
||||
|
||||
#### Salida
|
||||
|
||||
|
||||
@@ -27,12 +27,14 @@ En Sim, la integración con Zoom permite a tus agentes automatizar la programaci
|
||||
- Obtener detalles o invitaciones para cualquier reunión
|
||||
- Actualizar o eliminar reuniones existentes directamente desde tus automatizaciones
|
||||
|
||||
Estas capacidades te permiten agilizar la colaboración remota, automatizar sesiones de video recurrentes y gestionar el entorno Zoom de tu organización como parte de tus flujos de trabajo.
|
||||
Para conectarte a Zoom, arrastra el bloque de Zoom y haz clic en `Connect` para autenticarte con tu cuenta de Zoom. Una vez conectado, puedes usar las herramientas de Zoom para crear, listar, actualizar y eliminar reuniones de Zoom. En cualquier momento, puedes desconectar tu cuenta de Zoom haciendo clic en `Disconnect` en Configuración > Integraciones, y el acceso a tu cuenta de Zoom será revocado inmediatamente.
|
||||
|
||||
Estas capacidades te permiten agilizar la colaboración remota, automatizar sesiones de video recurrentes y gestionar el entorno de Zoom de tu organización, todo como parte de tus flujos de trabajo.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
## Instrucciones de uso
|
||||
|
||||
Integra Zoom en flujos de trabajo. Crea, lista, actualiza y elimina reuniones de Zoom. Obtén detalles de reuniones, invitaciones, grabaciones y participantes. Gestiona grabaciones en la nube de forma programática.
|
||||
Integra Zoom en los flujos de trabajo. Crea, lista, actualiza y elimina reuniones de Zoom. Obtén detalles de reuniones, invitaciones, grabaciones y participantes. Gestiona grabaciones en la nube de forma programática.
|
||||
|
||||
## Herramientas
|
||||
|
||||
@@ -44,12 +46,12 @@ Crear una nueva reunión de Zoom
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `userId` | string | Sí | El ID de usuario o dirección de correo electrónico. Usa "me" para el usuario autenticado. |
|
||||
| `userId` | string | Sí | El ID de usuario o dirección de correo electrónico. Use "me" para el usuario autenticado. |
|
||||
| `topic` | string | Sí | Tema de la reunión |
|
||||
| `type` | number | No | Tipo de reunión: 1=instantánea, 2=programada, 3=recurrente sin hora fija, 8=recurrente con hora fija |
|
||||
| `startTime` | string | No | Hora de inicio de la reunión en formato ISO 8601 \(p. ej., 2025-06-03T10:00:00Z\) |
|
||||
| `startTime` | string | No | Hora de inicio de la reunión en formato ISO 8601 \(ej., 2025-06-03T10:00:00Z\) |
|
||||
| `duration` | number | No | Duración de la reunión en minutos |
|
||||
| `timezone` | string | No | Zona horaria para la reunión \(p. ej., America/Los_Angeles\) |
|
||||
| `timezone` | string | No | Zona horaria para la reunión \(ej., America/Los_Angeles\) |
|
||||
| `password` | string | No | Contraseña de la reunión |
|
||||
| `agenda` | string | No | Agenda de la reunión |
|
||||
| `hostVideo` | boolean | No | Iniciar con video del anfitrión activado |
|
||||
@@ -67,13 +69,13 @@ Crear una nueva reunión de Zoom
|
||||
|
||||
### `zoom_list_meetings`
|
||||
|
||||
Listar todas las reuniones para un usuario de Zoom
|
||||
Listar todas las reuniones de un usuario de Zoom
|
||||
|
||||
#### Entrada
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `userId` | string | Sí | El ID de usuario o dirección de correo electrónico. Use "me" para el usuario autenticado. |
|
||||
| `userId` | string | Sí | El ID de usuario o dirección de correo electrónico. Usa "me" para el usuario autenticado. |
|
||||
| `type` | string | No | Filtro de tipo de reunión: scheduled, live, upcoming, upcoming_meetings, o previous_meetings |
|
||||
| `pageSize` | number | No | Número de registros por página \(máximo 300\) |
|
||||
| `nextPageToken` | string | No | Token para paginación para obtener la siguiente página de resultados |
|
||||
@@ -103,7 +105,7 @@ Obtener detalles de una reunión específica de Zoom
|
||||
| --------- | ---- | ----------- |
|
||||
| `meeting` | object | Los detalles de la reunión |
|
||||
|
||||
### `zoom_update_meeting`
|
||||
Obtener detalles de una reunión específica de Zoom
|
||||
|
||||
Actualizar una reunión existente de Zoom
|
||||
|
||||
@@ -122,9 +124,9 @@ Actualizar una reunión existente de Zoom
|
||||
| `hostVideo` | boolean | No | Iniciar con video del anfitrión activado |
|
||||
| `participantVideo` | boolean | No | Iniciar con video de participantes activado |
|
||||
| `joinBeforeHost` | boolean | No | Permitir que los participantes se unan antes que el anfitrión |
|
||||
| `muteUponEntry` | boolean | No | Silenciar a los participantes al entrar |
|
||||
| `muteUponEntry` | boolean | No | Silenciar participantes al entrar |
|
||||
| `waitingRoom` | boolean | No | Habilitar sala de espera |
|
||||
| `autoRecording` | string | No | Configuración de grabación automática: local, en la nube o ninguna |
|
||||
| `autoRecording` | string | No | Configuración de grabación automática: local, cloud o none |
|
||||
|
||||
#### Salida
|
||||
|
||||
@@ -132,7 +134,7 @@ Actualizar una reunión existente de Zoom
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Si la reunión se actualizó correctamente |
|
||||
|
||||
### `zoom_delete_meeting`
|
||||
Eliminar una reunión de Zoom
|
||||
|
||||
Eliminar o cancelar una reunión de Zoom
|
||||
|
||||
@@ -142,14 +144,14 @@ Eliminar o cancelar una reunión de Zoom
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `meetingId` | string | Sí | El ID de la reunión a eliminar |
|
||||
| `occurrenceId` | string | No | ID de ocurrencia para eliminar una ocurrencia específica de una reunión recurrente |
|
||||
| `scheduleForReminder` | boolean | No | Enviar correo electrónico de recordatorio de cancelación a los inscritos |
|
||||
| `cancelMeetingReminder` | boolean | No | Enviar correo electrónico de cancelación a los inscritos y anfitriones alternativos |
|
||||
| `scheduleForReminder` | boolean | No | Enviar correo electrónico de recordatorio de cancelación a los registrados |
|
||||
| `cancelMeetingReminder` | boolean | No | Enviar correo electrónico de cancelación a los registrados y anfitriones alternativos |
|
||||
|
||||
#### Salida
|
||||
|
||||
| Parámetro | Tipo | Descripción |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Indica si la reunión se eliminó correctamente |
|
||||
| `success` | boolean | Si la reunión se eliminó correctamente |
|
||||
|
||||
### `zoom_get_meeting_invitation`
|
||||
|
||||
@@ -175,10 +177,10 @@ Listar todas las grabaciones en la nube para un usuario de Zoom
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `userId` | string | Sí | El ID de usuario o dirección de correo electrónico. Use "me" para el usuario autenticado. |
|
||||
| `from` | string | No | Fecha de inicio en formato aaaa-mm-dd \(dentro de los últimos 6 meses\) |
|
||||
| `userId` | string | Sí | El ID de usuario o dirección de correo electrónico. Usa "me" para el usuario autenticado. |
|
||||
| `from` | string | No | Fecha de inicio en formato aaaa-mm-dd (dentro de los últimos 6 meses) |
|
||||
| `to` | string | No | Fecha de fin en formato aaaa-mm-dd |
|
||||
| `pageSize` | number | No | Número de registros por página \(máximo 300\) |
|
||||
| `pageSize` | number | No | Número de registros por página (máx. 300) |
|
||||
| `nextPageToken` | string | No | Token para paginación para obtener la siguiente página de resultados |
|
||||
| `trash` | boolean | No | Establecer como true para listar grabaciones de la papelera |
|
||||
|
||||
@@ -189,9 +191,9 @@ Listar todas las grabaciones en la nube para un usuario de Zoom
|
||||
| `recordings` | array | Lista de grabaciones |
|
||||
| `pageInfo` | object | Información de paginación |
|
||||
|
||||
### `zoom_get_meeting_recordings`
|
||||
Obtener todas las grabaciones para una reunión específica de Zoom
|
||||
|
||||
Obtener todas las grabaciones de una reunión específica de Zoom
|
||||
Obtener todas las grabaciones para una reunión específica de Zoom
|
||||
|
||||
#### Entrada
|
||||
|
||||
@@ -199,7 +201,7 @@ Obtener todas las grabaciones de una reunión específica de Zoom
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `meetingId` | string | Sí | El ID de la reunión o UUID de la reunión |
|
||||
| `includeFolderItems` | boolean | No | Incluir elementos dentro de una carpeta |
|
||||
| `ttl` | number | No | Tiempo de vida para las URLs de descarga en segundos \(máx. 604800\) |
|
||||
| `ttl` | number | No | Tiempo de vida para URLs de descarga en segundos \(máx. 604800\) |
|
||||
|
||||
#### Salida
|
||||
|
||||
@@ -207,9 +209,9 @@ Obtener todas las grabaciones de una reunión específica de Zoom
|
||||
| --------- | ---- | ----------- |
|
||||
| `recording` | object | La grabación de la reunión con todos los archivos |
|
||||
|
||||
### `zoom_delete_recording`
|
||||
Eliminar grabaciones en la nube para una reunión de Zoom
|
||||
|
||||
Eliminar grabaciones en la nube de una reunión de Zoom
|
||||
Eliminar grabaciones en la nube para una reunión de Zoom
|
||||
|
||||
#### Entrada
|
||||
|
||||
@@ -225,7 +227,7 @@ Eliminar grabaciones en la nube de una reunión de Zoom
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Si la grabación se eliminó correctamente |
|
||||
|
||||
### `zoom_list_past_participants`
|
||||
Listar participantes de una reunión pasada de Zoom
|
||||
|
||||
Listar participantes de una reunión pasada de Zoom
|
||||
|
||||
@@ -233,7 +235,7 @@ Listar participantes de una reunión pasada de Zoom
|
||||
|
||||
| Parámetro | Tipo | Obligatorio | Descripción |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `meetingId` | string | Sí | El ID o UUID de la reunión pasada |
|
||||
| `meetingId` | string | Sí | El ID de la reunión pasada o UUID |
|
||||
| `pageSize` | number | No | Número de registros por página \(máx. 300\) |
|
||||
| `nextPageToken` | string | No | Token para paginación para obtener la siguiente página de resultados |
|
||||
|
||||
|
||||
@@ -111,26 +111,24 @@ Différents types de blocs produisent différentes structures de sortie. Voici c
|
||||
|
||||
```json
|
||||
{
|
||||
"content": "Original content passed through",
|
||||
"conditionResult": true,
|
||||
"selectedPath": {
|
||||
"blockId": "2acd9007-27e8-4510-a487-73d3b825e7c1",
|
||||
"blockType": "agent",
|
||||
"blockTitle": "Follow-up Agent"
|
||||
},
|
||||
"selectedConditionId": "condition-1"
|
||||
"selectedOption": "condition-1"
|
||||
}
|
||||
```
|
||||
|
||||
### Champs de sortie du bloc de condition
|
||||
|
||||
- **content** : le contenu original transmis
|
||||
- **conditionResult** : résultat booléen de l'évaluation de la condition
|
||||
- **selectedPath** : informations sur le chemin sélectionné
|
||||
- **blockId** : ID du bloc suivant dans le chemin sélectionné
|
||||
- **blockType** : type du bloc suivant
|
||||
- **blockTitle** : titre du bloc suivant
|
||||
- **selectedConditionId** : ID de la condition sélectionnée
|
||||
- **selectedOption** : ID de la condition sélectionnée
|
||||
|
||||
</Tab>
|
||||
<Tab>
|
||||
|
||||
@@ -48,8 +48,13 @@ Récupérer des informations détaillées sur un ticket Jira spécifique
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | booléen | Statut de réussite de l'opération |
|
||||
| `output` | objet | Détails du ticket Jira avec la clé du ticket, le résumé, la description, les horodatages de création et de mise à jour |
|
||||
| `ts` | chaîne | Horodatage de l'opération |
|
||||
| `issueKey` | chaîne | Clé du ticket \(ex. : PROJ-123\) |
|
||||
| `summary` | chaîne | Résumé du ticket |
|
||||
| `description` | json | Contenu de la description du ticket |
|
||||
| `created` | chaîne | Horodatage de création du ticket |
|
||||
| `updated` | chaîne | Horodatage de dernière mise à jour du ticket |
|
||||
| `issue` | json | Objet complet du ticket avec tous les champs |
|
||||
|
||||
### `jira_update`
|
||||
|
||||
@@ -73,8 +78,9 @@ Mettre à jour un ticket Jira
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Détails de la demande Jira mise à jour avec horodatage, clé de la demande, résumé et statut de réussite |
|
||||
| `ts` | chaîne | Horodatage de l'opération |
|
||||
| `issueKey` | chaîne | Clé du ticket mis à jour \(ex. : PROJ-123\) |
|
||||
| `summary` | chaîne | Résumé du ticket après mise à jour |
|
||||
|
||||
### `jira_write`
|
||||
|
||||
@@ -83,22 +89,31 @@ Rédiger une demande Jira
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Oui | Votre domaine Jira (ex. : votreentreprise.atlassian.net) |
|
||||
| `projectId` | string | Oui | ID du projet pour la demande |
|
||||
| `summary` | string | Oui | Résumé de la demande |
|
||||
| `description` | string | Non | Description de la demande |
|
||||
| `priority` | string | Non | Priorité de la demande |
|
||||
| `assignee` | string | Non | Assigné de la demande |
|
||||
| `cloudId` | string | Non | ID Jira Cloud pour l'instance. S'il n'est pas fourni, il sera récupéré à l'aide du domaine. |
|
||||
| `issueType` | string | Oui | Type de demande à créer (ex. : Tâche, Story) |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
| `domain` | chaîne | Oui | Votre domaine Jira \(ex. : votreentreprise.atlassian.net\) |
|
||||
| `projectId` | chaîne | Oui | ID du projet pour le ticket |
|
||||
| `summary` | chaîne | Oui | Résumé du ticket |
|
||||
| `description` | chaîne | Non | Description du ticket |
|
||||
| `priority` | chaîne | Non | ID ou nom de la priorité du ticket \(ex. : "10000" ou "Haute"\) |
|
||||
| `assignee` | chaîne | Non | ID de compte de l'assigné pour le ticket |
|
||||
| `cloudId` | chaîne | Non | ID Cloud Jira pour l'instance. S'il n'est pas fourni, il sera récupéré à l'aide du domaine. |
|
||||
| `issueType` | chaîne | Oui | Type de ticket à créer \(ex. : tâche, story\) |
|
||||
| `labels` | tableau | Non | Étiquettes pour le ticket \(tableau de noms d'étiquettes\) |
|
||||
| `duedate` | chaîne | Non | Date d'échéance du ticket \(format : AAAA-MM-JJ\) |
|
||||
| `reporter` | chaîne | Non | ID de compte du rapporteur pour le ticket |
|
||||
| `environment` | chaîne | Non | Informations d'environnement pour le ticket |
|
||||
| `customFieldId` | chaîne | Non | ID du champ personnalisé \(ex. : customfield_10001\) |
|
||||
| `customFieldValue` | chaîne | Non | Valeur pour le champ personnalisé |
|
||||
|
||||
#### Sortie
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Détails de la demande Jira créée avec horodatage, clé de la demande, résumé, statut de réussite et URL |
|
||||
| `ts` | chaîne | Horodatage de l'opération |
|
||||
| `issueKey` | chaîne | Clé du ticket créé \(ex. : PROJ-123\) |
|
||||
| `summary` | chaîne | Résumé du ticket |
|
||||
| `url` | chaîne | URL vers le ticket créé |
|
||||
| `assigneeId` | chaîne | ID de compte de l'utilisateur assigné \(si assigné\) |
|
||||
|
||||
### `jira_bulk_read`
|
||||
|
||||
@@ -116,8 +131,7 @@ Récupérer plusieurs demandes Jira en masse
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | array | Tableau des tickets Jira avec résumé, description, horodatages de création et de mise à jour |
|
||||
| `issues` | tableau | Tableau des tickets Jira avec horodatages ts, résumé, description, création et mise à jour |
|
||||
|
||||
### `jira_delete_issue`
|
||||
|
||||
@@ -136,8 +150,8 @@ Supprimer un ticket Jira
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | booléen | Statut de réussite de l'opération |
|
||||
| `output` | objet | Détails du ticket supprimé avec horodatage, clé du ticket et statut de réussite |
|
||||
| `ts` | chaîne | Horodatage de l'opération |
|
||||
| `issueKey` | chaîne | Clé du ticket supprimé |
|
||||
|
||||
### `jira_assign_issue`
|
||||
|
||||
@@ -156,8 +170,9 @@ Assigner un ticket Jira à un utilisateur
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | booléen | Statut de réussite de l'opération |
|
||||
| `output` | objet | Détails de l'assignation avec horodatage, clé du ticket, ID de l'assigné et statut de réussite |
|
||||
| `ts` | chaîne | Horodatage de l'opération |
|
||||
| `issueKey` | chaîne | Clé du ticket qui a été assigné |
|
||||
| `assigneeId` | chaîne | ID de compte de l'assigné |
|
||||
|
||||
### `jira_transition_issue`
|
||||
|
||||
@@ -177,8 +192,9 @@ Déplacer un ticket Jira entre les statuts de workflow (par ex., À faire -> En
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | booléen | Statut de réussite de l'opération |
|
||||
| `output` | objet | Détails de la transition avec horodatage, clé du ticket, ID de transition et statut de réussite |
|
||||
| `ts` | string | Horodatage de l'opération |
|
||||
| `issueKey` | string | Clé du ticket qui a été transitionné |
|
||||
| `transitionId` | string | ID de la transition appliquée |
|
||||
|
||||
### `jira_search_issues`
|
||||
|
||||
@@ -199,8 +215,11 @@ Rechercher des tickets Jira à l'aide de JQL (Jira Query Language)
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | booléen | Statut de réussite de l'opération |
|
||||
| `output` | objet | Résultats de recherche avec horodatage, nombre total, détails de pagination et tableau des tickets correspondants |
|
||||
| `ts` | string | Horodatage de l'opération |
|
||||
| `total` | number | Nombre total de tickets correspondants |
|
||||
| `startAt` | number | Index de début de pagination |
|
||||
| `maxResults` | number | Nombre maximum de résultats par page |
|
||||
| `issues` | array | Tableau des tickets correspondants avec clé, résumé, statut, assigné, créé, mis à jour |
|
||||
|
||||
### `jira_add_comment`
|
||||
|
||||
@@ -219,8 +238,10 @@ Ajouter un commentaire à un ticket Jira
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | booléen | Statut de réussite de l'opération |
|
||||
| `output` | objet | Détails du commentaire avec horodatage, clé du ticket, ID du commentaire, corps et statut de réussite |
|
||||
| `ts` | string | Horodatage de l'opération |
|
||||
| `issueKey` | string | Clé du ticket auquel le commentaire a été ajouté |
|
||||
| `commentId` | string | ID du commentaire créé |
|
||||
| `body` | string | Contenu textuel du commentaire |
|
||||
|
||||
### `jira_get_comments`
|
||||
|
||||
@@ -240,8 +261,10 @@ Obtenir tous les commentaires d'un ticket Jira
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Données des commentaires avec horodatage, clé du ticket, nombre total et tableau de commentaires |
|
||||
| `ts` | string | Horodatage de l'opération |
|
||||
| `issueKey` | string | Clé du ticket |
|
||||
| `total` | number | Nombre total de commentaires |
|
||||
| `comments` | array | Tableau des commentaires avec id, auteur, corps, créé, mis à jour |
|
||||
|
||||
### `jira_update_comment`
|
||||
|
||||
@@ -261,8 +284,10 @@ Mettre à jour un commentaire existant sur un ticket Jira
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Détails du commentaire mis à jour avec horodatage, clé du ticket, ID du commentaire, texte du corps et statut de réussite |
|
||||
| `ts` | string | Horodatage de l'opération |
|
||||
| `issueKey` | string | Clé du ticket |
|
||||
| `commentId` | string | ID du commentaire mis à jour |
|
||||
| `body` | string | Texte du commentaire mis à jour |
|
||||
|
||||
### `jira_delete_comment`
|
||||
|
||||
@@ -281,8 +306,9 @@ Supprimer un commentaire d'un ticket Jira
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Détails de la suppression avec horodatage, clé du ticket, ID du commentaire et statut de réussite |
|
||||
| `ts` | string | Horodatage de l'opération |
|
||||
| `issueKey` | string | Clé du ticket |
|
||||
| `commentId` | string | ID du commentaire supprimé |
|
||||
|
||||
### `jira_get_attachments`
|
||||
|
||||
@@ -300,8 +326,9 @@ Obtenir toutes les pièces jointes d'un ticket Jira
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Données des pièces jointes avec horodatage, clé du ticket et tableau des pièces jointes |
|
||||
| `ts` | string | Horodatage de l'opération |
|
||||
| `issueKey` | string | Clé du ticket |
|
||||
| `attachments` | array | Tableau des pièces jointes avec id, nom de fichier, taille, type MIME, date de création, auteur |
|
||||
|
||||
### `jira_delete_attachment`
|
||||
|
||||
@@ -319,8 +346,8 @@ Supprimer une pièce jointe d'un ticket Jira
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Détails de la suppression avec horodatage, ID de la pièce jointe et statut de réussite |
|
||||
| `ts` | string | Horodatage de l'opération |
|
||||
| `attachmentId` | string | ID de la pièce jointe supprimée |
|
||||
|
||||
### `jira_add_worklog`
|
||||
|
||||
@@ -341,8 +368,10 @@ Ajouter une entrée de journal de travail pour le suivi du temps à un ticket Ji
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | booléen | Statut de réussite de l'opération |
|
||||
| `output` | objet | Détails du journal de travail avec horodatage, clé du ticket, ID du journal de travail, temps passé en secondes et statut de réussite |
|
||||
| `ts` | string | Horodatage de l'opération |
|
||||
| `issueKey` | string | Clé du ticket auquel le journal de travail a été ajouté |
|
||||
| `worklogId` | string | ID du journal de travail créé |
|
||||
| `timeSpentSeconds` | number | Temps passé en secondes |
|
||||
|
||||
### `jira_get_worklogs`
|
||||
|
||||
@@ -362,8 +391,10 @@ Obtenir toutes les entrées du journal de travail d'un ticket Jira
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Données des journaux de travail avec horodatage, clé du ticket, nombre total et tableau des journaux de travail |
|
||||
| `ts` | string | Horodatage de l'opération |
|
||||
| `issueKey` | string | Clé du ticket |
|
||||
| `total` | number | Nombre total de journaux de travail |
|
||||
| `worklogs` | array | Tableau des journaux de travail avec id, auteur, temps passé en secondes, temps passé, commentaire, date de création, mise à jour, démarrage |
|
||||
|
||||
### `jira_update_worklog`
|
||||
|
||||
@@ -385,8 +416,9 @@ Mettre à jour une entrée de journal de travail existante sur un ticket Jira
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Détails de la mise à jour du journal de travail avec horodatage, clé du ticket, ID du journal de travail et statut de réussite |
|
||||
| `ts` | string | Horodatage de l'opération |
|
||||
| `issueKey` | string | Clé du ticket |
|
||||
| `worklogId` | string | ID du journal de travail mis à jour |
|
||||
|
||||
### `jira_delete_worklog`
|
||||
|
||||
@@ -405,8 +437,9 @@ Supprimer une entrée de journal de travail d'un ticket Jira
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Détails de la suppression avec horodatage, clé de la demande, ID du journal de travail et statut de réussite |
|
||||
| `ts` | string | Horodatage de l'opération |
|
||||
| `issueKey` | string | Clé du ticket |
|
||||
| `worklogId` | string | ID du journal de travail supprimé |
|
||||
|
||||
### `jira_create_issue_link`
|
||||
|
||||
@@ -427,8 +460,11 @@ Créer une relation de lien entre deux tickets Jira
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Détails du lien entre tickets avec horodatage, clé du ticket entrant, clé du ticket sortant, type de lien et statut de réussite |
|
||||
| `ts` | string | Horodatage de l'opération |
|
||||
| `inwardIssue` | string | Clé du ticket entrant |
|
||||
| `outwardIssue` | string | Clé du ticket sortant |
|
||||
| `linkType` | string | Type de lien entre tickets |
|
||||
| `linkId` | string | ID du lien créé |
|
||||
|
||||
### `jira_delete_issue_link`
|
||||
|
||||
@@ -446,8 +482,8 @@ Supprimer un lien entre deux tickets Jira
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Détails de la suppression avec horodatage, ID du lien et statut de réussite |
|
||||
| `ts` | string | Horodatage de l'opération |
|
||||
| `linkId` | string | ID du lien supprimé |
|
||||
|
||||
### `jira_add_watcher`
|
||||
|
||||
@@ -466,8 +502,9 @@ Ajouter un observateur à un ticket Jira pour recevoir des notifications sur les
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Détails de l'observateur avec horodatage, clé du ticket, ID de compte de l'observateur et statut de réussite |
|
||||
| `ts` | string | Horodatage de l'opération |
|
||||
| `issueKey` | string | Clé du ticket |
|
||||
| `watcherAccountId` | string | ID du compte observateur ajouté |
|
||||
|
||||
### `jira_remove_watcher`
|
||||
|
||||
@@ -486,10 +523,35 @@ Supprimer un observateur d'un ticket Jira
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Statut de réussite de l'opération |
|
||||
| `output` | object | Détails de la suppression avec horodatage, clé de la demande, ID du compte observateur et statut de réussite |
|
||||
| `ts` | string | Horodatage de l'opération |
|
||||
| `issueKey` | string | Clé du ticket |
|
||||
| `watcherAccountId` | string | ID du compte observateur supprimé |
|
||||
|
||||
## Notes
|
||||
### `jira_get_users`
|
||||
|
||||
Récupère les utilisateurs Jira. Si un ID de compte est fourni, renvoie un seul utilisateur. Sinon, renvoie une liste de tous les utilisateurs.
|
||||
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
| `domain` | chaîne | Oui | Votre domaine Jira \(ex. : votreentreprise.atlassian.net\) |
|
||||
| `accountId` | chaîne | Non | ID de compte optionnel pour obtenir un utilisateur spécifique. S'il n'est pas fourni, renvoie tous les utilisateurs. |
|
||||
| `startAt` | nombre | Non | L'index du premier utilisateur à renvoyer \(pour la pagination, par défaut : 0\) |
|
||||
| `maxResults` | nombre | Non | Nombre maximum d'utilisateurs à renvoyer \(par défaut : 50\) |
|
||||
| `cloudId` | chaîne | Non | ID Cloud Jira pour l'instance. S'il n'est pas fourni, il sera récupéré à l'aide du domaine. |
|
||||
|
||||
#### Sortie
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | chaîne | Horodatage de l'opération |
|
||||
| `users` | json | Tableau d'utilisateurs avec accountId, displayName, emailAddress, statut actif et avatarUrls |
|
||||
| `total` | nombre | Nombre total d'utilisateurs renvoyés |
|
||||
| `startAt` | nombre | Index de début de pagination |
|
||||
| `maxResults` | nombre | Nombre maximum de résultats par page |
|
||||
|
||||
## Remarques
|
||||
|
||||
- Catégorie : `tools`
|
||||
- Type : `jira`
|
||||
|
||||
124
apps/docs/content/docs/fr/tools/servicenow.mdx
Normal file
124
apps/docs/content/docs/fr/tools/servicenow.mdx
Normal file
@@ -0,0 +1,124 @@
|
||||
---
|
||||
title: ServiceNow
|
||||
description: Créer, lire, mettre à jour et supprimer des enregistrements ServiceNow
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="servicenow"
|
||||
color="#032D42"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[ServiceNow](https://www.servicenow.com/) est une plateforme cloud puissante conçue pour rationaliser et automatiser la gestion des services informatiques (ITSM), les workflows et les processus métier au sein de votre organisation. ServiceNow vous permet de gérer les incidents, les demandes, les tâches, les utilisateurs et bien plus encore grâce à son API étendue.
|
||||
|
||||
Avec ServiceNow, vous pouvez :
|
||||
|
||||
- **Automatiser les workflows informatiques** : créer, lire, mettre à jour et supprimer des enregistrements dans n'importe quelle table ServiceNow, tels que les incidents, les tâches, les demandes de changement et les utilisateurs.
|
||||
- **Intégrer les systèmes** : connecter ServiceNow avec vos autres outils et processus pour une automatisation transparente.
|
||||
- **Maintenir une source unique de vérité** : garder toutes vos données de service et d'exploitation organisées et accessibles.
|
||||
- **Améliorer l'efficacité opérationnelle** : réduire le travail manuel et améliorer la qualité du service grâce à des workflows personnalisables et à l'automatisation.
|
||||
|
||||
Dans Sim, l'intégration ServiceNow permet à vos agents d'interagir directement avec votre instance ServiceNow dans le cadre de leurs workflows. Les agents peuvent créer, lire, mettre à jour ou supprimer des enregistrements dans n'importe quelle table ServiceNow et exploiter les données de tickets ou d'utilisateurs pour une automatisation et une prise de décision sophistiquées. Cette intégration relie votre automatisation de workflow et vos opérations informatiques, permettant à vos agents de gérer les demandes de service, les incidents, les utilisateurs et les actifs sans intervention manuelle. En connectant Sim avec ServiceNow, vous pouvez automatiser les tâches de gestion des services, améliorer les temps de réponse et garantir un accès cohérent et sécurisé aux données de service vitales de votre organisation.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
## Instructions d'utilisation
|
||||
|
||||
Intégrez ServiceNow dans votre workflow. Créez, lisez, mettez à jour et supprimez des enregistrements dans n'importe quelle table ServiceNow, y compris les incidents, les tâches, les demandes de changement, les utilisateurs et bien plus encore.
|
||||
|
||||
## Outils
|
||||
|
||||
### `servicenow_create_record`
|
||||
|
||||
Créer un nouvel enregistrement dans une table ServiceNow
|
||||
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Requis | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `instanceUrl` | string | Oui | URL de l'instance ServiceNow (par ex., https://instance.service-now.com) |
|
||||
| `username` | string | Oui | Nom d'utilisateur ServiceNow |
|
||||
| `password` | string | Oui | Mot de passe ServiceNow |
|
||||
| `tableName` | string | Oui | Nom de la table (par ex., incident, task, sys_user) |
|
||||
| `fields` | json | Oui | Champs à définir sur l'enregistrement (objet JSON) |
|
||||
|
||||
#### Sortie
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `record` | json | Enregistrement ServiceNow créé avec sys_id et autres champs |
|
||||
| `metadata` | json | Métadonnées de l'opération |
|
||||
|
||||
### `servicenow_read_record`
|
||||
|
||||
Lire des enregistrements d'une table ServiceNow
|
||||
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Requis | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `instanceUrl` | string | Oui | URL de l'instance ServiceNow (par ex., https://instance.service-now.com) |
|
||||
| `username` | string | Oui | Nom d'utilisateur ServiceNow |
|
||||
| `password` | string | Oui | Mot de passe ServiceNow |
|
||||
| `tableName` | string | Oui | Nom de la table |
|
||||
| `sysId` | string | Non | sys_id d'enregistrement spécifique |
|
||||
| `number` | string | Non | Numéro d'enregistrement (par ex., INC0010001) |
|
||||
| `query` | string | Non | Chaîne de requête encodée (par ex., "active=true^priority=1") |
|
||||
| `limit` | number | Non | Nombre maximum d'enregistrements à retourner |
|
||||
| `fields` | string | Non | Liste de champs à retourner, séparés par des virgules |
|
||||
|
||||
#### Sortie
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `records` | array | Tableau d'enregistrements ServiceNow |
|
||||
| `metadata` | json | Métadonnées de l'opération |
|
||||
|
||||
### `servicenow_update_record`
|
||||
|
||||
Mettre à jour un enregistrement existant dans une table ServiceNow
|
||||
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Requis | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `instanceUrl` | string | Oui | URL de l'instance ServiceNow \(par exemple, https://instance.service-now.com\) |
|
||||
| `username` | string | Oui | Nom d'utilisateur ServiceNow |
|
||||
| `password` | string | Oui | Mot de passe ServiceNow |
|
||||
| `tableName` | string | Oui | Nom de la table |
|
||||
| `sysId` | string | Oui | sys_id de l'enregistrement à mettre à jour |
|
||||
| `fields` | json | Oui | Champs à mettre à jour \(objet JSON\) |
|
||||
|
||||
#### Sortie
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `record` | json | Enregistrement ServiceNow mis à jour |
|
||||
| `metadata` | json | Métadonnées de l'opération |
|
||||
|
||||
### `servicenow_delete_record`
|
||||
|
||||
Supprimer un enregistrement d'une table ServiceNow
|
||||
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Requis | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `instanceUrl` | string | Oui | URL de l'instance ServiceNow \(par exemple, https://instance.service-now.com\) |
|
||||
| `username` | string | Oui | Nom d'utilisateur ServiceNow |
|
||||
| `password` | string | Oui | Mot de passe ServiceNow |
|
||||
| `tableName` | string | Oui | Nom de la table |
|
||||
| `sysId` | string | Oui | sys_id de l'enregistrement à supprimer |
|
||||
|
||||
#### Sortie
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Indique si la suppression a réussi |
|
||||
| `metadata` | json | Métadonnées de l'opération |
|
||||
|
||||
## Remarques
|
||||
|
||||
- Catégorie : `tools`
|
||||
- Type : `servicenow`
|
||||
@@ -54,17 +54,18 @@ Intégrez Slack dans le flux de travail. Peut envoyer, mettre à jour et supprim
|
||||
|
||||
### `slack_message`
|
||||
|
||||
Envoyez des messages aux canaux ou utilisateurs Slack via l'API Slack. Prend en charge le formatage mrkdwn de Slack.
|
||||
Envoyez des messages aux canaux Slack ou en messages directs. Prend en charge le formatage mrkdwn de Slack.
|
||||
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | ---------- | ----------- |
|
||||
| `authMethod` | chaîne | Non | Méthode d'authentification : oauth ou bot_token |
|
||||
| `botToken` | chaîne | Non | Jeton du bot pour le Bot personnalisé |
|
||||
| `channel` | chaîne | Oui | Canal Slack cible \(par ex., #general\) |
|
||||
| `botToken` | chaîne | Non | Jeton du bot pour Bot personnalisé |
|
||||
| `channel` | chaîne | Non | Canal Slack cible \(ex. : #general\) |
|
||||
| `userId` | chaîne | Non | ID utilisateur Slack cible pour les messages directs \(ex. : U1234567890\) |
|
||||
| `text` | chaîne | Oui | Texte du message à envoyer \(prend en charge le formatage mrkdwn de Slack\) |
|
||||
| `thread_ts` | chaîne | Non | Horodatage du fil pour répondre \(crée une réponse dans le fil\) |
|
||||
| `thread_ts` | chaîne | Non | Horodatage du fil de discussion auquel répondre \(crée une réponse dans le fil\) |
|
||||
| `files` | fichier[] | Non | Fichiers à joindre au message |
|
||||
|
||||
#### Sortie
|
||||
@@ -106,13 +107,14 @@ Lisez les derniers messages des canaux Slack. Récupérez l'historique des conve
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | ---------- | ----------- |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
| `authMethod` | chaîne | Non | Méthode d'authentification : oauth ou bot_token |
|
||||
| `botToken` | chaîne | Non | Jeton du bot pour Bot personnalisé |
|
||||
| `channel` | chaîne | Oui | Canal Slack pour lire les messages \(ex. : #general\) |
|
||||
| `limit` | nombre | Non | Nombre de messages à récupérer \(par défaut : 10, max : 100\) |
|
||||
| `oldest` | chaîne | Non | Début de la plage temporelle \(horodatage\) |
|
||||
| `latest` | chaîne | Non | Fin de la plage temporelle \(horodatage\) |
|
||||
| `channel` | chaîne | Non | Canal Slack depuis lequel lire les messages \(ex. : #general\) |
|
||||
| `userId` | chaîne | Non | ID utilisateur pour la conversation en message direct \(ex. : U1234567890\) |
|
||||
| `limit` | nombre | Non | Nombre de messages à récupérer \(par défaut : 10, max : 15\) |
|
||||
| `oldest` | chaîne | Non | Début de la plage horaire \(horodatage\) |
|
||||
| `latest` | chaîne | Non | Fin de la plage horaire \(horodatage\) |
|
||||
|
||||
#### Sortie
|
||||
|
||||
|
||||
@@ -37,16 +37,18 @@ Envoyez une requête de complétion de chat à n'importe quel fournisseur de LLM
|
||||
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | ---------- | ----------- |
|
||||
| `model` | chaîne | Oui | Le modèle à utiliser (ex. : gpt-4o, claude-sonnet-4-5, gemini-2.0-flash) |
|
||||
| `systemPrompt` | chaîne | Non | Instruction système pour définir le comportement de l'assistant |
|
||||
| `context` | chaîne | Oui | Le message utilisateur ou le contexte à envoyer au modèle |
|
||||
| `apiKey` | chaîne | Non | Clé API pour le fournisseur (utilise la clé de plateforme si non fournie pour les modèles hébergés) |
|
||||
| `temperature` | nombre | Non | Température pour la génération de réponse (0-2) |
|
||||
| `maxTokens` | nombre | Non | Nombre maximum de tokens dans la réponse |
|
||||
| `azureEndpoint` | chaîne | Non | URL du point de terminaison Azure OpenAI |
|
||||
| `azureApiVersion` | chaîne | Non | Version de l'API Azure OpenAI |
|
||||
| Paramètre | Type | Requis | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `model` | string | Oui | Le modèle à utiliser \(par exemple, gpt-4o, claude-sonnet-4-5, gemini-2.0-flash\) |
|
||||
| `systemPrompt` | string | Non | Prompt système pour définir le comportement de l'assistant |
|
||||
| `context` | string | Oui | Le message utilisateur ou le contexte à envoyer au modèle |
|
||||
| `apiKey` | string | Non | Clé API pour le fournisseur \(utilise la clé de la plateforme si non fournie pour les modèles hébergés\) |
|
||||
| `temperature` | number | Non | Température pour la génération de réponse \(0-2\) |
|
||||
| `maxTokens` | number | Non | Nombre maximum de tokens dans la réponse |
|
||||
| `azureEndpoint` | string | Non | URL du point de terminaison Azure OpenAI |
|
||||
| `azureApiVersion` | string | Non | Version de l'API Azure OpenAI |
|
||||
| `vertexProject` | string | Non | ID du projet Google Cloud pour Vertex AI |
|
||||
| `vertexLocation` | string | Non | Emplacement Google Cloud pour Vertex AI \(par défaut us-central1\) |
|
||||
|
||||
#### Sortie
|
||||
|
||||
|
||||
@@ -27,12 +27,14 @@ Dans Sim, l'intégration Zoom permet à vos agents d'automatiser la planificatio
|
||||
- Récupérer les détails ou les invitations pour n'importe quelle réunion
|
||||
- Mettre à jour ou supprimer des réunions existantes directement depuis vos automatisations
|
||||
|
||||
Ces capacités vous permettent de rationaliser la collaboration à distance, d'automatiser les sessions vidéo récurrentes et de gérer l'environnement Zoom de votre organisation, le tout dans le cadre de vos flux de travail.
|
||||
Pour vous connecter à Zoom, déposez le bloc Zoom et cliquez sur `Connect` pour vous authentifier avec votre compte Zoom. Une fois connecté, vous pouvez utiliser les outils Zoom pour créer, lister, mettre à jour et supprimer des réunions Zoom. À tout moment, vous pouvez déconnecter votre compte Zoom en cliquant sur `Disconnect` dans Paramètres > Intégrations, et l'accès à votre compte Zoom sera immédiatement révoqué.
|
||||
|
||||
Ces fonctionnalités vous permettent de rationaliser la collaboration à distance, d'automatiser les sessions vidéo récurrentes et de gérer l'environnement Zoom de votre organisation, le tout dans le cadre de vos flux de travail.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
## Instructions d'utilisation
|
||||
|
||||
Intégrez Zoom dans les flux de travail. Créez, listez, mettez à jour et supprimez des réunions Zoom. Obtenez les détails des réunions, les invitations, les enregistrements et les participants. Gérez les enregistrements cloud par programmation.
|
||||
Intégrez Zoom dans vos flux de travail. Créez, listez, mettez à jour et supprimez des réunions Zoom. Obtenez les détails des réunions, les invitations, les enregistrements et les participants. Gérez les enregistrements cloud de manière programmatique.
|
||||
|
||||
## Outils
|
||||
|
||||
@@ -43,7 +45,7 @@ Créer une nouvelle réunion Zoom
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
| `userId` | string | Oui | L'ID utilisateur ou l'adresse e-mail. Utilisez "me" pour l'utilisateur authentifié. |
|
||||
| `topic` | string | Oui | Sujet de la réunion |
|
||||
| `type` | number | Non | Type de réunion : 1=instantanée, 2=programmée, 3=récurrente sans heure fixe, 8=récurrente à heure fixe |
|
||||
@@ -57,7 +59,7 @@ Créer une nouvelle réunion Zoom
|
||||
| `joinBeforeHost` | boolean | Non | Autoriser les participants à rejoindre avant l'hôte |
|
||||
| `muteUponEntry` | boolean | Non | Mettre les participants en sourdine à l'entrée |
|
||||
| `waitingRoom` | boolean | Non | Activer la salle d'attente |
|
||||
| `autoRecording` | string | Non | Paramètre d'enregistrement automatique : local, cloud ou none |
|
||||
| `autoRecording` | string | Non | Paramètre d'enregistrement automatique : local, cloud, ou none |
|
||||
|
||||
#### Sortie
|
||||
|
||||
@@ -72,11 +74,11 @@ Lister toutes les réunions pour un utilisateur Zoom
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `userId` | string | Oui | L'ID utilisateur ou l'adresse e-mail. Utilisez "me" pour l'utilisateur authentifié. |
|
||||
| `type` | string | Non | Filtre de type de réunion : scheduled, live, upcoming, upcoming_meetings, ou previous_meetings |
|
||||
| `pageSize` | number | Non | Nombre d'enregistrements par page \(max 300\) |
|
||||
| `nextPageToken` | string | Non | Jeton pour la pagination afin d'obtenir la page suivante des résultats |
|
||||
| `nextPageToken` | string | Non | Jeton pour la pagination pour obtenir la page suivante de résultats |
|
||||
|
||||
#### Sortie
|
||||
|
||||
@@ -92,7 +94,7 @@ Obtenir les détails d'une réunion Zoom spécifique
|
||||
#### Entrée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | ----------- | ----------- |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `meetingId` | string | Oui | L'ID de la réunion |
|
||||
| `occurrenceId` | string | Non | ID d'occurrence pour les réunions récurrentes |
|
||||
| `showPreviousOccurrences` | boolean | Non | Afficher les occurrences précédentes pour les réunions récurrentes |
|
||||
@@ -103,7 +105,7 @@ Obtenir les détails d'une réunion Zoom spécifique
|
||||
| --------- | ---- | ----------- |
|
||||
| `meeting` | object | Les détails de la réunion |
|
||||
|
||||
### `zoom_update_meeting`
|
||||
Obtenir les détails d'une réunion Zoom spécifique
|
||||
|
||||
Mettre à jour une réunion Zoom existante
|
||||
|
||||
@@ -113,7 +115,7 @@ Mettre à jour une réunion Zoom existante
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `meetingId` | string | Oui | L'ID de la réunion à mettre à jour |
|
||||
| `topic` | string | Non | Sujet de la réunion |
|
||||
| `type` | number | Non | Type de réunion : 1=instantanée, 2=programmée, 3=récurrente sans horaire fixe, 8=récurrente avec horaire fixe |
|
||||
| `type` | number | Non | Type de réunion : 1=instantanée, 2=programmée, 3=récurrente sans heure fixe, 8=récurrente à heure fixe |
|
||||
| `startTime` | string | Non | Heure de début de la réunion au format ISO 8601 \(ex., 2025-06-03T10:00:00Z\) |
|
||||
| `duration` | number | Non | Durée de la réunion en minutes |
|
||||
| `timezone` | string | Non | Fuseau horaire pour la réunion \(ex., America/Los_Angeles\) |
|
||||
@@ -132,7 +134,7 @@ Mettre à jour une réunion Zoom existante
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Indique si la réunion a été mise à jour avec succès |
|
||||
|
||||
### `zoom_delete_meeting`
|
||||
Supprimer une réunion Zoom
|
||||
|
||||
Supprimer ou annuler une réunion Zoom
|
||||
|
||||
@@ -159,13 +161,13 @@ Obtenir le texte d'invitation pour une réunion Zoom
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `meetingId` | string | Oui | L'identifiant de la réunion |
|
||||
| `meetingId` | string | Oui | L'ID de la réunion |
|
||||
|
||||
#### Sortie
|
||||
|
||||
| Paramètre | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `invitation` | string | Le texte d'invitation à la réunion |
|
||||
| `invitation` | string | Le texte d'invitation de la réunion |
|
||||
|
||||
### `zoom_list_recordings`
|
||||
|
||||
@@ -175,11 +177,11 @@ Lister tous les enregistrements cloud pour un utilisateur Zoom
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `userId` | string | Oui | L'identifiant ou l'adresse e-mail de l'utilisateur. Utilisez "me" pour l'utilisateur authentifié. |
|
||||
| `from` | string | Non | Date de début au format aaaa-mm-jj \(dans les 6 derniers mois\) |
|
||||
| `userId` | string | Oui | L'ID utilisateur ou l'adresse e-mail. Utilisez "me" pour l'utilisateur authentifié. |
|
||||
| `from` | string | Non | Date de début au format aaaa-mm-jj (dans les 6 derniers mois) |
|
||||
| `to` | string | Non | Date de fin au format aaaa-mm-jj |
|
||||
| `pageSize` | number | Non | Nombre d'enregistrements par page \(max 300\) |
|
||||
| `nextPageToken` | string | Non | Jeton pour la pagination afin d'obtenir la page suivante des résultats |
|
||||
| `pageSize` | number | Non | Nombre d'enregistrements par page (max 300) |
|
||||
| `nextPageToken` | string | Non | Jeton pour la pagination pour obtenir la page suivante de résultats |
|
||||
| `trash` | boolean | Non | Définir sur true pour lister les enregistrements de la corbeille |
|
||||
|
||||
#### Sortie
|
||||
@@ -189,7 +191,7 @@ Lister tous les enregistrements cloud pour un utilisateur Zoom
|
||||
| `recordings` | array | Liste des enregistrements |
|
||||
| `pageInfo` | object | Informations de pagination |
|
||||
|
||||
### `zoom_get_meeting_recordings`
|
||||
Obtenir tous les enregistrements pour une réunion Zoom spécifique
|
||||
|
||||
Obtenir tous les enregistrements pour une réunion Zoom spécifique
|
||||
|
||||
@@ -207,9 +209,9 @@ Obtenir tous les enregistrements pour une réunion Zoom spécifique
|
||||
| --------- | ---- | ----------- |
|
||||
| `recording` | object | L'enregistrement de la réunion avec tous les fichiers |
|
||||
|
||||
### `zoom_delete_recording`
|
||||
Supprimer les enregistrements cloud pour une réunion Zoom
|
||||
|
||||
Supprimer les enregistrements cloud d'une réunion Zoom
|
||||
Supprimer les enregistrements cloud pour une réunion Zoom
|
||||
|
||||
#### Entrée
|
||||
|
||||
@@ -225,7 +227,7 @@ Supprimer les enregistrements cloud d'une réunion Zoom
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Si l'enregistrement a été supprimé avec succès |
|
||||
|
||||
### `zoom_list_past_participants`
|
||||
Lister les participants d'une réunion Zoom passée
|
||||
|
||||
Lister les participants d'une réunion Zoom passée
|
||||
|
||||
@@ -233,7 +235,7 @@ Lister les participants d'une réunion Zoom passée
|
||||
|
||||
| Paramètre | Type | Obligatoire | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `meetingId` | string | Oui | L'ID ou l'UUID de la réunion passée |
|
||||
| `meetingId` | string | Oui | L'ID de la réunion passée ou l'UUID |
|
||||
| `pageSize` | number | Non | Nombre d'enregistrements par page \(max 300\) |
|
||||
| `nextPageToken` | string | Non | Jeton pour la pagination pour obtenir la page suivante de résultats |
|
||||
|
||||
|
||||
@@ -110,26 +110,24 @@ import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
|
||||
|
||||
```json
|
||||
{
|
||||
"content": "Original content passed through",
|
||||
"conditionResult": true,
|
||||
"selectedPath": {
|
||||
"blockId": "2acd9007-27e8-4510-a487-73d3b825e7c1",
|
||||
"blockType": "agent",
|
||||
"blockTitle": "Follow-up Agent"
|
||||
},
|
||||
"selectedConditionId": "condition-1"
|
||||
"selectedOption": "condition-1"
|
||||
}
|
||||
```
|
||||
|
||||
### 条件ブロックの出力フィールド
|
||||
|
||||
- **content**: そのまま渡される元のコンテンツ
|
||||
- **conditionResult**: 条件評価の真偽値結果
|
||||
- **selectedPath**: 選択されたパスに関する情報
|
||||
- **blockId**: 選択されたパスの次のブロックのID
|
||||
- **blockType**: 次のブロックのタイプ
|
||||
- **blockTitle**: 次のブロックのタイトル
|
||||
- **selectedConditionId**: 選択された条件のID
|
||||
- **selectedOption**: 選択された条件のID
|
||||
|
||||
</Tab>
|
||||
<Tab>
|
||||
|
||||
@@ -48,8 +48,13 @@ Jiraをワークフローに統合します。課題の読み取り、書き込
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | 課題キー、要約、説明、作成日時、更新日時を含むJira課題の詳細 |
|
||||
| `ts` | string | 操作のタイムスタンプ |
|
||||
| `issueKey` | string | 課題キー(例:PROJ-123) |
|
||||
| `summary` | string | 課題の要約 |
|
||||
| `description` | json | 課題の説明内容 |
|
||||
| `created` | string | 課題作成タイムスタンプ |
|
||||
| `updated` | string | 課題最終更新タイムスタンプ |
|
||||
| `issue` | json | すべてのフィールドを含む完全な課題オブジェクト |
|
||||
|
||||
### `jira_update`
|
||||
|
||||
@@ -73,8 +78,9 @@ Jira課題を更新する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | タイムスタンプ、課題キー、要約、成功ステータスを含む更新されたJira課題の詳細 |
|
||||
| `ts` | string | 操作のタイムスタンプ |
|
||||
| `issueKey` | string | 更新された課題キー(例:PROJ-123) |
|
||||
| `summary` | string | 更新後の課題要約 |
|
||||
|
||||
### `jira_write`
|
||||
|
||||
@@ -88,17 +94,26 @@ Jira課題を作成する
|
||||
| `projectId` | string | はい | 課題のプロジェクトID |
|
||||
| `summary` | string | はい | 課題の要約 |
|
||||
| `description` | string | いいえ | 課題の説明 |
|
||||
| `priority` | string | いいえ | 課題の優先度 |
|
||||
| `assignee` | string | いいえ | 課題の担当者 |
|
||||
| `priority` | string | いいえ | 課題の優先度IDまたは名前(例:「10000」または「高」) |
|
||||
| `assignee` | string | いいえ | 課題の担当者アカウントID |
|
||||
| `cloudId` | string | いいえ | インスタンスのJira Cloud ID。提供されない場合、ドメインを使用して取得されます。 |
|
||||
| `issueType` | string | はい | 作成する課題のタイプ(例:タスク、ストーリー) |
|
||||
| `labels` | array | いいえ | 課題のラベル(ラベル名の配列) |
|
||||
| `duedate` | string | いいえ | 課題の期限(形式:YYYY-MM-DD) |
|
||||
| `reporter` | string | いいえ | 課題の報告者アカウントID |
|
||||
| `environment` | string | いいえ | 課題の環境情報 |
|
||||
| `customFieldId` | string | いいえ | カスタムフィールドID(例:customfield_10001) |
|
||||
| `customFieldValue` | string | いいえ | カスタムフィールドの値 |
|
||||
|
||||
#### 出力
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | タイムスタンプ、課題キー、要約、成功ステータス、URLを含む作成されたJira課題の詳細 |
|
||||
| `ts` | string | 操作のタイムスタンプ |
|
||||
| `issueKey` | string | 作成された課題キー(例:PROJ-123) |
|
||||
| `summary` | string | 課題の要約 |
|
||||
| `url` | string | 作成された課題のURL |
|
||||
| `assigneeId` | string | 割り当てられたユーザーのアカウントID(割り当てられている場合) |
|
||||
|
||||
### `jira_bulk_read`
|
||||
|
||||
@@ -116,8 +131,7 @@ Jira課題を作成する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | array | 概要、説明、作成日時、更新日時を含むJiraの課題の配列 |
|
||||
| `issues` | array | タイムスタンプ、要約、説明、作成日時、更新日時を含むJira課題の配列 |
|
||||
|
||||
### `jira_delete_issue`
|
||||
|
||||
@@ -136,8 +150,8 @@ Jira課題を削除する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | タイムスタンプ、課題キー、成功ステータスを含む削除された課題の詳細 |
|
||||
| `ts` | string | 操作のタイムスタンプ |
|
||||
| `issueKey` | string | 削除された課題キー |
|
||||
|
||||
### `jira_assign_issue`
|
||||
|
||||
@@ -156,8 +170,9 @@ Jira課題をユーザーに割り当てる
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | タイムスタンプ、課題キー、担当者ID、成功ステータスを含む割り当ての詳細 |
|
||||
| `ts` | string | 操作のタイムスタンプ |
|
||||
| `issueKey` | string | 割り当てられた課題キー |
|
||||
| `assigneeId` | string | 担当者のアカウントID |
|
||||
|
||||
### `jira_transition_issue`
|
||||
|
||||
@@ -177,8 +192,9 @@ Jira課題をワークフローステータス間で移動する(例:To Do
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | タイムスタンプ、課題キー、移行ID、成功ステータスを含む移行の詳細 |
|
||||
| `ts` | string | 操作のタイムスタンプ |
|
||||
| `issueKey` | string | 遷移した課題キー |
|
||||
| `transitionId` | string | 適用されたトランジションID |
|
||||
|
||||
### `jira_search_issues`
|
||||
|
||||
@@ -199,8 +215,11 @@ JQL(Jira Query Language)を使用してJira課題を検索する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | タイムスタンプ、合計数、ページネーション詳細、一致する課題の配列を含む検索結果 |
|
||||
| `ts` | string | 操作のタイムスタンプ |
|
||||
| `total` | number | 一致する課題の総数 |
|
||||
| `startAt` | number | ページネーション開始インデックス |
|
||||
| `maxResults` | number | ページあたりの最大結果数 |
|
||||
| `issues` | array | キー、要約、ステータス、担当者、作成日時、更新日時を含む一致する課題の配列 |
|
||||
|
||||
### `jira_add_comment`
|
||||
|
||||
@@ -219,8 +238,10 @@ Jira課題にコメントを追加する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | タイムスタンプ、課題キー、コメントID、本文、成功ステータスを含むコメント詳細 |
|
||||
| `ts` | string | 操作のタイムスタンプ |
|
||||
| `issueKey` | string | コメントが追加された課題キー |
|
||||
| `commentId` | string | 作成されたコメントID |
|
||||
| `body` | string | コメントのテキスト内容 |
|
||||
|
||||
### `jira_get_comments`
|
||||
|
||||
@@ -240,8 +261,10 @@ Jira課題からすべてのコメントを取得する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | タイムスタンプ、課題キー、合計数、コメントの配列を含むコメントデータ |
|
||||
| `ts` | string | 操作のタイムスタンプ |
|
||||
| `issueKey` | string | 課題キー |
|
||||
| `total` | number | コメントの総数 |
|
||||
| `comments` | array | ID、作成者、本文、作成日時、更新日時を含むコメントの配列 |
|
||||
|
||||
### `jira_update_comment`
|
||||
|
||||
@@ -261,8 +284,10 @@ Jira課題の既存コメントを更新する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | タイムスタンプ、課題キー、コメントID、本文テキスト、成功ステータスを含む更新されたコメントの詳細 |
|
||||
| `ts` | string | 操作のタイムスタンプ |
|
||||
| `issueKey` | string | 課題キー |
|
||||
| `commentId` | string | 更新されたコメントID |
|
||||
| `body` | string | 更新されたコメントテキスト |
|
||||
|
||||
### `jira_delete_comment`
|
||||
|
||||
@@ -281,8 +306,9 @@ Jira課題からコメントを削除する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | タイムスタンプ、課題キー、コメントID、成功ステータスを含む削除の詳細 |
|
||||
| `ts` | string | 操作のタイムスタンプ |
|
||||
| `issueKey` | string | 課題キー |
|
||||
| `commentId` | string | 削除されたコメントID |
|
||||
|
||||
### `jira_get_attachments`
|
||||
|
||||
@@ -300,8 +326,9 @@ Jira課題からすべての添付ファイルを取得する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | タイムスタンプ、課題キー、添付ファイルの配列を含む添付ファイルデータ |
|
||||
| `ts` | string | 操作のタイムスタンプ |
|
||||
| `issueKey` | string | 課題キー |
|
||||
| `attachments` | array | ID、ファイル名、サイズ、MIMEタイプ、作成日時、作成者を含む添付ファイルの配列 |
|
||||
|
||||
### `jira_delete_attachment`
|
||||
|
||||
@@ -319,8 +346,8 @@ Jira課題から添付ファイルを削除する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | タイムスタンプ、添付ファイルID、成功ステータスを含む削除の詳細 |
|
||||
| `ts` | string | 操作のタイムスタンプ |
|
||||
| `attachmentId` | string | 削除された添付ファイルID |
|
||||
|
||||
### `jira_add_worklog`
|
||||
|
||||
@@ -341,8 +368,10 @@ Jira課題に作業時間記録エントリを追加する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | タイムスタンプ、課題キー、作業ログID、秒単位の作業時間、成功ステータスを含む作業ログの詳細 |
|
||||
| `ts` | string | 操作のタイムスタンプ |
|
||||
| `issueKey` | string | 作業ログが追加された課題キー |
|
||||
| `worklogId` | string | 作成された作業ログID |
|
||||
| `timeSpentSeconds` | number | 秒単位の作業時間 |
|
||||
|
||||
### `jira_get_worklogs`
|
||||
|
||||
@@ -362,8 +391,10 @@ Jira課題からすべての作業ログエントリを取得する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | タイムスタンプ、課題キー、合計数、作業ログの配列を含む作業ログデータ |
|
||||
| `ts` | string | 操作のタイムスタンプ |
|
||||
| `issueKey` | string | 課題キー |
|
||||
| `total` | number | 作業ログの総数 |
|
||||
| `worklogs` | array | ID、作成者、秒単位の作業時間、作業時間、コメント、作成日時、更新日時、開始日時を含む作業ログの配列 |
|
||||
|
||||
### `jira_update_worklog`
|
||||
|
||||
@@ -385,8 +416,9 @@ Jira課題の既存の作業ログエントリを更新する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | タイムスタンプ、課題キー、作業ログID、成功ステータスを含む作業ログ更新の詳細 |
|
||||
| `ts` | string | 操作のタイムスタンプ |
|
||||
| `issueKey` | string | 課題キー |
|
||||
| `worklogId` | string | 更新された作業ログID |
|
||||
|
||||
### `jira_delete_worklog`
|
||||
|
||||
@@ -405,8 +437,9 @@ Jira課題から作業ログエントリを削除する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | タイムスタンプ、課題キー、作業ログID、成功ステータスを含む削除の詳細 |
|
||||
| `ts` | string | 操作のタイムスタンプ |
|
||||
| `issueKey` | string | 課題キー |
|
||||
| `worklogId` | string | 削除された作業ログID |
|
||||
|
||||
### `jira_create_issue_link`
|
||||
|
||||
@@ -427,8 +460,11 @@ Jira課題から作業ログエントリを削除する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | タイムスタンプ、インワード課題キー、アウトワード課題キー、リンクタイプ、成功ステータスを含む課題リンクの詳細 |
|
||||
| `ts` | string | 操作のタイムスタンプ |
|
||||
| `inwardIssue` | string | インワード課題キー |
|
||||
| `outwardIssue` | string | アウトワード課題キー |
|
||||
| `linkType` | string | 課題リンクのタイプ |
|
||||
| `linkId` | string | 作成されたリンクID |
|
||||
|
||||
### `jira_delete_issue_link`
|
||||
|
||||
@@ -446,8 +482,8 @@ Jira課題から作業ログエントリを削除する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | タイムスタンプ、リンクID、成功ステータスを含む削除の詳細 |
|
||||
| `ts` | string | 操作のタイムスタンプ |
|
||||
| `linkId` | string | 削除されたリンクID |
|
||||
|
||||
### `jira_add_watcher`
|
||||
|
||||
@@ -466,8 +502,9 @@ Jira課題から作業ログエントリを削除する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | タイムスタンプ、課題キー、ウォッチャーアカウントID、成功ステータスを含むウォッチャーの詳細 |
|
||||
| `ts` | string | 操作のタイムスタンプ |
|
||||
| `issueKey` | string | 課題キー |
|
||||
| `watcherAccountId` | string | 追加されたウォッチャーのアカウントID |
|
||||
|
||||
### `jira_remove_watcher`
|
||||
|
||||
@@ -486,10 +523,35 @@ Jira課題からウォッチャーを削除する
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功ステータス |
|
||||
| `output` | object | タイムスタンプ、課題キー、ウォッチャーアカウントID、成功ステータスを含む削除詳細 |
|
||||
| `ts` | string | 操作のタイムスタンプ |
|
||||
| `issueKey` | string | 課題キー |
|
||||
| `watcherAccountId` | string | 削除されたウォッチャーのアカウントID |
|
||||
|
||||
## 注意事項
|
||||
### `jira_get_users`
|
||||
|
||||
- カテゴリー: `tools`
|
||||
- タイプ: `jira`
|
||||
Jiraユーザーを取得します。アカウントIDが提供された場合、単一のユーザーを返します。それ以外の場合、すべてのユーザーのリストを返します。
|
||||
|
||||
#### 入力
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | はい | あなたのJiraドメイン(例:yourcompany.atlassian.net) |
|
||||
| `accountId` | string | いいえ | 特定のユーザーを取得するためのオプションのアカウントID。提供されない場合、すべてのユーザーを返します。 |
|
||||
| `startAt` | number | いいえ | 返す最初のユーザーのインデックス(ページネーション用、デフォルト:0) |
|
||||
| `maxResults` | number | いいえ | 返すユーザーの最大数(デフォルト:50) |
|
||||
| `cloudId` | string | いいえ | インスタンスのJira Cloud ID。提供されない場合、ドメインを使用して取得されます。 |
|
||||
|
||||
#### 出力
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | 操作のタイムスタンプ |
|
||||
| `users` | json | accountId、displayName、emailAddress、activeステータス、avatarUrlsを含むユーザーの配列 |
|
||||
| `total` | number | 返されたユーザーの総数 |
|
||||
| `startAt` | number | ページネーション開始インデックス |
|
||||
| `maxResults` | number | ページあたりの最大結果数 |
|
||||
|
||||
## 注記
|
||||
|
||||
- カテゴリ:`tools`
|
||||
- タイプ:`jira`
|
||||
|
||||
124
apps/docs/content/docs/ja/tools/servicenow.mdx
Normal file
124
apps/docs/content/docs/ja/tools/servicenow.mdx
Normal file
@@ -0,0 +1,124 @@
|
||||
---
|
||||
title: ServiceNow
|
||||
description: ServiceNowレコードの作成、読み取り、更新、削除
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="servicenow"
|
||||
color="#032D42"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[ServiceNow](https://www.servicenow.com/)は、組織全体のITサービス管理(ITSM)、ワークフロー、ビジネスプロセスを効率化し自動化するために設計された強力なクラウドプラットフォームです。ServiceNowを使用すると、広範なAPIを使用してインシデント、リクエスト、タスク、ユーザーなどを管理できます。
|
||||
|
||||
ServiceNowでは、次のことができます。
|
||||
|
||||
- **ITワークフローの自動化**: インシデント、タスク、変更リクエスト、ユーザーなど、任意のServiceNowテーブルのレコードを作成、読み取り、更新、削除します。
|
||||
- **システムの統合**: ServiceNowを他のツールやプロセスと接続して、シームレスな自動化を実現します。
|
||||
- **単一の信頼できる情報源の維持**: すべてのサービスおよび運用データを整理してアクセス可能な状態に保ちます。
|
||||
- **運用効率の向上**: カスタマイズ可能なワークフローと自動化により、手作業を削減し、サービス品質を向上させます。
|
||||
|
||||
Simでは、ServiceNow統合により、エージェントがワークフローの一部としてServiceNowインスタンスと直接やり取りできるようになります。エージェントは、任意のServiceNowテーブルのレコードを作成、読み取り、更新、削除でき、チケットやユーザーデータを活用して高度な自動化と意思決定を行うことができます。この統合により、ワークフロー自動化とIT運用が橋渡しされ、エージェントは手動介入なしでサービスリクエスト、インシデント、ユーザー、資産を管理できるようになります。SimとServiceNowを接続することで、サービス管理タスクを自動化し、応答時間を改善し、組織の重要なサービスデータへの一貫性のある安全なアクセスを確保できます。
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
## 使用方法
|
||||
|
||||
ServiceNowをワークフローに統合します。インシデント、タスク、変更リクエスト、ユーザーなど、任意のServiceNowテーブルのレコードを作成、読み取り、更新、削除します。
|
||||
|
||||
## ツール
|
||||
|
||||
### `servicenow_create_record`
|
||||
|
||||
ServiceNowテーブルに新しいレコードを作成
|
||||
|
||||
#### 入力
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `instanceUrl` | string | はい | ServiceNowインスタンスURL(例: https://instance.service-now.com) |
|
||||
| `username` | string | はい | ServiceNowユーザー名 |
|
||||
| `password` | string | はい | ServiceNowパスワード |
|
||||
| `tableName` | string | はい | テーブル名(例: incident、task、sys_user) |
|
||||
| `fields` | json | はい | レコードに設定するフィールド(JSONオブジェクト) |
|
||||
|
||||
#### 出力
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `record` | json | sys_idおよびその他のフィールドを含む作成されたServiceNowレコード |
|
||||
| `metadata` | json | 操作メタデータ |
|
||||
|
||||
### `servicenow_read_record`
|
||||
|
||||
ServiceNowテーブルからレコードを読み取ります
|
||||
|
||||
#### 入力
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `instanceUrl` | string | はい | ServiceNowインスタンスURL(例: https://instance.service-now.com) |
|
||||
| `username` | string | はい | ServiceNowユーザー名 |
|
||||
| `password` | string | はい | ServiceNowパスワード |
|
||||
| `tableName` | string | はい | テーブル名 |
|
||||
| `sysId` | string | いいえ | 特定のレコードのsys_id |
|
||||
| `number` | string | いいえ | レコード番号(例: INC0010001) |
|
||||
| `query` | string | いいえ | エンコードされたクエリ文字列(例: "active=true^priority=1") |
|
||||
| `limit` | number | いいえ | 返すレコードの最大数 |
|
||||
| `fields` | string | いいえ | 返すフィールドのカンマ区切りリスト |
|
||||
|
||||
#### 出力
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `records` | array | ServiceNowレコードの配列 |
|
||||
| `metadata` | json | 操作メタデータ |
|
||||
|
||||
### `servicenow_update_record`
|
||||
|
||||
ServiceNowテーブル内の既存のレコードを更新
|
||||
|
||||
#### 入力
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `instanceUrl` | string | はい | ServiceNowインスタンスURL(例:https://instance.service-now.com) |
|
||||
| `username` | string | はい | ServiceNowユーザー名 |
|
||||
| `password` | string | はい | ServiceNowパスワード |
|
||||
| `tableName` | string | はい | テーブル名 |
|
||||
| `sysId` | string | はい | 更新するレコードのsys_id |
|
||||
| `fields` | json | はい | 更新するフィールド(JSONオブジェクト) |
|
||||
|
||||
#### 出力
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `record` | json | 更新されたServiceNowレコード |
|
||||
| `metadata` | json | 操作メタデータ |
|
||||
|
||||
### `servicenow_delete_record`
|
||||
|
||||
ServiceNowテーブルからレコードを削除
|
||||
|
||||
#### 入力
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `instanceUrl` | string | はい | ServiceNowインスタンスURL(例:https://instance.service-now.com) |
|
||||
| `username` | string | はい | ServiceNowユーザー名 |
|
||||
| `password` | string | はい | ServiceNowパスワード |
|
||||
| `tableName` | string | はい | テーブル名 |
|
||||
| `sysId` | string | はい | 削除するレコードのsys_id |
|
||||
|
||||
#### 出力
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 削除が成功したかどうか |
|
||||
| `metadata` | json | 操作メタデータ |
|
||||
|
||||
## 注意事項
|
||||
|
||||
- カテゴリ: `tools`
|
||||
- タイプ: `servicenow`
|
||||
@@ -53,7 +53,7 @@ Slackをワークフローに統合します。メッセージの送信、更新
|
||||
|
||||
### `slack_message`
|
||||
|
||||
Slack APIを通じてSlackチャンネルまたはユーザーにメッセージを送信します。Slack mrkdwnフォーマットをサポートしています。
|
||||
Slackチャンネルまたはダイレクトメッセージにメッセージを送信します。Slack mrkdwn形式をサポートしています。
|
||||
|
||||
#### 入力
|
||||
|
||||
@@ -61,7 +61,8 @@ Slack APIを通じてSlackチャンネルまたはユーザーにメッセージ
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authMethod` | string | いいえ | 認証方法:oauthまたはbot_token |
|
||||
| `botToken` | string | いいえ | カスタムボット用のボットトークン |
|
||||
| `channel` | string | はい | 対象のSlackチャンネル(例:#general) |
|
||||
| `channel` | string | いいえ | 対象のSlackチャンネル(例:#general) |
|
||||
| `userId` | string | いいえ | ダイレクトメッセージ用の対象SlackユーザーID(例:U1234567890) |
|
||||
| `text` | string | はい | 送信するメッセージテキスト(Slack mrkdwn形式をサポート) |
|
||||
| `thread_ts` | string | いいえ | 返信するスレッドのタイムスタンプ(スレッド返信を作成) |
|
||||
| `files` | file[] | いいえ | メッセージに添付するファイル |
|
||||
@@ -108,8 +109,9 @@ Slackチャンネルから最新のメッセージを読み取ります。フィ
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authMethod` | string | いいえ | 認証方法:oauthまたはbot_token |
|
||||
| `botToken` | string | いいえ | カスタムボット用のボットトークン |
|
||||
| `channel` | string | はい | メッセージを読み取るSlackチャンネル(例:#general) |
|
||||
| `limit` | number | いいえ | 取得するメッセージ数(デフォルト:10、最大:100) |
|
||||
| `channel` | string | いいえ | メッセージを読み取るSlackチャンネル(例:#general) |
|
||||
| `userId` | string | いいえ | DM会話用のユーザーID(例:U1234567890) |
|
||||
| `limit` | number | いいえ | 取得するメッセージ数(デフォルト:10、最大:15) |
|
||||
| `oldest` | string | いいえ | 時間範囲の開始(タイムスタンプ) |
|
||||
| `latest` | string | いいえ | 時間範囲の終了(タイムスタンプ) |
|
||||
|
||||
|
||||
@@ -42,11 +42,13 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
| `model` | string | はい | 使用するモデル(例:gpt-4o、claude-sonnet-4-5、gemini-2.0-flash) |
|
||||
| `systemPrompt` | string | いいえ | アシスタントの動作を設定するシステムプロンプト |
|
||||
| `context` | string | はい | モデルに送信するユーザーメッセージまたはコンテキスト |
|
||||
| `apiKey` | string | いいえ | プロバイダーのAPIキー(ホストされたモデルの場合、提供されなければプラットフォームキーを使用) |
|
||||
| `apiKey` | string | いいえ | プロバイダーのAPIキー(ホストされたモデルの場合、提供されない場合はプラットフォームキーを使用) |
|
||||
| `temperature` | number | いいえ | レスポンス生成の温度(0-2) |
|
||||
| `maxTokens` | number | いいえ | レスポンスの最大トークン数 |
|
||||
| `azureEndpoint` | string | いいえ | Azure OpenAIエンドポイントURL |
|
||||
| `azureApiVersion` | string | いいえ | Azure OpenAI APIバージョン |
|
||||
| `vertexProject` | string | いいえ | Vertex AI用のGoogle CloudプロジェクトID |
|
||||
| `vertexLocation` | string | いいえ | Vertex AI用のGoogle Cloudロケーション(デフォルトはus-central1) |
|
||||
|
||||
#### 出力
|
||||
|
||||
|
||||
@@ -27,10 +27,12 @@ Simでは、Zoom統合によりエージェントがスケジュール設定と
|
||||
- 任意の会議の詳細や招待状を取得
|
||||
- 自動化から直接既存の会議を更新または削除
|
||||
|
||||
これらの機能により、リモートコラボレーションの効率化、定期的なビデオセッションの自動化、ワークフローの一部として組織のZoom環境を管理することができます。
|
||||
Zoomに接続するには、Zoomブロックをドロップして `Connect` をクリックし、Zoomアカウントで認証します。接続後、Zoomツールを使用してZoomミーティングの作成、一覧表示、更新、削除ができます。いつでも設定 > 統合から `Disconnect` をクリックしてZoomアカウントの接続を解除でき、Zoomアカウントへのアクセスは直ちに取り消されます。
|
||||
|
||||
これらの機能により、リモートコラボレーションの効率化、定期的なビデオセッションの自動化、組織のZoom環境の管理をワークフローの一部として行うことができます。
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
## 使用方法
|
||||
## 使用手順
|
||||
|
||||
Zoomをワークフローに統合します。Zoomミーティングの作成、一覧表示、更新、削除ができます。ミーティングの詳細、招待状、録画、参加者を取得します。クラウド録画をプログラムで管理します。
|
||||
|
||||
@@ -42,11 +44,11 @@ Zoomをワークフローに統合します。Zoomミーティングの作成、
|
||||
|
||||
#### 入力
|
||||
|
||||
| パラメータ | 種類 | 必須 | 説明 |
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `userId` | string | はい | ユーザーIDまたはメールアドレス。認証済みユーザーの場合は「me」を使用。 |
|
||||
| `topic` | string | はい | ミーティングのトピック |
|
||||
| `type` | number | いいえ | ミーティングタイプ: 1=即時、2=予定、3=固定時間なしの定期的、8=固定時間の定期的 |
|
||||
| `type` | number | いいえ | ミーティングタイプ: 1=即時、2=予定済み、3=固定時間なしの定期的、8=固定時間の定期的 |
|
||||
| `startTime` | string | いいえ | ISO 8601形式のミーティング開始時間(例:2025-06-03T10:00:00Z) |
|
||||
| `duration` | number | いいえ | ミーティング時間(分) |
|
||||
| `timezone` | string | いいえ | ミーティングのタイムゾーン(例:America/Los_Angeles) |
|
||||
@@ -56,8 +58,8 @@ Zoomをワークフローに統合します。Zoomミーティングの作成、
|
||||
| `participantVideo` | boolean | いいえ | 参加者のビデオをオンにして開始 |
|
||||
| `joinBeforeHost` | boolean | いいえ | ホスト前の参加者の入室を許可 |
|
||||
| `muteUponEntry` | boolean | いいえ | 入室時に参加者をミュート |
|
||||
| `waitingRoom` | boolean | いいえ | 待機室を有効化 |
|
||||
| `autoRecording` | string | いいえ | 自動録画設定:local、cloud、またはnone |
|
||||
| `waitingRoom` | boolean | いいえ | 待機室を有効にする |
|
||||
| `autoRecording` | string | いいえ | 自動録画設定: local、cloud、またはnone |
|
||||
|
||||
#### 出力
|
||||
|
||||
@@ -67,14 +69,14 @@ Zoomをワークフローに統合します。Zoomミーティングの作成、
|
||||
|
||||
### `zoom_list_meetings`
|
||||
|
||||
Zoomユーザーのすべてのミーティングを一覧表示する
|
||||
Zoomユーザーのすべてのミーティングをリスト表示する
|
||||
|
||||
#### 入力
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `userId` | string | はい | ユーザーIDまたはメールアドレス。認証済みユーザーの場合は「me」を使用。 |
|
||||
| `type` | string | いいえ | ミーティングタイプフィルター: scheduled、live、upcoming、upcoming_meetings、または previous_meetings |
|
||||
| `type` | string | いいえ | ミーティングタイプフィルター: scheduled、live、upcoming、upcoming_meetings、またはprevious_meetings |
|
||||
| `pageSize` | number | いいえ | ページあたりのレコード数(最大300) |
|
||||
| `nextPageToken` | string | いいえ | 次のページの結果を取得するためのページネーショントークン |
|
||||
|
||||
@@ -94,8 +96,8 @@ Zoomユーザーのすべてのミーティングを一覧表示する
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `meetingId` | string | はい | ミーティングID |
|
||||
| `occurrenceId` | string | いいえ | 定期的なミーティングの開催ID |
|
||||
| `showPreviousOccurrences` | boolean | いいえ | 定期的なミーティングの過去の開催を表示 |
|
||||
| `occurrenceId` | string | いいえ | 定期的なミーティングの発生ID |
|
||||
| `showPreviousOccurrences` | boolean | いいえ | 定期的なミーティングの過去の発生を表示 |
|
||||
|
||||
#### 出力
|
||||
|
||||
@@ -103,17 +105,17 @@ Zoomユーザーのすべてのミーティングを一覧表示する
|
||||
| --------- | ---- | ----------- |
|
||||
| `meeting` | object | ミーティングの詳細 |
|
||||
|
||||
### `zoom_update_meeting`
|
||||
特定のZoomミーティングの詳細を取得する
|
||||
|
||||
既存のZoomミーティングを更新する
|
||||
|
||||
#### 入力
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| パラメータ | 種類 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `meetingId` | string | はい | 更新するミーティングID |
|
||||
| `topic` | string | いいえ | ミーティングのトピック |
|
||||
| `type` | number | いいえ | ミーティングタイプ: 1=インスタント、2=予定済み、3=定期的(固定時間なし)、8=定期的(固定時間) |
|
||||
| `type` | number | いいえ | ミーティングタイプ: 1=即時、2=予定、3=固定時間なしの定期的、8=固定時間の定期的 |
|
||||
| `startTime` | string | いいえ | ISO 8601形式のミーティング開始時間(例:2025-06-03T10:00:00Z) |
|
||||
| `duration` | number | いいえ | ミーティング時間(分) |
|
||||
| `timezone` | string | いいえ | ミーティングのタイムゾーン(例:America/Los_Angeles) |
|
||||
@@ -121,10 +123,10 @@ Zoomユーザーのすべてのミーティングを一覧表示する
|
||||
| `agenda` | string | いいえ | ミーティングの議題 |
|
||||
| `hostVideo` | boolean | いいえ | ホストのビデオをオンにして開始 |
|
||||
| `participantVideo` | boolean | いいえ | 参加者のビデオをオンにして開始 |
|
||||
| `joinBeforeHost` | boolean | いいえ | ホストより前の参加者の入室を許可 |
|
||||
| `joinBeforeHost` | boolean | いいえ | ホスト前の参加者の入室を許可 |
|
||||
| `muteUponEntry` | boolean | いいえ | 入室時に参加者をミュート |
|
||||
| `waitingRoom` | boolean | いいえ | 待機室を有効にする |
|
||||
| `autoRecording` | string | いいえ | 自動録画設定:ローカル、クラウド、または無し |
|
||||
| `waitingRoom` | boolean | いいえ | 待機室を有効化 |
|
||||
| `autoRecording` | string | いいえ | 自動録画設定:local、cloud、またはnone |
|
||||
|
||||
#### 出力
|
||||
|
||||
@@ -132,17 +134,17 @@ Zoomユーザーのすべてのミーティングを一覧表示する
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | ミーティングが正常に更新されたかどうか |
|
||||
|
||||
### `zoom_delete_meeting`
|
||||
Zoomミーティングを削除またはキャンセルする
|
||||
|
||||
Zoomミーティングを削除またはキャンセルする
|
||||
|
||||
#### 入力
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| パラメータ | 種類 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `meetingId` | string | はい | 削除するミーティングID |
|
||||
| `occurrenceId` | string | いいえ | 定期的なミーティングの特定の回を削除するための発生ID |
|
||||
| `scheduleForReminder` | boolean | いいえ | 登録者にキャンセルリマインダーメールを送信 |
|
||||
| `occurrenceId` | string | いいえ | 定期的なミーティングの特定の回を削除するための回数ID |
|
||||
| `scheduleForReminder` | boolean | いいえ | 登録者にキャンセルのリマインダーメールを送信 |
|
||||
| `cancelMeetingReminder` | boolean | いいえ | 登録者と代替ホストにキャンセルメールを送信 |
|
||||
|
||||
#### 出力
|
||||
@@ -169,18 +171,18 @@ Zoomミーティングの招待テキストを取得する
|
||||
|
||||
### `zoom_list_recordings`
|
||||
|
||||
Zoomユーザーのすべてのクラウド録画をリスト表示する
|
||||
Zoomユーザーのすべてのクラウド録画を一覧表示する
|
||||
|
||||
#### 入力
|
||||
|
||||
| パラメータ | 型 | 必須 | 説明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `userId` | string | はい | ユーザーIDまたはメールアドレス。認証済みユーザーの場合は「me」を使用。 |
|
||||
| `from` | string | いいえ | 開始日(yyyy-mm-dd形式)\(過去6ヶ月以内\) |
|
||||
| `from` | string | いいえ | 開始日(yyyy-mm-dd形式)(過去6ヶ月以内) |
|
||||
| `to` | string | いいえ | 終了日(yyyy-mm-dd形式) |
|
||||
| `pageSize` | number | いいえ | ページあたりのレコード数(最大300) |
|
||||
| `pageSize` | number | いいえ | 1ページあたりのレコード数(最大300) |
|
||||
| `nextPageToken` | string | いいえ | 次のページの結果を取得するためのページネーショントークン |
|
||||
| `trash` | boolean | いいえ | ゴミ箱から録画をリスト表示するにはtrueに設定 |
|
||||
| `trash` | boolean | いいえ | ゴミ箱から録画を一覧表示するにはtrueに設定 |
|
||||
|
||||
#### 出力
|
||||
|
||||
@@ -189,9 +191,9 @@ Zoomユーザーのすべてのクラウド録画をリスト表示する
|
||||
| `recordings` | array | 録画のリスト |
|
||||
| `pageInfo` | object | ページネーション情報 |
|
||||
|
||||
### `zoom_get_meeting_recordings`
|
||||
特定のZoomミーティングのすべての録画を取得する
|
||||
|
||||
特定のZoomミーティングの全ての録画を取得する
|
||||
特定のZoomミーティングのすべての録画を取得する
|
||||
|
||||
#### 入力
|
||||
|
||||
@@ -207,7 +209,7 @@ Zoomユーザーのすべてのクラウド録画をリスト表示する
|
||||
| --------- | ---- | ----------- |
|
||||
| `recording` | object | すべてのファイルを含むミーティング録画 |
|
||||
|
||||
### `zoom_delete_recording`
|
||||
Zoomミーティングのクラウド録画を削除する
|
||||
|
||||
Zoomミーティングのクラウド録画を削除する
|
||||
|
||||
@@ -225,7 +227,7 @@ Zoomミーティングのクラウド録画を削除する
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 録画が正常に削除されたかどうか |
|
||||
|
||||
### `zoom_list_past_participants`
|
||||
過去のZoomミーティングの参加者を一覧表示する
|
||||
|
||||
過去のZoomミーティングの参加者を一覧表示する
|
||||
|
||||
@@ -235,16 +237,16 @@ Zoomミーティングのクラウド録画を削除する
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `meetingId` | string | はい | 過去のミーティングIDまたはUUID |
|
||||
| `pageSize` | number | いいえ | ページあたりのレコード数(最大300) |
|
||||
| `nextPageToken` | string | いいえ | 結果の次のページを取得するためのページネーショントークン |
|
||||
| `nextPageToken` | string | いいえ | 結果の次のページを取得するための改ページトークン |
|
||||
|
||||
#### 出力
|
||||
|
||||
| パラメータ | 型 | 説明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `participants` | array | 会議参加者のリスト |
|
||||
| `participants` | array | ミーティング参加者のリスト |
|
||||
| `pageInfo` | object | ページネーション情報 |
|
||||
|
||||
## 注意事項
|
||||
|
||||
- カテゴリー: `tools`
|
||||
- カテゴリ: `tools`
|
||||
- タイプ: `zoom`
|
||||
|
||||
@@ -110,26 +110,24 @@ import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
|
||||
|
||||
```json
|
||||
{
|
||||
"content": "Original content passed through",
|
||||
"conditionResult": true,
|
||||
"selectedPath": {
|
||||
"blockId": "2acd9007-27e8-4510-a487-73d3b825e7c1",
|
||||
"blockType": "agent",
|
||||
"blockTitle": "Follow-up Agent"
|
||||
},
|
||||
"selectedConditionId": "condition-1"
|
||||
"selectedOption": "condition-1"
|
||||
}
|
||||
```
|
||||
|
||||
### 条件模块输出字段
|
||||
|
||||
- **content**:传递的原始内容
|
||||
- **conditionResult**:条件评估的布尔结果
|
||||
- **selectedPath**:关于选定路径的信息
|
||||
- **blockId**:选定路径中下一个模块的 ID
|
||||
- **blockType**:下一个模块的类型
|
||||
- **blockTitle**:下一个模块的标题
|
||||
- **selectedConditionId**:选定条件的 ID
|
||||
- **conditionResult**:条件判断的布尔值结果
|
||||
- **selectedPath**:所选路径的信息
|
||||
- **blockId**:所选路径下一个区块的 ID
|
||||
- **blockType**:下一个区块的类型
|
||||
- **blockTitle**:下一个区块的标题
|
||||
- **selectedOption**:所选条件的 ID
|
||||
|
||||
</Tab>
|
||||
<Tab>
|
||||
|
||||
@@ -48,8 +48,13 @@ Jira 的主要功能包括:
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | 布尔值 | 操作成功状态 |
|
||||
| `output` | 对象 | 包含问题键、摘要、描述、创建和更新时间戳的 Jira 问题详细信息 |
|
||||
| `ts` | 字符串 | 操作的时间戳 |
|
||||
| `issueKey` | 字符串 | 问题键 \(例如:PROJ-123\) |
|
||||
| `summary` | 字符串 | 问题摘要 |
|
||||
| `description` | JSON | 问题描述内容 |
|
||||
| `created` | 字符串 | 问题创建的时间戳 |
|
||||
| `updated` | 字符串 | 问题最后更新的时间戳 |
|
||||
| `issue` | JSON | 包含所有字段的完整问题对象 |
|
||||
|
||||
### `jira_update`
|
||||
|
||||
@@ -73,8 +78,9 @@ Jira 的主要功能包括:
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | 布尔值 | 操作成功状态 |
|
||||
| `output` | 对象 | 更新的 Jira 问题详情,包括时间戳、问题键、摘要和成功状态 |
|
||||
| `ts` | 字符串 | 操作的时间戳 |
|
||||
| `issueKey` | 字符串 | 更新后的问题键 \(例如:PROJ-123\) |
|
||||
| `summary` | 字符串 | 更新后的问题摘要 |
|
||||
|
||||
### `jira_write`
|
||||
|
||||
@@ -85,20 +91,29 @@ Jira 的主要功能包括:
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | 字符串 | 是 | 您的 Jira 域名 \(例如:yourcompany.atlassian.net\) |
|
||||
| `projectId` | 字符串 | 是 | 问题的项目 ID |
|
||||
| `summary` | 字符串 | 是 | 问题的摘要 |
|
||||
| `description` | 字符串 | 否 | 问题的描述 |
|
||||
| `priority` | 字符串 | 否 | 问题的优先级 |
|
||||
| `assignee` | 字符串 | 否 | 问题的负责人 |
|
||||
| `cloudId` | 字符串 | 否 | 实例的 Jira 云 ID。如果未提供,将使用域名获取。 |
|
||||
| `issueType` | 字符串 | 是 | 要创建的问题类型 \(例如:任务、故事\) |
|
||||
| `projectId` | 字符串 | 是 | 问题所属项目 ID |
|
||||
| `summary` | 字符串 | 是 | 问题摘要 |
|
||||
| `description` | 字符串 | 否 | 问题描述 |
|
||||
| `priority` | 字符串 | 否 | 问题优先级 ID 或名称 \(例如:“10000”或“High”\) |
|
||||
| `assignee` | 字符串 | 否 | 问题负责人账户 ID |
|
||||
| `cloudId` | 字符串 | 否 | 实例的 Jira Cloud ID。如果未提供,将使用域名获取。 |
|
||||
| `issueType` | 字符串 | 是 | 要创建的问题类型 \(例如:Task、Story\) |
|
||||
| `labels` | 数组 | 否 | 问题标签 \(标签名称数组\) |
|
||||
| `duedate` | 字符串 | 否 | 问题截止日期 \(格式:YYYY-MM-DD\) |
|
||||
| `reporter` | 字符串 | 否 | 问题报告人账户 ID |
|
||||
| `environment` | 字符串 | 否 | 问题环境信息 |
|
||||
| `customFieldId` | 字符串 | 否 | 自定义字段 ID \(例如:customfield_10001\) |
|
||||
| `customFieldValue` | 字符串 | 否 | 自定义字段的值 |
|
||||
|
||||
#### 输出
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | 布尔值 | 操作成功状态 |
|
||||
| `output` | 对象 | 创建的 Jira 问题详情,包括时间戳、问题键、摘要、成功状态和 URL |
|
||||
| `ts` | 字符串 | 操作的时间戳 |
|
||||
| `issueKey` | 字符串 | 创建的问题键 \(例如:PROJ-123\) |
|
||||
| `summary` | 字符串 | 问题摘要 |
|
||||
| `url` | 字符串 | 创建的问题的 URL |
|
||||
| `assigneeId` | 字符串 | 已分配用户的账户 ID(如已分配) |
|
||||
|
||||
### `jira_bulk_read`
|
||||
|
||||
@@ -116,8 +131,7 @@ Jira 的主要功能包括:
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 操作成功状态 |
|
||||
| `output` | array | 包含 Jira 问题的数组,包括摘要、描述、创建和更新的时间戳 |
|
||||
| `issues` | 数组 | 包含时间戳、摘要、描述、创建和更新时间戳的 Jira 问题数组 |
|
||||
|
||||
### `jira_delete_issue`
|
||||
|
||||
@@ -136,8 +150,8 @@ Jira 的主要功能包括:
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | 布尔值 | 操作成功状态 |
|
||||
| `output` | 对象 | 删除的问题详情,包括时间戳、问题键和成功状态 |
|
||||
| `ts` | 字符串 | 操作的时间戳 |
|
||||
| `issueKey` | 字符串 | 删除的问题键 |
|
||||
|
||||
### `jira_assign_issue`
|
||||
|
||||
@@ -156,8 +170,9 @@ Jira 的主要功能包括:
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | 布尔值 | 操作成功状态 |
|
||||
| `output` | 对象 | 分配详情,包括时间戳、问题键、分配人 ID 和成功状态 |
|
||||
| `ts` | 字符串 | 操作的时间戳 |
|
||||
| `issueKey` | 字符串 | 被分配的任务键 |
|
||||
| `assigneeId` | 字符串 | 分配者的账户 ID |
|
||||
|
||||
### `jira_transition_issue`
|
||||
|
||||
@@ -177,8 +192,9 @@ Jira 的主要功能包括:
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | 布尔值 | 操作成功状态 |
|
||||
| `output` | 对象 | 转换详情,包括时间戳、问题键、转换 ID 和成功状态 |
|
||||
| `ts` | string | 操作的时间戳 |
|
||||
| `issueKey` | string | 已转换的问题键 |
|
||||
| `transitionId` | string | 应用的转换 ID |
|
||||
|
||||
### `jira_search_issues`
|
||||
|
||||
@@ -199,8 +215,11 @@ Jira 的主要功能包括:
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | 布尔值 | 操作成功状态 |
|
||||
| `output` | 对象 | 搜索结果,包括时间戳、总数、分页详情和匹配问题的数组 |
|
||||
| `ts` | string | 操作的时间戳 |
|
||||
| `total` | number | 匹配问题的总数 |
|
||||
| `startAt` | number | 分页起始索引 |
|
||||
| `maxResults` | number | 每页的最大结果数 |
|
||||
| `issues` | array | 包含键、摘要、状态、负责人、创建时间和更新时间的匹配问题数组 |
|
||||
|
||||
### `jira_add_comment`
|
||||
|
||||
@@ -219,8 +238,10 @@ Jira 的主要功能包括:
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | 布尔值 | 操作成功状态 |
|
||||
| `output` | 对象 | 评论详情,包括时间戳、问题键、评论 ID、正文和成功状态 |
|
||||
| `ts` | string | 操作的时间戳 |
|
||||
| `issueKey` | string | 添加评论的问题键 |
|
||||
| `commentId` | string | 创建的评论 ID |
|
||||
| `body` | string | 评论的文本内容 |
|
||||
|
||||
### `jira_get_comments`
|
||||
|
||||
@@ -240,8 +261,10 @@ Jira 的主要功能包括:
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | 布尔值 | 操作成功状态 |
|
||||
| `output` | 对象 | 评论数据,包括时间戳、问题键、总数和评论数组 |
|
||||
| `ts` | string | 操作的时间戳 |
|
||||
| `issueKey` | string | 问题键 |
|
||||
| `total` | number | 评论的总数 |
|
||||
| `comments` | array | 包含 ID、作者、正文、创建时间和更新时间的评论数组 |
|
||||
|
||||
### `jira_update_comment`
|
||||
|
||||
@@ -261,8 +284,10 @@ Jira 的主要功能包括:
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | 布尔值 | 操作成功状态 |
|
||||
| `output` | 对象 | 更新的评论详情,包括时间戳、问题键、评论 ID、正文文本和成功状态 |
|
||||
| `ts` | string | 操作的时间戳 |
|
||||
| `issueKey` | string | 问题键 |
|
||||
| `commentId` | string | 更新的评论 ID |
|
||||
| `body` | string | 更新的评论文本 |
|
||||
|
||||
### `jira_delete_comment`
|
||||
|
||||
@@ -281,8 +306,9 @@ Jira 的主要功能包括:
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | 布尔值 | 操作成功状态 |
|
||||
| `output` | 对象 | 删除详情,包括时间戳、问题键、评论 ID 和成功状态 |
|
||||
| `ts` | string | 操作的时间戳 |
|
||||
| `issueKey` | string | 问题键 |
|
||||
| `commentId` | string | 已删除的评论 ID |
|
||||
|
||||
### `jira_get_attachments`
|
||||
|
||||
@@ -300,8 +326,9 @@ Jira 的主要功能包括:
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | 布尔值 | 操作成功状态 |
|
||||
| `output` | 对象 | 附件数据,包括时间戳、问题键和附件数组 |
|
||||
| `ts` | string | 操作的时间戳 |
|
||||
| `issueKey` | string | 问题键 |
|
||||
| `attachments` | array | 附件数组,包括 id、文件名、大小、mimeType、创建时间、作者 |
|
||||
|
||||
### `jira_delete_attachment`
|
||||
|
||||
@@ -319,8 +346,8 @@ Jira 的主要功能包括:
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | 布尔值 | 操作成功状态 |
|
||||
| `output` | 对象 | 删除详情,包括时间戳、附件 ID 和成功状态 |
|
||||
| `ts` | string | 操作的时间戳 |
|
||||
| `attachmentId` | string | 已删除的附件 ID |
|
||||
|
||||
### `jira_add_worklog`
|
||||
|
||||
@@ -341,8 +368,10 @@ Jira 的主要功能包括:
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | 布尔值 | 操作成功状态 |
|
||||
| `output` | 对象 | 工作日志详情,包括时间戳、问题键、工作日志 ID、花费的时间(以秒为单位)和成功状态 |
|
||||
| `ts` | string | 操作的时间戳 |
|
||||
| `issueKey` | string | 添加工作日志的相关问题键 |
|
||||
| `worklogId` | string | 创建的工作日志 ID |
|
||||
| `timeSpentSeconds` | number | 花费的时间(以秒为单位) |
|
||||
|
||||
### `jira_get_worklogs`
|
||||
|
||||
@@ -362,8 +391,10 @@ Jira 的主要功能包括:
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | 布尔值 | 操作成功状态 |
|
||||
| `output` | 对象 | 包含时间戳、问题键、总数和工作日志数组的工作日志数据 |
|
||||
| `ts` | string | 操作的时间戳 |
|
||||
| `issueKey` | string | 问题键 |
|
||||
| `total` | number | 工作日志的总数 |
|
||||
| `worklogs` | array | 工作日志数组,包括 id、作者、timeSpentSeconds、timeSpent、评论、创建时间、更新时间、开始时间 |
|
||||
|
||||
### `jira_update_worklog`
|
||||
|
||||
@@ -385,8 +416,9 @@ Jira 的主要功能包括:
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | 布尔值 | 操作成功状态 |
|
||||
| `output` | 对象 | 包含时间戳、问题键、工作日志 ID 和成功状态的工作日志更新详情 |
|
||||
| `ts` | string | 操作的时间戳 |
|
||||
| `issueKey` | string | 问题键 |
|
||||
| `worklogId` | string | 更新的工作日志 ID |
|
||||
|
||||
### `jira_delete_worklog`
|
||||
|
||||
@@ -405,8 +437,9 @@ Jira 的主要功能包括:
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | 布尔值 | 操作成功状态 |
|
||||
| `output` | 对象 | 删除详情,包括时间戳、问题键、工作日志 ID 和成功状态 |
|
||||
| `ts` | string | 操作的时间戳 |
|
||||
| `issueKey` | string | 问题键 |
|
||||
| `worklogId` | string | 已删除的工作日志 ID |
|
||||
|
||||
### `jira_create_issue_link`
|
||||
|
||||
@@ -427,8 +460,11 @@ Jira 的主要功能包括:
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | 布尔值 | 操作成功状态 |
|
||||
| `output` | 对象 | 问题链接详情,包括时间戳、内部问题键、外部问题键、链接类型和成功状态 |
|
||||
| `ts` | string | 操作的时间戳 |
|
||||
| `inwardIssue` | string | 内部问题键 |
|
||||
| `outwardIssue` | string | 外部问题键 |
|
||||
| `linkType` | string | 问题链接的类型 |
|
||||
| `linkId` | string | 创建的链接 ID |
|
||||
|
||||
### `jira_delete_issue_link`
|
||||
|
||||
@@ -446,8 +482,8 @@ Jira 的主要功能包括:
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | 布尔值 | 操作成功状态 |
|
||||
| `output` | 对象 | 删除详情,包括时间戳、链接 ID 和成功状态 |
|
||||
| `ts` | string | 操作的时间戳 |
|
||||
| `linkId` | string | 已删除的链接 ID |
|
||||
|
||||
### `jira_add_watcher`
|
||||
|
||||
@@ -466,8 +502,9 @@ Jira 的主要功能包括:
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | 布尔值 | 操作成功状态 |
|
||||
| `output` | 对象 | 观察者详情,包括时间戳、问题键、观察者账户 ID 和成功状态 |
|
||||
| `ts` | string | 操作的时间戳 |
|
||||
| `issueKey` | string | 问题键 |
|
||||
| `watcherAccountId` | string | 添加的观察者账户 ID |
|
||||
|
||||
### `jira_remove_watcher`
|
||||
|
||||
@@ -486,10 +523,35 @@ Jira 的主要功能包括:
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | 布尔值 | 操作成功状态 |
|
||||
| `output` | 对象 | 移除详情,包括时间戳、问题键、观察者账户 ID 和成功状态 |
|
||||
| `ts` | string | 操作的时间戳 |
|
||||
| `issueKey` | string | 问题键 |
|
||||
| `watcherAccountId` | string | 移除的观察者账户 ID |
|
||||
|
||||
## 注意事项
|
||||
### `jira_get_users`
|
||||
|
||||
- 类别: `tools`
|
||||
- 类型: `jira`
|
||||
获取 Jira 用户。如果提供了账户 ID,则返回单个用户,否则返回所有用户的列表。
|
||||
|
||||
#### 输入
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | 字符串 | 是 | 您的 Jira 域名 \(例如:yourcompany.atlassian.net\) |
|
||||
| `accountId` | 字符串 | 否 | 可选账户 ID,用于获取特定用户。如果未提供,则返回所有用户。 |
|
||||
| `startAt` | 数字 | 否 | 要返回的第一个用户的索引 \(用于分页,默认值:0\) |
|
||||
| `maxResults` | 数字 | 否 | 要返回的最大用户数 \(默认值:50\) |
|
||||
| `cloudId` | 字符串 | 否 | 实例的 Jira Cloud ID。如果未提供,将使用域名获取。 |
|
||||
|
||||
#### 输出
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | 字符串 | 操作的时间戳 |
|
||||
| `users` | json | 用户数组,包含 accountId、displayName、emailAddress、active 状态和 avatarUrls |
|
||||
| `total` | 数字 | 返回的用户总数 |
|
||||
| `startAt` | 数字 | 分页起始索引 |
|
||||
| `maxResults` | 数字 | 每页最大结果数 |
|
||||
|
||||
## 备注
|
||||
|
||||
- 分类:`tools`
|
||||
- 类型:`jira`
|
||||
|
||||
124
apps/docs/content/docs/zh/tools/servicenow.mdx
Normal file
124
apps/docs/content/docs/zh/tools/servicenow.mdx
Normal file
@@ -0,0 +1,124 @@
|
||||
---
|
||||
title: ServiceNow
|
||||
description: 创建、读取、更新和删除 ServiceNow 记录
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="servicenow"
|
||||
color="#032D42"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[ServiceNow](https://www.servicenow.com/) 是一款强大的云平台,旨在简化和自动化 IT 服务管理(ITSM)、工作流以及企业各类业务流程。ServiceNow 让您能够通过其强大的 API 管理事件、请求、任务、用户等多种内容。
|
||||
|
||||
使用 ServiceNow,您可以:
|
||||
|
||||
- **自动化 IT 工作流**:在任意 ServiceNow 表中创建、读取、更新和删除记录,如事件、任务、变更请求和用户等。
|
||||
- **集成系统**:将 ServiceNow 与您的其他工具和流程连接,实现无缝自动化。
|
||||
- **维护单一数据源**:让所有服务和运营数据井然有序,便于访问。
|
||||
- **提升运营效率**:通过可定制的工作流和自动化,减少手动操作,提高服务质量。
|
||||
|
||||
在 Sim 中,ServiceNow 集成让您的代理能够在工作流中直接与 ServiceNow 实例交互。代理可以在任意 ServiceNow 表中创建、读取、更新或删除记录,并利用工单或用户数据实现复杂的自动化和决策。这一集成将您的工作流自动化与 IT 运维无缝衔接,使代理能够自动化管理服务请求、事件、用户和资产,无需人工干预。通过将 Sim 与 ServiceNow 连接,您可以自动化服务管理任务、提升响应速度,并确保对组织关键服务数据的持续、安全访问。
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
## 使用说明
|
||||
|
||||
将 ServiceNow 集成到您的工作流中。在任意 ServiceNow 表(包括事件、任务、变更请求、用户等)中创建、读取、更新和删除记录。
|
||||
|
||||
## 工具
|
||||
|
||||
### `servicenow_create_record`
|
||||
|
||||
在 ServiceNow 表中创建新记录
|
||||
|
||||
#### 输入
|
||||
|
||||
| 参数 | 类型 | 是否必填 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `instanceUrl` | string | 是 | ServiceNow 实例 URL(例如:https://instance.service-now.com) |
|
||||
| `username` | string | 是 | ServiceNow 用户名 |
|
||||
| `password` | string | 是 | ServiceNow 密码 |
|
||||
| `tableName` | string | 是 | 表名(例如:incident、task、sys_user) |
|
||||
| `fields` | json | 是 | 记录中要设置的字段(JSON 对象) |
|
||||
|
||||
#### 输出
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `record` | json | 创建的 ServiceNow 记录,包含 sys_id 及其他字段 |
|
||||
| `metadata` | json | 操作元数据 |
|
||||
|
||||
### `servicenow_read_record`
|
||||
|
||||
从 ServiceNow 表中读取记录
|
||||
|
||||
#### 输入
|
||||
|
||||
| 参数 | 类型 | 是否必填 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `instanceUrl` | string | 是 | ServiceNow 实例 URL(例如:https://instance.service-now.com) |
|
||||
| `username` | string | 是 | ServiceNow 用户名 |
|
||||
| `password` | string | 是 | ServiceNow 密码 |
|
||||
| `tableName` | string | 是 | 表名 |
|
||||
| `sysId` | string | 否 | 指定记录 sys_id |
|
||||
| `number` | string | 否 | 记录编号(例如:INC0010001) |
|
||||
| `query` | string | 否 | 编码查询字符串(例如:"active=true^priority=1") |
|
||||
| `limit` | number | 否 | 返回的最大记录数 |
|
||||
| `fields` | string | 否 | 要返回的字段列表(以逗号分隔) |
|
||||
|
||||
#### 输出
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `records` | array | ServiceNow 记录数组 |
|
||||
| `metadata` | json | 操作元数据 |
|
||||
|
||||
### `servicenow_update_record`
|
||||
|
||||
更新 ServiceNow 表中的现有记录
|
||||
|
||||
#### 输入
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `instanceUrl` | string | 是 | ServiceNow 实例 URL(例如:https://instance.service-now.com) |
|
||||
| `username` | string | 是 | ServiceNow 用户名 |
|
||||
| `password` | string | 是 | ServiceNow 密码 |
|
||||
| `tableName` | string | 是 | 表名 |
|
||||
| `sysId` | string | 是 | 要更新的记录 sys_id |
|
||||
| `fields` | json | 是 | 要更新的字段(JSON 对象) |
|
||||
|
||||
#### 输出
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `record` | json | 已更新的 ServiceNow 记录 |
|
||||
| `metadata` | json | 操作元数据 |
|
||||
|
||||
### `servicenow_delete_record`
|
||||
|
||||
从 ServiceNow 表中删除记录
|
||||
|
||||
#### 输入
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `instanceUrl` | string | 是 | ServiceNow 实例 URL(例如:https://instance.service-now.com) |
|
||||
| `username` | string | 是 | ServiceNow 用户名 |
|
||||
| `password` | string | 是 | ServiceNow 密码 |
|
||||
| `tableName` | string | 是 | 表名 |
|
||||
| `sysId` | string | 是 | 要删除的记录 sys_id |
|
||||
|
||||
#### 输出
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 删除是否成功 |
|
||||
| `metadata` | json | 操作元数据 |
|
||||
|
||||
## 备注
|
||||
|
||||
- 分类:`tools`
|
||||
- 类型:`servicenow`
|
||||
@@ -52,7 +52,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
### `slack_message`
|
||||
|
||||
通过 Slack API 向 Slack 频道或用户发送消息。支持 Slack mrkdwn 格式化。
|
||||
向 Slack 频道或直接消息发送消息。支持 Slack mrkdwn 格式。
|
||||
|
||||
#### 输入
|
||||
|
||||
@@ -60,10 +60,11 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authMethod` | string | 否 | 认证方法:oauth 或 bot_token |
|
||||
| `botToken` | string | 否 | 自定义 Bot 的令牌 |
|
||||
| `channel` | string | 是 | 目标 Slack 频道(例如,#general) |
|
||||
| `channel` | string | 否 | 目标 Slack 频道(例如,#general) |
|
||||
| `userId` | string | 否 | 目标 Slack 用户 ID,用于直接消息(例如,U1234567890) |
|
||||
| `text` | string | 是 | 要发送的消息文本(支持 Slack mrkdwn 格式) |
|
||||
| `thread_ts` | string | 否 | 要回复的线程时间戳(创建线程回复) |
|
||||
| `files` | file[] | 否 | 要附加到消息的文件 |
|
||||
| `thread_ts` | string | 否 | 回复的线程时间戳(创建线程回复) |
|
||||
| `files` | file[] | 否 | 附加到消息的文件 |
|
||||
|
||||
#### 输出
|
||||
|
||||
@@ -103,14 +104,15 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
#### 输入
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| 参数 | 类型 | 是否必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `authMethod` | string | 否 | 认证方法:oauth 或 bot_token |
|
||||
| `botToken` | string | 否 | 自定义 Bot 的 Bot token |
|
||||
| `channel` | string | 是 | 要读取消息的 Slack 频道(例如:#general) |
|
||||
| `limit` | number | 否 | 要检索的消息数量(默认:10,最大:100) |
|
||||
| `oldest` | string | 否 | 时间范围的开始(时间戳) |
|
||||
| `latest` | string | 否 | 时间范围的结束(时间戳) |
|
||||
| `botToken` | string | 否 | 自定义 Bot 的令牌 |
|
||||
| `channel` | string | 否 | 要读取消息的 Slack 频道(例如,#general) |
|
||||
| `userId` | string | 否 | DM 会话的用户 ID(例如,U1234567890) |
|
||||
| `limit` | number | 否 | 要检索的消息数量(默认:10,最大:15) |
|
||||
| `oldest` | string | 否 | 时间范围起始(时间戳) |
|
||||
| `latest` | string | 否 | 时间范围结束(时间戳) |
|
||||
|
||||
#### 输出
|
||||
|
||||
|
||||
@@ -37,16 +37,18 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
#### 输入
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `model` | string | 是 | 要使用的模型 \(例如,gpt-4o、claude-sonnet-4-5、gemini-2.0-flash\) |
|
||||
| `systemPrompt` | string | 否 | 设置助手行为的系统提示 |
|
||||
| `context` | string | 是 | 要发送给模型的用户消息或上下文 |
|
||||
| `apiKey` | string | 否 | 提供商的 API 密钥 \(如果未为托管模型提供,则使用平台密钥\) |
|
||||
| `temperature` | number | 否 | 响应生成的温度 \(0-2\) |
|
||||
| `maxTokens` | number | 否 | 响应的最大令牌数 |
|
||||
| `azureEndpoint` | string | 否 | Azure OpenAI 端点 URL |
|
||||
| `model` | string | 是 | 要使用的模型(例如 gpt-4o、claude-sonnet-4-5、gemini-2.0-flash) |
|
||||
| `systemPrompt` | string | 否 | 设置助手行为的 system prompt |
|
||||
| `context` | string | 是 | 发送给模型的用户消息或上下文 |
|
||||
| `apiKey` | string | 否 | 提供方的 API key(如未提供,托管模型将使用平台密钥) |
|
||||
| `temperature` | number | 否 | 响应生成的 temperature(0-2) |
|
||||
| `maxTokens` | number | 否 | 响应中的最大 tokens 数 |
|
||||
| `azureEndpoint` | string | 否 | Azure OpenAI endpoint URL |
|
||||
| `azureApiVersion` | string | 否 | Azure OpenAI API 版本 |
|
||||
| `vertexProject` | string | 否 | Vertex AI 的 Google Cloud 项目 ID |
|
||||
| `vertexLocation` | string | 否 | Vertex AI 的 Google Cloud 区域(默认为 us-central1) |
|
||||
|
||||
#### 输出
|
||||
|
||||
|
||||
@@ -27,12 +27,14 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
- 检索任何会议的详情或邀请
|
||||
- 直接从您的自动化中更新或删除现有会议
|
||||
|
||||
这些功能使您能够简化远程协作、自动化定期视频会议,并将您的组织 Zoom 环境管理整合到工作流程中。
|
||||
要连接到 Zoom,请拖放 Zoom 模块并点击 `Connect`,使用您的 Zoom 账户进行认证。连接后,您可以使用 Zoom 工具创建、列出、更新和删除 Zoom 会议。您可以随时通过点击“设置 > 集成”中的 `Disconnect` 断开您的 Zoom 账户连接,您的 Zoom 账户访问权限将立即被撤销。
|
||||
|
||||
这些功能使您能够简化远程协作、自动化定期视频会议,并在工作流中管理您的组织的 Zoom 环境。
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
## 使用说明
|
||||
|
||||
将 Zoom 集成到工作流程中。创建、列出、更新和删除 Zoom 会议。获取会议详情、邀请、录制内容和参与者信息。以编程方式管理云录制内容。
|
||||
将 Zoom 集成到工作流中。创建、列出、更新和删除 Zoom 会议。获取会议详情、邀请、录制和参与者信息。以编程方式管理云录制。
|
||||
|
||||
## 工具
|
||||
|
||||
@@ -46,18 +48,18 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `userId` | string | 是 | 用户 ID 或电子邮件地址。使用 "me" 表示已认证用户。 |
|
||||
| `topic` | string | 是 | 会议主题 |
|
||||
| `type` | number | 否 | 会议类型:1=即时,2=计划,3=无固定时间的定期会议,8=有固定时间的定期会议 |
|
||||
| `startTime` | string | 否 | ISO 8601 格式的会议开始时间 \(例如:2025-06-03T10:00:00Z\) |
|
||||
| `type` | number | 否 | 会议类型:1=即时会议,2=预定会议,3=无固定时间的循环会议,8=有固定时间的循环会议 |
|
||||
| `startTime` | string | 否 | 会议开始时间,采用 ISO 8601 格式 \(例如:2025-06-03T10:00:00Z\) |
|
||||
| `duration` | number | 否 | 会议时长(分钟) |
|
||||
| `timezone` | string | 否 | 会议的时区 \(例如:America/Los_Angeles\) |
|
||||
| `password` | string | 否 | 会议密码 |
|
||||
| `agenda` | string | 否 | 会议议程 |
|
||||
| `hostVideo` | boolean | 否 | 主持人视频开启时开始 |
|
||||
| `participantVideo` | boolean | 否 | 参与者视频开启时开始 |
|
||||
| `joinBeforeHost` | boolean | 否 | 允许参与者在主持人之前加入 |
|
||||
| `hostVideo` | boolean | 否 | 主持人视频开启时开始会议 |
|
||||
| `participantVideo` | boolean | 否 | 参与者视频开启时开始会议 |
|
||||
| `joinBeforeHost` | boolean | 否 | 允许参与者在主持人之前加入会议 |
|
||||
| `muteUponEntry` | boolean | 否 | 参与者进入时静音 |
|
||||
| `waitingRoom` | boolean | 否 | 启用等候室 |
|
||||
| `autoRecording` | string | 否 | 自动录制设置:本地、云或无 |
|
||||
| `autoRecording` | string | 否 | 自动录制设置:本地、云端或无 |
|
||||
|
||||
#### 输出
|
||||
|
||||
@@ -94,14 +96,14 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `meetingId` | string | 是 | 会议 ID |
|
||||
| `occurrenceId` | string | 否 | 循环会议的发生 ID |
|
||||
| `showPreviousOccurrences` | boolean | 否 | 显示循环会议的先前发生记录 |
|
||||
| `occurrenceId` | string | 否 | 定期会议的发生 ID |
|
||||
| `showPreviousOccurrences` | boolean | 否 | 显示定期会议的先前发生记录 |
|
||||
|
||||
#### 输出
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `meeting` | object | 会议详情 |
|
||||
| `meeting` | object | 会议详细信息 |
|
||||
|
||||
### `zoom_update_meeting`
|
||||
|
||||
@@ -113,7 +115,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `meetingId` | string | 是 | 要更新的会议 ID |
|
||||
| `topic` | string | 否 | 会议主题 |
|
||||
| `type` | number | 否 | 会议类型:1=即时会议,2=预定会议,3=无固定时间的循环会议,8=固定时间的循环会议 |
|
||||
| `type` | number | 否 | 会议类型:1=即时,2=计划,3=无固定时间的定期会议,8=有固定时间的定期会议 |
|
||||
| `startTime` | string | 否 | ISO 8601 格式的会议开始时间 \(例如:2025-06-03T10:00:00Z\) |
|
||||
| `duration` | number | 否 | 会议时长(分钟) |
|
||||
| `timezone` | string | 否 | 会议的时区 \(例如:America/Los_Angeles\) |
|
||||
@@ -124,7 +126,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
| `joinBeforeHost` | boolean | 否 | 允许参与者在主持人之前加入 |
|
||||
| `muteUponEntry` | boolean | 否 | 参与者进入时静音 |
|
||||
| `waitingRoom` | boolean | 否 | 启用等候室 |
|
||||
| `autoRecording` | string | 否 | 自动录制设置:本地、云端或无 |
|
||||
| `autoRecording` | string | 否 | 自动录制设置:本地、云或无 |
|
||||
|
||||
#### 输出
|
||||
|
||||
@@ -141,7 +143,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `meetingId` | string | 是 | 要删除的会议 ID |
|
||||
| `occurrenceId` | string | 否 | 删除循环会议中特定场次的场次 ID |
|
||||
| `occurrenceId` | string | 否 | 删除定期会议的特定场次的场次 ID |
|
||||
| `scheduleForReminder` | boolean | 否 | 向注册者发送取消提醒邮件 |
|
||||
| `cancelMeetingReminder` | boolean | 否 | 向注册者和替代主持人发送取消邮件 |
|
||||
|
||||
@@ -175,10 +177,10 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `userId` | string | 是 | 用户 ID 或电子邮件地址。对于已认证用户,请使用 "me"。 |
|
||||
| `from` | string | 否 | 开始日期,格式为 yyyy-mm-dd(最近 6 个月内) |
|
||||
| `userId` | string | 是 | 用户 ID 或电子邮件地址。使用 "me" 表示已认证用户。 |
|
||||
| `from` | string | 否 | 开始日期,格式为 yyyy-mm-dd \(最近 6 个月内\) |
|
||||
| `to` | string | 否 | 结束日期,格式为 yyyy-mm-dd |
|
||||
| `pageSize` | number | 否 | 每页记录数(最大 300) |
|
||||
| `pageSize` | number | 否 | 每页记录数 \(最大 300\) |
|
||||
| `nextPageToken` | string | 否 | 分页令牌,用于获取下一页结果 |
|
||||
| `trash` | boolean | 否 | 设置为 true 以列出回收站中的录制 |
|
||||
|
||||
@@ -191,39 +193,39 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
### `zoom_get_meeting_recordings`
|
||||
|
||||
获取特定 Zoom 会议的所有录制内容
|
||||
获取特定 Zoom 会议的所有录制
|
||||
|
||||
#### 输入
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `meetingId` | string | 是 | 会议 ID 或会议 UUID |
|
||||
| `includeFolderItems` | boolean | 否 | 包括文件夹内的项目 |
|
||||
| `includeFolderItems` | boolean | 否 | 包括文件夹中的项目 |
|
||||
| `ttl` | number | 否 | 下载 URL 的有效时间(秒)\(最大值 604800\) |
|
||||
|
||||
#### 输出
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `recording` | object | 包含所有文件的会议录制内容 |
|
||||
| `recording` | object | 包含所有文件的会议录制 |
|
||||
|
||||
### `zoom_delete_recording`
|
||||
|
||||
删除 Zoom 会议的云录制内容
|
||||
删除 Zoom 会议的云录制
|
||||
|
||||
#### 输入
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `meetingId` | string | 是 | 会议 ID 或会议 UUID |
|
||||
| `recordingId` | string | 否 | 要删除的特定录制文件 ID。如果未提供,则删除所有录制内容。 |
|
||||
| `recordingId` | string | 否 | 要删除的特定录制文件 ID。如果未提供,则删除所有录制。 |
|
||||
| `action` | string | 否 | 删除操作:"trash" \(移至回收站\) 或 "delete" \(永久删除\) |
|
||||
|
||||
#### 输出
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | 录制内容是否成功删除 |
|
||||
| `success` | boolean | 录制是否成功删除 |
|
||||
|
||||
### `zoom_list_past_participants`
|
||||
|
||||
@@ -241,10 +243,10 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
| 参数 | 类型 | 描述 |
|
||||
| --------- | ---- | ----------- |
|
||||
| `participants` | 数组 | 会议参与者列表 |
|
||||
| `pageInfo` | 对象 | 分页信息 |
|
||||
| `participants` | array | 会议参与者列表 |
|
||||
| `pageInfo` | object | 分页信息 |
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 类别:`tools`
|
||||
- 类型:`zoom`
|
||||
- 类别: `tools`
|
||||
- 类型: `zoom`
|
||||
|
||||
@@ -557,7 +557,7 @@ checksums:
|
||||
content/8: 6325adefb6e1520835225285b18b6a45
|
||||
content/9: b7fa85fce9c7476fe132df189e27dac1
|
||||
content/10: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/11: 985f435f721b00df4d13fa0a5552684c
|
||||
content/11: 7ad14ccfe548588081626cfe769ad492
|
||||
content/12: bcadfc362b69078beee0088e5936c98b
|
||||
content/13: 6af66efd0da20944a87fdb8d9defa358
|
||||
content/14: b3f310d5ef115bea5a8b75bf25d7ea9a
|
||||
@@ -889,9 +889,9 @@ checksums:
|
||||
content/10: 71c6cf129630acff9d8df39d0a5c5407
|
||||
content/11: 9c8aa3f09c9b2bd50ea4cdff3598ea4e
|
||||
content/12: 8ee83eff32425b2c52929284e8485c20
|
||||
content/13: 6cda87dc9837779f4572ed70b87a5654
|
||||
content/13: c1ec0b00cb68561551e48616731ea43a
|
||||
content/14: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/15: 2f696275726cdeefd7d7280b5bb43b21
|
||||
content/15: 117e42c934a7f2a76b0399235841260e
|
||||
content/16: bcadfc362b69078beee0088e5936c98b
|
||||
content/17: bb43e4f36fdc1eb6211f46ddeed9e0aa
|
||||
content/18: 05540cb3028d4d781521c14e5f9e3835
|
||||
@@ -903,7 +903,7 @@ checksums:
|
||||
content/24: 228a8ece96627883153b826a1cbaa06c
|
||||
content/25: 53abe061a259c296c82676b4770ddd1b
|
||||
content/26: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/27: 170ccdc4ce7ee086e9c6b5073efca582
|
||||
content/27: 5b9546f77fbafc0741f3fc2548f81c7e
|
||||
content/28: bcadfc362b69078beee0088e5936c98b
|
||||
content/29: b82def7d82657f941fbe60df3924eeeb
|
||||
content/30: 1ca7ee3856805fa1718031c5f75b6ffb
|
||||
@@ -2511,135 +2511,141 @@ checksums:
|
||||
content/12: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/13: 8c25606fde43bf4ff519760364fda052
|
||||
content/14: bcadfc362b69078beee0088e5936c98b
|
||||
content/15: 9ee0b1e8873ef165299443a76823e7bc
|
||||
content/15: 4ec31e928a8498d050922adb2f977c98
|
||||
content/16: be5c68d578443b68c062029104bd6ddb
|
||||
content/17: 3b38aa70e04f841184b7d958b087af8c
|
||||
content/18: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/19: 5269008adfae57a3a8fb5d8bf5922498
|
||||
content/20: bcadfc362b69078beee0088e5936c98b
|
||||
content/21: c66b2996f62f0f7150fce59eed9ad7a8
|
||||
content/21: 5317f2e9eb34d7297064b381aef6912c
|
||||
content/22: ef92d95455e378abe4d27a1cdc5e1aed
|
||||
content/23: febd6019055f3754953fd93395d0dbf2
|
||||
content/24: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/25: 7ef3f388e5ee9346bac54c771d825f40
|
||||
content/25: caf6acbe2a4495ca055cb9006ce47250
|
||||
content/26: bcadfc362b69078beee0088e5936c98b
|
||||
content/27: 7bccc537f32fabcbb4cd0a85bef612de
|
||||
content/27: 57662dd91f8d1d807377fd48fa0e9142
|
||||
content/28: b463f54cd5fe2458b5842549fbb5e1ce
|
||||
content/29: 55f8c724e1a2463bc29a32518a512c73
|
||||
content/30: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/31: 770b1f7b3105937d452bc2815ebb6e05
|
||||
content/32: bcadfc362b69078beee0088e5936c98b
|
||||
content/33: b7be768fe967164e71af56dd5cd13f86
|
||||
content/33: 0b92b54ce40dc29bb6faccf82eace18b
|
||||
content/34: f426b59ee38021a4254fe566995c416c
|
||||
content/35: 2455b2f418cc79f4a67558678ae444bc
|
||||
content/36: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/37: 320e2962c7e2d24690c75b2f69e5be9d
|
||||
content/38: bcadfc362b69078beee0088e5936c98b
|
||||
content/39: 25501290045d87dad2c5819200528091
|
||||
content/39: 76f532ccbddb41115e56a7d56d97aa96
|
||||
content/40: 52233c13208d6e10497340a37b11ef3a
|
||||
content/41: 9c6bf4c4180c96e31668941aa36f2cde
|
||||
content/42: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/43: a792b3be1ab2bc6bc570f5dafca56aaa
|
||||
content/44: bcadfc362b69078beee0088e5936c98b
|
||||
content/45: 120bc8dacae493c314aed0f4a4094c7f
|
||||
content/45: 838a4016055b35389dae383f8b4ec2ac
|
||||
content/46: fe4880697d8adcd75c3a2c7e5b0fec86
|
||||
content/47: fdd9ab6e60b2c42a18a41ef869fb925b
|
||||
content/48: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/49: 0ccf3f3f59955dd78558a0e3589579a6
|
||||
content/50: bcadfc362b69078beee0088e5936c98b
|
||||
content/51: 35ce33f78ffa1130c2719885759406a5
|
||||
content/51: 8defd6d29c0ddbd9a811caa8f2cf3f39
|
||||
content/52: f04e8809e7d4f701cf24b339d844bea5
|
||||
content/53: 811c364b512dd61a2f40fb8418b6b0cd
|
||||
content/54: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/55: 2ce7e48a76065784447b75b6bc0fbff9
|
||||
content/56: bcadfc362b69078beee0088e5936c98b
|
||||
content/57: 437fae06c917576e309864634ac006d5
|
||||
content/57: 47be5344f0c8a9ede380f37f769b5b3f
|
||||
content/58: 8d41bb08f7d4000b665e6786583aa2b5
|
||||
content/59: 48adb1980e062be3783331522082edaf
|
||||
content/60: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/61: b89e1b7a15f022c0c13adadb25e3f49d
|
||||
content/62: bcadfc362b69078beee0088e5936c98b
|
||||
content/63: 74e0d576bddb246c672c98a8e9f4fd32
|
||||
content/63: 44b209460093fb955b8f6b4e575dde17
|
||||
content/64: c02f43d19361be7571e8141a61e83980
|
||||
content/65: 21a0f57793fb19dd8761b644e22ee731
|
||||
content/66: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/67: 6aa3e815a37986856a2781ccfdb7b4de
|
||||
content/68: bcadfc362b69078beee0088e5936c98b
|
||||
content/69: b76190aa5e84cb17cdcf2e061edf706b
|
||||
content/69: 9032d6a71c23f90a39232d653c3daf36
|
||||
content/70: 1845561cd920176e2dbfed65eaccca9e
|
||||
content/71: 2960e1e609b8c512f5cf1ef715c2a684
|
||||
content/72: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/73: e3586b13bcb7c91515437d76a0027201
|
||||
content/74: bcadfc362b69078beee0088e5936c98b
|
||||
content/75: 520c03c754968a673ed5def1706f919d
|
||||
content/75: f14261cdc2105f3c2380a90629edc172
|
||||
content/76: 14a4d1b2c2f1257eadb4b26b85672fab
|
||||
content/77: 45bd9b1b321f85dc31c720051c12f681
|
||||
content/78: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/79: 176f866fa8ad12f179eb5466bc968914
|
||||
content/80: bcadfc362b69078beee0088e5936c98b
|
||||
content/81: 1fc32b27418b8efe2abba1beb6d31868
|
||||
content/81: 8f1d8635d9e542fbce4ee4167d8a2bb1
|
||||
content/82: 5c0b4adc7825b3ed5831bf6c4d83a6a2
|
||||
content/83: cace0c917728a3a5bc93d26dd65669f7
|
||||
content/84: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/85: 490b3a3ef8840f1e01a58c6875af027b
|
||||
content/86: bcadfc362b69078beee0088e5936c98b
|
||||
content/87: f4c4cbcb48dbbd87b27fdf76105aaa8c
|
||||
content/87: 501cead9242b6febffe4659a63cef613
|
||||
content/88: efa34ea34fd3d30088470cf6f4476106
|
||||
content/89: 47fc6e4fd184baaf72d61223ec944148
|
||||
content/90: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/91: 43349c22c04743b654d4aee849cc81ff
|
||||
content/92: bcadfc362b69078beee0088e5936c98b
|
||||
content/93: 82634f835e924e6b2242df6127f0969a
|
||||
content/93: a53a400b7cddac3a34435d23332db795
|
||||
content/94: e3a5c53a79de7fe47abd7f7a9f86fb65
|
||||
content/95: 2053815e47b54488983f0571f49cd11a
|
||||
content/96: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/97: 846be448c3cedb87e4bd923c63e04512
|
||||
content/98: bcadfc362b69078beee0088e5936c98b
|
||||
content/99: 11608f282141ee6c31f933f6c2fcaa0d
|
||||
content/99: d227cd028bf20eab7f826827efa9ea90
|
||||
content/100: 17d1e59a4290138d979568f39e6fea9b
|
||||
content/101: cf9c3c1b441bde10b35d04d776e9f5ce
|
||||
content/102: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/103: 599eb9d3e6b88cba7a15d007ce1111f4
|
||||
content/104: bcadfc362b69078beee0088e5936c98b
|
||||
content/105: ad1bd5c40adabc9f2a97682be1671d67
|
||||
content/105: 09d70143b5598699ae1ed593baa4ac61
|
||||
content/106: afa20ccc5f708cf36a0cb6ede6ec0c4f
|
||||
content/107: 8a64259005d325f6849527186097f390
|
||||
content/108: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/109: c2b8a2c90d216d94f24850c8124849ef
|
||||
content/110: bcadfc362b69078beee0088e5936c98b
|
||||
content/111: b5e005f9e95aead5c9596968b21821e1
|
||||
content/111: 6bab23a5c82acbd6cd79fbdfc9bbcbde
|
||||
content/112: 05141d844a911fb66fd7bdc2b98e8160
|
||||
content/113: f04861fc73d9abc76dd8e140703baa14
|
||||
content/114: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/115: 09a884f6fcddd37ad4968718fb48db9e
|
||||
content/116: bcadfc362b69078beee0088e5936c98b
|
||||
content/117: 62695bf37dd3fe43470266996e09ef86
|
||||
content/117: a1645115447094e7520eb3d45244a3c8
|
||||
content/118: 96b30990733f35886cb04bc6bae18613
|
||||
content/119: b22baefc2beca09303baa0778b27a4d6
|
||||
content/120: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/121: 9b936f3424feac6551b709d0c5d5713c
|
||||
content/122: bcadfc362b69078beee0088e5936c98b
|
||||
content/123: 1a249d65588fd4a33bb50af768870bbc
|
||||
content/123: 6cd2f15ea11b6f07e6ac7e170a90a91f
|
||||
content/124: 573661fdf0cd751a2433052dff8dcdfe
|
||||
content/125: 81d70f8bd307578a5814374f31a7d6c2
|
||||
content/126: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/127: d04dcb9d64bc76606af4f9309eeacb75
|
||||
content/128: bcadfc362b69078beee0088e5936c98b
|
||||
content/129: 7a78c9363ed6fba5d7277a24186c7296
|
||||
content/129: b6b0fd5e140401e9f4c4c8a0e5ab0da1
|
||||
content/130: ce10caa6dcfe95e58c32db33157d989a
|
||||
content/131: 49df30ca91d4139a38b591311b7a83a8
|
||||
content/132: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/133: b61b7e3b5e5b7c05fcb65478d744abb5
|
||||
content/134: bcadfc362b69078beee0088e5936c98b
|
||||
content/135: 9ec868a621316d03619fc37582084053
|
||||
content/135: 310e65d225fb68cf48f1d44d1047ea12
|
||||
content/136: 805790ac8b4ae77c30fbfc9f6023bac8
|
||||
content/137: 1a4e93e8a49abd71333809a3bc0856c9
|
||||
content/138: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/139: 33fde4c3da4584b51f06183b7b192a78
|
||||
content/140: bcadfc362b69078beee0088e5936c98b
|
||||
content/141: a4e11bf0073b1f1e45c07e3c1c7dd969
|
||||
content/142: b3f310d5ef115bea5a8b75bf25d7ea9a
|
||||
content/143: 4930918f803340baa861bed9cdf789de
|
||||
content/141: b7451190f100388d999c183958d787a7
|
||||
content/142: d0f9e799e2e5cc62de60668d35fd846f
|
||||
content/143: b19069ff19899fe202217e06e002c447
|
||||
content/144: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/145: 480fd62f8d9cc18467e82f4c3f70beea
|
||||
content/146: bcadfc362b69078beee0088e5936c98b
|
||||
content/147: 4e73a65d3b873f3979587e10a0f39e72
|
||||
content/148: b3f310d5ef115bea5a8b75bf25d7ea9a
|
||||
content/149: 4930918f803340baa861bed9cdf789de
|
||||
8f76e389f6226f608571622b015ca6a1:
|
||||
meta/title: ddfe2191ea61b34d8b7cc1d7c19b94ac
|
||||
meta/description: 049ff551f2ebabb15cdea0c71bd8e4eb
|
||||
@@ -4811,9 +4817,9 @@ checksums:
|
||||
content/19: 85547efea8ae0e8170ac4e2030f6be25
|
||||
content/20: 25c56dcdc4af1516c3fbf9d82d96b48d
|
||||
content/21: 56dbe63da14a319cd520ab1615c94be7
|
||||
content/22: e092cde0c92ef09c642a62636e7e3ae3
|
||||
content/22: e039f6c905c8aa148cc3e7af19f05239
|
||||
content/23: c7004f5db8f7134d7e3a36a1916691a2
|
||||
content/24: bbc26961050b132b9bc4f14ba11f407a
|
||||
content/24: 26555018b90fc8fb3ac65cece15f3966
|
||||
content/25: 56dbe63da14a319cd520ab1615c94be7
|
||||
content/26: 3e835ecc38acf2c76179034360d41670
|
||||
content/27: a13bbc3dac7388e1ef4e9cbafdcc8241
|
||||
@@ -47626,72 +47632,73 @@ checksums:
|
||||
content/4: 0f0165c7e21355d8f8e332c2252100db
|
||||
content/5: 11289606ffb19f4564a7f0a867a39a55
|
||||
content/6: 05eb6fe6951b12bcddd3ae36aacc7bb3
|
||||
content/7: 715b7f8ee32c3d0dcd20cc0a57a9367b
|
||||
content/8: 821e6394b0a953e2b0842b04ae8f3105
|
||||
content/9: e5f8dc06b6db9aeef348d8af9617c787
|
||||
content/10: 9c8aa3f09c9b2bd50ea4cdff3598ea4e
|
||||
content/11: 0ec27ddd5601764fadfc363811376d88
|
||||
content/12: 18e3253cead6514fe5e939d98b64d8fb
|
||||
content/13: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/14: 676120936020f1a25faea0d5608b7958
|
||||
content/15: bcadfc362b69078beee0088e5936c98b
|
||||
content/16: ecad0614a5ec681a43fea86034a30905
|
||||
content/17: 8eb606aad3db305e12679efb6fe7363e
|
||||
content/18: 7d9ab020b8312987af94a42c7797a6bc
|
||||
content/19: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/20: 6854dad4e5e5419803d3b0a387bf36c1
|
||||
content/21: bcadfc362b69078beee0088e5936c98b
|
||||
content/22: 051e39427d40ab7c4b5ebbbf65c7910f
|
||||
content/23: 9c42c50fa5ce2db382867e2da5bca90d
|
||||
content/24: 7bc9a20018bc365ecf55a54a53ad1013
|
||||
content/25: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/26: 299dce7368070dd19957ba06efef836b
|
||||
content/27: bcadfc362b69078beee0088e5936c98b
|
||||
content/28: d9a5be31d4296b81660b38dcb4c695cc
|
||||
content/29: d732fd0df847a742d6dabfc5110ba31d
|
||||
content/30: 9782a621e6d591e72b7ce5e27face7af
|
||||
content/31: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/32: 2d7dd3ac552ff837d614c510033646ba
|
||||
content/33: bcadfc362b69078beee0088e5936c98b
|
||||
content/34: 37d84e8cc60979a8d3f1e48483d23113
|
||||
content/35: 90bc3ad5e30e5d579f48787e7d8181ae
|
||||
content/36: 7db1faa939033f49aad8ef462e630c26
|
||||
content/37: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/38: 5e289bbda737273fa75101e276358ec8
|
||||
content/39: bcadfc362b69078beee0088e5936c98b
|
||||
content/40: 805d1a06016797ba04f3cb840ac59e44
|
||||
content/41: 88260e555a61ba6886e56f3bc06512dc
|
||||
content/42: 80c4006b7d25c461c18e9a8a35cfac72
|
||||
content/43: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/44: 645735bd919e64e034c03b98cc75d5be
|
||||
content/45: bcadfc362b69078beee0088e5936c98b
|
||||
content/46: 842462f8cd7a897eda330bba54d297df
|
||||
content/47: 96d58ab5053c5f5db3f15f82442eb3dd
|
||||
content/48: 148c8f5f3872aa6e9944e221c35bc9a0
|
||||
content/49: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/50: 7a449b1878a28cff4b713a104581ec63
|
||||
content/51: bcadfc362b69078beee0088e5936c98b
|
||||
content/52: 502548c4b9d6be040f73fc431c3c8fd6
|
||||
content/53: 1304212656a10261692509a67cfae220
|
||||
content/54: 042eb9071c13eb10ee5fd0bfd4e00c8a
|
||||
content/55: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/56: 4263559dc306f5edcdfa1bf758adffdf
|
||||
content/57: bcadfc362b69078beee0088e5936c98b
|
||||
content/58: 984b85e501fbd9993b7fe38898e5d445
|
||||
content/59: 12f6776606adce02255b1db24cd58d29
|
||||
content/60: fc53b00cfedd65f5fd906daebd9c04df
|
||||
content/61: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/62: 242a47e9aaf5c07859e2a472bed6d3ac
|
||||
content/63: bcadfc362b69078beee0088e5936c98b
|
||||
content/64: 2823402034702ff5ca56b9cad3572c4d
|
||||
content/65: 8ddcef9d1d32bff76ac8e6c5a0e0dca5
|
||||
content/66: 94a960dd84bd71825b58d2219b98dd74
|
||||
content/67: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/68: e7777f90d25d134aa0a3cae9cdfc6563
|
||||
content/69: bcadfc362b69078beee0088e5936c98b
|
||||
content/70: affc25ff3d47510647c984a1d59b0a0e
|
||||
content/71: b3f310d5ef115bea5a8b75bf25d7ea9a
|
||||
content/72: 2d9d3b6969330e7b2d8e1169cfcf0031
|
||||
content/7: e474de0de136881473833dd6502b6d06
|
||||
content/8: 715b7f8ee32c3d0dcd20cc0a57a9367b
|
||||
content/9: 821e6394b0a953e2b0842b04ae8f3105
|
||||
content/10: e5f8dc06b6db9aeef348d8af9617c787
|
||||
content/11: 9c8aa3f09c9b2bd50ea4cdff3598ea4e
|
||||
content/12: 0ec27ddd5601764fadfc363811376d88
|
||||
content/13: 18e3253cead6514fe5e939d98b64d8fb
|
||||
content/14: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/15: 676120936020f1a25faea0d5608b7958
|
||||
content/16: bcadfc362b69078beee0088e5936c98b
|
||||
content/17: ecad0614a5ec681a43fea86034a30905
|
||||
content/18: 8eb606aad3db305e12679efb6fe7363e
|
||||
content/19: 7d9ab020b8312987af94a42c7797a6bc
|
||||
content/20: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/21: 6854dad4e5e5419803d3b0a387bf36c1
|
||||
content/22: bcadfc362b69078beee0088e5936c98b
|
||||
content/23: 051e39427d40ab7c4b5ebbbf65c7910f
|
||||
content/24: 9c42c50fa5ce2db382867e2da5bca90d
|
||||
content/25: 7bc9a20018bc365ecf55a54a53ad1013
|
||||
content/26: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/27: 299dce7368070dd19957ba06efef836b
|
||||
content/28: bcadfc362b69078beee0088e5936c98b
|
||||
content/29: d9a5be31d4296b81660b38dcb4c695cc
|
||||
content/30: d732fd0df847a742d6dabfc5110ba31d
|
||||
content/31: 9782a621e6d591e72b7ce5e27face7af
|
||||
content/32: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/33: 2d7dd3ac552ff837d614c510033646ba
|
||||
content/34: bcadfc362b69078beee0088e5936c98b
|
||||
content/35: 37d84e8cc60979a8d3f1e48483d23113
|
||||
content/36: 90bc3ad5e30e5d579f48787e7d8181ae
|
||||
content/37: 7db1faa939033f49aad8ef462e630c26
|
||||
content/38: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/39: 5e289bbda737273fa75101e276358ec8
|
||||
content/40: bcadfc362b69078beee0088e5936c98b
|
||||
content/41: 805d1a06016797ba04f3cb840ac59e44
|
||||
content/42: 88260e555a61ba6886e56f3bc06512dc
|
||||
content/43: 80c4006b7d25c461c18e9a8a35cfac72
|
||||
content/44: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/45: 645735bd919e64e034c03b98cc75d5be
|
||||
content/46: bcadfc362b69078beee0088e5936c98b
|
||||
content/47: 842462f8cd7a897eda330bba54d297df
|
||||
content/48: 96d58ab5053c5f5db3f15f82442eb3dd
|
||||
content/49: 148c8f5f3872aa6e9944e221c35bc9a0
|
||||
content/50: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/51: 7a449b1878a28cff4b713a104581ec63
|
||||
content/52: bcadfc362b69078beee0088e5936c98b
|
||||
content/53: 502548c4b9d6be040f73fc431c3c8fd6
|
||||
content/54: 1304212656a10261692509a67cfae220
|
||||
content/55: 042eb9071c13eb10ee5fd0bfd4e00c8a
|
||||
content/56: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/57: 4263559dc306f5edcdfa1bf758adffdf
|
||||
content/58: bcadfc362b69078beee0088e5936c98b
|
||||
content/59: 984b85e501fbd9993b7fe38898e5d445
|
||||
content/60: 12f6776606adce02255b1db24cd58d29
|
||||
content/61: fc53b00cfedd65f5fd906daebd9c04df
|
||||
content/62: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/63: 242a47e9aaf5c07859e2a472bed6d3ac
|
||||
content/64: bcadfc362b69078beee0088e5936c98b
|
||||
content/65: 2823402034702ff5ca56b9cad3572c4d
|
||||
content/66: 8ddcef9d1d32bff76ac8e6c5a0e0dca5
|
||||
content/67: 94a960dd84bd71825b58d2219b98dd74
|
||||
content/68: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/69: e7777f90d25d134aa0a3cae9cdfc6563
|
||||
content/70: bcadfc362b69078beee0088e5936c98b
|
||||
content/71: affc25ff3d47510647c984a1d59b0a0e
|
||||
content/72: b3f310d5ef115bea5a8b75bf25d7ea9a
|
||||
content/73: 2d9d3b6969330e7b2d8e1169cfcf0031
|
||||
1db887f91df2e066fc769749f3b2a930:
|
||||
meta/title: b4c01a60ed020f21556b4a8ef3f24cae
|
||||
meta/description: b2f402630c2605cff14c3d7ad2c52d16
|
||||
@@ -49821,3 +49828,41 @@ checksums:
|
||||
content/472: dbc5fceeefb3ab5fa505394becafef4e
|
||||
content/473: b3f310d5ef115bea5a8b75bf25d7ea9a
|
||||
content/474: 27c398e669b297cea076e4ce4cc0c5eb
|
||||
9a28da736b42bf8de55126d4c06b6150:
|
||||
meta/title: 418d5c8a18ad73520b38765741601f32
|
||||
meta/description: 41cb31abf94297849fb8a4023cf0211d
|
||||
content/0: 1b031fb0c62c46b177aeed5c3d3f8f80
|
||||
content/1: e72670f88454b5b1c955b029de5fa8b5
|
||||
content/2: d586e5af506d99add847369c0accfb4d
|
||||
content/3: a2ce9ed4954ab55bcebed927cec8e890
|
||||
content/4: 5fc7b723a6adcf201e8deb3f5ed9a9e3
|
||||
content/5: a78981875c359a3343f26ed4d115f899
|
||||
content/6: 821e6394b0a953e2b0842b04ae8f3105
|
||||
content/7: 56a538eaccb1158fb1f7a01cc32f7331
|
||||
content/8: 9c8aa3f09c9b2bd50ea4cdff3598ea4e
|
||||
content/9: 263633aee6db9332de806ae50d87de05
|
||||
content/10: 5a7e2171e5f73fec5eae21a50e5de661
|
||||
content/11: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/12: 5905ef5d0db0354c08394acb0b5cda4b
|
||||
content/13: bcadfc362b69078beee0088e5936c98b
|
||||
content/14: d81ef802f80143282cf4e534561a9570
|
||||
content/15: 02233e6212003c1d121424cfd8b86b62
|
||||
content/16: efe2c6dd368708de68a1addbfdb11b0c
|
||||
content/17: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/18: 2722e8bee100e7bc4590fa02710e9508
|
||||
content/19: bcadfc362b69078beee0088e5936c98b
|
||||
content/20: 953f353184dc27db1f20156db2a9ad90
|
||||
content/21: 2011e87d0555cd0ab133ef2d35e7a37b
|
||||
content/22: dbf08acb413d845ec419e45b1f986bdb
|
||||
content/23: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/24: afc35de2990ed0e9bb8f98dc1b9609ce
|
||||
content/25: bcadfc362b69078beee0088e5936c98b
|
||||
content/26: c06a5bb458242baa23d34957034c2fe7
|
||||
content/27: ff043e912417bc29ac7c64520160c07d
|
||||
content/28: 9c2175ab469cb6ff9e62bc8bdcf7621d
|
||||
content/29: 371d0e46b4bd2c23f559b8bc112f6955
|
||||
content/30: 20e6bddad8e7f34a3d09e5b0c5678c13
|
||||
content/31: bcadfc362b69078beee0088e5936c98b
|
||||
content/32: fd0f38eb3fe5cf95be366a4ff6b4fb90
|
||||
content/33: b3f310d5ef115bea5a8b75bf25d7ea9a
|
||||
content/34: 4a7b2c644e487f3d12b6a6b54f8c6773
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import type { InferPageType } from 'fumadocs-core/source'
|
||||
import type { source } from '@/lib/source'
|
||||
import type { PageData, source } from '@/lib/source'
|
||||
|
||||
export async function getLLMText(page: InferPageType<typeof source>) {
|
||||
const processed = await page.data.getText('processed')
|
||||
return `# ${page.data.title} (${page.url})
|
||||
const data = page.data as PageData
|
||||
const processed = await data.getText('processed')
|
||||
return `# ${data.title} (${page.url})
|
||||
|
||||
${processed}`
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { loader } from 'fumadocs-core/source'
|
||||
import { type InferPageType, loader } from 'fumadocs-core/source'
|
||||
import type { DocData, DocMethods } from 'fumadocs-mdx/runtime/types'
|
||||
import { docs } from '@/.source/server'
|
||||
import { i18n } from './i18n'
|
||||
|
||||
@@ -7,3 +8,13 @@ export const source = loader({
|
||||
source: docs.toFumadocsSource(),
|
||||
i18n,
|
||||
})
|
||||
|
||||
/** Full page data type including MDX content and metadata */
|
||||
export type PageData = DocData &
|
||||
DocMethods & {
|
||||
title: string
|
||||
description?: string
|
||||
full?: boolean
|
||||
}
|
||||
|
||||
export type Page = InferPageType<typeof source>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"fumadocs-mdx": "14.1.0",
|
||||
"fumadocs-ui": "16.2.3",
|
||||
"lucide-react": "^0.511.0",
|
||||
"next": "16.0.9",
|
||||
"next": "16.1.0-canary.21",
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "19.2.1",
|
||||
"react-dom": "19.2.1",
|
||||
|
||||
@@ -573,10 +573,10 @@ export default function LoginPage({
|
||||
<Dialog open={forgotPasswordOpen} onOpenChange={setForgotPasswordOpen}>
|
||||
<DialogContent className='auth-card auth-card-shadow max-w-[540px] rounded-[10px] border backdrop-blur-sm'>
|
||||
<DialogHeader>
|
||||
<DialogTitle className='auth-text-primary font-semibold text-xl tracking-tight'>
|
||||
<DialogTitle className='font-semibold text-black text-xl tracking-tight'>
|
||||
Reset Password
|
||||
</DialogTitle>
|
||||
<DialogDescription className='auth-text-secondary text-sm'>
|
||||
<DialogDescription className='text-muted-foreground text-sm'>
|
||||
Enter your email address and we'll send you a link to reset your password if your
|
||||
account exists.
|
||||
</DialogDescription>
|
||||
|
||||
@@ -4,6 +4,7 @@ export const FOOTER_BLOCKS = [
|
||||
'Condition',
|
||||
'Evaluator',
|
||||
'Function',
|
||||
'Guardrails',
|
||||
'Human In The Loop',
|
||||
'Loop',
|
||||
'Parallel',
|
||||
@@ -30,7 +31,6 @@ export const FOOTER_TOOLS = [
|
||||
'GitHub',
|
||||
'Gmail',
|
||||
'Google Drive',
|
||||
'Guardrails',
|
||||
'HubSpot',
|
||||
'HuggingFace',
|
||||
'Hunter',
|
||||
@@ -70,6 +70,7 @@ export const FOOTER_TOOLS = [
|
||||
'Salesforce',
|
||||
'SendGrid',
|
||||
'Serper',
|
||||
'ServiceNow',
|
||||
'SharePoint',
|
||||
'Slack',
|
||||
'Smtp',
|
||||
|
||||
@@ -109,7 +109,7 @@ export default function Footer({ fullWidth = false }: FooterProps) {
|
||||
{FOOTER_BLOCKS.map((block) => (
|
||||
<Link
|
||||
key={block}
|
||||
href={`https://docs.sim.ai/blocks/${block.toLowerCase().replace(' ', '-')}`}
|
||||
href={`https://docs.sim.ai/blocks/${block.toLowerCase().replaceAll(' ', '-')}`}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='text-[14px] text-muted-foreground transition-colors hover:text-foreground'
|
||||
|
||||
@@ -2,7 +2,6 @@ import { Suspense } from 'react'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { Background, Footer, Nav, StructuredData } from '@/app/(landing)/components'
|
||||
|
||||
// Lazy load heavy components for better initial load performance
|
||||
const Hero = dynamic(() => import('@/app/(landing)/components/hero/hero'), {
|
||||
loading: () => <div className='h-[600px] animate-pulse bg-gray-50' />,
|
||||
})
|
||||
|
||||
@@ -64,6 +64,7 @@ export default async function Page({ params }: { params: Promise<{ slug: string
|
||||
sizes='(max-width: 768px) 100vw, 450px'
|
||||
priority
|
||||
itemProp='image'
|
||||
unoptimized
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -144,6 +145,7 @@ export default async function Page({ params }: { params: Promise<{ slug: string
|
||||
className='h-[160px] w-full object-cover'
|
||||
sizes='(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw'
|
||||
loading='lazy'
|
||||
unoptimized
|
||||
/>
|
||||
<div className='p-3'>
|
||||
<div className='mb-1 text-gray-600 text-xs'>
|
||||
|
||||
@@ -38,6 +38,7 @@ export default async function AuthorPage({ params }: { params: Promise<{ id: str
|
||||
width={40}
|
||||
height={40}
|
||||
className='rounded-full'
|
||||
unoptimized
|
||||
/>
|
||||
) : null}
|
||||
<h1 className='font-medium text-[32px] leading-tight'>{author.name}</h1>
|
||||
@@ -52,6 +53,7 @@ export default async function AuthorPage({ params }: { params: Promise<{ id: str
|
||||
width={600}
|
||||
height={315}
|
||||
className='h-[160px] w-full object-cover transition-transform group-hover:scale-[1.02]'
|
||||
unoptimized
|
||||
/>
|
||||
<div className='p-3'>
|
||||
<div className='mb-1 text-gray-600 text-xs'>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
|
||||
import { getAllPostMeta } from '@/lib/blog/registry'
|
||||
import { soehne } from '@/app/_styles/fonts/soehne/soehne'
|
||||
import { PostGrid } from '@/app/(landing)/studio/post-grid'
|
||||
|
||||
export const revalidate = 3600
|
||||
|
||||
@@ -18,7 +17,6 @@ export default async function StudioIndex({
|
||||
const all = await getAllPostMeta()
|
||||
const filtered = tag ? all.filter((p) => p.tags.includes(tag)) : all
|
||||
|
||||
// Sort to ensure featured post is first on page 1
|
||||
const sorted =
|
||||
pageNum === 1
|
||||
? filtered.sort((a, b) => {
|
||||
@@ -63,68 +61,7 @@ export default async function StudioIndex({
|
||||
</div> */}
|
||||
|
||||
{/* Grid layout for consistent rows */}
|
||||
<div className='grid grid-cols-1 gap-4 md:grid-cols-2 md:gap-6 lg:grid-cols-3'>
|
||||
{posts.map((p, i) => {
|
||||
return (
|
||||
<Link key={p.slug} href={`/studio/${p.slug}`} className='group flex flex-col'>
|
||||
<div className='flex h-full flex-col overflow-hidden rounded-xl border border-gray-200 transition-colors duration-300 hover:border-gray-300'>
|
||||
<Image
|
||||
src={p.ogImage}
|
||||
alt={p.title}
|
||||
width={800}
|
||||
height={450}
|
||||
className='h-48 w-full object-cover'
|
||||
sizes='(max-width: 768px) 100vw, (max-width: 1024px) 50vw, 33vw'
|
||||
loading='lazy'
|
||||
/>
|
||||
<div className='flex flex-1 flex-col p-4'>
|
||||
<div className='mb-2 text-gray-600 text-xs'>
|
||||
{new Date(p.date).toLocaleDateString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric',
|
||||
})}
|
||||
</div>
|
||||
<h3 className='shine-text mb-1 font-medium text-lg leading-tight'>{p.title}</h3>
|
||||
<p className='mb-3 line-clamp-3 flex-1 text-gray-700 text-sm'>{p.description}</p>
|
||||
<div className='flex items-center gap-2'>
|
||||
<div className='-space-x-1.5 flex'>
|
||||
{(p.authors && p.authors.length > 0 ? p.authors : [p.author])
|
||||
.slice(0, 3)
|
||||
.map((author, idx) => (
|
||||
<Avatar key={idx} className='size-4 border border-white'>
|
||||
<AvatarImage src={author?.avatarUrl} alt={author?.name} />
|
||||
<AvatarFallback className='border border-white bg-gray-100 text-[10px] text-gray-600'>
|
||||
{author?.name.slice(0, 2)}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
))}
|
||||
</div>
|
||||
<span className='text-gray-600 text-xs'>
|
||||
{(p.authors && p.authors.length > 0 ? p.authors : [p.author])
|
||||
.slice(0, 2)
|
||||
.map((a) => a?.name)
|
||||
.join(', ')}
|
||||
{(p.authors && p.authors.length > 0 ? p.authors : [p.author]).length > 2 && (
|
||||
<>
|
||||
{' '}
|
||||
and{' '}
|
||||
{(p.authors && p.authors.length > 0 ? p.authors : [p.author]).length - 2}{' '}
|
||||
other
|
||||
{(p.authors && p.authors.length > 0 ? p.authors : [p.author]).length - 2 >
|
||||
1
|
||||
? 's'
|
||||
: ''}
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<PostGrid posts={posts} />
|
||||
|
||||
{totalPages > 1 && (
|
||||
<div className='mt-10 flex items-center justify-center gap-3'>
|
||||
|
||||
90
apps/sim/app/(landing)/studio/post-grid.tsx
Normal file
90
apps/sim/app/(landing)/studio/post-grid.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
'use client'
|
||||
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
|
||||
|
||||
interface Author {
|
||||
id: string
|
||||
name: string
|
||||
avatarUrl?: string
|
||||
url?: string
|
||||
}
|
||||
|
||||
interface Post {
|
||||
slug: string
|
||||
title: string
|
||||
description: string
|
||||
date: string
|
||||
ogImage: string
|
||||
author: Author
|
||||
authors?: Author[]
|
||||
featured?: boolean
|
||||
}
|
||||
|
||||
export function PostGrid({ posts }: { posts: Post[] }) {
|
||||
return (
|
||||
<div className='grid grid-cols-1 gap-4 md:grid-cols-2 md:gap-6 lg:grid-cols-3'>
|
||||
{posts.map((p, index) => (
|
||||
<Link key={p.slug} href={`/studio/${p.slug}`} className='group flex flex-col'>
|
||||
<div className='flex h-full flex-col overflow-hidden rounded-xl border border-gray-200 transition-colors duration-300 hover:border-gray-300'>
|
||||
{/* Image container with fixed aspect ratio to prevent layout shift */}
|
||||
<div className='relative aspect-video w-full overflow-hidden'>
|
||||
<Image
|
||||
src={p.ogImage}
|
||||
alt={p.title}
|
||||
sizes='(max-width: 768px) 100vw, (max-width: 1024px) 50vw, 33vw'
|
||||
unoptimized
|
||||
priority={index < 6}
|
||||
loading={index < 6 ? undefined : 'lazy'}
|
||||
fill
|
||||
style={{ objectFit: 'cover' }}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex flex-1 flex-col p-4'>
|
||||
<div className='mb-2 text-gray-600 text-xs'>
|
||||
{new Date(p.date).toLocaleDateString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric',
|
||||
})}
|
||||
</div>
|
||||
<h3 className='shine-text mb-1 font-medium text-lg leading-tight'>{p.title}</h3>
|
||||
<p className='mb-3 line-clamp-3 flex-1 text-gray-700 text-sm'>{p.description}</p>
|
||||
<div className='flex items-center gap-2'>
|
||||
<div className='-space-x-1.5 flex'>
|
||||
{(p.authors && p.authors.length > 0 ? p.authors : [p.author])
|
||||
.slice(0, 3)
|
||||
.map((author, idx) => (
|
||||
<Avatar key={idx} className='size-4 border border-white'>
|
||||
<AvatarImage src={author?.avatarUrl} alt={author?.name} />
|
||||
<AvatarFallback className='border border-white bg-gray-100 text-[10px] text-gray-600'>
|
||||
{author?.name.slice(0, 2)}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
))}
|
||||
</div>
|
||||
<span className='text-gray-600 text-xs'>
|
||||
{(p.authors && p.authors.length > 0 ? p.authors : [p.author])
|
||||
.slice(0, 2)
|
||||
.map((a) => a?.name)
|
||||
.join(', ')}
|
||||
{(p.authors && p.authors.length > 0 ? p.authors : [p.author]).length > 2 && (
|
||||
<>
|
||||
{' '}
|
||||
and {(p.authors && p.authors.length > 0 ? p.authors : [p.author]).length - 2}{' '}
|
||||
other
|
||||
{(p.authors && p.authors.length > 0 ? p.authors : [p.author]).length - 2 > 1
|
||||
? 's'
|
||||
: ''}
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -12,6 +12,7 @@ export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
|
||||
pathname === '/' ||
|
||||
pathname.startsWith('/login') ||
|
||||
pathname.startsWith('/signup') ||
|
||||
pathname.startsWith('/reset-password') ||
|
||||
pathname.startsWith('/sso') ||
|
||||
pathname.startsWith('/terms') ||
|
||||
pathname.startsWith('/privacy') ||
|
||||
|
||||
@@ -759,3 +759,24 @@ input[type="search"]::-ms-clear {
|
||||
--surface-elevated: #202020;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove backticks from inline code in prose (Tailwind Typography default)
|
||||
*/
|
||||
.prose code::before,
|
||||
.prose code::after {
|
||||
content: none !important;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove underlines from heading anchor links in prose
|
||||
*/
|
||||
.prose h1 a,
|
||||
.prose h2 a,
|
||||
.prose h3 a,
|
||||
.prose h4 a,
|
||||
.prose h5 a,
|
||||
.prose h6 a {
|
||||
text-decoration: none !important;
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,17 @@ export async function GET(request: NextRequest) {
|
||||
.from(account)
|
||||
.where(and(...whereConditions))
|
||||
|
||||
return NextResponse.json({ accounts })
|
||||
// Use the user's email as the display name (consistent with credential selector)
|
||||
const userEmail = session.user.email
|
||||
|
||||
const accountsWithDisplayName = accounts.map((acc) => ({
|
||||
id: acc.id,
|
||||
accountId: acc.accountId,
|
||||
providerId: acc.providerId,
|
||||
displayName: userEmail || acc.providerId,
|
||||
}))
|
||||
|
||||
return NextResponse.json({ accounts: accountsWithDisplayName })
|
||||
} catch (error) {
|
||||
logger.error('Failed to fetch accounts', { error })
|
||||
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { createMockRequest, setupAuthApiMocks } from '@/app/api/__test-utils__/utils'
|
||||
|
||||
vi.mock('@/lib/core/utils/urls', () => ({
|
||||
getBaseUrl: vi.fn(() => 'https://app.example.com'),
|
||||
}))
|
||||
|
||||
describe('Forget Password API Route', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetModules()
|
||||
@@ -15,7 +19,7 @@ describe('Forget Password API Route', () => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('should send password reset email successfully', async () => {
|
||||
it('should send password reset email successfully with same-origin redirectTo', async () => {
|
||||
setupAuthApiMocks({
|
||||
operations: {
|
||||
forgetPassword: { success: true },
|
||||
@@ -24,7 +28,7 @@ describe('Forget Password API Route', () => {
|
||||
|
||||
const req = createMockRequest('POST', {
|
||||
email: 'test@example.com',
|
||||
redirectTo: 'https://example.com/reset',
|
||||
redirectTo: 'https://app.example.com/reset',
|
||||
})
|
||||
|
||||
const { POST } = await import('@/app/api/auth/forget-password/route')
|
||||
@@ -39,12 +43,36 @@ describe('Forget Password API Route', () => {
|
||||
expect(auth.auth.api.forgetPassword).toHaveBeenCalledWith({
|
||||
body: {
|
||||
email: 'test@example.com',
|
||||
redirectTo: 'https://example.com/reset',
|
||||
redirectTo: 'https://app.example.com/reset',
|
||||
},
|
||||
method: 'POST',
|
||||
})
|
||||
})
|
||||
|
||||
it('should reject external redirectTo URL', async () => {
|
||||
setupAuthApiMocks({
|
||||
operations: {
|
||||
forgetPassword: { success: true },
|
||||
},
|
||||
})
|
||||
|
||||
const req = createMockRequest('POST', {
|
||||
email: 'test@example.com',
|
||||
redirectTo: 'https://evil.com/phishing',
|
||||
})
|
||||
|
||||
const { POST } = await import('@/app/api/auth/forget-password/route')
|
||||
|
||||
const response = await POST(req)
|
||||
const data = await response.json()
|
||||
|
||||
expect(response.status).toBe(400)
|
||||
expect(data.message).toBe('Redirect URL must be a valid same-origin URL')
|
||||
|
||||
const auth = await import('@/lib/auth')
|
||||
expect(auth.auth.api.forgetPassword).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should send password reset email without redirectTo', async () => {
|
||||
setupAuthApiMocks({
|
||||
operations: {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { auth } from '@/lib/auth'
|
||||
import { isSameOrigin } from '@/lib/core/utils/validation'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
@@ -13,10 +14,15 @@ const forgetPasswordSchema = z.object({
|
||||
.email('Please provide a valid email address'),
|
||||
redirectTo: z
|
||||
.string()
|
||||
.url('Redirect URL must be a valid URL')
|
||||
.optional()
|
||||
.or(z.literal(''))
|
||||
.transform((val) => (val === '' ? undefined : val)),
|
||||
.transform((val) => (val === '' || val === undefined ? undefined : val))
|
||||
.refine(
|
||||
(val) => val === undefined || (z.string().url().safeParse(val).success && isSameOrigin(val)),
|
||||
{
|
||||
message: 'Redirect URL must be a valid same-origin URL',
|
||||
}
|
||||
),
|
||||
})
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
|
||||
@@ -38,7 +38,6 @@ vi.mock('@/lib/logs/console/logger', () => ({
|
||||
}))
|
||||
|
||||
import { db } from '@sim/db'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { refreshOAuthToken } from '@/lib/oauth/oauth'
|
||||
import {
|
||||
getCredential,
|
||||
@@ -49,7 +48,6 @@ import {
|
||||
|
||||
const mockDb = db as any
|
||||
const mockRefreshOAuthToken = refreshOAuthToken as any
|
||||
const mockLogger = (createLogger as any)()
|
||||
|
||||
describe('OAuth Utils', () => {
|
||||
beforeEach(() => {
|
||||
@@ -87,7 +85,6 @@ describe('OAuth Utils', () => {
|
||||
const userId = await getUserId('request-id')
|
||||
|
||||
expect(userId).toBeUndefined()
|
||||
expect(mockLogger.warn).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should return undefined if workflow is not found', async () => {
|
||||
@@ -96,7 +93,6 @@ describe('OAuth Utils', () => {
|
||||
const userId = await getUserId('request-id', 'nonexistent-workflow-id')
|
||||
|
||||
expect(userId).toBeUndefined()
|
||||
expect(mockLogger.warn).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -121,7 +117,6 @@ describe('OAuth Utils', () => {
|
||||
const credential = await getCredential('request-id', 'nonexistent-id', 'test-user-id')
|
||||
|
||||
expect(credential).toBeUndefined()
|
||||
expect(mockLogger.warn).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -139,7 +134,6 @@ describe('OAuth Utils', () => {
|
||||
|
||||
expect(mockRefreshOAuthToken).not.toHaveBeenCalled()
|
||||
expect(result).toEqual({ accessToken: 'valid-token', refreshed: false })
|
||||
expect(mockLogger.info).toHaveBeenCalledWith(expect.stringContaining('Access token is valid'))
|
||||
})
|
||||
|
||||
it('should refresh token when expired', async () => {
|
||||
@@ -163,9 +157,6 @@ describe('OAuth Utils', () => {
|
||||
expect(mockDb.update).toHaveBeenCalled()
|
||||
expect(mockDb.set).toHaveBeenCalled()
|
||||
expect(result).toEqual({ accessToken: 'new-token', refreshed: true })
|
||||
expect(mockLogger.info).toHaveBeenCalledWith(
|
||||
expect.stringContaining('Successfully refreshed')
|
||||
)
|
||||
})
|
||||
|
||||
it('should handle refresh token error', async () => {
|
||||
@@ -182,8 +173,6 @@ describe('OAuth Utils', () => {
|
||||
await expect(
|
||||
refreshTokenIfNeeded('request-id', mockCredential, 'credential-id')
|
||||
).rejects.toThrow('Failed to refresh token')
|
||||
|
||||
expect(mockLogger.error).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not attempt refresh if no refresh token', async () => {
|
||||
@@ -251,7 +240,6 @@ describe('OAuth Utils', () => {
|
||||
const token = await refreshAccessTokenIfNeeded('nonexistent-id', 'test-user-id', 'request-id')
|
||||
|
||||
expect(token).toBeNull()
|
||||
expect(mockLogger.warn).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should return null if refresh fails', async () => {
|
||||
@@ -270,7 +258,6 @@ describe('OAuth Utils', () => {
|
||||
const token = await refreshAccessTokenIfNeeded('credential-id', 'test-user-id', 'request-id')
|
||||
|
||||
expect(token).toBeNull()
|
||||
expect(mockLogger.error).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -18,6 +18,7 @@ interface AccountInsertData {
|
||||
updatedAt: Date
|
||||
refreshToken?: string
|
||||
idToken?: string
|
||||
accessTokenExpiresAt?: Date
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,6 +104,7 @@ export async function getOAuthToken(userId: string, providerId: string): Promise
|
||||
accessToken: account.accessToken,
|
||||
refreshToken: account.refreshToken,
|
||||
accessTokenExpiresAt: account.accessTokenExpiresAt,
|
||||
idToken: account.idToken,
|
||||
})
|
||||
.from(account)
|
||||
.where(and(eq(account.userId, userId), eq(account.providerId, providerId)))
|
||||
|
||||
@@ -2,6 +2,7 @@ import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { auth } from '@/lib/auth'
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import { REDACTED_MARKER } from '@/lib/core/security/redaction'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
|
||||
const logger = createLogger('SSO-Register')
|
||||
@@ -236,13 +237,13 @@ export async function POST(request: NextRequest) {
|
||||
oidcConfig: providerConfig.oidcConfig
|
||||
? {
|
||||
...providerConfig.oidcConfig,
|
||||
clientSecret: '[REDACTED]',
|
||||
clientSecret: REDACTED_MARKER,
|
||||
}
|
||||
: undefined,
|
||||
samlConfig: providerConfig.samlConfig
|
||||
? {
|
||||
...providerConfig.samlConfig,
|
||||
cert: '[REDACTED]',
|
||||
cert: REDACTED_MARKER,
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
|
||||
@@ -3,6 +3,7 @@ import { userStats } from '@sim/db/schema'
|
||||
import { eq, sql } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { logModelUsage } from '@/lib/billing/core/usage-log'
|
||||
import { checkAndBillOverageThreshold } from '@/lib/billing/threshold-billing'
|
||||
import { checkInternalApiKey } from '@/lib/copilot/utils'
|
||||
import { isBillingEnabled } from '@/lib/core/config/feature-flags'
|
||||
@@ -14,6 +15,9 @@ const logger = createLogger('BillingUpdateCostAPI')
|
||||
const UpdateCostSchema = z.object({
|
||||
userId: z.string().min(1, 'User ID is required'),
|
||||
cost: z.number().min(0, 'Cost must be a non-negative number'),
|
||||
model: z.string().min(1, 'Model is required'),
|
||||
inputTokens: z.number().min(0).default(0),
|
||||
outputTokens: z.number().min(0).default(0),
|
||||
})
|
||||
|
||||
/**
|
||||
@@ -71,11 +75,12 @@ export async function POST(req: NextRequest) {
|
||||
)
|
||||
}
|
||||
|
||||
const { userId, cost } = validation.data
|
||||
const { userId, cost, model, inputTokens, outputTokens } = validation.data
|
||||
|
||||
logger.info(`[${requestId}] Processing cost update`, {
|
||||
userId,
|
||||
cost,
|
||||
model,
|
||||
})
|
||||
|
||||
// Check if user stats record exists (same as ExecutionLogger)
|
||||
@@ -107,6 +112,16 @@ export async function POST(req: NextRequest) {
|
||||
addedCost: cost,
|
||||
})
|
||||
|
||||
// Log usage for complete audit trail
|
||||
await logModelUsage({
|
||||
userId,
|
||||
source: 'copilot',
|
||||
model,
|
||||
inputTokens,
|
||||
outputTokens,
|
||||
cost,
|
||||
})
|
||||
|
||||
// Check if user has hit overage threshold and bill incrementally
|
||||
await checkAndBillOverageThreshold(userId)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { randomUUID } from 'crypto'
|
||||
import { db } from '@sim/db'
|
||||
import { chat } from '@sim/db/schema'
|
||||
import { chat, workflow } from '@sim/db/schema'
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
@@ -94,6 +94,21 @@ export async function POST(
|
||||
if (!deployment.isActive) {
|
||||
logger.warn(`[${requestId}] Chat is not active: ${identifier}`)
|
||||
|
||||
const [workflowRecord] = await db
|
||||
.select({ workspaceId: workflow.workspaceId })
|
||||
.from(workflow)
|
||||
.where(eq(workflow.id, deployment.workflowId))
|
||||
.limit(1)
|
||||
|
||||
const workspaceId = workflowRecord?.workspaceId
|
||||
if (!workspaceId) {
|
||||
logger.warn(`[${requestId}] Cannot log: workflow ${deployment.workflowId} has no workspace`)
|
||||
return addCorsHeaders(
|
||||
createErrorResponse('This chat is currently unavailable', 403),
|
||||
request
|
||||
)
|
||||
}
|
||||
|
||||
const executionId = randomUUID()
|
||||
const loggingSession = new LoggingSession(
|
||||
deployment.workflowId,
|
||||
@@ -104,7 +119,7 @@ export async function POST(
|
||||
|
||||
await loggingSession.safeStart({
|
||||
userId: deployment.userId,
|
||||
workspaceId: '', // Will be resolved if needed
|
||||
workspaceId,
|
||||
variables: {},
|
||||
})
|
||||
|
||||
@@ -169,7 +184,14 @@ export async function POST(
|
||||
|
||||
const { actorUserId, workflowRecord } = preprocessResult
|
||||
const workspaceOwnerId = actorUserId!
|
||||
const workspaceId = workflowRecord?.workspaceId || ''
|
||||
const workspaceId = workflowRecord?.workspaceId
|
||||
if (!workspaceId) {
|
||||
logger.error(`[${requestId}] Workflow ${deployment.workflowId} has no workspaceId`)
|
||||
return addCorsHeaders(
|
||||
createErrorResponse('Workflow has no associated workspace', 500),
|
||||
request
|
||||
)
|
||||
}
|
||||
|
||||
try {
|
||||
const selectedOutputs: string[] = []
|
||||
|
||||
@@ -303,6 +303,14 @@ export async function POST(req: NextRequest) {
|
||||
apiVersion: 'preview',
|
||||
endpoint: env.AZURE_OPENAI_ENDPOINT,
|
||||
}
|
||||
} else if (providerEnv === 'vertex') {
|
||||
providerConfig = {
|
||||
provider: 'vertex',
|
||||
model: modelToUse,
|
||||
apiKey: env.COPILOT_API_KEY,
|
||||
vertexProject: env.VERTEX_PROJECT,
|
||||
vertexLocation: env.VERTEX_LOCATION,
|
||||
}
|
||||
} else {
|
||||
providerConfig = {
|
||||
provider: providerEnv,
|
||||
|
||||
@@ -11,7 +11,7 @@ import { createLogger } from '@/lib/logs/console/logger'
|
||||
|
||||
const logger = createLogger('CopilotChatsListAPI')
|
||||
|
||||
export async function GET(_req: NextRequest) {
|
||||
export async function GET(_request: NextRequest) {
|
||||
try {
|
||||
const { userId, isAuthenticated } = await authenticateCopilotRequestSessionOnly()
|
||||
if (!isAuthenticated || !userId) {
|
||||
|
||||
@@ -66,6 +66,14 @@ export async function POST(req: NextRequest) {
|
||||
apiVersion: env.AZURE_OPENAI_API_VERSION,
|
||||
endpoint: env.AZURE_OPENAI_ENDPOINT,
|
||||
}
|
||||
} else if (providerEnv === 'vertex') {
|
||||
providerConfig = {
|
||||
provider: 'vertex',
|
||||
model: modelToUse,
|
||||
apiKey: env.COPILOT_API_KEY,
|
||||
vertexProject: env.VERTEX_PROJECT,
|
||||
vertexLocation: env.VERTEX_LOCATION,
|
||||
}
|
||||
} else {
|
||||
providerConfig = {
|
||||
provider: providerEnv,
|
||||
|
||||
@@ -38,14 +38,13 @@ export async function GET(
|
||||
const cloudKey = isCloudPath ? path.slice(1).join('/') : fullPath
|
||||
|
||||
const contextParam = request.nextUrl.searchParams.get('context')
|
||||
const legacyBucketType = request.nextUrl.searchParams.get('bucket')
|
||||
|
||||
const context = contextParam || (isCloudPath ? inferContextFromKey(cloudKey) : undefined)
|
||||
|
||||
if (context === 'profile-pictures') {
|
||||
logger.info('Serving public profile picture:', { cloudKey })
|
||||
if (context === 'profile-pictures' || context === 'og-images') {
|
||||
logger.info(`Serving public ${context}:`, { cloudKey })
|
||||
if (isUsingCloudStorage() || isCloudPath) {
|
||||
return await handleCloudProxyPublic(cloudKey, context, legacyBucketType)
|
||||
return await handleCloudProxyPublic(cloudKey, context)
|
||||
}
|
||||
return await handleLocalFilePublic(fullPath)
|
||||
}
|
||||
@@ -182,8 +181,7 @@ async function handleCloudProxy(
|
||||
|
||||
async function handleCloudProxyPublic(
|
||||
cloudKey: string,
|
||||
context: StorageContext,
|
||||
legacyBucketType?: string | null
|
||||
context: StorageContext
|
||||
): Promise<NextResponse> {
|
||||
try {
|
||||
let fileBuffer: Buffer
|
||||
|
||||
@@ -141,6 +141,23 @@ export async function DELETE(
|
||||
)
|
||||
}
|
||||
|
||||
// Check if deleting this folder would delete the last workflow(s) in the workspace
|
||||
const workflowsInFolder = await countWorkflowsInFolderRecursively(
|
||||
id,
|
||||
existingFolder.workspaceId
|
||||
)
|
||||
const totalWorkflowsInWorkspace = await db
|
||||
.select({ id: workflow.id })
|
||||
.from(workflow)
|
||||
.where(eq(workflow.workspaceId, existingFolder.workspaceId))
|
||||
|
||||
if (workflowsInFolder > 0 && workflowsInFolder >= totalWorkflowsInWorkspace.length) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Cannot delete folder containing the only workflow(s) in the workspace' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// Recursively delete folder and all its contents
|
||||
const deletionStats = await deleteFolderRecursively(id, existingFolder.workspaceId)
|
||||
|
||||
@@ -202,6 +219,34 @@ async function deleteFolderRecursively(
|
||||
return stats
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the number of workflows in a folder and all its subfolders recursively.
|
||||
*/
|
||||
async function countWorkflowsInFolderRecursively(
|
||||
folderId: string,
|
||||
workspaceId: string
|
||||
): Promise<number> {
|
||||
let count = 0
|
||||
|
||||
const workflowsInFolder = await db
|
||||
.select({ id: workflow.id })
|
||||
.from(workflow)
|
||||
.where(and(eq(workflow.folderId, folderId), eq(workflow.workspaceId, workspaceId)))
|
||||
|
||||
count += workflowsInFolder.length
|
||||
|
||||
const childFolders = await db
|
||||
.select({ id: workflowFolder.id })
|
||||
.from(workflowFolder)
|
||||
.where(and(eq(workflowFolder.parentId, folderId), eq(workflowFolder.workspaceId, workspaceId)))
|
||||
|
||||
for (const childFolder of childFolders) {
|
||||
count += await countWorkflowsInFolderRecursively(childFolder.id, workspaceId)
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
// Helper function to check for circular references
|
||||
async function checkForCircularReference(folderId: string, parentId: string): Promise<boolean> {
|
||||
let currentParentId: string | null = parentId
|
||||
|
||||
@@ -7,10 +7,80 @@ import { NextRequest } from 'next/server'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { createMockRequest } from '@/app/api/__test-utils__/utils'
|
||||
|
||||
const mockCreateContext = vi.fn()
|
||||
const mockRunInContext = vi.fn()
|
||||
const mockScript = vi.fn()
|
||||
const mockExecuteInE2B = vi.fn()
|
||||
vi.mock('@/lib/execution/isolated-vm', () => ({
|
||||
executeInIsolatedVM: vi.fn().mockImplementation(async (req) => {
|
||||
const { code, params, envVars, contextVariables } = req
|
||||
const stdoutChunks: string[] = []
|
||||
|
||||
const mockConsole = {
|
||||
log: (...args: unknown[]) => {
|
||||
stdoutChunks.push(
|
||||
`${args.map((a) => (typeof a === 'object' ? JSON.stringify(a) : String(a))).join(' ')}\n`
|
||||
)
|
||||
},
|
||||
error: (...args: unknown[]) => {
|
||||
stdoutChunks.push(
|
||||
'ERROR: ' +
|
||||
args.map((a) => (typeof a === 'object' ? JSON.stringify(a) : String(a))).join(' ') +
|
||||
'\n'
|
||||
)
|
||||
},
|
||||
warn: (...args: unknown[]) => mockConsole.log('WARN:', ...args),
|
||||
info: (...args: unknown[]) => mockConsole.log(...args),
|
||||
}
|
||||
|
||||
try {
|
||||
const escapePattern = /this\.constructor\.constructor|\.constructor\s*\(/
|
||||
if (escapePattern.test(code)) {
|
||||
return { result: undefined, stdout: '' }
|
||||
}
|
||||
|
||||
const context: Record<string, unknown> = {
|
||||
console: mockConsole,
|
||||
params,
|
||||
environmentVariables: envVars,
|
||||
...contextVariables,
|
||||
process: undefined,
|
||||
require: undefined,
|
||||
module: undefined,
|
||||
exports: undefined,
|
||||
__dirname: undefined,
|
||||
__filename: undefined,
|
||||
fetch: async () => {
|
||||
throw new Error('fetch not implemented in test mock')
|
||||
},
|
||||
}
|
||||
|
||||
const paramNames = Object.keys(context)
|
||||
const paramValues = Object.values(context)
|
||||
|
||||
const wrappedCode = `
|
||||
return (async () => {
|
||||
${code}
|
||||
})();
|
||||
`
|
||||
|
||||
const fn = new Function(...paramNames, wrappedCode)
|
||||
const result = await fn(...paramValues)
|
||||
|
||||
return {
|
||||
result,
|
||||
stdout: stdoutChunks.join(''),
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
const err = error as Error
|
||||
return {
|
||||
result: null,
|
||||
stdout: stdoutChunks.join(''),
|
||||
error: {
|
||||
message: err.message || String(error),
|
||||
name: err.name || 'Error',
|
||||
stack: err.stack,
|
||||
},
|
||||
}
|
||||
}
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/logs/console/logger', () => ({
|
||||
createLogger: vi.fn(() => ({
|
||||
@@ -21,35 +91,20 @@ vi.mock('@/lib/logs/console/logger', () => ({
|
||||
})),
|
||||
}))
|
||||
|
||||
vi.mock('vm', () => ({
|
||||
createContext: vi.fn(),
|
||||
Script: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/execution/e2b', () => ({
|
||||
executeInE2B: vi.fn(),
|
||||
}))
|
||||
|
||||
import { createContext, Script } from 'vm'
|
||||
import { validateProxyUrl } from '@/lib/core/security/input-validation'
|
||||
import { executeInE2B } from '@/lib/execution/e2b'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { POST } from './route'
|
||||
|
||||
const mockedCreateContext = vi.mocked(createContext)
|
||||
const mockedScript = vi.mocked(Script)
|
||||
const mockedExecuteInE2B = vi.mocked(executeInE2B)
|
||||
const mockedCreateLogger = vi.mocked(createLogger)
|
||||
|
||||
describe('Function Execute API Route', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
|
||||
mockedCreateContext.mockReturnValue({})
|
||||
mockRunInContext.mockResolvedValue('vm success')
|
||||
mockedScript.mockImplementation((): any => ({
|
||||
runInContext: mockRunInContext,
|
||||
}))
|
||||
mockedExecuteInE2B.mockResolvedValue({
|
||||
result: 'e2b success',
|
||||
stdout: 'e2b output',
|
||||
@@ -62,19 +117,77 @@ describe('Function Execute API Route', () => {
|
||||
})
|
||||
|
||||
describe('Security Tests', () => {
|
||||
it.concurrent('should create secure fetch in VM context', async () => {
|
||||
it.concurrent('should use isolated-vm for secure sandboxed execution', async () => {
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'return "test"',
|
||||
})
|
||||
|
||||
await POST(req)
|
||||
const response = await POST(req)
|
||||
const data = await response.json()
|
||||
|
||||
expect(mockedCreateContext).toHaveBeenCalled()
|
||||
const contextArgs = mockedCreateContext.mock.calls[0][0]
|
||||
expect(contextArgs).toHaveProperty('fetch')
|
||||
expect(typeof (contextArgs as any).fetch).toBe('function')
|
||||
expect(response.status).toBe(200)
|
||||
expect(data.success).toBe(true)
|
||||
expect(data.output.result).toBe('test')
|
||||
})
|
||||
|
||||
expect((contextArgs as any).fetch?.name).toBe('secureFetch')
|
||||
it.concurrent('should prevent VM escape via constructor chain', async () => {
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'return this.constructor.constructor("return process")().env',
|
||||
})
|
||||
|
||||
const response = await POST(req)
|
||||
const data = await response.json()
|
||||
|
||||
if (response.status === 500) {
|
||||
expect(data.success).toBe(false)
|
||||
} else {
|
||||
const result = data.output?.result
|
||||
expect(result === undefined || result === null).toBe(true)
|
||||
}
|
||||
})
|
||||
|
||||
it.concurrent('should prevent access to require via constructor chain', async () => {
|
||||
const req = createMockRequest('POST', {
|
||||
code: `
|
||||
const proc = this.constructor.constructor("return process")();
|
||||
const fs = proc.mainModule.require("fs");
|
||||
return fs.readFileSync("/etc/passwd", "utf8");
|
||||
`,
|
||||
})
|
||||
|
||||
const response = await POST(req)
|
||||
const data = await response.json()
|
||||
|
||||
if (response.status === 200) {
|
||||
const result = data.output?.result
|
||||
if (result !== undefined && result !== null && typeof result === 'string') {
|
||||
expect(result).not.toContain('root:')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it.concurrent('should not expose process object', async () => {
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'return typeof process',
|
||||
})
|
||||
|
||||
const response = await POST(req)
|
||||
const data = await response.json()
|
||||
|
||||
expect(response.status).toBe(200)
|
||||
expect(data.output.result).toBe('undefined')
|
||||
})
|
||||
|
||||
it.concurrent('should not expose require function', async () => {
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'return typeof require',
|
||||
})
|
||||
|
||||
const response = await POST(req)
|
||||
const data = await response.json()
|
||||
|
||||
expect(response.status).toBe(200)
|
||||
expect(data.output.result).toBe('undefined')
|
||||
})
|
||||
|
||||
it.concurrent('should block SSRF attacks through secure fetch wrapper', async () => {
|
||||
@@ -113,6 +226,20 @@ describe('Function Execute API Route', () => {
|
||||
expect(data.output).toHaveProperty('executionTime')
|
||||
})
|
||||
|
||||
it.concurrent('should return computed result for multi-line code', async () => {
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'const a = 1;\nconst b = 2;\nconst c = 3;\nconst d = 4;\nreturn a + b + c + d;',
|
||||
timeout: 5000,
|
||||
})
|
||||
|
||||
const response = await POST(req)
|
||||
const data = await response.json()
|
||||
|
||||
expect(response.status).toBe(200)
|
||||
expect(data.success).toBe(true)
|
||||
expect(data.output.result).toBe(10)
|
||||
})
|
||||
|
||||
it.concurrent('should handle missing code parameter', async () => {
|
||||
const req = createMockRequest('POST', {
|
||||
timeout: 5000,
|
||||
@@ -312,20 +439,6 @@ describe('Function Execute API Route', () => {
|
||||
|
||||
describe('Enhanced Error Handling', () => {
|
||||
it('should provide detailed syntax error with line content', async () => {
|
||||
const syntaxError = new Error('Invalid or unexpected token')
|
||||
syntaxError.name = 'SyntaxError'
|
||||
syntaxError.stack = `user-function.js:5
|
||||
description: "This has a missing closing quote
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
SyntaxError: Invalid or unexpected token
|
||||
at new Script (node:vm:117:7)
|
||||
at POST (/path/to/route.ts:123:24)`
|
||||
|
||||
mockedScript.mockImplementationOnce(() => {
|
||||
throw syntaxError
|
||||
})
|
||||
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'const obj = {\n name: "test",\n description: "This has a missing closing quote\n};\nreturn obj;',
|
||||
timeout: 5000,
|
||||
@@ -336,28 +449,10 @@ SyntaxError: Invalid or unexpected token
|
||||
|
||||
expect(response.status).toBe(500)
|
||||
expect(data.success).toBe(false)
|
||||
expect(data.error).toContain('Syntax Error')
|
||||
expect(data.error).toContain('Line 3')
|
||||
expect(data.error).toContain('description: "This has a missing closing quote')
|
||||
expect(data.error).toContain('Invalid or unexpected token')
|
||||
expect(data.error).toContain('(Check for missing quotes, brackets, or semicolons)')
|
||||
|
||||
expect(data.debug).toBeDefined()
|
||||
expect(data.debug.line).toBe(3)
|
||||
expect(data.debug.errorType).toBe('SyntaxError')
|
||||
expect(data.debug.lineContent).toBe('description: "This has a missing closing quote')
|
||||
expect(data.error).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should provide detailed runtime error with line and column', async () => {
|
||||
const runtimeError = new Error("Cannot read properties of null (reading 'someMethod')")
|
||||
runtimeError.name = 'TypeError'
|
||||
runtimeError.stack = `TypeError: Cannot read properties of null (reading 'someMethod')
|
||||
at user-function.js:4:16
|
||||
at user-function.js:9:3
|
||||
at Script.runInContext (node:vm:147:14)`
|
||||
|
||||
mockRunInContext.mockRejectedValueOnce(runtimeError)
|
||||
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'const obj = null;\nreturn obj.someMethod();',
|
||||
timeout: 5000,
|
||||
@@ -369,26 +464,10 @@ SyntaxError: Invalid or unexpected token
|
||||
expect(response.status).toBe(500)
|
||||
expect(data.success).toBe(false)
|
||||
expect(data.error).toContain('Type Error')
|
||||
expect(data.error).toContain('Line 2')
|
||||
expect(data.error).toContain('return obj.someMethod();')
|
||||
expect(data.error).toContain('Cannot read properties of null')
|
||||
|
||||
expect(data.debug).toBeDefined()
|
||||
expect(data.debug.line).toBe(2)
|
||||
expect(data.debug.column).toBe(16)
|
||||
expect(data.debug.errorType).toBe('TypeError')
|
||||
expect(data.debug.lineContent).toBe('return obj.someMethod();')
|
||||
})
|
||||
|
||||
it('should handle ReferenceError with enhanced details', async () => {
|
||||
const referenceError = new Error('undefinedVariable is not defined')
|
||||
referenceError.name = 'ReferenceError'
|
||||
referenceError.stack = `ReferenceError: undefinedVariable is not defined
|
||||
at user-function.js:4:8
|
||||
at Script.runInContext (node:vm:147:14)`
|
||||
|
||||
mockRunInContext.mockRejectedValueOnce(referenceError)
|
||||
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'const x = 42;\nreturn undefinedVariable + x;',
|
||||
timeout: 5000,
|
||||
@@ -400,21 +479,12 @@ SyntaxError: Invalid or unexpected token
|
||||
expect(response.status).toBe(500)
|
||||
expect(data.success).toBe(false)
|
||||
expect(data.error).toContain('Reference Error')
|
||||
expect(data.error).toContain('Line 2')
|
||||
expect(data.error).toContain('return undefinedVariable + x;')
|
||||
expect(data.error).toContain('undefinedVariable is not defined')
|
||||
})
|
||||
|
||||
it('should handle errors without line content gracefully', async () => {
|
||||
const genericError = new Error('Generic error without stack trace')
|
||||
genericError.name = 'Error'
|
||||
|
||||
mockedScript.mockImplementationOnce(() => {
|
||||
throw genericError
|
||||
})
|
||||
|
||||
it('should handle thrown errors gracefully', async () => {
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'return "test";',
|
||||
code: 'throw new Error("Custom error message");',
|
||||
timeout: 5000,
|
||||
})
|
||||
|
||||
@@ -423,51 +493,10 @@ SyntaxError: Invalid or unexpected token
|
||||
|
||||
expect(response.status).toBe(500)
|
||||
expect(data.success).toBe(false)
|
||||
expect(data.error).toBe('Generic error without stack trace')
|
||||
|
||||
expect(data.debug).toBeDefined()
|
||||
expect(data.debug.errorType).toBe('Error')
|
||||
expect(data.debug.line).toBeUndefined()
|
||||
expect(data.debug.lineContent).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should extract line numbers from different stack trace formats', async () => {
|
||||
const testError = new Error('Test error')
|
||||
testError.name = 'Error'
|
||||
testError.stack = `Error: Test error
|
||||
at user-function.js:7:25
|
||||
at async function
|
||||
at Script.runInContext (node:vm:147:14)`
|
||||
|
||||
mockedScript.mockImplementationOnce(() => {
|
||||
throw testError
|
||||
})
|
||||
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'const a = 1;\nconst b = 2;\nconst c = 3;\nconst d = 4;\nreturn a + b + c + d;',
|
||||
timeout: 5000,
|
||||
})
|
||||
|
||||
const response = await POST(req)
|
||||
const data = await response.json()
|
||||
|
||||
expect(response.status).toBe(500)
|
||||
expect(data.success).toBe(false)
|
||||
|
||||
expect(data.debug.line).toBe(5)
|
||||
expect(data.debug.column).toBe(25)
|
||||
expect(data.debug.lineContent).toBe('return a + b + c + d;')
|
||||
expect(data.error).toContain('Custom error message')
|
||||
})
|
||||
|
||||
it.concurrent('should provide helpful suggestions for common syntax errors', async () => {
|
||||
const syntaxError = new Error('Unexpected end of input')
|
||||
syntaxError.name = 'SyntaxError'
|
||||
syntaxError.stack = 'user-function.js:4\nSyntaxError: Unexpected end of input'
|
||||
|
||||
mockedScript.mockImplementationOnce(() => {
|
||||
throw syntaxError
|
||||
})
|
||||
|
||||
const req = createMockRequest('POST', {
|
||||
code: 'const obj = {\n name: "test"\n// Missing closing brace',
|
||||
timeout: 5000,
|
||||
@@ -478,9 +507,7 @@ SyntaxError: Invalid or unexpected token
|
||||
|
||||
expect(response.status).toBe(500)
|
||||
expect(data.success).toBe(false)
|
||||
expect(data.error).toContain('Syntax Error')
|
||||
expect(data.error).toContain('Unexpected end of input')
|
||||
expect(data.error).toContain('(Check for missing closing brackets or braces)')
|
||||
expect(data.error).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import { createContext, Script } from 'vm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { isE2bEnabled } from '@/lib/core/config/feature-flags'
|
||||
import { validateProxyUrl } from '@/lib/core/security/input-validation'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { executeInE2B } from '@/lib/execution/e2b'
|
||||
import { executeInIsolatedVM } from '@/lib/execution/isolated-vm'
|
||||
import { CodeLanguage, DEFAULT_CODE_LANGUAGE, isValidCodeLanguage } from '@/lib/execution/languages'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import {
|
||||
createEnvVarPattern,
|
||||
createWorkflowVariablePattern,
|
||||
} from '@/executor/utils/reference-validation'
|
||||
export const dynamic = 'force-dynamic'
|
||||
export const runtime = 'nodejs'
|
||||
|
||||
@@ -13,30 +16,6 @@ export const MAX_DURATION = 210
|
||||
|
||||
const logger = createLogger('FunctionExecuteAPI')
|
||||
|
||||
function createSecureFetch(requestId: string) {
|
||||
const originalFetch = (globalThis as any).fetch || require('node-fetch').default
|
||||
|
||||
return async function secureFetch(input: any, init?: any) {
|
||||
const url = typeof input === 'string' ? input : input?.url || input
|
||||
|
||||
if (!url || typeof url !== 'string') {
|
||||
throw new Error('Invalid URL provided to fetch')
|
||||
}
|
||||
|
||||
const validation = validateProxyUrl(url)
|
||||
if (!validation.isValid) {
|
||||
logger.warn(`[${requestId}] Blocked fetch request due to SSRF validation`, {
|
||||
url: url.substring(0, 100),
|
||||
error: validation.error,
|
||||
})
|
||||
throw new Error(`Security Error: ${validation.error}`)
|
||||
}
|
||||
|
||||
return originalFetch(input, init)
|
||||
}
|
||||
}
|
||||
|
||||
// Constants for E2B code wrapping line counts
|
||||
const E2B_JS_WRAPPER_LINES = 3 // Lines before user code: ';(async () => {', ' try {', ' const __sim_result = await (async () => {'
|
||||
const E2B_PYTHON_WRAPPER_LINES = 1 // Lines before user code: 'def __sim_main__():'
|
||||
|
||||
@@ -346,9 +325,9 @@ function createUserFriendlyErrorMessage(
|
||||
): string {
|
||||
let errorMessage = enhanced.message
|
||||
|
||||
// Add line and column information if available
|
||||
// Add line information if available
|
||||
if (enhanced.line !== undefined) {
|
||||
let lineInfo = `Line ${enhanced.line}${enhanced.column !== undefined ? `:${enhanced.column}` : ''}`
|
||||
let lineInfo = `Line ${enhanced.line}`
|
||||
|
||||
// Add the actual line content if available
|
||||
if (enhanced.lineContent) {
|
||||
@@ -362,8 +341,7 @@ function createUserFriendlyErrorMessage(
|
||||
const stackMatch = enhanced.stack.match(/user-function\.js:(\d+)(?::(\d+))?/)
|
||||
if (stackMatch) {
|
||||
const line = Number.parseInt(stackMatch[1], 10)
|
||||
const column = stackMatch[2] ? Number.parseInt(stackMatch[2], 10) : undefined
|
||||
let lineInfo = `Line ${line}${column ? `:${column}` : ''}`
|
||||
let lineInfo = `Line ${line}`
|
||||
|
||||
// Try to get line content if we have userCode
|
||||
if (userCode) {
|
||||
@@ -400,27 +378,6 @@ function createUserFriendlyErrorMessage(
|
||||
}
|
||||
}
|
||||
|
||||
// For syntax errors, provide additional context
|
||||
if (enhanced.name === 'SyntaxError') {
|
||||
if (errorMessage.includes('Invalid or unexpected token')) {
|
||||
errorMessage += ' (Check for missing quotes, brackets, or semicolons)'
|
||||
} else if (errorMessage.includes('Unexpected end of input')) {
|
||||
errorMessage += ' (Check for missing closing brackets or braces)'
|
||||
} else if (errorMessage.includes('Unexpected token')) {
|
||||
// Check if this might be due to incomplete code
|
||||
if (
|
||||
enhanced.lineContent &&
|
||||
((enhanced.lineContent.includes('(') && !enhanced.lineContent.includes(')')) ||
|
||||
(enhanced.lineContent.includes('[') && !enhanced.lineContent.includes(']')) ||
|
||||
(enhanced.lineContent.includes('{') && !enhanced.lineContent.includes('}')))
|
||||
) {
|
||||
errorMessage += ' (Check for missing closing parentheses, brackets, or braces)'
|
||||
} else {
|
||||
errorMessage += ' (Check your syntax)'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errorMessage
|
||||
}
|
||||
|
||||
@@ -434,19 +391,27 @@ function resolveWorkflowVariables(
|
||||
): string {
|
||||
let resolvedCode = code
|
||||
|
||||
const variableMatches = resolvedCode.match(/<variable\.([^>]+)>/g) || []
|
||||
for (const match of variableMatches) {
|
||||
const variableName = match.slice('<variable.'.length, -1).trim()
|
||||
const regex = createWorkflowVariablePattern()
|
||||
let match: RegExpExecArray | null
|
||||
const replacements: Array<{
|
||||
match: string
|
||||
index: number
|
||||
variableName: string
|
||||
variableValue: unknown
|
||||
}> = []
|
||||
|
||||
while ((match = regex.exec(code)) !== null) {
|
||||
const variableName = match[1].trim()
|
||||
|
||||
// Find the variable by name (workflowVariables is indexed by ID, values are variable objects)
|
||||
const foundVariable = Object.entries(workflowVariables).find(
|
||||
([_, variable]) => (variable.name || '').replace(/\s+/g, '') === variableName
|
||||
)
|
||||
|
||||
let variableValue: unknown = ''
|
||||
if (foundVariable) {
|
||||
const variable = foundVariable[1]
|
||||
// Get the typed value - handle different variable types
|
||||
let variableValue = variable.value
|
||||
variableValue = variable.value
|
||||
|
||||
if (variable.value !== undefined && variable.value !== null) {
|
||||
try {
|
||||
@@ -468,22 +433,30 @@ function resolveWorkflowVariables(
|
||||
// Keep original value if JSON parsing fails
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
} catch {
|
||||
// Fallback to original value on error
|
||||
variableValue = variable.value
|
||||
}
|
||||
}
|
||||
|
||||
// Create a safe variable reference
|
||||
const safeVarName = `__variable_${variableName.replace(/[^a-zA-Z0-9_]/g, '_')}`
|
||||
contextVariables[safeVarName] = variableValue
|
||||
|
||||
// Replace the variable reference with the safe variable name
|
||||
resolvedCode = resolvedCode.replace(new RegExp(escapeRegExp(match), 'g'), safeVarName)
|
||||
} else {
|
||||
// Variable not found - replace with empty string to avoid syntax errors
|
||||
resolvedCode = resolvedCode.replace(new RegExp(escapeRegExp(match), 'g'), '')
|
||||
}
|
||||
|
||||
replacements.push({
|
||||
match: match[0],
|
||||
index: match.index,
|
||||
variableName,
|
||||
variableValue,
|
||||
})
|
||||
}
|
||||
|
||||
// Process replacements in reverse order to maintain correct indices
|
||||
for (let i = replacements.length - 1; i >= 0; i--) {
|
||||
const { match: matchStr, index, variableName, variableValue } = replacements[i]
|
||||
|
||||
// Use variable reference approach
|
||||
const safeVarName = `__variable_${variableName.replace(/[^a-zA-Z0-9_]/g, '_')}`
|
||||
contextVariables[safeVarName] = variableValue
|
||||
resolvedCode =
|
||||
resolvedCode.slice(0, index) + safeVarName + resolvedCode.slice(index + matchStr.length)
|
||||
}
|
||||
|
||||
return resolvedCode
|
||||
@@ -500,18 +473,29 @@ function resolveEnvironmentVariables(
|
||||
): string {
|
||||
let resolvedCode = code
|
||||
|
||||
const envVarMatches = resolvedCode.match(/\{\{([^}]+)\}\}/g) || []
|
||||
for (const match of envVarMatches) {
|
||||
const varName = match.slice(2, -2).trim()
|
||||
// Priority: 1. Environment variables from workflow, 2. Params
|
||||
const varValue = envVars[varName] || params[varName] || ''
|
||||
const regex = createEnvVarPattern()
|
||||
let match: RegExpExecArray | null
|
||||
const replacements: Array<{ match: string; index: number; varName: string; varValue: string }> =
|
||||
[]
|
||||
|
||||
while ((match = regex.exec(code)) !== null) {
|
||||
const varName = match[1].trim()
|
||||
const varValue = envVars[varName] || params[varName] || ''
|
||||
replacements.push({
|
||||
match: match[0],
|
||||
index: match.index,
|
||||
varName,
|
||||
varValue: String(varValue),
|
||||
})
|
||||
}
|
||||
|
||||
for (let i = replacements.length - 1; i >= 0; i--) {
|
||||
const { match: matchStr, index, varName, varValue } = replacements[i]
|
||||
|
||||
// Instead of injecting large JSON directly, create a variable reference
|
||||
const safeVarName = `__var_${varName.replace(/[^a-zA-Z0-9_]/g, '_')}`
|
||||
contextVariables[safeVarName] = varValue
|
||||
|
||||
// Replace the template with a variable reference
|
||||
resolvedCode = resolvedCode.replace(new RegExp(escapeRegExp(match), 'g'), safeVarName)
|
||||
resolvedCode =
|
||||
resolvedCode.slice(0, index) + safeVarName + resolvedCode.slice(index + matchStr.length)
|
||||
}
|
||||
|
||||
return resolvedCode
|
||||
@@ -899,28 +883,7 @@ export async function POST(req: NextRequest) {
|
||||
})
|
||||
}
|
||||
|
||||
const executionMethod = 'vm'
|
||||
const context = createContext({
|
||||
params: executionParams,
|
||||
environmentVariables: envVars,
|
||||
...contextVariables,
|
||||
fetch: createSecureFetch(requestId),
|
||||
console: {
|
||||
log: (...args: any[]) => {
|
||||
const logMessage = `${args
|
||||
.map((arg) => (typeof arg === 'object' ? JSON.stringify(arg) : String(arg)))
|
||||
.join(' ')}\n`
|
||||
stdout += logMessage
|
||||
},
|
||||
error: (...args: any[]) => {
|
||||
const errorMessage = `${args
|
||||
.map((arg) => (typeof arg === 'object' ? JSON.stringify(arg) : String(arg)))
|
||||
.join(' ')}\n`
|
||||
logger.error(`[${requestId}] Code Console Error: ${errorMessage}`)
|
||||
stdout += `ERROR: ${errorMessage}`
|
||||
},
|
||||
},
|
||||
})
|
||||
const executionMethod = 'isolated-vm'
|
||||
|
||||
const wrapperLines = ['(async () => {', ' try {']
|
||||
if (isCustomTool) {
|
||||
@@ -930,36 +893,99 @@ export async function POST(req: NextRequest) {
|
||||
})
|
||||
}
|
||||
userCodeStartLine = wrapperLines.length + 1
|
||||
const fullScript = [
|
||||
...wrapperLines,
|
||||
` ${resolvedCode.split('\n').join('\n ')}`,
|
||||
' } catch (error) {',
|
||||
' console.error(error);',
|
||||
' throw error;',
|
||||
' }',
|
||||
'})()',
|
||||
].join('\n')
|
||||
|
||||
const script = new Script(fullScript, {
|
||||
filename: 'user-function.js',
|
||||
lineOffset: 0,
|
||||
columnOffset: 0,
|
||||
})
|
||||
let codeToExecute = resolvedCode
|
||||
let prependedLineCount = 0
|
||||
if (isCustomTool) {
|
||||
const paramKeys = Object.keys(executionParams)
|
||||
const paramDestructuring = paramKeys.map((key) => `const ${key} = params.${key};`).join('\n')
|
||||
codeToExecute = `${paramDestructuring}\n${resolvedCode}`
|
||||
prependedLineCount = paramKeys.length
|
||||
}
|
||||
|
||||
const result = await script.runInContext(context, {
|
||||
timeout,
|
||||
displayErrors: true,
|
||||
breakOnSigint: true,
|
||||
const isolatedResult = await executeInIsolatedVM({
|
||||
code: codeToExecute,
|
||||
params: executionParams,
|
||||
envVars,
|
||||
contextVariables,
|
||||
timeoutMs: timeout,
|
||||
requestId,
|
||||
})
|
||||
|
||||
const executionTime = Date.now() - startTime
|
||||
|
||||
if (isolatedResult.error) {
|
||||
logger.error(`[${requestId}] Function execution failed in isolated-vm`, {
|
||||
error: isolatedResult.error,
|
||||
executionTime,
|
||||
})
|
||||
|
||||
const ivmError = isolatedResult.error
|
||||
// Adjust line number for prepended param destructuring in custom tools
|
||||
let adjustedLine = ivmError.line
|
||||
let adjustedLineContent = ivmError.lineContent
|
||||
if (prependedLineCount > 0 && ivmError.line !== undefined) {
|
||||
adjustedLine = Math.max(1, ivmError.line - prependedLineCount)
|
||||
// Get line content from original user code, not the prepended code
|
||||
const codeLines = resolvedCode.split('\n')
|
||||
if (adjustedLine <= codeLines.length) {
|
||||
adjustedLineContent = codeLines[adjustedLine - 1]?.trim()
|
||||
}
|
||||
}
|
||||
const enhancedError: EnhancedError = {
|
||||
message: ivmError.message,
|
||||
name: ivmError.name,
|
||||
stack: ivmError.stack,
|
||||
originalError: ivmError,
|
||||
line: adjustedLine,
|
||||
column: ivmError.column,
|
||||
lineContent: adjustedLineContent,
|
||||
}
|
||||
|
||||
const userFriendlyErrorMessage = createUserFriendlyErrorMessage(
|
||||
enhancedError,
|
||||
requestId,
|
||||
resolvedCode
|
||||
)
|
||||
|
||||
logger.error(`[${requestId}] Enhanced error details`, {
|
||||
originalMessage: ivmError.message,
|
||||
enhancedMessage: userFriendlyErrorMessage,
|
||||
line: enhancedError.line,
|
||||
column: enhancedError.column,
|
||||
lineContent: enhancedError.lineContent,
|
||||
errorType: enhancedError.name,
|
||||
})
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: userFriendlyErrorMessage,
|
||||
output: {
|
||||
result: null,
|
||||
stdout: cleanStdout(isolatedResult.stdout),
|
||||
executionTime,
|
||||
},
|
||||
debug: {
|
||||
line: enhancedError.line,
|
||||
column: enhancedError.column,
|
||||
errorType: enhancedError.name,
|
||||
lineContent: enhancedError.lineContent,
|
||||
stack: enhancedError.stack,
|
||||
},
|
||||
},
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
|
||||
stdout = isolatedResult.stdout
|
||||
logger.info(`[${requestId}] Function executed successfully using ${executionMethod}`, {
|
||||
executionTime,
|
||||
})
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
output: { result, stdout: cleanStdout(stdout), executionTime },
|
||||
output: { result: isolatedResult.result, stdout: cleanStdout(stdout), executionTime },
|
||||
})
|
||||
} catch (error: any) {
|
||||
const executionTime = Date.now() - startTime
|
||||
@@ -976,7 +1002,6 @@ export async function POST(req: NextRequest) {
|
||||
resolvedCode
|
||||
)
|
||||
|
||||
// Log enhanced error details for debugging
|
||||
logger.error(`[${requestId}] Enhanced error details`, {
|
||||
originalMessage: error.message,
|
||||
enhancedMessage: userFriendlyErrorMessage,
|
||||
@@ -995,7 +1020,6 @@ export async function POST(req: NextRequest) {
|
||||
stdout: cleanStdout(stdout),
|
||||
executionTime,
|
||||
},
|
||||
// Include debug information in development or for debugging
|
||||
debug: {
|
||||
line: enhancedError.line,
|
||||
column: enhancedError.column,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { runs } from '@trigger.dev/sdk'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { authenticateApiKeyFromHeader, updateApiKeyLastUsed } from '@/lib/api-key/service'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { createErrorResponse } from '@/app/api/workflows/utils'
|
||||
@@ -18,38 +17,44 @@ export async function GET(
|
||||
try {
|
||||
logger.debug(`[${requestId}] Getting status for task: ${taskId}`)
|
||||
|
||||
// Try session auth first (for web UI)
|
||||
const session = await getSession()
|
||||
let authenticatedUserId: string | null = session?.user?.id || null
|
||||
|
||||
if (!authenticatedUserId) {
|
||||
const apiKeyHeader = request.headers.get('x-api-key')
|
||||
if (apiKeyHeader) {
|
||||
const authResult = await authenticateApiKeyFromHeader(apiKeyHeader)
|
||||
if (authResult.success && authResult.userId) {
|
||||
authenticatedUserId = authResult.userId
|
||||
if (authResult.keyId) {
|
||||
await updateApiKeyLastUsed(authResult.keyId).catch((error) => {
|
||||
logger.warn(`[${requestId}] Failed to update API key last used timestamp:`, {
|
||||
keyId: authResult.keyId,
|
||||
error,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||
if (!authResult.success || !authResult.userId) {
|
||||
logger.warn(`[${requestId}] Unauthorized task status request`)
|
||||
return createErrorResponse(authResult.error || 'Authentication required', 401)
|
||||
}
|
||||
|
||||
if (!authenticatedUserId) {
|
||||
return createErrorResponse('Authentication required', 401)
|
||||
}
|
||||
const authenticatedUserId = authResult.userId
|
||||
|
||||
// Fetch task status from Trigger.dev
|
||||
const run = await runs.retrieve(taskId)
|
||||
|
||||
logger.debug(`[${requestId}] Task ${taskId} status: ${run.status}`)
|
||||
|
||||
// Map Trigger.dev status to our format
|
||||
const payload = run.payload as any
|
||||
if (payload?.workflowId) {
|
||||
const { verifyWorkflowAccess } = await import('@/socket-server/middleware/permissions')
|
||||
const accessCheck = await verifyWorkflowAccess(authenticatedUserId, payload.workflowId)
|
||||
if (!accessCheck.hasAccess) {
|
||||
logger.warn(`[${requestId}] User ${authenticatedUserId} denied access to task ${taskId}`, {
|
||||
workflowId: payload.workflowId,
|
||||
})
|
||||
return createErrorResponse('Access denied', 403)
|
||||
}
|
||||
logger.debug(`[${requestId}] User ${authenticatedUserId} has access to task ${taskId}`)
|
||||
} else {
|
||||
if (payload?.userId && payload.userId !== authenticatedUserId) {
|
||||
logger.warn(
|
||||
`[${requestId}] User ${authenticatedUserId} attempted to access task ${taskId} owned by ${payload.userId}`
|
||||
)
|
||||
return createErrorResponse('Access denied', 403)
|
||||
}
|
||||
if (!payload?.userId) {
|
||||
logger.warn(
|
||||
`[${requestId}] Task ${taskId} has no ownership information in payload. Denying access for security.`
|
||||
)
|
||||
return createErrorResponse('Access denied', 403)
|
||||
}
|
||||
}
|
||||
|
||||
const statusMap = {
|
||||
QUEUED: 'queued',
|
||||
WAITING_FOR_DEPLOY: 'queued',
|
||||
@@ -67,7 +72,6 @@ export async function GET(
|
||||
|
||||
const mappedStatus = statusMap[run.status as keyof typeof statusMap] || 'unknown'
|
||||
|
||||
// Build response based on status
|
||||
const response: any = {
|
||||
success: true,
|
||||
taskId,
|
||||
@@ -77,21 +81,18 @@ export async function GET(
|
||||
},
|
||||
}
|
||||
|
||||
// Add completion details if finished
|
||||
if (mappedStatus === 'completed') {
|
||||
response.output = run.output // This contains the workflow execution results
|
||||
response.metadata.completedAt = run.finishedAt
|
||||
response.metadata.duration = run.durationMs
|
||||
}
|
||||
|
||||
// Add error details if failed
|
||||
if (mappedStatus === 'failed') {
|
||||
response.error = run.error
|
||||
response.metadata.completedAt = run.finishedAt
|
||||
response.metadata.duration = run.durationMs
|
||||
}
|
||||
|
||||
// Add progress info if still processing
|
||||
if (mappedStatus === 'processing' || mappedStatus === 'queued') {
|
||||
response.estimatedDuration = 180000 // 3 minutes max from our config
|
||||
}
|
||||
@@ -107,6 +108,3 @@ export async function GET(
|
||||
return createErrorResponse('Failed to fetch task status', 500)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Implement task cancellation via Trigger.dev API if needed
|
||||
// export async function DELETE() { ... }
|
||||
|
||||
@@ -156,6 +156,7 @@ export async function POST(
|
||||
const validatedData = CreateChunkSchema.parse(searchParams)
|
||||
|
||||
const docTags = {
|
||||
// Text tags (7 slots)
|
||||
tag1: doc.tag1 ?? null,
|
||||
tag2: doc.tag2 ?? null,
|
||||
tag3: doc.tag3 ?? null,
|
||||
@@ -163,6 +164,19 @@ export async function POST(
|
||||
tag5: doc.tag5 ?? null,
|
||||
tag6: doc.tag6 ?? null,
|
||||
tag7: doc.tag7 ?? null,
|
||||
// Number tags (5 slots)
|
||||
number1: doc.number1 ?? null,
|
||||
number2: doc.number2 ?? null,
|
||||
number3: doc.number3 ?? null,
|
||||
number4: doc.number4 ?? null,
|
||||
number5: doc.number5 ?? null,
|
||||
// Date tags (2 slots)
|
||||
date1: doc.date1 ?? null,
|
||||
date2: doc.date2 ?? null,
|
||||
// Boolean tags (3 slots)
|
||||
boolean1: doc.boolean1 ?? null,
|
||||
boolean2: doc.boolean2 ?? null,
|
||||
boolean3: doc.boolean3 ?? null,
|
||||
}
|
||||
|
||||
const newChunk = await createChunk(
|
||||
|
||||
@@ -72,6 +72,16 @@ describe('Document By ID API Route', () => {
|
||||
tag5: null,
|
||||
tag6: null,
|
||||
tag7: null,
|
||||
number1: null,
|
||||
number2: null,
|
||||
number3: null,
|
||||
number4: null,
|
||||
number5: null,
|
||||
date1: null,
|
||||
date2: null,
|
||||
boolean1: null,
|
||||
boolean2: null,
|
||||
boolean3: null,
|
||||
deletedAt: null,
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ const UpdateDocumentSchema = z.object({
|
||||
processingError: z.string().optional(),
|
||||
markFailedDueToTimeout: z.boolean().optional(),
|
||||
retryProcessing: z.boolean().optional(),
|
||||
// Tag fields
|
||||
// Text tag fields
|
||||
tag1: z.string().optional(),
|
||||
tag2: z.string().optional(),
|
||||
tag3: z.string().optional(),
|
||||
@@ -31,6 +31,19 @@ const UpdateDocumentSchema = z.object({
|
||||
tag5: z.string().optional(),
|
||||
tag6: z.string().optional(),
|
||||
tag7: z.string().optional(),
|
||||
// Number tag fields
|
||||
number1: z.string().optional(),
|
||||
number2: z.string().optional(),
|
||||
number3: z.string().optional(),
|
||||
number4: z.string().optional(),
|
||||
number5: z.string().optional(),
|
||||
// Date tag fields
|
||||
date1: z.string().optional(),
|
||||
date2: z.string().optional(),
|
||||
// Boolean tag fields
|
||||
boolean1: z.string().optional(),
|
||||
boolean2: z.string().optional(),
|
||||
boolean3: z.string().optional(),
|
||||
})
|
||||
|
||||
export async function GET(
|
||||
|
||||
@@ -80,6 +80,16 @@ describe('Knowledge Base Documents API Route', () => {
|
||||
tag5: null,
|
||||
tag6: null,
|
||||
tag7: null,
|
||||
number1: null,
|
||||
number2: null,
|
||||
number3: null,
|
||||
number4: null,
|
||||
number5: null,
|
||||
date1: null,
|
||||
date2: null,
|
||||
boolean1: null,
|
||||
boolean2: null,
|
||||
boolean3: null,
|
||||
deletedAt: null,
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ const UpdateKnowledgeBaseSchema = z.object({
|
||||
.optional(),
|
||||
})
|
||||
|
||||
export async function GET(_req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
||||
export async function GET(_request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
||||
const requestId = generateRequestId()
|
||||
const { id } = await params
|
||||
|
||||
@@ -133,7 +133,10 @@ export async function PUT(req: NextRequest, { params }: { params: Promise<{ id:
|
||||
}
|
||||
}
|
||||
|
||||
export async function DELETE(_req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
||||
export async function DELETE(
|
||||
_request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
const requestId = generateRequestId()
|
||||
const { id } = await params
|
||||
|
||||
|
||||
@@ -64,6 +64,11 @@ vi.mock('@/app/api/knowledge/utils', () => ({
|
||||
checkKnowledgeBaseAccess: mockCheckKnowledgeBaseAccess,
|
||||
}))
|
||||
|
||||
const mockGetDocumentTagDefinitions = vi.fn()
|
||||
vi.mock('@/lib/knowledge/tags/service', () => ({
|
||||
getDocumentTagDefinitions: mockGetDocumentTagDefinitions,
|
||||
}))
|
||||
|
||||
const mockHandleTagOnlySearch = vi.fn()
|
||||
const mockHandleVectorOnlySearch = vi.fn()
|
||||
const mockHandleTagAndVectorSearch = vi.fn()
|
||||
@@ -156,6 +161,7 @@ describe('Knowledge Search API Route', () => {
|
||||
doc1: 'Document 1',
|
||||
doc2: 'Document 2',
|
||||
})
|
||||
mockGetDocumentTagDefinitions.mockClear()
|
||||
|
||||
vi.stubGlobal('crypto', {
|
||||
randomUUID: vi.fn().mockReturnValue('mock-uuid-1234-5678'),
|
||||
@@ -659,8 +665,8 @@ describe('Knowledge Search API Route', () => {
|
||||
|
||||
describe('Optional Query Search', () => {
|
||||
const mockTagDefinitions = [
|
||||
{ tagSlot: 'tag1', displayName: 'category' },
|
||||
{ tagSlot: 'tag2', displayName: 'priority' },
|
||||
{ tagSlot: 'tag1', displayName: 'category', fieldType: 'text' },
|
||||
{ tagSlot: 'tag2', displayName: 'priority', fieldType: 'text' },
|
||||
]
|
||||
|
||||
const mockTaggedResults = [
|
||||
@@ -689,9 +695,7 @@ describe('Knowledge Search API Route', () => {
|
||||
it('should perform tag-only search without query', async () => {
|
||||
const tagOnlyData = {
|
||||
knowledgeBaseIds: 'kb-123',
|
||||
filters: {
|
||||
category: 'api',
|
||||
},
|
||||
tagFilters: [{ tagName: 'category', value: 'api', fieldType: 'text', operator: 'eq' }],
|
||||
topK: 10,
|
||||
}
|
||||
|
||||
@@ -706,10 +710,11 @@ describe('Knowledge Search API Route', () => {
|
||||
},
|
||||
})
|
||||
|
||||
// Mock tag definitions queries for filter mapping and display mapping
|
||||
mockDbChain.limit
|
||||
.mockResolvedValueOnce(mockTagDefinitions) // Tag definitions for filter mapping
|
||||
.mockResolvedValueOnce(mockTagDefinitions) // Tag definitions for display mapping
|
||||
// Mock tag definitions for validation
|
||||
mockGetDocumentTagDefinitions.mockResolvedValue(mockTagDefinitions)
|
||||
|
||||
// Mock tag definitions queries for display mapping
|
||||
mockDbChain.limit.mockResolvedValueOnce(mockTagDefinitions)
|
||||
|
||||
// Mock the tag-only search handler
|
||||
mockHandleTagOnlySearch.mockResolvedValue(mockTaggedResults)
|
||||
@@ -729,7 +734,9 @@ describe('Knowledge Search API Route', () => {
|
||||
expect(mockHandleTagOnlySearch).toHaveBeenCalledWith({
|
||||
knowledgeBaseIds: ['kb-123'],
|
||||
topK: 10,
|
||||
filters: { category: 'api' }, // Note: When no tag definitions are found, it uses the original filter key
|
||||
structuredFilters: [
|
||||
{ tagSlot: 'tag1', fieldType: 'text', operator: 'eq', value: 'api', valueTo: undefined },
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
@@ -737,9 +744,7 @@ describe('Knowledge Search API Route', () => {
|
||||
const combinedData = {
|
||||
knowledgeBaseIds: 'kb-123',
|
||||
query: 'test search',
|
||||
filters: {
|
||||
category: 'api',
|
||||
},
|
||||
tagFilters: [{ tagName: 'category', value: 'api', fieldType: 'text', operator: 'eq' }],
|
||||
topK: 10,
|
||||
}
|
||||
|
||||
@@ -754,10 +759,11 @@ describe('Knowledge Search API Route', () => {
|
||||
},
|
||||
})
|
||||
|
||||
// Mock tag definitions queries for filter mapping and display mapping
|
||||
mockDbChain.limit
|
||||
.mockResolvedValueOnce(mockTagDefinitions) // Tag definitions for filter mapping
|
||||
.mockResolvedValueOnce(mockTagDefinitions) // Tag definitions for display mapping
|
||||
// Mock tag definitions for validation
|
||||
mockGetDocumentTagDefinitions.mockResolvedValue(mockTagDefinitions)
|
||||
|
||||
// Mock tag definitions queries for display mapping
|
||||
mockDbChain.limit.mockResolvedValueOnce(mockTagDefinitions)
|
||||
|
||||
// Mock the tag + vector search handler
|
||||
mockHandleTagAndVectorSearch.mockResolvedValue(mockSearchResults)
|
||||
@@ -784,7 +790,9 @@ describe('Knowledge Search API Route', () => {
|
||||
expect(mockHandleTagAndVectorSearch).toHaveBeenCalledWith({
|
||||
knowledgeBaseIds: ['kb-123'],
|
||||
topK: 10,
|
||||
filters: { category: 'api' }, // Note: When no tag definitions are found, it uses the original filter key
|
||||
structuredFilters: [
|
||||
{ tagSlot: 'tag1', fieldType: 'text', operator: 'eq', value: 'api', valueTo: undefined },
|
||||
],
|
||||
queryVector: JSON.stringify(mockEmbedding),
|
||||
distanceThreshold: 1, // Single KB uses threshold of 1.0
|
||||
})
|
||||
@@ -928,10 +936,10 @@ describe('Knowledge Search API Route', () => {
|
||||
it('should handle tag-only search with multiple knowledge bases', async () => {
|
||||
const multiKbTagData = {
|
||||
knowledgeBaseIds: ['kb-123', 'kb-456'],
|
||||
filters: {
|
||||
category: 'docs',
|
||||
priority: 'high',
|
||||
},
|
||||
tagFilters: [
|
||||
{ tagName: 'category', value: 'docs', fieldType: 'text', operator: 'eq' },
|
||||
{ tagName: 'priority', value: 'high', fieldType: 'text', operator: 'eq' },
|
||||
],
|
||||
topK: 10,
|
||||
}
|
||||
|
||||
@@ -951,37 +959,14 @@ describe('Knowledge Search API Route', () => {
|
||||
knowledgeBase: { id: 'kb-456', userId: 'user-123', name: 'Test KB 2' },
|
||||
})
|
||||
|
||||
// Reset all mocks before setting up specific behavior
|
||||
Object.values(mockDbChain).forEach((fn) => {
|
||||
if (typeof fn === 'function') {
|
||||
fn.mockClear().mockReturnThis()
|
||||
}
|
||||
})
|
||||
// Mock tag definitions for validation
|
||||
mockGetDocumentTagDefinitions.mockResolvedValue(mockTagDefinitions)
|
||||
|
||||
// Create fresh mocks for multiple database calls needed for multi-KB tag search
|
||||
const mockTagDefsQuery1 = {
|
||||
...mockDbChain,
|
||||
limit: vi.fn().mockResolvedValue(mockTagDefinitions),
|
||||
}
|
||||
const mockTagSearchQuery = {
|
||||
...mockDbChain,
|
||||
limit: vi.fn().mockResolvedValue(mockTaggedResults),
|
||||
}
|
||||
const mockTagDefsQuery2 = {
|
||||
...mockDbChain,
|
||||
limit: vi.fn().mockResolvedValue(mockTagDefinitions),
|
||||
}
|
||||
const mockTagDefsQuery3 = {
|
||||
...mockDbChain,
|
||||
limit: vi.fn().mockResolvedValue(mockTagDefinitions),
|
||||
}
|
||||
// Mock the tag-only search handler
|
||||
mockHandleTagOnlySearch.mockResolvedValue(mockTaggedResults)
|
||||
|
||||
// Chain the mocks for: tag defs, search, display mapping KB1, display mapping KB2
|
||||
mockDbChain.select
|
||||
.mockReturnValueOnce(mockTagDefsQuery1)
|
||||
.mockReturnValueOnce(mockTagSearchQuery)
|
||||
.mockReturnValueOnce(mockTagDefsQuery2)
|
||||
.mockReturnValueOnce(mockTagDefsQuery3)
|
||||
// Mock tag definitions queries for display mapping
|
||||
mockDbChain.limit.mockResolvedValueOnce(mockTagDefinitions)
|
||||
|
||||
const req = createMockRequest('POST', multiKbTagData)
|
||||
const { POST } = await import('@/app/api/knowledge/search/route')
|
||||
@@ -1076,6 +1061,11 @@ describe('Knowledge Search API Route', () => {
|
||||
},
|
||||
})
|
||||
|
||||
// Mock tag definitions for validation
|
||||
mockGetDocumentTagDefinitions.mockResolvedValue([
|
||||
{ tagSlot: 'tag1', displayName: 'tag1', fieldType: 'text' },
|
||||
])
|
||||
|
||||
mockHandleTagOnlySearch.mockResolvedValue([
|
||||
{
|
||||
id: 'chunk-2',
|
||||
@@ -1108,13 +1098,15 @@ describe('Knowledge Search API Route', () => {
|
||||
const mockTagDefs = {
|
||||
select: vi.fn().mockReturnThis(),
|
||||
from: vi.fn().mockReturnThis(),
|
||||
where: vi.fn().mockResolvedValue([]),
|
||||
where: vi
|
||||
.fn()
|
||||
.mockResolvedValue([{ tagSlot: 'tag1', displayName: 'tag1', fieldType: 'text' }]),
|
||||
}
|
||||
mockDbChain.select.mockReturnValueOnce(mockTagDefs)
|
||||
|
||||
const req = createMockRequest('POST', {
|
||||
knowledgeBaseIds: ['kb-123'],
|
||||
filters: { tag1: 'api' },
|
||||
tagFilters: [{ tagName: 'tag1', value: 'api', fieldType: 'text', operator: 'eq' }],
|
||||
topK: 10,
|
||||
})
|
||||
|
||||
@@ -1143,6 +1135,11 @@ describe('Knowledge Search API Route', () => {
|
||||
},
|
||||
})
|
||||
|
||||
// Mock tag definitions for validation
|
||||
mockGetDocumentTagDefinitions.mockResolvedValue([
|
||||
{ tagSlot: 'tag1', displayName: 'tag1', fieldType: 'text' },
|
||||
])
|
||||
|
||||
mockHandleTagAndVectorSearch.mockResolvedValue([
|
||||
{
|
||||
id: 'chunk-3',
|
||||
@@ -1176,14 +1173,16 @@ describe('Knowledge Search API Route', () => {
|
||||
const mockTagDefs = {
|
||||
select: vi.fn().mockReturnThis(),
|
||||
from: vi.fn().mockReturnThis(),
|
||||
where: vi.fn().mockResolvedValue([]),
|
||||
where: vi
|
||||
.fn()
|
||||
.mockResolvedValue([{ tagSlot: 'tag1', displayName: 'tag1', fieldType: 'text' }]),
|
||||
}
|
||||
mockDbChain.select.mockReturnValueOnce(mockTagDefs)
|
||||
|
||||
const req = createMockRequest('POST', {
|
||||
knowledgeBaseIds: ['kb-123'],
|
||||
query: 'relevant content',
|
||||
filters: { tag1: 'guide' },
|
||||
tagFilters: [{ tagName: 'tag1', value: 'guide', fieldType: 'text', operator: 'eq' }],
|
||||
topK: 10,
|
||||
})
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { TAG_SLOTS } from '@/lib/knowledge/constants'
|
||||
import { ALL_TAG_SLOTS } from '@/lib/knowledge/constants'
|
||||
import { getDocumentTagDefinitions } from '@/lib/knowledge/tags/service'
|
||||
import { buildUndefinedTagsError, validateTagValue } from '@/lib/knowledge/tags/utils'
|
||||
import type { StructuredFilter } from '@/lib/knowledge/types'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { estimateTokenCount } from '@/lib/tokenization/estimators'
|
||||
import { getUserId } from '@/app/api/auth/oauth/utils'
|
||||
@@ -20,6 +22,16 @@ import { calculateCost } from '@/providers/utils'
|
||||
|
||||
const logger = createLogger('VectorSearchAPI')
|
||||
|
||||
/** Structured tag filter with operator support */
|
||||
const StructuredTagFilterSchema = z.object({
|
||||
tagName: z.string(),
|
||||
tagSlot: z.string().optional(),
|
||||
fieldType: z.enum(['text', 'number', 'date', 'boolean']).default('text'),
|
||||
operator: z.string().default('eq'),
|
||||
value: z.union([z.string(), z.number(), z.boolean()]),
|
||||
valueTo: z.union([z.string(), z.number()]).optional(),
|
||||
})
|
||||
|
||||
const VectorSearchSchema = z
|
||||
.object({
|
||||
knowledgeBaseIds: z.union([
|
||||
@@ -39,18 +51,17 @@ const VectorSearchSchema = z
|
||||
.nullable()
|
||||
.default(10)
|
||||
.transform((val) => val ?? 10),
|
||||
filters: z
|
||||
.record(z.string())
|
||||
tagFilters: z
|
||||
.array(StructuredTagFilterSchema)
|
||||
.optional()
|
||||
.nullable()
|
||||
.transform((val) => val || undefined), // Allow dynamic filter keys (display names)
|
||||
.transform((val) => val || undefined),
|
||||
})
|
||||
.refine(
|
||||
(data) => {
|
||||
// Ensure at least query or filters are provided
|
||||
const hasQuery = data.query && data.query.trim().length > 0
|
||||
const hasFilters = data.filters && Object.keys(data.filters).length > 0
|
||||
return hasQuery || hasFilters
|
||||
const hasTagFilters = data.tagFilters && data.tagFilters.length > 0
|
||||
return hasQuery || hasTagFilters
|
||||
},
|
||||
{
|
||||
message: 'Please provide either a search query or tag filters to search your knowledge base',
|
||||
@@ -88,45 +99,81 @@ export async function POST(request: NextRequest) {
|
||||
)
|
||||
|
||||
// Map display names to tag slots for filtering
|
||||
let mappedFilters: Record<string, string> = {}
|
||||
if (validatedData.filters && accessibleKbIds.length > 0) {
|
||||
try {
|
||||
// Fetch tag definitions for the first accessible KB (since we're using single KB now)
|
||||
const kbId = accessibleKbIds[0]
|
||||
const tagDefs = await getDocumentTagDefinitions(kbId)
|
||||
let structuredFilters: StructuredFilter[] = []
|
||||
|
||||
logger.debug(`[${requestId}] Found tag definitions:`, tagDefs)
|
||||
logger.debug(`[${requestId}] Original filters:`, validatedData.filters)
|
||||
// Handle tag filters
|
||||
if (validatedData.tagFilters && accessibleKbIds.length > 0) {
|
||||
const kbId = accessibleKbIds[0]
|
||||
const tagDefs = await getDocumentTagDefinitions(kbId)
|
||||
|
||||
// Create mapping from display name to tag slot
|
||||
const displayNameToSlot: Record<string, string> = {}
|
||||
tagDefs.forEach((def) => {
|
||||
displayNameToSlot[def.displayName] = def.tagSlot
|
||||
})
|
||||
// Create mapping from display name to tag slot and fieldType
|
||||
const displayNameToTagDef: Record<string, { tagSlot: string; fieldType: string }> = {}
|
||||
tagDefs.forEach((def) => {
|
||||
displayNameToTagDef[def.displayName] = {
|
||||
tagSlot: def.tagSlot,
|
||||
fieldType: def.fieldType,
|
||||
}
|
||||
})
|
||||
|
||||
// Map the filters and handle OR logic
|
||||
Object.entries(validatedData.filters).forEach(([key, value]) => {
|
||||
if (value) {
|
||||
const tagSlot = displayNameToSlot[key] || key // Fallback to key if no mapping found
|
||||
// Validate all tag filters first
|
||||
const undefinedTags: string[] = []
|
||||
const typeErrors: string[] = []
|
||||
|
||||
// Check if this is an OR filter (contains |OR| separator)
|
||||
if (value.includes('|OR|')) {
|
||||
logger.debug(
|
||||
`[${requestId}] OR filter detected: "${key}" -> "${tagSlot}" = "${value}"`
|
||||
)
|
||||
}
|
||||
for (const filter of validatedData.tagFilters) {
|
||||
const tagDef = displayNameToTagDef[filter.tagName]
|
||||
|
||||
mappedFilters[tagSlot] = value
|
||||
logger.debug(`[${requestId}] Mapped filter: "${key}" -> "${tagSlot}" = "${value}"`)
|
||||
}
|
||||
})
|
||||
// Check if tag exists
|
||||
if (!tagDef) {
|
||||
undefinedTags.push(filter.tagName)
|
||||
continue
|
||||
}
|
||||
|
||||
logger.debug(`[${requestId}] Final mapped filters:`, mappedFilters)
|
||||
} catch (error) {
|
||||
logger.error(`[${requestId}] Filter mapping error:`, error)
|
||||
// If mapping fails, use original filters
|
||||
mappedFilters = validatedData.filters
|
||||
// Validate value type using shared validation
|
||||
const validationError = validateTagValue(
|
||||
filter.tagName,
|
||||
String(filter.value),
|
||||
tagDef.fieldType
|
||||
)
|
||||
if (validationError) {
|
||||
typeErrors.push(validationError)
|
||||
}
|
||||
}
|
||||
|
||||
// Throw combined error if there are any validation issues
|
||||
if (undefinedTags.length > 0 || typeErrors.length > 0) {
|
||||
const errorParts: string[] = []
|
||||
|
||||
if (undefinedTags.length > 0) {
|
||||
errorParts.push(buildUndefinedTagsError(undefinedTags))
|
||||
}
|
||||
|
||||
if (typeErrors.length > 0) {
|
||||
errorParts.push(...typeErrors)
|
||||
}
|
||||
|
||||
return NextResponse.json({ error: errorParts.join('\n') }, { status: 400 })
|
||||
}
|
||||
|
||||
// Build structured filters with validated data
|
||||
structuredFilters = validatedData.tagFilters.map((filter) => {
|
||||
const tagDef = displayNameToTagDef[filter.tagName]!
|
||||
const tagSlot = filter.tagSlot || tagDef.tagSlot
|
||||
const fieldType = filter.fieldType || tagDef.fieldType
|
||||
|
||||
logger.debug(
|
||||
`[${requestId}] Structured filter: ${filter.tagName} -> ${tagSlot} (${fieldType}) ${filter.operator} ${filter.value}`
|
||||
)
|
||||
|
||||
return {
|
||||
tagSlot,
|
||||
fieldType,
|
||||
operator: filter.operator,
|
||||
value: filter.value,
|
||||
valueTo: filter.valueTo,
|
||||
}
|
||||
})
|
||||
|
||||
logger.debug(`[${requestId}] Processed ${structuredFilters.length} structured filters`)
|
||||
}
|
||||
|
||||
if (accessibleKbIds.length === 0) {
|
||||
@@ -155,26 +202,29 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
let results: SearchResult[]
|
||||
|
||||
const hasFilters = mappedFilters && Object.keys(mappedFilters).length > 0
|
||||
const hasFilters = structuredFilters && structuredFilters.length > 0
|
||||
|
||||
if (!hasQuery && hasFilters) {
|
||||
// Tag-only search without vector similarity
|
||||
logger.debug(`[${requestId}] Executing tag-only search with filters:`, mappedFilters)
|
||||
logger.debug(`[${requestId}] Executing tag-only search with filters:`, structuredFilters)
|
||||
results = await handleTagOnlySearch({
|
||||
knowledgeBaseIds: accessibleKbIds,
|
||||
topK: validatedData.topK,
|
||||
filters: mappedFilters,
|
||||
structuredFilters,
|
||||
})
|
||||
} else if (hasQuery && hasFilters) {
|
||||
// Tag + Vector search
|
||||
logger.debug(`[${requestId}] Executing tag + vector search with filters:`, mappedFilters)
|
||||
logger.debug(
|
||||
`[${requestId}] Executing tag + vector search with filters:`,
|
||||
structuredFilters
|
||||
)
|
||||
const strategy = getQueryStrategy(accessibleKbIds.length, validatedData.topK)
|
||||
const queryVector = JSON.stringify(await queryEmbeddingPromise)
|
||||
|
||||
results = await handleTagAndVectorSearch({
|
||||
knowledgeBaseIds: accessibleKbIds,
|
||||
topK: validatedData.topK,
|
||||
filters: mappedFilters,
|
||||
structuredFilters,
|
||||
queryVector,
|
||||
distanceThreshold: strategy.distanceThreshold,
|
||||
})
|
||||
@@ -257,9 +307,9 @@ export async function POST(request: NextRequest) {
|
||||
// Create tags object with display names
|
||||
const tags: Record<string, any> = {}
|
||||
|
||||
TAG_SLOTS.forEach((slot) => {
|
||||
ALL_TAG_SLOTS.forEach((slot) => {
|
||||
const tagValue = (result as any)[slot]
|
||||
if (tagValue) {
|
||||
if (tagValue !== null && tagValue !== undefined) {
|
||||
const displayName = kbTagMap[slot] || slot
|
||||
logger.debug(
|
||||
`[${requestId}] Mapping ${slot}="${tagValue}" -> "${displayName}"="${tagValue}"`
|
||||
|
||||
@@ -54,7 +54,7 @@ describe('Knowledge Search Utils', () => {
|
||||
const params = {
|
||||
knowledgeBaseIds: ['kb-123'],
|
||||
topK: 10,
|
||||
filters: {},
|
||||
structuredFilters: [],
|
||||
}
|
||||
|
||||
await expect(handleTagOnlySearch(params)).rejects.toThrow(
|
||||
@@ -66,14 +66,14 @@ describe('Knowledge Search Utils', () => {
|
||||
const params = {
|
||||
knowledgeBaseIds: ['kb-123'],
|
||||
topK: 10,
|
||||
filters: { tag1: 'api' },
|
||||
structuredFilters: [{ tagSlot: 'tag1', fieldType: 'text', operator: 'eq', value: 'api' }],
|
||||
}
|
||||
|
||||
// This test validates the function accepts the right parameters
|
||||
// The actual database interaction is tested via route tests
|
||||
expect(params.knowledgeBaseIds).toEqual(['kb-123'])
|
||||
expect(params.topK).toBe(10)
|
||||
expect(params.filters).toEqual({ tag1: 'api' })
|
||||
expect(params.structuredFilters).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -123,7 +123,7 @@ describe('Knowledge Search Utils', () => {
|
||||
const params = {
|
||||
knowledgeBaseIds: ['kb-123'],
|
||||
topK: 10,
|
||||
filters: {},
|
||||
structuredFilters: [],
|
||||
queryVector: JSON.stringify([0.1, 0.2, 0.3]),
|
||||
distanceThreshold: 0.8,
|
||||
}
|
||||
@@ -137,7 +137,7 @@ describe('Knowledge Search Utils', () => {
|
||||
const params = {
|
||||
knowledgeBaseIds: ['kb-123'],
|
||||
topK: 10,
|
||||
filters: { tag1: 'api' },
|
||||
structuredFilters: [{ tagSlot: 'tag1', fieldType: 'text', operator: 'eq', value: 'api' }],
|
||||
distanceThreshold: 0.8,
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ describe('Knowledge Search Utils', () => {
|
||||
const params = {
|
||||
knowledgeBaseIds: ['kb-123'],
|
||||
topK: 10,
|
||||
filters: { tag1: 'api' },
|
||||
structuredFilters: [{ tagSlot: 'tag1', fieldType: 'text', operator: 'eq', value: 'api' }],
|
||||
queryVector: JSON.stringify([0.1, 0.2, 0.3]),
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ describe('Knowledge Search Utils', () => {
|
||||
const params = {
|
||||
knowledgeBaseIds: ['kb-123'],
|
||||
topK: 10,
|
||||
filters: { tag1: 'api' },
|
||||
structuredFilters: [{ tagSlot: 'tag1', fieldType: 'text', operator: 'eq', value: 'api' }],
|
||||
queryVector: JSON.stringify([0.1, 0.2, 0.3]),
|
||||
distanceThreshold: 0.8,
|
||||
}
|
||||
@@ -171,7 +171,7 @@ describe('Knowledge Search Utils', () => {
|
||||
// This test validates the function accepts the right parameters
|
||||
expect(params.knowledgeBaseIds).toEqual(['kb-123'])
|
||||
expect(params.topK).toBe(10)
|
||||
expect(params.filters).toEqual({ tag1: 'api' })
|
||||
expect(params.structuredFilters).toHaveLength(1)
|
||||
expect(params.queryVector).toBe(JSON.stringify([0.1, 0.2, 0.3]))
|
||||
expect(params.distanceThreshold).toBe(0.8)
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { db } from '@sim/db'
|
||||
import { document, embedding } from '@sim/db/schema'
|
||||
import { and, eq, inArray, isNull, sql } from 'drizzle-orm'
|
||||
import type { StructuredFilter } from '@/lib/knowledge/types'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
|
||||
const logger = createLogger('KnowledgeSearchUtils')
|
||||
@@ -34,6 +35,7 @@ export interface SearchResult {
|
||||
content: string
|
||||
documentId: string
|
||||
chunkIndex: number
|
||||
// Text tags
|
||||
tag1: string | null
|
||||
tag2: string | null
|
||||
tag3: string | null
|
||||
@@ -41,6 +43,19 @@ export interface SearchResult {
|
||||
tag5: string | null
|
||||
tag6: string | null
|
||||
tag7: string | null
|
||||
// Number tags (5 slots)
|
||||
number1: number | null
|
||||
number2: number | null
|
||||
number3: number | null
|
||||
number4: number | null
|
||||
number5: number | null
|
||||
// Date tags (2 slots)
|
||||
date1: Date | null
|
||||
date2: Date | null
|
||||
// Boolean tags (3 slots)
|
||||
boolean1: boolean | null
|
||||
boolean2: boolean | null
|
||||
boolean3: boolean | null
|
||||
distance: number
|
||||
knowledgeBaseId: string
|
||||
}
|
||||
@@ -48,7 +63,7 @@ export interface SearchResult {
|
||||
export interface SearchParams {
|
||||
knowledgeBaseIds: string[]
|
||||
topK: number
|
||||
filters?: Record<string, string>
|
||||
structuredFilters?: StructuredFilter[]
|
||||
queryVector?: string
|
||||
distanceThreshold?: number
|
||||
}
|
||||
@@ -56,46 +71,230 @@ export interface SearchParams {
|
||||
// Use shared embedding utility
|
||||
export { generateSearchEmbedding } from '@/lib/knowledge/embeddings'
|
||||
|
||||
function getTagFilters(filters: Record<string, string>, embedding: any) {
|
||||
return Object.entries(filters).map(([key, value]) => {
|
||||
// Handle OR logic within same tag
|
||||
const values = value.includes('|OR|') ? value.split('|OR|') : [value]
|
||||
logger.debug(`[getTagFilters] Processing ${key}="${value}" -> values:`, values)
|
||||
/** All valid tag slot keys */
|
||||
const TAG_SLOT_KEYS = [
|
||||
// Text tags (7 slots)
|
||||
'tag1',
|
||||
'tag2',
|
||||
'tag3',
|
||||
'tag4',
|
||||
'tag5',
|
||||
'tag6',
|
||||
'tag7',
|
||||
// Number tags (5 slots)
|
||||
'number1',
|
||||
'number2',
|
||||
'number3',
|
||||
'number4',
|
||||
'number5',
|
||||
// Date tags (2 slots)
|
||||
'date1',
|
||||
'date2',
|
||||
// Boolean tags (3 slots)
|
||||
'boolean1',
|
||||
'boolean2',
|
||||
'boolean3',
|
||||
] as const
|
||||
|
||||
const getColumnForKey = (key: string) => {
|
||||
switch (key) {
|
||||
case 'tag1':
|
||||
return embedding.tag1
|
||||
case 'tag2':
|
||||
return embedding.tag2
|
||||
case 'tag3':
|
||||
return embedding.tag3
|
||||
case 'tag4':
|
||||
return embedding.tag4
|
||||
case 'tag5':
|
||||
return embedding.tag5
|
||||
case 'tag6':
|
||||
return embedding.tag6
|
||||
case 'tag7':
|
||||
return embedding.tag7
|
||||
default:
|
||||
return null
|
||||
}
|
||||
type TagSlotKey = (typeof TAG_SLOT_KEYS)[number]
|
||||
|
||||
function isTagSlotKey(key: string): key is TagSlotKey {
|
||||
return TAG_SLOT_KEYS.includes(key as TagSlotKey)
|
||||
}
|
||||
|
||||
/** Common fields selected for search results */
|
||||
const getSearchResultFields = (distanceExpr: any) => ({
|
||||
id: embedding.id,
|
||||
content: embedding.content,
|
||||
documentId: embedding.documentId,
|
||||
chunkIndex: embedding.chunkIndex,
|
||||
// Text tags
|
||||
tag1: embedding.tag1,
|
||||
tag2: embedding.tag2,
|
||||
tag3: embedding.tag3,
|
||||
tag4: embedding.tag4,
|
||||
tag5: embedding.tag5,
|
||||
tag6: embedding.tag6,
|
||||
tag7: embedding.tag7,
|
||||
// Number tags (5 slots)
|
||||
number1: embedding.number1,
|
||||
number2: embedding.number2,
|
||||
number3: embedding.number3,
|
||||
number4: embedding.number4,
|
||||
number5: embedding.number5,
|
||||
// Date tags (2 slots)
|
||||
date1: embedding.date1,
|
||||
date2: embedding.date2,
|
||||
// Boolean tags (3 slots)
|
||||
boolean1: embedding.boolean1,
|
||||
boolean2: embedding.boolean2,
|
||||
boolean3: embedding.boolean3,
|
||||
distance: distanceExpr,
|
||||
knowledgeBaseId: embedding.knowledgeBaseId,
|
||||
})
|
||||
|
||||
/**
|
||||
* Build a single SQL condition for a filter
|
||||
*/
|
||||
function buildFilterCondition(filter: StructuredFilter, embeddingTable: any) {
|
||||
const { tagSlot, fieldType, operator, value, valueTo } = filter
|
||||
|
||||
if (!isTagSlotKey(tagSlot)) {
|
||||
logger.debug(`[getStructuredTagFilters] Unknown tag slot: ${tagSlot}`)
|
||||
return null
|
||||
}
|
||||
|
||||
const column = embeddingTable[tagSlot]
|
||||
if (!column) return null
|
||||
|
||||
logger.debug(
|
||||
`[getStructuredTagFilters] Processing ${tagSlot} (${fieldType}) ${operator} ${value}`
|
||||
)
|
||||
|
||||
// Handle text operators
|
||||
if (fieldType === 'text') {
|
||||
const stringValue = String(value)
|
||||
switch (operator) {
|
||||
case 'eq':
|
||||
return sql`LOWER(${column}) = LOWER(${stringValue})`
|
||||
case 'neq':
|
||||
return sql`LOWER(${column}) != LOWER(${stringValue})`
|
||||
case 'contains':
|
||||
return sql`LOWER(${column}) LIKE LOWER(${`%${stringValue}%`})`
|
||||
case 'not_contains':
|
||||
return sql`LOWER(${column}) NOT LIKE LOWER(${`%${stringValue}%`})`
|
||||
case 'starts_with':
|
||||
return sql`LOWER(${column}) LIKE LOWER(${`${stringValue}%`})`
|
||||
case 'ends_with':
|
||||
return sql`LOWER(${column}) LIKE LOWER(${`%${stringValue}`})`
|
||||
default:
|
||||
return sql`LOWER(${column}) = LOWER(${stringValue})`
|
||||
}
|
||||
}
|
||||
|
||||
// Handle number operators
|
||||
if (fieldType === 'number') {
|
||||
const numValue = typeof value === 'number' ? value : Number.parseFloat(String(value))
|
||||
if (Number.isNaN(numValue)) return null
|
||||
|
||||
switch (operator) {
|
||||
case 'eq':
|
||||
return sql`${column} = ${numValue}`
|
||||
case 'neq':
|
||||
return sql`${column} != ${numValue}`
|
||||
case 'gt':
|
||||
return sql`${column} > ${numValue}`
|
||||
case 'gte':
|
||||
return sql`${column} >= ${numValue}`
|
||||
case 'lt':
|
||||
return sql`${column} < ${numValue}`
|
||||
case 'lte':
|
||||
return sql`${column} <= ${numValue}`
|
||||
case 'between':
|
||||
if (valueTo !== undefined) {
|
||||
const numValueTo =
|
||||
typeof valueTo === 'number' ? valueTo : Number.parseFloat(String(valueTo))
|
||||
if (Number.isNaN(numValueTo)) return sql`${column} = ${numValue}`
|
||||
return sql`${column} >= ${numValue} AND ${column} <= ${numValueTo}`
|
||||
}
|
||||
return sql`${column} = ${numValue}`
|
||||
default:
|
||||
return sql`${column} = ${numValue}`
|
||||
}
|
||||
}
|
||||
|
||||
// Handle date operators - expects YYYY-MM-DD format from frontend
|
||||
if (fieldType === 'date') {
|
||||
const dateStr = String(value)
|
||||
// Validate YYYY-MM-DD format
|
||||
if (!/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) {
|
||||
logger.debug(`[getStructuredTagFilters] Invalid date format: ${dateStr}, expected YYYY-MM-DD`)
|
||||
return null
|
||||
}
|
||||
|
||||
const column = getColumnForKey(key)
|
||||
if (!column) return sql`1=1` // No-op for unknown keys
|
||||
|
||||
if (values.length === 1) {
|
||||
// Single value - simple equality
|
||||
logger.debug(`[getTagFilters] Single value filter: ${key} = ${values[0]}`)
|
||||
return sql`LOWER(${column}) = LOWER(${values[0]})`
|
||||
switch (operator) {
|
||||
case 'eq':
|
||||
return sql`${column}::date = ${dateStr}::date`
|
||||
case 'neq':
|
||||
return sql`${column}::date != ${dateStr}::date`
|
||||
case 'gt':
|
||||
return sql`${column}::date > ${dateStr}::date`
|
||||
case 'gte':
|
||||
return sql`${column}::date >= ${dateStr}::date`
|
||||
case 'lt':
|
||||
return sql`${column}::date < ${dateStr}::date`
|
||||
case 'lte':
|
||||
return sql`${column}::date <= ${dateStr}::date`
|
||||
case 'between':
|
||||
if (valueTo !== undefined) {
|
||||
const dateStrTo = String(valueTo)
|
||||
if (!/^\d{4}-\d{2}-\d{2}$/.test(dateStrTo)) {
|
||||
return sql`${column}::date = ${dateStr}::date`
|
||||
}
|
||||
return sql`${column}::date >= ${dateStr}::date AND ${column}::date <= ${dateStrTo}::date`
|
||||
}
|
||||
return sql`${column}::date = ${dateStr}::date`
|
||||
default:
|
||||
return sql`${column}::date = ${dateStr}::date`
|
||||
}
|
||||
// Multiple values - OR logic
|
||||
logger.debug(`[getTagFilters] OR filter: ${key} IN (${values.join(', ')})`)
|
||||
const orConditions = values.map((v) => sql`LOWER(${column}) = LOWER(${v})`)
|
||||
return sql`(${sql.join(orConditions, sql` OR `)})`
|
||||
})
|
||||
}
|
||||
|
||||
// Handle boolean operators
|
||||
if (fieldType === 'boolean') {
|
||||
const boolValue = value === true || value === 'true'
|
||||
switch (operator) {
|
||||
case 'eq':
|
||||
return sql`${column} = ${boolValue}`
|
||||
case 'neq':
|
||||
return sql`${column} != ${boolValue}`
|
||||
default:
|
||||
return sql`${column} = ${boolValue}`
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to equality
|
||||
return sql`${column} = ${value}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Build SQL conditions from structured filters with operator support
|
||||
* - Same tag multiple times: OR logic
|
||||
* - Different tags: AND logic
|
||||
*/
|
||||
function getStructuredTagFilters(filters: StructuredFilter[], embeddingTable: any) {
|
||||
// Group filters by tagSlot
|
||||
const filtersBySlot = new Map<string, StructuredFilter[]>()
|
||||
for (const filter of filters) {
|
||||
const slot = filter.tagSlot
|
||||
if (!filtersBySlot.has(slot)) {
|
||||
filtersBySlot.set(slot, [])
|
||||
}
|
||||
filtersBySlot.get(slot)!.push(filter)
|
||||
}
|
||||
|
||||
// Build conditions: OR within same slot, AND across different slots
|
||||
const conditions: ReturnType<typeof sql>[] = []
|
||||
|
||||
for (const [slot, slotFilters] of filtersBySlot) {
|
||||
const slotConditions = slotFilters
|
||||
.map((f) => buildFilterCondition(f, embeddingTable))
|
||||
.filter((c): c is ReturnType<typeof sql> => c !== null)
|
||||
|
||||
if (slotConditions.length === 0) continue
|
||||
|
||||
if (slotConditions.length === 1) {
|
||||
// Single condition for this slot
|
||||
conditions.push(slotConditions[0])
|
||||
} else {
|
||||
// Multiple conditions for same slot - OR them together
|
||||
logger.debug(
|
||||
`[getStructuredTagFilters] OR'ing ${slotConditions.length} conditions for ${slot}`
|
||||
)
|
||||
conditions.push(sql`(${sql.join(slotConditions, sql` OR `)})`)
|
||||
}
|
||||
}
|
||||
|
||||
return conditions
|
||||
}
|
||||
|
||||
export function getQueryStrategy(kbCount: number, topK: number) {
|
||||
@@ -113,8 +312,10 @@ export function getQueryStrategy(kbCount: number, topK: number) {
|
||||
|
||||
async function executeTagFilterQuery(
|
||||
knowledgeBaseIds: string[],
|
||||
filters: Record<string, string>
|
||||
structuredFilters: StructuredFilter[]
|
||||
): Promise<{ id: string }[]> {
|
||||
const tagFilterConditions = getStructuredTagFilters(structuredFilters, embedding)
|
||||
|
||||
if (knowledgeBaseIds.length === 1) {
|
||||
return await db
|
||||
.select({ id: embedding.id })
|
||||
@@ -125,7 +326,7 @@ async function executeTagFilterQuery(
|
||||
eq(embedding.knowledgeBaseId, knowledgeBaseIds[0]),
|
||||
eq(embedding.enabled, true),
|
||||
isNull(document.deletedAt),
|
||||
...getTagFilters(filters, embedding)
|
||||
...tagFilterConditions
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -138,7 +339,7 @@ async function executeTagFilterQuery(
|
||||
inArray(embedding.knowledgeBaseId, knowledgeBaseIds),
|
||||
eq(embedding.enabled, true),
|
||||
isNull(document.deletedAt),
|
||||
...getTagFilters(filters, embedding)
|
||||
...tagFilterConditions
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -154,21 +355,11 @@ async function executeVectorSearchOnIds(
|
||||
}
|
||||
|
||||
return await db
|
||||
.select({
|
||||
id: embedding.id,
|
||||
content: embedding.content,
|
||||
documentId: embedding.documentId,
|
||||
chunkIndex: embedding.chunkIndex,
|
||||
tag1: embedding.tag1,
|
||||
tag2: embedding.tag2,
|
||||
tag3: embedding.tag3,
|
||||
tag4: embedding.tag4,
|
||||
tag5: embedding.tag5,
|
||||
tag6: embedding.tag6,
|
||||
tag7: embedding.tag7,
|
||||
distance: sql<number>`${embedding.embedding} <=> ${queryVector}::vector`.as('distance'),
|
||||
knowledgeBaseId: embedding.knowledgeBaseId,
|
||||
})
|
||||
.select(
|
||||
getSearchResultFields(
|
||||
sql<number>`${embedding.embedding} <=> ${queryVector}::vector`.as('distance')
|
||||
)
|
||||
)
|
||||
.from(embedding)
|
||||
.innerJoin(document, eq(embedding.documentId, document.id))
|
||||
.where(
|
||||
@@ -183,15 +374,16 @@ async function executeVectorSearchOnIds(
|
||||
}
|
||||
|
||||
export async function handleTagOnlySearch(params: SearchParams): Promise<SearchResult[]> {
|
||||
const { knowledgeBaseIds, topK, filters } = params
|
||||
const { knowledgeBaseIds, topK, structuredFilters } = params
|
||||
|
||||
if (!filters || Object.keys(filters).length === 0) {
|
||||
if (!structuredFilters || structuredFilters.length === 0) {
|
||||
throw new Error('Tag filters are required for tag-only search')
|
||||
}
|
||||
|
||||
logger.debug(`[handleTagOnlySearch] Executing tag-only search with filters:`, filters)
|
||||
logger.debug(`[handleTagOnlySearch] Executing tag-only search with filters:`, structuredFilters)
|
||||
|
||||
const strategy = getQueryStrategy(knowledgeBaseIds.length, topK)
|
||||
const tagFilterConditions = getStructuredTagFilters(structuredFilters, embedding)
|
||||
|
||||
if (strategy.useParallel) {
|
||||
// Parallel approach for many KBs
|
||||
@@ -199,21 +391,7 @@ export async function handleTagOnlySearch(params: SearchParams): Promise<SearchR
|
||||
|
||||
const queryPromises = knowledgeBaseIds.map(async (kbId) => {
|
||||
return await db
|
||||
.select({
|
||||
id: embedding.id,
|
||||
content: embedding.content,
|
||||
documentId: embedding.documentId,
|
||||
chunkIndex: embedding.chunkIndex,
|
||||
tag1: embedding.tag1,
|
||||
tag2: embedding.tag2,
|
||||
tag3: embedding.tag3,
|
||||
tag4: embedding.tag4,
|
||||
tag5: embedding.tag5,
|
||||
tag6: embedding.tag6,
|
||||
tag7: embedding.tag7,
|
||||
distance: sql<number>`0`.as('distance'), // No distance for tag-only searches
|
||||
knowledgeBaseId: embedding.knowledgeBaseId,
|
||||
})
|
||||
.select(getSearchResultFields(sql<number>`0`.as('distance')))
|
||||
.from(embedding)
|
||||
.innerJoin(document, eq(embedding.documentId, document.id))
|
||||
.where(
|
||||
@@ -221,7 +399,7 @@ export async function handleTagOnlySearch(params: SearchParams): Promise<SearchR
|
||||
eq(embedding.knowledgeBaseId, kbId),
|
||||
eq(embedding.enabled, true),
|
||||
isNull(document.deletedAt),
|
||||
...getTagFilters(filters, embedding)
|
||||
...tagFilterConditions
|
||||
)
|
||||
)
|
||||
.limit(parallelLimit)
|
||||
@@ -232,21 +410,7 @@ export async function handleTagOnlySearch(params: SearchParams): Promise<SearchR
|
||||
}
|
||||
// Single query for fewer KBs
|
||||
return await db
|
||||
.select({
|
||||
id: embedding.id,
|
||||
content: embedding.content,
|
||||
documentId: embedding.documentId,
|
||||
chunkIndex: embedding.chunkIndex,
|
||||
tag1: embedding.tag1,
|
||||
tag2: embedding.tag2,
|
||||
tag3: embedding.tag3,
|
||||
tag4: embedding.tag4,
|
||||
tag5: embedding.tag5,
|
||||
tag6: embedding.tag6,
|
||||
tag7: embedding.tag7,
|
||||
distance: sql<number>`0`.as('distance'), // No distance for tag-only searches
|
||||
knowledgeBaseId: embedding.knowledgeBaseId,
|
||||
})
|
||||
.select(getSearchResultFields(sql<number>`0`.as('distance')))
|
||||
.from(embedding)
|
||||
.innerJoin(document, eq(embedding.documentId, document.id))
|
||||
.where(
|
||||
@@ -254,7 +418,7 @@ export async function handleTagOnlySearch(params: SearchParams): Promise<SearchR
|
||||
inArray(embedding.knowledgeBaseId, knowledgeBaseIds),
|
||||
eq(embedding.enabled, true),
|
||||
isNull(document.deletedAt),
|
||||
...getTagFilters(filters, embedding)
|
||||
...tagFilterConditions
|
||||
)
|
||||
)
|
||||
.limit(topK)
|
||||
@@ -271,27 +435,15 @@ export async function handleVectorOnlySearch(params: SearchParams): Promise<Sear
|
||||
|
||||
const strategy = getQueryStrategy(knowledgeBaseIds.length, topK)
|
||||
|
||||
const distanceExpr = sql<number>`${embedding.embedding} <=> ${queryVector}::vector`.as('distance')
|
||||
|
||||
if (strategy.useParallel) {
|
||||
// Parallel approach for many KBs
|
||||
const parallelLimit = Math.ceil(topK / knowledgeBaseIds.length) + 5
|
||||
|
||||
const queryPromises = knowledgeBaseIds.map(async (kbId) => {
|
||||
return await db
|
||||
.select({
|
||||
id: embedding.id,
|
||||
content: embedding.content,
|
||||
documentId: embedding.documentId,
|
||||
chunkIndex: embedding.chunkIndex,
|
||||
tag1: embedding.tag1,
|
||||
tag2: embedding.tag2,
|
||||
tag3: embedding.tag3,
|
||||
tag4: embedding.tag4,
|
||||
tag5: embedding.tag5,
|
||||
tag6: embedding.tag6,
|
||||
tag7: embedding.tag7,
|
||||
distance: sql<number>`${embedding.embedding} <=> ${queryVector}::vector`.as('distance'),
|
||||
knowledgeBaseId: embedding.knowledgeBaseId,
|
||||
})
|
||||
.select(getSearchResultFields(distanceExpr))
|
||||
.from(embedding)
|
||||
.innerJoin(document, eq(embedding.documentId, document.id))
|
||||
.where(
|
||||
@@ -312,21 +464,7 @@ export async function handleVectorOnlySearch(params: SearchParams): Promise<Sear
|
||||
}
|
||||
// Single query for fewer KBs
|
||||
return await db
|
||||
.select({
|
||||
id: embedding.id,
|
||||
content: embedding.content,
|
||||
documentId: embedding.documentId,
|
||||
chunkIndex: embedding.chunkIndex,
|
||||
tag1: embedding.tag1,
|
||||
tag2: embedding.tag2,
|
||||
tag3: embedding.tag3,
|
||||
tag4: embedding.tag4,
|
||||
tag5: embedding.tag5,
|
||||
tag6: embedding.tag6,
|
||||
tag7: embedding.tag7,
|
||||
distance: sql<number>`${embedding.embedding} <=> ${queryVector}::vector`.as('distance'),
|
||||
knowledgeBaseId: embedding.knowledgeBaseId,
|
||||
})
|
||||
.select(getSearchResultFields(distanceExpr))
|
||||
.from(embedding)
|
||||
.innerJoin(document, eq(embedding.documentId, document.id))
|
||||
.where(
|
||||
@@ -342,19 +480,22 @@ export async function handleVectorOnlySearch(params: SearchParams): Promise<Sear
|
||||
}
|
||||
|
||||
export async function handleTagAndVectorSearch(params: SearchParams): Promise<SearchResult[]> {
|
||||
const { knowledgeBaseIds, topK, filters, queryVector, distanceThreshold } = params
|
||||
const { knowledgeBaseIds, topK, structuredFilters, queryVector, distanceThreshold } = params
|
||||
|
||||
if (!filters || Object.keys(filters).length === 0) {
|
||||
if (!structuredFilters || structuredFilters.length === 0) {
|
||||
throw new Error('Tag filters are required for tag and vector search')
|
||||
}
|
||||
if (!queryVector || !distanceThreshold) {
|
||||
throw new Error('Query vector and distance threshold are required for tag and vector search')
|
||||
}
|
||||
|
||||
logger.debug(`[handleTagAndVectorSearch] Executing tag + vector search with filters:`, filters)
|
||||
logger.debug(
|
||||
`[handleTagAndVectorSearch] Executing tag + vector search with filters:`,
|
||||
structuredFilters
|
||||
)
|
||||
|
||||
// Step 1: Filter by tags first
|
||||
const tagFilteredIds = await executeTagFilterQuery(knowledgeBaseIds, filters)
|
||||
const tagFilteredIds = await executeTagFilterQuery(knowledgeBaseIds, structuredFilters)
|
||||
|
||||
if (tagFilteredIds.length === 0) {
|
||||
logger.debug(`[handleTagAndVectorSearch] No results found after tag filtering`)
|
||||
|
||||
@@ -35,7 +35,7 @@ export interface DocumentData {
|
||||
enabled: boolean
|
||||
deletedAt?: Date | null
|
||||
uploadedAt: Date
|
||||
// Document tags
|
||||
// Text tags
|
||||
tag1?: string | null
|
||||
tag2?: string | null
|
||||
tag3?: string | null
|
||||
@@ -43,6 +43,19 @@ export interface DocumentData {
|
||||
tag5?: string | null
|
||||
tag6?: string | null
|
||||
tag7?: string | null
|
||||
// Number tags (5 slots)
|
||||
number1?: number | null
|
||||
number2?: number | null
|
||||
number3?: number | null
|
||||
number4?: number | null
|
||||
number5?: number | null
|
||||
// Date tags (2 slots)
|
||||
date1?: Date | null
|
||||
date2?: Date | null
|
||||
// Boolean tags (3 slots)
|
||||
boolean1?: boolean | null
|
||||
boolean2?: boolean | null
|
||||
boolean3?: boolean | null
|
||||
}
|
||||
|
||||
export interface EmbeddingData {
|
||||
@@ -58,7 +71,7 @@ export interface EmbeddingData {
|
||||
embeddingModel: string
|
||||
startOffset: number
|
||||
endOffset: number
|
||||
// Tag fields for filtering
|
||||
// Text tags
|
||||
tag1?: string | null
|
||||
tag2?: string | null
|
||||
tag3?: string | null
|
||||
@@ -66,6 +79,19 @@ export interface EmbeddingData {
|
||||
tag5?: string | null
|
||||
tag6?: string | null
|
||||
tag7?: string | null
|
||||
// Number tags (5 slots)
|
||||
number1?: number | null
|
||||
number2?: number | null
|
||||
number3?: number | null
|
||||
number4?: number | null
|
||||
number5?: number | null
|
||||
// Date tags (2 slots)
|
||||
date1?: Date | null
|
||||
date2?: Date | null
|
||||
// Boolean tags (3 slots)
|
||||
boolean1?: boolean | null
|
||||
boolean2?: boolean | null
|
||||
boolean3?: boolean | null
|
||||
enabled: boolean
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
@@ -232,6 +258,27 @@ export async function checkDocumentWriteAccess(
|
||||
processingStartedAt: document.processingStartedAt,
|
||||
processingCompletedAt: document.processingCompletedAt,
|
||||
knowledgeBaseId: document.knowledgeBaseId,
|
||||
// Text tags
|
||||
tag1: document.tag1,
|
||||
tag2: document.tag2,
|
||||
tag3: document.tag3,
|
||||
tag4: document.tag4,
|
||||
tag5: document.tag5,
|
||||
tag6: document.tag6,
|
||||
tag7: document.tag7,
|
||||
// Number tags (5 slots)
|
||||
number1: document.number1,
|
||||
number2: document.number2,
|
||||
number3: document.number3,
|
||||
number4: document.number4,
|
||||
number5: document.number5,
|
||||
// Date tags (2 slots)
|
||||
date1: document.date1,
|
||||
date2: document.date2,
|
||||
// Boolean tags (3 slots)
|
||||
boolean1: document.boolean1,
|
||||
boolean2: document.boolean2,
|
||||
boolean3: document.boolean3,
|
||||
})
|
||||
.from(document)
|
||||
.where(and(eq(document.id, documentId), isNull(document.deletedAt)))
|
||||
|
||||
@@ -1,32 +1,72 @@
|
||||
import { db } from '@sim/db'
|
||||
import { workflowExecutionLogs, workflowExecutionSnapshots } from '@sim/db/schema'
|
||||
import { eq } from 'drizzle-orm'
|
||||
import {
|
||||
permissions,
|
||||
workflow,
|
||||
workflowExecutionLogs,
|
||||
workflowExecutionSnapshots,
|
||||
} from '@sim/db/schema'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
|
||||
const logger = createLogger('LogsByExecutionIdAPI')
|
||||
|
||||
export async function GET(
|
||||
_request: NextRequest,
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ executionId: string }> }
|
||||
) {
|
||||
const requestId = generateRequestId()
|
||||
|
||||
try {
|
||||
const { executionId } = await params
|
||||
|
||||
logger.debug(`Fetching execution data for: ${executionId}`)
|
||||
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
|
||||
if (!authResult.success || !authResult.userId) {
|
||||
logger.warn(`[${requestId}] Unauthorized execution data access attempt for: ${executionId}`)
|
||||
return NextResponse.json(
|
||||
{ error: authResult.error || 'Authentication required' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
const authenticatedUserId = authResult.userId
|
||||
|
||||
logger.debug(
|
||||
`[${requestId}] Fetching execution data for: ${executionId} (auth: ${authResult.authType})`
|
||||
)
|
||||
|
||||
// Get the workflow execution log to find the snapshot
|
||||
const [workflowLog] = await db
|
||||
.select()
|
||||
.select({
|
||||
id: workflowExecutionLogs.id,
|
||||
workflowId: workflowExecutionLogs.workflowId,
|
||||
executionId: workflowExecutionLogs.executionId,
|
||||
stateSnapshotId: workflowExecutionLogs.stateSnapshotId,
|
||||
trigger: workflowExecutionLogs.trigger,
|
||||
startedAt: workflowExecutionLogs.startedAt,
|
||||
endedAt: workflowExecutionLogs.endedAt,
|
||||
totalDurationMs: workflowExecutionLogs.totalDurationMs,
|
||||
cost: workflowExecutionLogs.cost,
|
||||
})
|
||||
.from(workflowExecutionLogs)
|
||||
.innerJoin(workflow, eq(workflowExecutionLogs.workflowId, workflow.id))
|
||||
.innerJoin(
|
||||
permissions,
|
||||
and(
|
||||
eq(permissions.entityType, 'workspace'),
|
||||
eq(permissions.entityId, workflow.workspaceId),
|
||||
eq(permissions.userId, authenticatedUserId)
|
||||
)
|
||||
)
|
||||
.where(eq(workflowExecutionLogs.executionId, executionId))
|
||||
.limit(1)
|
||||
|
||||
if (!workflowLog) {
|
||||
logger.warn(`[${requestId}] Execution not found or access denied: ${executionId}`)
|
||||
return NextResponse.json({ error: 'Workflow execution not found' }, { status: 404 })
|
||||
}
|
||||
|
||||
// Get the workflow state snapshot
|
||||
const [snapshot] = await db
|
||||
.select()
|
||||
.from(workflowExecutionSnapshots)
|
||||
@@ -34,6 +74,7 @@ export async function GET(
|
||||
.limit(1)
|
||||
|
||||
if (!snapshot) {
|
||||
logger.warn(`[${requestId}] Workflow state snapshot not found for execution: ${executionId}`)
|
||||
return NextResponse.json({ error: 'Workflow state snapshot not found' }, { status: 404 })
|
||||
}
|
||||
|
||||
@@ -50,14 +91,14 @@ export async function GET(
|
||||
},
|
||||
}
|
||||
|
||||
logger.debug(`Successfully fetched execution data for: ${executionId}`)
|
||||
logger.debug(`[${requestId}] Successfully fetched execution data for: ${executionId}`)
|
||||
logger.debug(
|
||||
`Workflow state contains ${Object.keys((snapshot.stateData as any)?.blocks || {}).length} blocks`
|
||||
`[${requestId}] Workflow state contains ${Object.keys((snapshot.stateData as any)?.blocks || {}).length} blocks`
|
||||
)
|
||||
|
||||
return NextResponse.json(response)
|
||||
} catch (error) {
|
||||
logger.error('Error fetching execution data:', error)
|
||||
logger.error(`[${requestId}] Error fetching execution data:`, error)
|
||||
return NextResponse.json({ error: 'Failed to fetch execution data' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ export async function GET(request: NextRequest) {
|
||||
workflowName: workflow.name,
|
||||
}
|
||||
|
||||
let conditions: SQL | undefined = eq(workflow.workspaceId, params.workspaceId)
|
||||
let conditions: SQL | undefined = eq(workflowExecutionLogs.workspaceId, params.workspaceId)
|
||||
|
||||
if (params.level && params.level !== 'all') {
|
||||
const levels = params.level.split(',').filter(Boolean)
|
||||
@@ -134,7 +134,7 @@ export async function GET(request: NextRequest) {
|
||||
permissions,
|
||||
and(
|
||||
eq(permissions.entityType, 'workspace'),
|
||||
eq(permissions.entityId, workflow.workspaceId),
|
||||
eq(permissions.entityId, workflowExecutionLogs.workspaceId),
|
||||
eq(permissions.userId, userId)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -6,7 +6,22 @@ import {
|
||||
workflowDeploymentVersion,
|
||||
workflowExecutionLogs,
|
||||
} from '@sim/db/schema'
|
||||
import { and, desc, eq, gte, inArray, isNotNull, isNull, lte, or, type SQL, sql } from 'drizzle-orm'
|
||||
import {
|
||||
and,
|
||||
desc,
|
||||
eq,
|
||||
gt,
|
||||
gte,
|
||||
inArray,
|
||||
isNotNull,
|
||||
isNull,
|
||||
lt,
|
||||
lte,
|
||||
ne,
|
||||
or,
|
||||
type SQL,
|
||||
sql,
|
||||
} from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { getSession } from '@/lib/auth'
|
||||
@@ -22,14 +37,19 @@ const QueryParamsSchema = z.object({
|
||||
limit: z.coerce.number().optional().default(100),
|
||||
offset: z.coerce.number().optional().default(0),
|
||||
level: z.string().optional(),
|
||||
workflowIds: z.string().optional(), // Comma-separated list of workflow IDs
|
||||
folderIds: z.string().optional(), // Comma-separated list of folder IDs
|
||||
triggers: z.string().optional(), // Comma-separated list of trigger types
|
||||
workflowIds: z.string().optional(),
|
||||
folderIds: z.string().optional(),
|
||||
triggers: z.string().optional(),
|
||||
startDate: z.string().optional(),
|
||||
endDate: z.string().optional(),
|
||||
search: z.string().optional(),
|
||||
workflowName: z.string().optional(),
|
||||
folderName: z.string().optional(),
|
||||
executionId: z.string().optional(),
|
||||
costOperator: z.enum(['=', '>', '<', '>=', '<=', '!=']).optional(),
|
||||
costValue: z.coerce.number().optional(),
|
||||
durationOperator: z.enum(['=', '>', '<', '>=', '<=', '!=']).optional(),
|
||||
durationValue: z.coerce.number().optional(),
|
||||
workspaceId: z.string(),
|
||||
})
|
||||
|
||||
@@ -49,7 +69,6 @@ export async function GET(request: NextRequest) {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const params = QueryParamsSchema.parse(Object.fromEntries(searchParams.entries()))
|
||||
|
||||
// Conditionally select columns based on detail level to optimize performance
|
||||
const selectColumns =
|
||||
params.details === 'full'
|
||||
? {
|
||||
@@ -63,9 +82,9 @@ export async function GET(request: NextRequest) {
|
||||
startedAt: workflowExecutionLogs.startedAt,
|
||||
endedAt: workflowExecutionLogs.endedAt,
|
||||
totalDurationMs: workflowExecutionLogs.totalDurationMs,
|
||||
executionData: workflowExecutionLogs.executionData, // Large field - only in full mode
|
||||
executionData: workflowExecutionLogs.executionData,
|
||||
cost: workflowExecutionLogs.cost,
|
||||
files: workflowExecutionLogs.files, // Large field - only in full mode
|
||||
files: workflowExecutionLogs.files,
|
||||
createdAt: workflowExecutionLogs.createdAt,
|
||||
workflowName: workflow.name,
|
||||
workflowDescription: workflow.description,
|
||||
@@ -82,7 +101,6 @@ export async function GET(request: NextRequest) {
|
||||
deploymentVersionName: workflowDeploymentVersion.name,
|
||||
}
|
||||
: {
|
||||
// Basic mode - exclude large fields for better performance
|
||||
id: workflowExecutionLogs.id,
|
||||
workflowId: workflowExecutionLogs.workflowId,
|
||||
executionId: workflowExecutionLogs.executionId,
|
||||
@@ -93,9 +111,9 @@ export async function GET(request: NextRequest) {
|
||||
startedAt: workflowExecutionLogs.startedAt,
|
||||
endedAt: workflowExecutionLogs.endedAt,
|
||||
totalDurationMs: workflowExecutionLogs.totalDurationMs,
|
||||
executionData: sql<null>`NULL`, // Exclude large execution data in basic mode
|
||||
executionData: sql<null>`NULL`,
|
||||
cost: workflowExecutionLogs.cost,
|
||||
files: sql<null>`NULL`, // Exclude files in basic mode
|
||||
files: sql<null>`NULL`,
|
||||
createdAt: workflowExecutionLogs.createdAt,
|
||||
workflowName: workflow.name,
|
||||
workflowDescription: workflow.description,
|
||||
@@ -109,9 +127,11 @@ export async function GET(request: NextRequest) {
|
||||
pausedTotalPauseCount: pausedExecutions.totalPauseCount,
|
||||
pausedResumedCount: pausedExecutions.resumedCount,
|
||||
deploymentVersion: workflowDeploymentVersion.version,
|
||||
deploymentVersionName: sql<null>`NULL`, // Only needed in full mode for details panel
|
||||
deploymentVersionName: sql<null>`NULL`,
|
||||
}
|
||||
|
||||
const workspaceFilter = eq(workflowExecutionLogs.workspaceId, params.workspaceId)
|
||||
|
||||
const baseQuery = db
|
||||
.select(selectColumns)
|
||||
.from(workflowExecutionLogs)
|
||||
@@ -123,50 +143,38 @@ export async function GET(request: NextRequest) {
|
||||
workflowDeploymentVersion,
|
||||
eq(workflowDeploymentVersion.id, workflowExecutionLogs.deploymentVersionId)
|
||||
)
|
||||
.innerJoin(
|
||||
workflow,
|
||||
and(
|
||||
eq(workflowExecutionLogs.workflowId, workflow.id),
|
||||
eq(workflow.workspaceId, params.workspaceId)
|
||||
)
|
||||
)
|
||||
.innerJoin(workflow, eq(workflowExecutionLogs.workflowId, workflow.id))
|
||||
.innerJoin(
|
||||
permissions,
|
||||
and(
|
||||
eq(permissions.entityType, 'workspace'),
|
||||
eq(permissions.entityId, workflow.workspaceId),
|
||||
eq(permissions.entityId, workflowExecutionLogs.workspaceId),
|
||||
eq(permissions.userId, userId)
|
||||
)
|
||||
)
|
||||
|
||||
// Build additional conditions for the query
|
||||
let conditions: SQL | undefined
|
||||
|
||||
// Filter by level with support for derived statuses (running, pending)
|
||||
if (params.level && params.level !== 'all') {
|
||||
const levels = params.level.split(',').filter(Boolean)
|
||||
const levelConditions: SQL[] = []
|
||||
|
||||
for (const level of levels) {
|
||||
if (level === 'error') {
|
||||
// Direct database field
|
||||
levelConditions.push(eq(workflowExecutionLogs.level, 'error'))
|
||||
} else if (level === 'info') {
|
||||
// Completed info logs only (not running, not pending)
|
||||
const condition = and(
|
||||
eq(workflowExecutionLogs.level, 'info'),
|
||||
isNotNull(workflowExecutionLogs.endedAt)
|
||||
)
|
||||
if (condition) levelConditions.push(condition)
|
||||
} else if (level === 'running') {
|
||||
// Running logs: info level with no endedAt
|
||||
const condition = and(
|
||||
eq(workflowExecutionLogs.level, 'info'),
|
||||
isNull(workflowExecutionLogs.endedAt)
|
||||
)
|
||||
if (condition) levelConditions.push(condition)
|
||||
} else if (level === 'pending') {
|
||||
// Pending logs: info level with pause status indicators
|
||||
const condition = and(
|
||||
eq(workflowExecutionLogs.level, 'info'),
|
||||
or(
|
||||
@@ -189,7 +197,6 @@ export async function GET(request: NextRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
// Filter by specific workflow IDs
|
||||
if (params.workflowIds) {
|
||||
const workflowIds = params.workflowIds.split(',').filter(Boolean)
|
||||
if (workflowIds.length > 0) {
|
||||
@@ -197,7 +204,6 @@ export async function GET(request: NextRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
// Filter by folder IDs
|
||||
if (params.folderIds) {
|
||||
const folderIds = params.folderIds.split(',').filter(Boolean)
|
||||
if (folderIds.length > 0) {
|
||||
@@ -205,7 +211,6 @@ export async function GET(request: NextRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
// Filter by triggers
|
||||
if (params.triggers) {
|
||||
const triggers = params.triggers.split(',').filter(Boolean)
|
||||
if (triggers.length > 0 && !triggers.includes('all')) {
|
||||
@@ -213,7 +218,6 @@ export async function GET(request: NextRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
// Filter by date range
|
||||
if (params.startDate) {
|
||||
conditions = and(
|
||||
conditions,
|
||||
@@ -224,33 +228,79 @@ export async function GET(request: NextRequest) {
|
||||
conditions = and(conditions, lte(workflowExecutionLogs.startedAt, new Date(params.endDate)))
|
||||
}
|
||||
|
||||
// Filter by search query
|
||||
if (params.search) {
|
||||
const searchTerm = `%${params.search}%`
|
||||
// With message removed, restrict search to executionId only
|
||||
conditions = and(conditions, sql`${workflowExecutionLogs.executionId} ILIKE ${searchTerm}`)
|
||||
}
|
||||
|
||||
// Filter by workflow name (from advanced search input)
|
||||
if (params.workflowName) {
|
||||
const nameTerm = `%${params.workflowName}%`
|
||||
conditions = and(conditions, sql`${workflow.name} ILIKE ${nameTerm}`)
|
||||
}
|
||||
|
||||
// Filter by folder name (best-effort text match when present on workflows)
|
||||
if (params.folderName) {
|
||||
const folderTerm = `%${params.folderName}%`
|
||||
conditions = and(conditions, sql`${workflow.name} ILIKE ${folderTerm}`)
|
||||
}
|
||||
|
||||
// Execute the query using the optimized join
|
||||
if (params.executionId) {
|
||||
conditions = and(conditions, eq(workflowExecutionLogs.executionId, params.executionId))
|
||||
}
|
||||
|
||||
if (params.costOperator && params.costValue !== undefined) {
|
||||
const costField = sql`(${workflowExecutionLogs.cost}->>'total')::numeric`
|
||||
switch (params.costOperator) {
|
||||
case '=':
|
||||
conditions = and(conditions, sql`${costField} = ${params.costValue}`)
|
||||
break
|
||||
case '>':
|
||||
conditions = and(conditions, sql`${costField} > ${params.costValue}`)
|
||||
break
|
||||
case '<':
|
||||
conditions = and(conditions, sql`${costField} < ${params.costValue}`)
|
||||
break
|
||||
case '>=':
|
||||
conditions = and(conditions, sql`${costField} >= ${params.costValue}`)
|
||||
break
|
||||
case '<=':
|
||||
conditions = and(conditions, sql`${costField} <= ${params.costValue}`)
|
||||
break
|
||||
case '!=':
|
||||
conditions = and(conditions, sql`${costField} != ${params.costValue}`)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (params.durationOperator && params.durationValue !== undefined) {
|
||||
const durationField = workflowExecutionLogs.totalDurationMs
|
||||
switch (params.durationOperator) {
|
||||
case '=':
|
||||
conditions = and(conditions, eq(durationField, params.durationValue))
|
||||
break
|
||||
case '>':
|
||||
conditions = and(conditions, gt(durationField, params.durationValue))
|
||||
break
|
||||
case '<':
|
||||
conditions = and(conditions, lt(durationField, params.durationValue))
|
||||
break
|
||||
case '>=':
|
||||
conditions = and(conditions, gte(durationField, params.durationValue))
|
||||
break
|
||||
case '<=':
|
||||
conditions = and(conditions, lte(durationField, params.durationValue))
|
||||
break
|
||||
case '!=':
|
||||
conditions = and(conditions, ne(durationField, params.durationValue))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const logs = await baseQuery
|
||||
.where(conditions)
|
||||
.where(and(workspaceFilter, conditions))
|
||||
.orderBy(desc(workflowExecutionLogs.startedAt))
|
||||
.limit(params.limit)
|
||||
.offset(params.offset)
|
||||
|
||||
// Get total count for pagination using the same join structure
|
||||
const countQuery = db
|
||||
.select({ count: sql<number>`count(*)` })
|
||||
.from(workflowExecutionLogs)
|
||||
@@ -258,34 +308,25 @@ export async function GET(request: NextRequest) {
|
||||
pausedExecutions,
|
||||
eq(pausedExecutions.executionId, workflowExecutionLogs.executionId)
|
||||
)
|
||||
.innerJoin(
|
||||
workflow,
|
||||
and(
|
||||
eq(workflowExecutionLogs.workflowId, workflow.id),
|
||||
eq(workflow.workspaceId, params.workspaceId)
|
||||
)
|
||||
)
|
||||
.innerJoin(workflow, eq(workflowExecutionLogs.workflowId, workflow.id))
|
||||
.innerJoin(
|
||||
permissions,
|
||||
and(
|
||||
eq(permissions.entityType, 'workspace'),
|
||||
eq(permissions.entityId, workflow.workspaceId),
|
||||
eq(permissions.entityId, workflowExecutionLogs.workspaceId),
|
||||
eq(permissions.userId, userId)
|
||||
)
|
||||
)
|
||||
.where(conditions)
|
||||
.where(and(eq(workflowExecutionLogs.workspaceId, params.workspaceId), conditions))
|
||||
|
||||
const countResult = await countQuery
|
||||
|
||||
const count = countResult[0]?.count || 0
|
||||
|
||||
// Block executions are now extracted from trace spans instead of separate table
|
||||
const blockExecutionsByExecution: Record<string, any[]> = {}
|
||||
|
||||
// Create clean trace spans from block executions
|
||||
const createTraceSpans = (blockExecutions: any[]) => {
|
||||
return blockExecutions.map((block, index) => {
|
||||
// For error blocks, include error information in the output
|
||||
let output = block.outputData
|
||||
if (block.status === 'error' && block.errorMessage) {
|
||||
output = {
|
||||
@@ -314,7 +355,6 @@ export async function GET(request: NextRequest) {
|
||||
})
|
||||
}
|
||||
|
||||
// Extract cost information from block executions
|
||||
const extractCostSummary = (blockExecutions: any[]) => {
|
||||
let totalCost = 0
|
||||
let totalInputCost = 0
|
||||
@@ -333,7 +373,6 @@ export async function GET(request: NextRequest) {
|
||||
totalPromptTokens += block.cost.tokens?.prompt || 0
|
||||
totalCompletionTokens += block.cost.tokens?.completion || 0
|
||||
|
||||
// Track per-model costs
|
||||
if (block.cost.model) {
|
||||
if (!models.has(block.cost.model)) {
|
||||
models.set(block.cost.model, {
|
||||
@@ -363,34 +402,29 @@ export async function GET(request: NextRequest) {
|
||||
prompt: totalPromptTokens,
|
||||
completion: totalCompletionTokens,
|
||||
},
|
||||
models: Object.fromEntries(models), // Convert Map to object for JSON serialization
|
||||
models: Object.fromEntries(models),
|
||||
}
|
||||
}
|
||||
|
||||
// Transform to clean log format with workflow data included
|
||||
const enhancedLogs = logs.map((log) => {
|
||||
const blockExecutions = blockExecutionsByExecution[log.executionId] || []
|
||||
|
||||
// Only process trace spans and detailed cost in full mode
|
||||
let traceSpans = []
|
||||
let finalOutput: any
|
||||
let costSummary = (log.cost as any) || { total: 0 }
|
||||
|
||||
if (params.details === 'full' && log.executionData) {
|
||||
// Use stored trace spans if available, otherwise create from block executions
|
||||
const storedTraceSpans = (log.executionData as any)?.traceSpans
|
||||
traceSpans =
|
||||
storedTraceSpans && Array.isArray(storedTraceSpans) && storedTraceSpans.length > 0
|
||||
? storedTraceSpans
|
||||
: createTraceSpans(blockExecutions)
|
||||
|
||||
// Prefer stored cost JSON; otherwise synthesize from blocks
|
||||
costSummary =
|
||||
log.cost && Object.keys(log.cost as any).length > 0
|
||||
? (log.cost as any)
|
||||
: extractCostSummary(blockExecutions)
|
||||
|
||||
// Include finalOutput if present on executionData
|
||||
try {
|
||||
const fo = (log.executionData as any)?.finalOutput
|
||||
if (fo !== undefined) finalOutput = fo
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { db } from '@sim/db'
|
||||
import { permissions, workflow, workflowExecutionLogs } from '@sim/db/schema'
|
||||
import { permissions, workflowExecutionLogs } from '@sim/db/schema'
|
||||
import { and, eq, isNotNull, sql } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
@@ -42,23 +42,17 @@ export async function GET(request: NextRequest) {
|
||||
trigger: workflowExecutionLogs.trigger,
|
||||
})
|
||||
.from(workflowExecutionLogs)
|
||||
.innerJoin(
|
||||
workflow,
|
||||
and(
|
||||
eq(workflowExecutionLogs.workflowId, workflow.id),
|
||||
eq(workflow.workspaceId, params.workspaceId)
|
||||
)
|
||||
)
|
||||
.innerJoin(
|
||||
permissions,
|
||||
and(
|
||||
eq(permissions.entityType, 'workspace'),
|
||||
eq(permissions.entityId, workflow.workspaceId),
|
||||
eq(permissions.entityId, workflowExecutionLogs.workspaceId),
|
||||
eq(permissions.userId, userId)
|
||||
)
|
||||
)
|
||||
.where(
|
||||
and(
|
||||
eq(workflowExecutionLogs.workspaceId, params.workspaceId),
|
||||
isNotNull(workflowExecutionLogs.trigger),
|
||||
sql`${workflowExecutionLogs.trigger} NOT IN ('api', 'manual', 'webhook', 'chat', 'schedule')`
|
||||
)
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { NextRequest } from 'next/server'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { withMcpAuth } from '@/lib/mcp/middleware'
|
||||
import { mcpService } from '@/lib/mcp/service'
|
||||
import type { McpServerStatusConfig } from '@/lib/mcp/types'
|
||||
import { createMcpErrorResponse, createMcpSuccessResponse } from '@/lib/mcp/utils'
|
||||
|
||||
const logger = createLogger('McpServerRefreshAPI')
|
||||
@@ -50,6 +51,12 @@ export const POST = withMcpAuth<{ id: string }>('read')(
|
||||
let toolCount = 0
|
||||
let lastError: string | null = null
|
||||
|
||||
const currentStatusConfig: McpServerStatusConfig =
|
||||
(server.statusConfig as McpServerStatusConfig | null) ?? {
|
||||
consecutiveFailures: 0,
|
||||
lastSuccessfulDiscovery: null,
|
||||
}
|
||||
|
||||
try {
|
||||
const tools = await mcpService.discoverServerTools(userId, serverId, workspaceId)
|
||||
connectionStatus = 'connected'
|
||||
@@ -63,20 +70,40 @@ export const POST = withMcpAuth<{ id: string }>('read')(
|
||||
logger.warn(`[${requestId}] Failed to connect to server ${serverId}:`, error)
|
||||
}
|
||||
|
||||
const now = new Date()
|
||||
const newStatusConfig =
|
||||
connectionStatus === 'connected'
|
||||
? { consecutiveFailures: 0, lastSuccessfulDiscovery: now.toISOString() }
|
||||
: {
|
||||
consecutiveFailures: currentStatusConfig.consecutiveFailures + 1,
|
||||
lastSuccessfulDiscovery: currentStatusConfig.lastSuccessfulDiscovery,
|
||||
}
|
||||
|
||||
const [refreshedServer] = await db
|
||||
.update(mcpServers)
|
||||
.set({
|
||||
lastToolsRefresh: new Date(),
|
||||
lastToolsRefresh: now,
|
||||
connectionStatus,
|
||||
lastError,
|
||||
lastConnected: connectionStatus === 'connected' ? new Date() : server.lastConnected,
|
||||
lastConnected: connectionStatus === 'connected' ? now : server.lastConnected,
|
||||
toolCount,
|
||||
updatedAt: new Date(),
|
||||
statusConfig: newStatusConfig,
|
||||
updatedAt: now,
|
||||
})
|
||||
.where(eq(mcpServers.id, serverId))
|
||||
.returning()
|
||||
|
||||
logger.info(`[${requestId}] Successfully refreshed MCP server: ${serverId}`)
|
||||
if (connectionStatus === 'connected') {
|
||||
logger.info(
|
||||
`[${requestId}] Successfully refreshed MCP server: ${serverId} (${toolCount} tools)`
|
||||
)
|
||||
await mcpService.clearCache(workspaceId)
|
||||
} else {
|
||||
logger.warn(
|
||||
`[${requestId}] Refresh completed for MCP server ${serverId} but connection failed: ${lastError}`
|
||||
)
|
||||
}
|
||||
|
||||
return createMcpSuccessResponse({
|
||||
status: connectionStatus,
|
||||
toolCount,
|
||||
|
||||
@@ -48,6 +48,19 @@ export const PATCH = withMcpAuth<{ id: string }>('write')(
|
||||
// Remove workspaceId from body to prevent it from being updated
|
||||
const { workspaceId: _, ...updateData } = body
|
||||
|
||||
// Get the current server to check if URL is changing
|
||||
const [currentServer] = await db
|
||||
.select({ url: mcpServers.url })
|
||||
.from(mcpServers)
|
||||
.where(
|
||||
and(
|
||||
eq(mcpServers.id, serverId),
|
||||
eq(mcpServers.workspaceId, workspaceId),
|
||||
isNull(mcpServers.deletedAt)
|
||||
)
|
||||
)
|
||||
.limit(1)
|
||||
|
||||
const [updatedServer] = await db
|
||||
.update(mcpServers)
|
||||
.set({
|
||||
@@ -71,8 +84,12 @@ export const PATCH = withMcpAuth<{ id: string }>('write')(
|
||||
)
|
||||
}
|
||||
|
||||
// Clear MCP service cache after update
|
||||
mcpService.clearCache(workspaceId)
|
||||
// Only clear cache if URL changed (requires re-discovery)
|
||||
const urlChanged = body.url && currentServer?.url !== body.url
|
||||
if (urlChanged) {
|
||||
await mcpService.clearCache(workspaceId)
|
||||
logger.info(`[${requestId}] Cleared cache due to URL change`)
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] Successfully updated MCP server: ${serverId}`)
|
||||
return createMcpSuccessResponse({ server: updatedServer })
|
||||
|
||||
@@ -117,12 +117,14 @@ export const POST = withMcpAuth('write')(
|
||||
timeout: body.timeout || 30000,
|
||||
retries: body.retries || 3,
|
||||
enabled: body.enabled !== false,
|
||||
connectionStatus: 'connected',
|
||||
lastConnected: new Date(),
|
||||
updatedAt: new Date(),
|
||||
deletedAt: null,
|
||||
})
|
||||
.where(eq(mcpServers.id, serverId))
|
||||
|
||||
mcpService.clearCache(workspaceId)
|
||||
await mcpService.clearCache(workspaceId)
|
||||
|
||||
logger.info(
|
||||
`[${requestId}] Successfully updated MCP server: ${body.name} (ID: ${serverId})`
|
||||
@@ -145,12 +147,14 @@ export const POST = withMcpAuth('write')(
|
||||
timeout: body.timeout || 30000,
|
||||
retries: body.retries || 3,
|
||||
enabled: body.enabled !== false,
|
||||
connectionStatus: 'connected',
|
||||
lastConnected: new Date(),
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.returning()
|
||||
|
||||
mcpService.clearCache(workspaceId)
|
||||
await mcpService.clearCache(workspaceId)
|
||||
|
||||
logger.info(
|
||||
`[${requestId}] Successfully registered MCP server: ${body.name} (ID: ${serverId})`
|
||||
@@ -212,7 +216,7 @@ export const DELETE = withMcpAuth('admin')(
|
||||
)
|
||||
}
|
||||
|
||||
mcpService.clearCache(workspaceId)
|
||||
await mcpService.clearCache(workspaceId)
|
||||
|
||||
logger.info(`[${requestId}] Successfully deleted MCP server: ${serverId}`)
|
||||
return createMcpSuccessResponse({ message: `Server ${serverId} deleted successfully` })
|
||||
|
||||
103
apps/sim/app/api/mcp/tools/stored/route.ts
Normal file
103
apps/sim/app/api/mcp/tools/stored/route.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import { db } from '@sim/db'
|
||||
import { workflow, workflowBlocks } from '@sim/db/schema'
|
||||
import { eq } from 'drizzle-orm'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { withMcpAuth } from '@/lib/mcp/middleware'
|
||||
import { createMcpErrorResponse, createMcpSuccessResponse } from '@/lib/mcp/utils'
|
||||
|
||||
const logger = createLogger('McpStoredToolsAPI')
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
interface StoredMcpTool {
|
||||
workflowId: string
|
||||
workflowName: string
|
||||
serverId: string
|
||||
serverUrl?: string
|
||||
toolName: string
|
||||
schema?: Record<string, unknown>
|
||||
}
|
||||
|
||||
/**
|
||||
* GET - Get all stored MCP tools from workflows in the workspace
|
||||
*
|
||||
* Scans all workflows in the workspace and extracts MCP tools that have been
|
||||
* added to agent blocks. Returns the stored state of each tool for comparison
|
||||
* against current server state.
|
||||
*/
|
||||
export const GET = withMcpAuth('read')(
|
||||
async (request: NextRequest, { userId, workspaceId, requestId }) => {
|
||||
try {
|
||||
logger.info(`[${requestId}] Fetching stored MCP tools for workspace ${workspaceId}`)
|
||||
|
||||
// Get all workflows in workspace
|
||||
const workflows = await db
|
||||
.select({
|
||||
id: workflow.id,
|
||||
name: workflow.name,
|
||||
})
|
||||
.from(workflow)
|
||||
.where(eq(workflow.workspaceId, workspaceId))
|
||||
|
||||
const workflowMap = new Map(workflows.map((w) => [w.id, w.name]))
|
||||
const workflowIds = workflows.map((w) => w.id)
|
||||
|
||||
if (workflowIds.length === 0) {
|
||||
return createMcpSuccessResponse({ tools: [] })
|
||||
}
|
||||
|
||||
// Get all agent blocks from these workflows
|
||||
const agentBlocks = await db
|
||||
.select({
|
||||
workflowId: workflowBlocks.workflowId,
|
||||
subBlocks: workflowBlocks.subBlocks,
|
||||
})
|
||||
.from(workflowBlocks)
|
||||
.where(eq(workflowBlocks.type, 'agent'))
|
||||
|
||||
const storedTools: StoredMcpTool[] = []
|
||||
|
||||
for (const block of agentBlocks) {
|
||||
if (!workflowMap.has(block.workflowId)) continue
|
||||
|
||||
const subBlocks = block.subBlocks as Record<string, unknown> | null
|
||||
if (!subBlocks) continue
|
||||
|
||||
const toolsSubBlock = subBlocks.tools as Record<string, unknown> | undefined
|
||||
const toolsValue = toolsSubBlock?.value
|
||||
|
||||
if (!toolsValue || !Array.isArray(toolsValue)) continue
|
||||
|
||||
for (const tool of toolsValue) {
|
||||
if (tool.type !== 'mcp') continue
|
||||
|
||||
const params = tool.params as Record<string, unknown> | undefined
|
||||
if (!params?.serverId || !params?.toolName) continue
|
||||
|
||||
storedTools.push({
|
||||
workflowId: block.workflowId,
|
||||
workflowName: workflowMap.get(block.workflowId) || 'Untitled',
|
||||
serverId: params.serverId as string,
|
||||
serverUrl: params.serverUrl as string | undefined,
|
||||
toolName: params.toolName as string,
|
||||
schema: tool.schema as Record<string, unknown> | undefined,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`[${requestId}] Found ${storedTools.length} stored MCP tools across ${workflows.length} workflows`
|
||||
)
|
||||
|
||||
return createMcpSuccessResponse({ tools: storedTools })
|
||||
} catch (error) {
|
||||
logger.error(`[${requestId}] Error fetching stored MCP tools:`, error)
|
||||
return createMcpErrorResponse(
|
||||
error instanceof Error ? error : new Error('Failed to fetch stored MCP tools'),
|
||||
'Failed to fetch stored MCP tools',
|
||||
500
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -3,8 +3,10 @@ import { memory, workflowBlocks } from '@sim/db/schema'
|
||||
import { and, eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { getWorkflowAccessContext } from '@/lib/workflows/utils'
|
||||
|
||||
const logger = createLogger('MemoryByIdAPI')
|
||||
|
||||
@@ -65,6 +67,65 @@ const memoryPutBodySchema = z.object({
|
||||
workflowId: z.string().uuid('Invalid workflow ID format'),
|
||||
})
|
||||
|
||||
/**
|
||||
* Validates authentication and workflow access for memory operations
|
||||
* @param request - The incoming request
|
||||
* @param workflowId - The workflow ID to check access for
|
||||
* @param requestId - Request ID for logging
|
||||
* @param action - 'read' for GET, 'write' for PUT/DELETE
|
||||
* @returns Object with userId if successful, or error response if failed
|
||||
*/
|
||||
async function validateMemoryAccess(
|
||||
request: NextRequest,
|
||||
workflowId: string,
|
||||
requestId: string,
|
||||
action: 'read' | 'write'
|
||||
): Promise<{ userId: string } | { error: NextResponse }> {
|
||||
const authResult = await checkHybridAuth(request, {
|
||||
requireWorkflowId: false,
|
||||
})
|
||||
if (!authResult.success || !authResult.userId) {
|
||||
logger.warn(`[${requestId}] Unauthorized memory ${action} attempt`)
|
||||
return {
|
||||
error: NextResponse.json(
|
||||
{ success: false, error: { message: 'Authentication required' } },
|
||||
{ status: 401 }
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
const accessContext = await getWorkflowAccessContext(workflowId, authResult.userId)
|
||||
if (!accessContext) {
|
||||
logger.warn(`[${requestId}] Workflow ${workflowId} not found`)
|
||||
return {
|
||||
error: NextResponse.json(
|
||||
{ success: false, error: { message: 'Workflow not found' } },
|
||||
{ status: 404 }
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
const { isOwner, workspacePermission } = accessContext
|
||||
const hasAccess =
|
||||
action === 'read'
|
||||
? isOwner || workspacePermission !== null
|
||||
: isOwner || workspacePermission === 'write' || workspacePermission === 'admin'
|
||||
|
||||
if (!hasAccess) {
|
||||
logger.warn(
|
||||
`[${requestId}] User ${authResult.userId} denied ${action} access to workflow ${workflowId}`
|
||||
)
|
||||
return {
|
||||
error: NextResponse.json(
|
||||
{ success: false, error: { message: 'Access denied' } },
|
||||
{ status: 403 }
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
return { userId: authResult.userId }
|
||||
}
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
export const runtime = 'nodejs'
|
||||
|
||||
@@ -101,6 +162,11 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
|
||||
|
||||
const { workflowId: validatedWorkflowId } = validation.data
|
||||
|
||||
const accessCheck = await validateMemoryAccess(request, validatedWorkflowId, requestId, 'read')
|
||||
if ('error' in accessCheck) {
|
||||
return accessCheck.error
|
||||
}
|
||||
|
||||
const memories = await db
|
||||
.select()
|
||||
.from(memory)
|
||||
@@ -203,6 +269,11 @@ export async function DELETE(
|
||||
|
||||
const { workflowId: validatedWorkflowId } = validation.data
|
||||
|
||||
const accessCheck = await validateMemoryAccess(request, validatedWorkflowId, requestId, 'write')
|
||||
if ('error' in accessCheck) {
|
||||
return accessCheck.error
|
||||
}
|
||||
|
||||
const existingMemory = await db
|
||||
.select({ id: memory.id })
|
||||
.from(memory)
|
||||
@@ -296,6 +367,11 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{
|
||||
)
|
||||
}
|
||||
|
||||
const accessCheck = await validateMemoryAccess(request, validatedWorkflowId, requestId, 'write')
|
||||
if ('error' in accessCheck) {
|
||||
return accessCheck.error
|
||||
}
|
||||
|
||||
const existingMemories = await db
|
||||
.select()
|
||||
.from(memory)
|
||||
|
||||
@@ -28,7 +28,7 @@ const updateInvitationSchema = z.object({
|
||||
|
||||
// Get invitation details
|
||||
export async function GET(
|
||||
_req: NextRequest,
|
||||
_request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string; invitationId: string }> }
|
||||
) {
|
||||
const { id: organizationId, invitationId } = await params
|
||||
|
||||
@@ -16,7 +16,6 @@ export async function GET() {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
// Get organizations where user is owner or admin
|
||||
const userOrganizations = await db
|
||||
.select({
|
||||
id: organization.id,
|
||||
@@ -32,8 +31,15 @@ export async function GET() {
|
||||
)
|
||||
)
|
||||
|
||||
const anyMembership = await db
|
||||
.select({ id: member.id })
|
||||
.from(member)
|
||||
.where(eq(member.userId, session.user.id))
|
||||
.limit(1)
|
||||
|
||||
return NextResponse.json({
|
||||
organizations: userOrganizations,
|
||||
isMemberOfAnyOrg: anyMembership.length > 0,
|
||||
})
|
||||
} catch (error) {
|
||||
logger.error('Failed to fetch organizations', {
|
||||
|
||||
@@ -45,7 +45,6 @@ export async function GET(request: NextRequest) {
|
||||
host: OLLAMA_HOST,
|
||||
})
|
||||
|
||||
// Return empty array instead of error to avoid breaking the UI
|
||||
return NextResponse.json({ models: [] })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@ export async function POST(request: NextRequest) {
|
||||
apiKey,
|
||||
azureEndpoint,
|
||||
azureApiVersion,
|
||||
vertexProject,
|
||||
vertexLocation,
|
||||
responseFormat,
|
||||
workflowId,
|
||||
workspaceId,
|
||||
@@ -58,6 +60,8 @@ export async function POST(request: NextRequest) {
|
||||
hasApiKey: !!apiKey,
|
||||
hasAzureEndpoint: !!azureEndpoint,
|
||||
hasAzureApiVersion: !!azureApiVersion,
|
||||
hasVertexProject: !!vertexProject,
|
||||
hasVertexLocation: !!vertexLocation,
|
||||
hasResponseFormat: !!responseFormat,
|
||||
workflowId,
|
||||
stream: !!stream,
|
||||
@@ -104,6 +108,8 @@ export async function POST(request: NextRequest) {
|
||||
apiKey: finalApiKey,
|
||||
azureEndpoint,
|
||||
azureApiVersion,
|
||||
vertexProject,
|
||||
vertexLocation,
|
||||
responseFormat,
|
||||
workflowId,
|
||||
workspaceId,
|
||||
|
||||
@@ -20,10 +20,16 @@ export async function GET(request: NextRequest) {
|
||||
baseUrl,
|
||||
})
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
|
||||
if (env.VLLM_API_KEY) {
|
||||
headers.Authorization = `Bearer ${env.VLLM_API_KEY}`
|
||||
}
|
||||
|
||||
const response = await fetch(`${baseUrl}/v1/models`, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
headers,
|
||||
next: { revalidate: 60 },
|
||||
})
|
||||
|
||||
@@ -50,7 +56,6 @@ export async function GET(request: NextRequest) {
|
||||
baseUrl,
|
||||
})
|
||||
|
||||
// Return empty array instead of error to avoid breaking the UI
|
||||
return NextResponse.json({ models: [] })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import { db } from '@sim/db'
|
||||
import { templates, user } from '@sim/db/schema'
|
||||
import { templates } from '@sim/db/schema'
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { verifySuperUser } from '@/lib/templates/permissions'
|
||||
|
||||
const logger = createLogger('TemplateApprovalAPI')
|
||||
|
||||
export const revalidate = 0
|
||||
|
||||
// POST /api/templates/[id]/approve - Approve a template (super users only)
|
||||
/**
|
||||
* POST /api/templates/[id]/approve - Approve a template (super users only)
|
||||
*/
|
||||
export async function POST(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
||||
const requestId = generateRequestId()
|
||||
const { id } = await params
|
||||
@@ -22,23 +25,18 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
// Check if user is a super user
|
||||
const currentUser = await db.select().from(user).where(eq(user.id, session.user.id)).limit(1)
|
||||
|
||||
if (!currentUser[0]?.isSuperUser) {
|
||||
const { isSuperUser } = await verifySuperUser(session.user.id)
|
||||
if (!isSuperUser) {
|
||||
logger.warn(`[${requestId}] Non-super user attempted to approve template: ${id}`)
|
||||
return NextResponse.json({ error: 'Only super users can approve templates' }, { status: 403 })
|
||||
}
|
||||
|
||||
// Check if template exists
|
||||
const existingTemplate = await db.select().from(templates).where(eq(templates.id, id)).limit(1)
|
||||
|
||||
if (existingTemplate.length === 0) {
|
||||
logger.warn(`[${requestId}] Template not found for approval: ${id}`)
|
||||
return NextResponse.json({ error: 'Template not found' }, { status: 404 })
|
||||
}
|
||||
|
||||
// Update template status to approved
|
||||
await db
|
||||
.update(templates)
|
||||
.set({ status: 'approved', updatedAt: new Date() })
|
||||
@@ -56,9 +54,11 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
||||
}
|
||||
}
|
||||
|
||||
// POST /api/templates/[id]/reject - Reject a template (super users only)
|
||||
/**
|
||||
* DELETE /api/templates/[id]/approve - Unapprove a template (super users only)
|
||||
*/
|
||||
export async function DELETE(
|
||||
request: NextRequest,
|
||||
_request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
const requestId = generateRequestId()
|
||||
@@ -71,23 +71,18 @@ export async function DELETE(
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
// Check if user is a super user
|
||||
const currentUser = await db.select().from(user).where(eq(user.id, session.user.id)).limit(1)
|
||||
|
||||
if (!currentUser[0]?.isSuperUser) {
|
||||
const { isSuperUser } = await verifySuperUser(session.user.id)
|
||||
if (!isSuperUser) {
|
||||
logger.warn(`[${requestId}] Non-super user attempted to reject template: ${id}`)
|
||||
return NextResponse.json({ error: 'Only super users can reject templates' }, { status: 403 })
|
||||
}
|
||||
|
||||
// Check if template exists
|
||||
const existingTemplate = await db.select().from(templates).where(eq(templates.id, id)).limit(1)
|
||||
|
||||
if (existingTemplate.length === 0) {
|
||||
logger.warn(`[${requestId}] Template not found for rejection: ${id}`)
|
||||
return NextResponse.json({ error: 'Template not found' }, { status: 404 })
|
||||
}
|
||||
|
||||
// Update template status to rejected
|
||||
await db
|
||||
.update(templates)
|
||||
.set({ status: 'rejected', updatedAt: new Date() })
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user