Compare commits

..

2 Commits

Author SHA1 Message Date
aadamgough
92a08b0a33 removed success from microsoft tools 2025-12-15 19:37:45 -08:00
aadamgough
5d3b216922 fixed jira output 2025-12-14 21:06:28 -08:00
5226 changed files with 134243 additions and 755052 deletions

View File

@@ -1,708 +0,0 @@
---
description: Create a block configuration for a Sim integration with proper subBlocks, conditions, and tool wiring
argument-hint: <service-name>
---
# Add Block Skill
You are an expert at creating block configurations for Sim. You understand the serializer, subBlock types, conditions, dependsOn, modes, and all UI patterns.
## Your Task
When the user asks you to create a block:
1. Create the block file in `apps/sim/blocks/blocks/{service}.ts`
2. Configure all subBlocks with proper types, conditions, and dependencies
3. Wire up tools correctly
## Block Configuration Structure
```typescript
import { {ServiceName}Icon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import { AuthMode } from '@/blocks/types'
export const {ServiceName}Block: BlockConfig = {
type: '{service}', // snake_case identifier
name: '{Service Name}', // Human readable
description: 'Brief description', // One sentence
longDescription: 'Detailed description for docs',
docsLink: 'https://docs.sim.ai/tools/{service}',
category: 'tools', // 'tools' | 'blocks' | 'triggers'
bgColor: '#HEXCOLOR', // Brand color
icon: {ServiceName}Icon,
// Auth mode
authMode: AuthMode.OAuth, // or AuthMode.ApiKey
subBlocks: [
// Define all UI fields here
],
tools: {
access: ['tool_id_1', 'tool_id_2'], // Array of tool IDs this block can use
config: {
tool: (params) => `{service}_${params.operation}`, // Tool selector function
params: (params) => ({
// Transform subBlock values to tool params
}),
},
},
inputs: {
// Optional: define expected inputs from other blocks
},
outputs: {
// Define outputs available to downstream blocks
},
}
```
## SubBlock Types Reference
**Critical:** Every subblock `id` must be unique within the block. Duplicate IDs cause conflicts even with different conditions.
### Text Inputs
```typescript
// Single-line input
{ id: 'field', title: 'Label', type: 'short-input', placeholder: '...' }
// Multi-line input
{ id: 'field', title: 'Label', type: 'long-input', placeholder: '...', rows: 6 }
// Password input
{ id: 'apiKey', title: 'API Key', type: 'short-input', password: true }
```
### Selection Inputs
```typescript
// Dropdown (static options)
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
options: [
{ label: 'Create', id: 'create' },
{ label: 'Update', id: 'update' },
],
value: () => 'create', // Default value function
}
// Combobox (searchable dropdown)
{
id: 'field',
title: 'Label',
type: 'combobox',
options: [...],
searchable: true,
}
```
### Code/JSON Inputs
```typescript
{
id: 'code',
title: 'Code',
type: 'code',
language: 'javascript', // 'javascript' | 'json' | 'python'
placeholder: '// Enter code...',
}
```
### OAuth/Credentials
```typescript
{
id: 'credential',
title: 'Account',
type: 'oauth-input',
serviceId: '{service}', // Must match OAuth provider
placeholder: 'Select account',
required: true,
}
```
### Selectors (with dynamic options)
```typescript
// Channel selector (Slack, Discord, etc.)
{
id: 'channel',
title: 'Channel',
type: 'channel-selector',
serviceId: '{service}',
placeholder: 'Select channel',
dependsOn: ['credential'],
}
// Project selector (Jira, etc.)
{
id: 'project',
title: 'Project',
type: 'project-selector',
serviceId: '{service}',
dependsOn: ['credential'],
}
// File selector (Google Drive, etc.)
{
id: 'file',
title: 'File',
type: 'file-selector',
serviceId: '{service}',
mimeType: 'application/pdf',
dependsOn: ['credential'],
}
// User selector
{
id: 'user',
title: 'User',
type: 'user-selector',
serviceId: '{service}',
dependsOn: ['credential'],
}
```
### Other Types
```typescript
// Switch/toggle
{ id: 'enabled', type: 'switch' }
// Slider
{ id: 'temperature', title: 'Temperature', type: 'slider', min: 0, max: 2, step: 0.1 }
// Table (key-value pairs)
{ id: 'headers', title: 'Headers', type: 'table', columns: ['Key', 'Value'] }
// File upload
{
id: 'files',
title: 'Attachments',
type: 'file-upload',
multiple: true,
acceptedTypes: 'image/*,application/pdf',
}
```
## File Input Handling
When your block accepts file uploads, use the basic/advanced mode pattern with `normalizeFileInput`.
### Basic/Advanced File Pattern
```typescript
// Basic mode: Visual file upload
{
id: 'uploadFile',
title: 'File',
type: 'file-upload',
canonicalParamId: 'file', // Both map to 'file' param
placeholder: 'Upload file',
mode: 'basic',
multiple: false,
required: true,
condition: { field: 'operation', value: 'upload' },
},
// Advanced mode: Reference from other blocks
{
id: 'fileRef',
title: 'File',
type: 'short-input',
canonicalParamId: 'file', // Both map to 'file' param
placeholder: 'Reference file (e.g., {{file_block.output}})',
mode: 'advanced',
required: true,
condition: { field: 'operation', value: 'upload' },
},
```
**Critical constraints:**
- `canonicalParamId` must NOT match any subblock's `id` in the same block
- Values are stored under subblock `id`, not `canonicalParamId`
### Normalizing File Input in tools.config
Use `normalizeFileInput` to handle all input variants:
```typescript
import { normalizeFileInput } from '@/blocks/utils'
tools: {
access: ['service_upload'],
config: {
tool: (params) => {
// Check all field IDs: uploadFile (basic), fileRef (advanced), fileContent (legacy)
const normalizedFile = normalizeFileInput(
params.uploadFile || params.fileRef || params.fileContent,
{ single: true }
)
if (normalizedFile) {
params.file = normalizedFile
}
return `service_${params.operation}`
},
},
}
```
**Why this pattern?**
- Values come through as `params.uploadFile` or `params.fileRef` (the subblock IDs)
- `canonicalParamId` only controls UI/schema mapping, not runtime values
- `normalizeFileInput` handles JSON strings from advanced mode template resolution
### File Input Types in `inputs`
Use `type: 'json'` for file inputs:
```typescript
inputs: {
uploadFile: { type: 'json', description: 'Uploaded file (UserFile)' },
fileRef: { type: 'json', description: 'File reference from previous block' },
// Legacy field for backwards compatibility
fileContent: { type: 'string', description: 'Legacy: base64 encoded content' },
}
```
### Multiple Files
For multiple file uploads:
```typescript
{
id: 'attachments',
title: 'Attachments',
type: 'file-upload',
multiple: true, // Allow multiple files
maxSize: 25, // Max size in MB per file
acceptedTypes: 'image/*,application/pdf,.doc,.docx',
}
// In tools.config:
const normalizedFiles = normalizeFileInput(
params.attachments || params.attachmentRefs,
// No { single: true } - returns array
)
if (normalizedFiles) {
params.files = normalizedFiles
}
```
## Condition Syntax
Controls when a field is shown based on other field values.
### Simple Condition
```typescript
condition: { field: 'operation', value: 'create' }
// Shows when operation === 'create'
```
### Multiple Values (OR)
```typescript
condition: { field: 'operation', value: ['create', 'update'] }
// Shows when operation is 'create' OR 'update'
```
### Negation
```typescript
condition: { field: 'operation', value: 'delete', not: true }
// Shows when operation !== 'delete'
```
### Compound (AND)
```typescript
condition: {
field: 'operation',
value: 'send',
and: {
field: 'type',
value: 'dm',
not: true,
}
}
// Shows when operation === 'send' AND type !== 'dm'
```
### Complex Example
```typescript
condition: {
field: 'operation',
value: ['list', 'search'],
not: true,
and: {
field: 'authMethod',
value: 'oauth',
}
}
// Shows when operation NOT in ['list', 'search'] AND authMethod === 'oauth'
```
## DependsOn Pattern
Controls when a field is enabled and when its options are refetched.
### Simple Array (all must be set)
```typescript
dependsOn: ['credential']
// Enabled only when credential has a value
// Options refetch when credential changes
dependsOn: ['credential', 'projectId']
// Enabled only when BOTH have values
```
### Complex (all + any)
```typescript
dependsOn: {
all: ['authMethod'], // All must be set
any: ['credential', 'apiKey'] // At least one must be set
}
// Enabled when authMethod is set AND (credential OR apiKey is set)
```
## Required Pattern
Can be boolean or condition-based.
### Simple Boolean
```typescript
required: true
required: false
```
### Conditional Required
```typescript
required: { field: 'operation', value: 'create' }
// Required only when operation === 'create'
required: { field: 'operation', value: ['create', 'update'] }
// Required when operation is 'create' OR 'update'
```
## Mode Pattern (Basic vs Advanced)
Controls which UI view shows the field.
### Mode Options
- `'basic'` - Only in basic view (default UI)
- `'advanced'` - Only in advanced view
- `'both'` - Both views (default if not specified)
- `'trigger'` - Only in trigger configuration
### canonicalParamId Pattern
Maps multiple UI fields to a single serialized parameter:
```typescript
// Basic mode: Visual selector
{
id: 'channel',
title: 'Channel',
type: 'channel-selector',
mode: 'basic',
canonicalParamId: 'channel', // Both map to 'channel' param
dependsOn: ['credential'],
}
// Advanced mode: Manual input
{
id: 'channelId',
title: 'Channel ID',
type: 'short-input',
mode: 'advanced',
canonicalParamId: 'channel', // Both map to 'channel' param
placeholder: 'Enter channel ID manually',
}
```
**How it works:**
- In basic mode: `channel` selector value → `params.channel`
- In advanced mode: `channelId` input value → `params.channel`
- The serializer consolidates based on current mode
**Critical constraints:**
- `canonicalParamId` must NOT match any other subblock's `id` in the same block (causes conflicts)
- `canonicalParamId` must be unique per block (only one basic/advanced pair per canonicalParamId)
- ONLY use `canonicalParamId` to link basic/advanced mode alternatives for the same logical parameter
- Do NOT use it for any other purpose
## WandConfig Pattern
Enables AI-assisted field generation.
```typescript
{
id: 'query',
title: 'Query',
type: 'code',
language: 'json',
wandConfig: {
enabled: true,
prompt: 'Generate a query based on the user request. Return ONLY the JSON.',
placeholder: 'Describe what you want to query...',
generationType: 'json-object', // Optional: affects AI behavior
maintainHistory: true, // Optional: keeps conversation context
},
}
```
### Generation Types
- `'javascript-function-body'` - JS code generation
- `'json-object'` - Raw JSON (adds "no markdown" instruction)
- `'json-schema'` - JSON Schema definitions
- `'sql-query'` - SQL statements
- `'timestamp'` - Adds current date/time context
## Tools Configuration
**Preferred:** Use tool names directly as dropdown option IDs to avoid switch cases:
```typescript
// Dropdown options use tool IDs directly
options: [
{ label: 'Create', id: 'service_create' },
{ label: 'Read', id: 'service_read' },
]
// Tool selector just returns the operation value
tool: (params) => params.operation,
```
### With Parameter Transformation
```typescript
tools: {
access: ['service_action'],
config: {
tool: (params) => 'service_action',
params: (params) => ({
id: params.resourceId,
data: typeof params.data === 'string' ? JSON.parse(params.data) : params.data,
}),
},
}
```
### V2 Versioned Tool Selector
```typescript
import { createVersionedToolSelector } from '@/blocks/utils'
tools: {
access: [
'service_create_v2',
'service_read_v2',
'service_update_v2',
],
config: {
tool: createVersionedToolSelector({
baseToolSelector: (params) => `service_${params.operation}`,
suffix: '_v2',
fallbackToolId: 'service_create_v2',
}),
},
}
```
## Outputs Definition
**IMPORTANT:** Block outputs have a simpler schema than tool outputs. Block outputs do NOT support:
- `optional: true` - This is only for tool outputs
- `items` property - This is only for tool outputs with array types
Block outputs only support:
- `type` - The data type ('string', 'number', 'boolean', 'json', 'array')
- `description` - Human readable description
- Nested object structure (for complex types)
```typescript
outputs: {
// Simple outputs
id: { type: 'string', description: 'Resource ID' },
success: { type: 'boolean', description: 'Whether operation succeeded' },
// Use type: 'json' for complex objects or arrays (NOT type: 'array' with items)
items: { type: 'json', description: 'List of items' },
metadata: { type: 'json', description: 'Response metadata' },
// Nested outputs (for structured data)
user: {
id: { type: 'string', description: 'User ID' },
name: { type: 'string', description: 'User name' },
email: { type: 'string', description: 'User email' },
},
}
```
## V2 Block Pattern
When creating V2 blocks (alongside legacy V1):
```typescript
// V1 Block - mark as legacy
export const ServiceBlock: BlockConfig = {
type: 'service',
name: 'Service (Legacy)',
hideFromToolbar: true, // Hide from toolbar
// ... rest of config
}
// V2 Block - visible, uses V2 tools
export const ServiceV2Block: BlockConfig = {
type: 'service_v2',
name: 'Service', // Clean name
hideFromToolbar: false, // Visible
subBlocks: ServiceBlock.subBlocks, // Reuse UI
tools: {
access: ServiceBlock.tools?.access?.map(id => `${id}_v2`) || [],
config: {
tool: createVersionedToolSelector({
baseToolSelector: (params) => (ServiceBlock.tools?.config as any)?.tool(params),
suffix: '_v2',
fallbackToolId: 'service_default_v2',
}),
params: ServiceBlock.tools?.config?.params,
},
},
outputs: {
// Flat, API-aligned outputs (not wrapped in content/metadata)
},
}
```
## Registering Blocks
After creating the block, remind the user to:
1. Import in `apps/sim/blocks/registry.ts`
2. Add to the `registry` object (alphabetically):
```typescript
import { ServiceBlock } from '@/blocks/blocks/service'
export const registry: Record<string, BlockConfig> = {
// ... existing blocks ...
service: ServiceBlock,
}
```
## Complete Example
```typescript
import { ServiceIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import { AuthMode } from '@/blocks/types'
export const ServiceBlock: BlockConfig = {
type: 'service',
name: 'Service',
description: 'Integrate with Service API',
longDescription: 'Full description for documentation...',
docsLink: 'https://docs.sim.ai/tools/service',
category: 'tools',
bgColor: '#FF6B6B',
icon: ServiceIcon,
authMode: AuthMode.OAuth,
subBlocks: [
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
options: [
{ label: 'Create', id: 'create' },
{ label: 'Read', id: 'read' },
{ label: 'Update', id: 'update' },
{ label: 'Delete', id: 'delete' },
],
value: () => 'create',
},
{
id: 'credential',
title: 'Service Account',
type: 'oauth-input',
serviceId: 'service',
placeholder: 'Select account',
required: true,
},
{
id: 'resourceId',
title: 'Resource ID',
type: 'short-input',
placeholder: 'Enter resource ID',
condition: { field: 'operation', value: ['read', 'update', 'delete'] },
required: { field: 'operation', value: ['read', 'update', 'delete'] },
},
{
id: 'name',
title: 'Name',
type: 'short-input',
placeholder: 'Resource name',
condition: { field: 'operation', value: ['create', 'update'] },
required: { field: 'operation', value: 'create' },
},
],
tools: {
access: ['service_create', 'service_read', 'service_update', 'service_delete'],
config: {
tool: (params) => `service_${params.operation}`,
},
},
outputs: {
id: { type: 'string', description: 'Resource ID' },
name: { type: 'string', description: 'Resource name' },
createdAt: { type: 'string', description: 'Creation timestamp' },
},
}
```
## Connecting Blocks with Triggers
If the service supports webhooks, connect the block to its triggers.
```typescript
import { getTrigger } from '@/triggers'
export const ServiceBlock: BlockConfig = {
// ... basic config ...
triggers: {
enabled: true,
available: ['service_event_a', 'service_event_b', 'service_webhook'],
},
subBlocks: [
// Tool subBlocks first...
{ id: 'operation', /* ... */ },
// Then spread trigger subBlocks
...getTrigger('service_event_a').subBlocks,
...getTrigger('service_event_b').subBlocks,
...getTrigger('service_webhook').subBlocks,
],
}
```
See the `/add-trigger` skill for creating triggers.
## Icon Requirement
If the icon doesn't already exist in `@/components/icons.tsx`, **do NOT search for it yourself**. After completing the block, ask the user to provide the SVG:
```
The block is complete, but I need an icon for {Service}.
Please provide the SVG and I'll convert it to a React component.
You can usually find this in the service's brand/press kit page, or copy it from their website.
```
## Checklist Before Finishing
- [ ] All subBlocks have `id`, `title` (except switch), and `type`
- [ ] Conditions use correct syntax (field, value, not, and)
- [ ] DependsOn set for fields that need other values
- [ ] Required fields marked correctly (boolean or condition)
- [ ] OAuth inputs have correct `serviceId`
- [ ] Tools.access lists all tool IDs
- [ ] Tools.config.tool returns correct tool ID
- [ ] Outputs match tool outputs
- [ ] Block registered in registry.ts
- [ ] If icon missing: asked user to provide SVG
- [ ] If triggers exist: `triggers` config set, trigger subBlocks spread

View File

@@ -1,697 +0,0 @@
---
description: Add a complete integration to Sim (tools, block, icon, registration)
argument-hint: <service-name> [api-docs-url]
---
# Add Integration Skill
You are an expert at adding complete integrations to Sim. This skill orchestrates the full process of adding a new service integration.
## Overview
Adding an integration involves these steps in order:
1. **Research** - Read the service's API documentation
2. **Create Tools** - Build tool configurations for each API operation
3. **Create Block** - Build the block UI configuration
4. **Add Icon** - Add the service's brand icon
5. **Create Triggers** (optional) - If the service supports webhooks
6. **Register** - Register tools, block, and triggers in their registries
7. **Generate Docs** - Run the docs generation script
## Step 1: Research the API
Before writing any code:
1. Use Context7 to find official documentation: `mcp__plugin_context7_context7__resolve-library-id`
2. Or use WebFetch to read API docs directly
3. Identify:
- Authentication method (OAuth, API Key, both)
- Available operations (CRUD, search, etc.)
- Required vs optional parameters
- Response structures
## Step 2: Create Tools
### Directory Structure
```
apps/sim/tools/{service}/
├── index.ts # Barrel exports
├── types.ts # TypeScript interfaces
├── {action1}.ts # Tool for action 1
├── {action2}.ts # Tool for action 2
└── ...
```
### Key Patterns
**types.ts:**
```typescript
import type { ToolResponse } from '@/tools/types'
export interface {Service}{Action}Params {
accessToken: string // For OAuth services
// OR
apiKey: string // For API key services
requiredParam: string
optionalParam?: string
}
export interface {Service}Response extends ToolResponse {
output: {
// Define output structure
}
}
```
**Tool file pattern:**
```typescript
export const {service}{Action}Tool: ToolConfig<Params, Response> = {
id: '{service}_{action}',
name: '{Service} {Action}',
description: '...',
version: '1.0.0',
oauth: { required: true, provider: '{service}' }, // If OAuth
params: {
accessToken: { type: 'string', required: true, visibility: 'hidden', description: '...' },
// ... other params
},
request: { url, method, headers, body },
transformResponse: async (response) => {
const data = await response.json()
return {
success: true,
output: {
field: data.field ?? null, // Always handle nullables
},
}
},
outputs: { /* ... */ },
}
```
### Critical Rules
- `visibility: 'hidden'` for OAuth tokens
- `visibility: 'user-only'` for API keys and user credentials
- `visibility: 'user-or-llm'` for operation parameters
- Always use `?? null` for nullable API response fields
- Always use `?? []` for optional array fields
- Set `optional: true` for outputs that may not exist
- Never output raw JSON dumps - extract meaningful fields
## Step 3: Create Block
### File Location
`apps/sim/blocks/blocks/{service}.ts`
### Block Structure
```typescript
import { {Service}Icon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import { AuthMode } from '@/blocks/types'
export const {Service}Block: BlockConfig = {
type: '{service}',
name: '{Service}',
description: '...',
longDescription: '...',
docsLink: 'https://docs.sim.ai/tools/{service}',
category: 'tools',
bgColor: '#HEXCOLOR',
icon: {Service}Icon,
authMode: AuthMode.OAuth, // or AuthMode.ApiKey
subBlocks: [
// Operation dropdown
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
options: [
{ label: 'Operation 1', id: 'action1' },
{ label: 'Operation 2', id: 'action2' },
],
value: () => 'action1',
},
// Credential field
{
id: 'credential',
title: '{Service} Account',
type: 'oauth-input',
serviceId: '{service}',
required: true,
},
// Conditional fields per operation
// ...
],
tools: {
access: ['{service}_action1', '{service}_action2'],
config: {
tool: (params) => `{service}_${params.operation}`,
},
},
outputs: { /* ... */ },
}
```
### Key SubBlock Patterns
**Condition-based visibility:**
```typescript
{
id: 'resourceId',
title: 'Resource ID',
type: 'short-input',
condition: { field: 'operation', value: ['read', 'update', 'delete'] },
required: { field: 'operation', value: ['read', 'update', 'delete'] },
}
```
**DependsOn for cascading selectors:**
```typescript
{
id: 'project',
type: 'project-selector',
dependsOn: ['credential'],
},
{
id: 'issue',
type: 'file-selector',
dependsOn: ['credential', 'project'],
}
```
**Basic/Advanced mode for dual UX:**
```typescript
// Basic: Visual selector
{
id: 'channel',
type: 'channel-selector',
mode: 'basic',
canonicalParamId: 'channel',
dependsOn: ['credential'],
},
// Advanced: Manual input
{
id: 'channelId',
type: 'short-input',
mode: 'advanced',
canonicalParamId: 'channel',
}
```
**Critical Canonical Param Rules:**
- `canonicalParamId` must NOT match any subblock's `id` in the block
- `canonicalParamId` must be unique per operation/condition context
- Only use `canonicalParamId` to link basic/advanced alternatives for the same logical parameter
- `mode` only controls UI visibility, NOT serialization. Without `canonicalParamId`, both basic and advanced field values would be sent
- Every subblock `id` must be unique within the block. Duplicate IDs cause conflicts even with different conditions
- **Required consistency:** If one subblock in a canonical group has `required: true`, ALL subblocks in that group must have `required: true` (prevents bypassing validation by switching modes)
- **Inputs section:** Must list canonical param IDs (e.g., `fileId`), NOT raw subblock IDs (e.g., `fileSelector`, `manualFileId`)
- **Params function:** Must use canonical param IDs, NOT raw subblock IDs (raw IDs are deleted after canonical transformation)
## Step 4: Add Icon
### File Location
`apps/sim/components/icons.tsx`
### Pattern
```typescript
export function {Service}Icon(props: SVGProps<SVGSVGElement>) {
return (
<svg
{...props}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
{/* SVG paths from user-provided SVG */}
</svg>
)
}
```
### Getting Icons
**Do NOT search for icons yourself.** At the end of implementation, ask the user to provide the SVG:
```
I've completed the integration. Before I can add the icon, please provide the SVG for {Service}.
You can usually find this in the service's brand/press kit page, or copy it from their website.
Paste the SVG code here and I'll convert it to a React component.
```
Once the user provides the SVG:
1. Extract the SVG paths/content
2. Create a React component that spreads props
3. Ensure viewBox is preserved from the original SVG
## Step 5: Create Triggers (Optional)
If the service supports webhooks, create triggers using the generic `buildTriggerSubBlocks` helper.
### Directory Structure
```
apps/sim/triggers/{service}/
├── index.ts # Barrel exports
├── utils.ts # Trigger options, setup instructions, extra fields
├── {event_a}.ts # Primary trigger (includes dropdown)
├── {event_b}.ts # Secondary triggers (no dropdown)
└── webhook.ts # Generic webhook (optional)
```
### Key Pattern
```typescript
import { buildTriggerSubBlocks } from '@/triggers'
import { {service}TriggerOptions, {service}SetupInstructions, build{Service}ExtraFields } from './utils'
// Primary trigger - includeDropdown: true
export const {service}EventATrigger: TriggerConfig = {
id: '{service}_event_a',
subBlocks: buildTriggerSubBlocks({
triggerId: '{service}_event_a',
triggerOptions: {service}TriggerOptions,
includeDropdown: true, // Only for primary trigger!
setupInstructions: {service}SetupInstructions('Event A'),
extraFields: build{Service}ExtraFields('{service}_event_a'),
}),
// ...
}
// Secondary triggers - no dropdown
export const {service}EventBTrigger: TriggerConfig = {
id: '{service}_event_b',
subBlocks: buildTriggerSubBlocks({
triggerId: '{service}_event_b',
triggerOptions: {service}TriggerOptions,
// No includeDropdown!
setupInstructions: {service}SetupInstructions('Event B'),
extraFields: build{Service}ExtraFields('{service}_event_b'),
}),
// ...
}
```
### Connect to Block
```typescript
import { getTrigger } from '@/triggers'
export const {Service}Block: BlockConfig = {
triggers: {
enabled: true,
available: ['{service}_event_a', '{service}_event_b'],
},
subBlocks: [
// Tool fields...
...getTrigger('{service}_event_a').subBlocks,
...getTrigger('{service}_event_b').subBlocks,
],
}
```
See `/add-trigger` skill for complete documentation.
## Step 6: Register Everything
### Tools Registry (`apps/sim/tools/registry.ts`)
```typescript
// Add import (alphabetically)
import {
{service}Action1Tool,
{service}Action2Tool,
} from '@/tools/{service}'
// Add to tools object (alphabetically)
export const tools: Record<string, ToolConfig> = {
// ... existing tools ...
{service}_action1: {service}Action1Tool,
{service}_action2: {service}Action2Tool,
}
```
### Block Registry (`apps/sim/blocks/registry.ts`)
```typescript
// Add import (alphabetically)
import { {Service}Block } from '@/blocks/blocks/{service}'
// Add to registry (alphabetically)
export const registry: Record<string, BlockConfig> = {
// ... existing blocks ...
{service}: {Service}Block,
}
```
### Trigger Registry (`apps/sim/triggers/registry.ts`) - If triggers exist
```typescript
// Add import (alphabetically)
import {
{service}EventATrigger,
{service}EventBTrigger,
{service}WebhookTrigger,
} from '@/triggers/{service}'
// Add to TRIGGER_REGISTRY (alphabetically)
export const TRIGGER_REGISTRY: TriggerRegistry = {
// ... existing triggers ...
{service}_event_a: {service}EventATrigger,
{service}_event_b: {service}EventBTrigger,
{service}_webhook: {service}WebhookTrigger,
}
```
## Step 7: Generate Docs
Run the documentation generator:
```bash
bun run scripts/generate-docs.ts
```
This creates `apps/docs/content/docs/en/tools/{service}.mdx`
## V2 Integration Pattern
If creating V2 versions (API-aligned outputs):
1. **V2 Tools** - Add `_v2` suffix, version `2.0.0`, flat outputs
2. **V2 Block** - Add `_v2` type, use `createVersionedToolSelector`
3. **V1 Block** - Add `(Legacy)` to name, set `hideFromToolbar: true`
4. **Registry** - Register both versions
```typescript
// In registry
{service}: {Service}Block, // V1 (legacy, hidden)
{service}_v2: {Service}V2Block, // V2 (visible)
```
## Complete Checklist
### Tools
- [ ] Created `tools/{service}/` directory
- [ ] Created `types.ts` with all interfaces
- [ ] Created tool file for each operation
- [ ] All params have correct visibility
- [ ] All nullable fields use `?? null`
- [ ] All optional outputs have `optional: true`
- [ ] Created `index.ts` barrel export
- [ ] Registered all tools in `tools/registry.ts`
### Block
- [ ] Created `blocks/blocks/{service}.ts`
- [ ] Defined operation dropdown with all operations
- [ ] Added credential field (oauth-input or short-input)
- [ ] Added conditional fields per operation
- [ ] Set up dependsOn for cascading selectors
- [ ] Configured tools.access with all tool IDs
- [ ] Configured tools.config.tool selector
- [ ] Defined outputs matching tool outputs
- [ ] Registered block in `blocks/registry.ts`
- [ ] If triggers: set `triggers.enabled` and `triggers.available`
- [ ] If triggers: spread trigger subBlocks with `getTrigger()`
### Icon
- [ ] Asked user to provide SVG
- [ ] Added icon to `components/icons.tsx`
- [ ] Icon spreads props correctly
### Triggers (if service supports webhooks)
- [ ] Created `triggers/{service}/` directory
- [ ] Created `utils.ts` with options, instructions, and extra fields helpers
- [ ] Primary trigger uses `includeDropdown: true`
- [ ] Secondary triggers do NOT have `includeDropdown`
- [ ] All triggers use `buildTriggerSubBlocks` helper
- [ ] Created `index.ts` barrel export
- [ ] Registered all triggers in `triggers/registry.ts`
### Docs
- [ ] Ran `bun run scripts/generate-docs.ts`
- [ ] Verified docs file created
## Example Command
When the user asks to add an integration:
```
User: Add a Stripe integration
You: I'll add the Stripe integration. Let me:
1. First, research the Stripe API using Context7
2. Create the tools for key operations (payments, subscriptions, etc.)
3. Create the block with operation dropdown
4. Register everything
5. Generate docs
6. Ask you for the Stripe icon SVG
[Proceed with implementation...]
[After completing steps 1-5...]
I've completed the Stripe integration. Before I can add the icon, please provide the SVG for Stripe.
You can usually find this in the service's brand/press kit page, or copy it from their website.
Paste the SVG code here and I'll convert it to a React component.
```
## File Handling
When your integration handles file uploads or downloads, follow these patterns to work with `UserFile` objects consistently.
### What is a UserFile?
A `UserFile` is the standard file representation in Sim:
```typescript
interface UserFile {
id: string // Unique identifier
name: string // Original filename
url: string // Presigned URL for download
size: number // File size in bytes
type: string // MIME type (e.g., 'application/pdf')
base64?: string // Optional base64 content (if small file)
key?: string // Internal storage key
context?: object // Storage context metadata
}
```
### File Input Pattern (Uploads)
For tools that accept file uploads, **always route through an internal API endpoint** rather than calling external APIs directly. This ensures proper file content retrieval.
#### 1. Block SubBlocks for File Input
Use the basic/advanced mode pattern:
```typescript
// Basic mode: File upload UI
{
id: 'uploadFile',
title: 'File',
type: 'file-upload',
canonicalParamId: 'file', // Maps to 'file' param
placeholder: 'Upload file',
mode: 'basic',
multiple: false,
required: true,
condition: { field: 'operation', value: 'upload' },
},
// Advanced mode: Reference from previous block
{
id: 'fileRef',
title: 'File',
type: 'short-input',
canonicalParamId: 'file', // Same canonical param
placeholder: 'Reference file (e.g., {{file_block.output}})',
mode: 'advanced',
required: true,
condition: { field: 'operation', value: 'upload' },
},
```
**Critical:** `canonicalParamId` must NOT match any subblock `id`.
#### 2. Normalize File Input in Block Config
In `tools.config.tool`, use `normalizeFileInput` to handle all input variants:
```typescript
import { normalizeFileInput } from '@/blocks/utils'
tools: {
config: {
tool: (params) => {
// Normalize file from basic (uploadFile), advanced (fileRef), or legacy (fileContent)
const normalizedFile = normalizeFileInput(
params.uploadFile || params.fileRef || params.fileContent,
{ single: true }
)
if (normalizedFile) {
params.file = normalizedFile
}
return `{service}_${params.operation}`
},
},
}
```
#### 3. Create Internal API Route
Create `apps/sim/app/api/tools/{service}/{action}/route.ts`:
```typescript
import { createLogger } from '@sim/logger'
import { NextResponse, type NextRequest } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateRequestId } from '@/lib/core/utils/request'
import { FileInputSchema, type RawFileInput } from '@/lib/uploads/utils/file-schemas'
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
const logger = createLogger('{Service}UploadAPI')
const RequestSchema = z.object({
accessToken: z.string(),
file: FileInputSchema.optional().nullable(),
// Legacy field for backwards compatibility
fileContent: z.string().optional().nullable(),
// ... other params
})
export async function POST(request: NextRequest) {
const requestId = generateRequestId()
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
if (!authResult.success) {
return NextResponse.json({ success: false, error: 'Unauthorized' }, { status: 401 })
}
const body = await request.json()
const data = RequestSchema.parse(body)
let fileBuffer: Buffer
let fileName: string
// Prefer UserFile input, fall back to legacy base64
if (data.file) {
const userFiles = processFilesToUserFiles([data.file as RawFileInput], requestId, logger)
if (userFiles.length === 0) {
return NextResponse.json({ success: false, error: 'Invalid file' }, { status: 400 })
}
const userFile = userFiles[0]
fileBuffer = await downloadFileFromStorage(userFile, requestId, logger)
fileName = userFile.name
} else if (data.fileContent) {
// Legacy: base64 string (backwards compatibility)
fileBuffer = Buffer.from(data.fileContent, 'base64')
fileName = 'file'
} else {
return NextResponse.json({ success: false, error: 'File required' }, { status: 400 })
}
// Now call external API with fileBuffer
const response = await fetch('https://api.{service}.com/upload', {
method: 'POST',
headers: { Authorization: `Bearer ${data.accessToken}` },
body: new Uint8Array(fileBuffer), // Convert Buffer for fetch
})
// ... handle response
}
```
#### 4. Update Tool to Use Internal Route
```typescript
export const {service}UploadTool: ToolConfig<Params, Response> = {
id: '{service}_upload',
// ...
params: {
file: { type: 'file', required: false, visibility: 'user-or-llm' },
fileContent: { type: 'string', required: false, visibility: 'hidden' }, // Legacy
},
request: {
url: '/api/tools/{service}/upload', // Internal route
method: 'POST',
body: (params) => ({
accessToken: params.accessToken,
file: params.file,
fileContent: params.fileContent,
}),
},
}
```
### File Output Pattern (Downloads)
For tools that return files, use `FileToolProcessor` to store files and return `UserFile` objects.
#### In Tool transformResponse
```typescript
import { FileToolProcessor } from '@/executor/utils/file-tool-processor'
transformResponse: async (response, context) => {
const data = await response.json()
// Process file outputs to UserFile objects
const fileProcessor = new FileToolProcessor(context)
const file = await fileProcessor.processFileData({
data: data.content, // base64 or buffer
mimeType: data.mimeType,
filename: data.filename,
})
return {
success: true,
output: { file },
}
}
```
#### In API Route (for complex file handling)
```typescript
// Return file data that FileToolProcessor can handle
return NextResponse.json({
success: true,
output: {
file: {
data: base64Content,
mimeType: 'application/pdf',
filename: 'document.pdf',
},
},
})
```
### Key Helpers Reference
| Helper | Location | Purpose |
|--------|----------|---------|
| `normalizeFileInput` | `@/blocks/utils` | Normalize file params in block config |
| `processFilesToUserFiles` | `@/lib/uploads/utils/file-utils` | Convert raw inputs to UserFile[] |
| `downloadFileFromStorage` | `@/lib/uploads/utils/file-utils.server` | Get file Buffer from UserFile |
| `FileToolProcessor` | `@/executor/utils/file-tool-processor` | Process tool output files |
| `isUserFile` | `@/lib/core/utils/user-file` | Type guard for UserFile objects |
| `FileInputSchema` | `@/lib/uploads/utils/file-schemas` | Zod schema for file validation |
### Common Gotchas
1. **OAuth serviceId must match** - The `serviceId` in oauth-input must match the OAuth provider configuration
2. **Tool IDs are snake_case** - `stripe_create_payment`, not `stripeCreatePayment`
3. **Block type is snake_case** - `type: 'stripe'`, not `type: 'Stripe'`
4. **Alphabetical ordering** - Keep imports and registry entries alphabetically sorted
5. **Required can be conditional** - Use `required: { field: 'op', value: 'create' }` instead of always true
6. **DependsOn clears options** - When a dependency changes, selector options are refetched
7. **Never pass Buffer directly to fetch** - Convert to `new Uint8Array(buffer)` for TypeScript compatibility
8. **Always handle legacy file params** - Keep hidden `fileContent` params for backwards compatibility

View File

@@ -1,284 +0,0 @@
---
description: Create tool configurations for a Sim integration by reading API docs
argument-hint: <service-name> [api-docs-url]
---
# Add Tools Skill
You are an expert at creating tool configurations for Sim integrations. Your job is to read API documentation and create properly structured tool files.
## Your Task
When the user asks you to create tools for a service:
1. Use Context7 or WebFetch to read the service's API documentation
2. Create the tools directory structure
3. Generate properly typed tool configurations
## Directory Structure
Create files in `apps/sim/tools/{service}/`:
```
tools/{service}/
├── index.ts # Barrel export
├── types.ts # Parameter & response types
└── {action}.ts # Individual tool files (one per operation)
```
## Tool Configuration Structure
Every tool MUST follow this exact structure:
```typescript
import type { {ServiceName}{Action}Params } from '@/tools/{service}/types'
import type { ToolConfig } from '@/tools/types'
interface {ServiceName}{Action}Response {
success: boolean
output: {
// Define output structure here
}
}
export const {serviceName}{Action}Tool: ToolConfig<
{ServiceName}{Action}Params,
{ServiceName}{Action}Response
> = {
id: '{service}_{action}', // snake_case, matches tool name
name: '{Service} {Action}', // Human readable
description: 'Brief description', // One sentence
version: '1.0.0',
// OAuth config (if service uses OAuth)
oauth: {
required: true,
provider: '{service}', // Must match OAuth provider ID
},
params: {
// Hidden params (system-injected, only use hidden for oauth accessToken)
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'OAuth access token',
},
// User-only params (credentials, api key, IDs user must provide)
someId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'The ID of the resource',
},
// User-or-LLM params (everything else, can be provided by user OR computed by LLM)
query: {
type: 'string',
required: false, // Use false for optional
visibility: 'user-or-llm',
description: 'Search query',
},
},
request: {
url: (params) => `https://api.service.com/v1/resource/${params.id}`,
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
}),
body: (params) => ({
// Request body - only for POST/PUT/PATCH
// Trim ID fields to prevent copy-paste whitespace errors:
// userId: params.userId?.trim(),
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
// Map API response to output
// Use ?? null for nullable fields
// Use ?? [] for optional arrays
},
}
},
outputs: {
// Define each output field
},
}
```
## Critical Rules for Parameters
### Visibility Options
- `'hidden'` - System-injected (OAuth tokens, internal params). User never sees.
- `'user-only'` - User must provide (credentials, api keys, account-specific IDs)
- `'user-or-llm'` - User provides OR LLM can compute (search queries, content, filters, most fall into this category)
### Parameter Types
- `'string'` - Text values
- `'number'` - Numeric values
- `'boolean'` - True/false
- `'json'` - Complex objects (NOT 'object', use 'json')
- `'file'` - Single file
- `'file[]'` - Multiple files
### Required vs Optional
- Always explicitly set `required: true` or `required: false`
- Optional params should have `required: false`
## Critical Rules for Outputs
### Output Types
- `'string'`, `'number'`, `'boolean'` - Primitives
- `'json'` - Complex objects (use this, NOT 'object')
- `'array'` - Arrays with `items` property
- `'object'` - Objects with `properties` property
### Optional Outputs
Add `optional: true` for fields that may not exist in the response:
```typescript
closedAt: {
type: 'string',
description: 'When the issue was closed',
optional: true,
},
```
### Nested Properties
For complex outputs, define nested structure:
```typescript
metadata: {
type: 'json',
description: 'Response metadata',
properties: {
id: { type: 'string', description: 'Unique ID' },
status: { type: 'string', description: 'Current status' },
count: { type: 'number', description: 'Total count' },
},
},
items: {
type: 'array',
description: 'List of items',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Item ID' },
name: { type: 'string', description: 'Item name' },
},
},
},
```
## Critical Rules for transformResponse
### Handle Nullable Fields
ALWAYS use `?? null` for fields that may be undefined:
```typescript
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
id: data.id,
title: data.title,
body: data.body ?? null, // May be undefined
assignee: data.assignee ?? null, // May be undefined
labels: data.labels ?? [], // Default to empty array
closedAt: data.closed_at ?? null, // May be undefined
},
}
}
```
### Never Output Raw JSON Dumps
DON'T do this:
```typescript
output: {
data: data, // BAD - raw JSON dump
}
```
DO this instead - extract meaningful fields:
```typescript
output: {
id: data.id,
name: data.name,
status: data.status,
metadata: {
createdAt: data.created_at,
updatedAt: data.updated_at,
},
}
```
## Types File Pattern
Create `types.ts` with interfaces for all params and responses:
```typescript
import type { ToolResponse } from '@/tools/types'
// Parameter interfaces
export interface {Service}{Action}Params {
accessToken: string
requiredField: string
optionalField?: string
}
// Response interfaces (extend ToolResponse)
export interface {Service}{Action}Response extends ToolResponse {
output: {
field1: string
field2: number
optionalField?: string | null
}
}
```
## Index.ts Barrel Export Pattern
```typescript
// Export all tools
export { serviceTool1 } from './{action1}'
export { serviceTool2 } from './{action2}'
// Export types
export * from './types'
```
## Registering Tools
After creating tools, remind the user to:
1. Import tools in `apps/sim/tools/registry.ts`
2. Add to the `tools` object with snake_case keys:
```typescript
import { serviceActionTool } from '@/tools/{service}'
export const tools = {
// ... existing tools ...
{service}_{action}: serviceActionTool,
}
```
## V2 Tool Pattern
If creating V2 tools (API-aligned outputs), use `_v2` suffix:
- Tool ID: `{service}_{action}_v2`
- Variable name: `{action}V2Tool`
- Version: `'2.0.0'`
- Outputs: Flat, API-aligned (no content/metadata wrapper)
## Checklist Before Finishing
- [ ] All params have explicit `required: true` or `required: false`
- [ ] All params have appropriate `visibility`
- [ ] All nullable response fields use `?? null`
- [ ] All optional outputs have `optional: true`
- [ ] No raw JSON dumps in outputs
- [ ] Types file has all interfaces
- [ ] Index.ts exports all tools
- [ ] Tool IDs use snake_case

View File

@@ -1,708 +0,0 @@
---
description: Create webhook triggers for a Sim integration using the generic trigger builder
argument-hint: <service-name>
---
# Add Trigger Skill
You are an expert at creating webhook triggers for Sim. You understand the trigger system, the generic `buildTriggerSubBlocks` helper, and how triggers connect to blocks.
## Your Task
When the user asks you to create triggers for a service:
1. Research what webhook events the service supports
2. Create the trigger files using the generic builder
3. Register triggers and connect them to the block
## Directory Structure
```
apps/sim/triggers/{service}/
├── index.ts # Barrel exports
├── utils.ts # Service-specific helpers (trigger options, setup instructions, extra fields)
├── {event_a}.ts # Primary trigger (includes dropdown)
├── {event_b}.ts # Secondary trigger (no dropdown)
├── {event_c}.ts # Secondary trigger (no dropdown)
└── webhook.ts # Generic webhook trigger (optional, for "all events")
```
## Step 1: Create utils.ts
This file contains service-specific helpers used by all triggers.
```typescript
import type { SubBlockConfig } from '@/blocks/types'
import type { TriggerOutput } from '@/triggers/types'
/**
* Dropdown options for the trigger type selector.
* These appear in the primary trigger's dropdown.
*/
export const {service}TriggerOptions = [
{ label: 'Event A', id: '{service}_event_a' },
{ label: 'Event B', id: '{service}_event_b' },
{ label: 'Event C', id: '{service}_event_c' },
{ label: 'Generic Webhook (All Events)', id: '{service}_webhook' },
]
/**
* Generates HTML setup instructions for the trigger.
* Displayed to users to help them configure webhooks in the external service.
*/
export function {service}SetupInstructions(eventType: string): string {
const instructions = [
'Copy the <strong>Webhook URL</strong> above',
'Go to <strong>{Service} Settings > Webhooks</strong>',
'Click <strong>Add Webhook</strong>',
'Paste the webhook URL',
`Select the <strong>${eventType}</strong> event type`,
'Save the webhook configuration',
'Click "Save" above to activate your trigger',
]
return instructions
.map((instruction, index) =>
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
)
.join('')
}
/**
* Service-specific extra fields to add to triggers.
* These are inserted between webhookUrl and triggerSave.
*/
export function build{Service}ExtraFields(triggerId: string): SubBlockConfig[] {
return [
{
id: 'projectId',
title: 'Project ID (Optional)',
type: 'short-input',
placeholder: 'Leave empty for all projects',
description: 'Optionally filter to a specific project',
mode: 'trigger',
condition: { field: 'selectedTriggerId', value: triggerId },
},
]
}
/**
* Build outputs for this trigger type.
* Outputs define what data is available to downstream blocks.
*/
export function build{Service}Outputs(): Record<string, TriggerOutput> {
return {
eventType: { type: 'string', description: 'The type of event that triggered this workflow' },
resourceId: { type: 'string', description: 'ID of the affected resource' },
timestamp: { type: 'string', description: 'When the event occurred (ISO 8601)' },
// Nested outputs for complex data
resource: {
id: { type: 'string', description: 'Resource ID' },
name: { type: 'string', description: 'Resource name' },
status: { type: 'string', description: 'Current status' },
},
webhook: { type: 'json', description: 'Full webhook payload' },
}
}
```
## Step 2: Create the Primary Trigger
The **primary trigger** is the first one listed. It MUST include `includeDropdown: true` so users can switch between trigger types.
```typescript
import { {Service}Icon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
build{Service}ExtraFields,
build{Service}Outputs,
{service}SetupInstructions,
{service}TriggerOptions,
} from '@/triggers/{service}/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* {Service} Event A Trigger
*
* This is the PRIMARY trigger - it includes the dropdown for selecting trigger type.
*/
export const {service}EventATrigger: TriggerConfig = {
id: '{service}_event_a',
name: '{Service} Event A',
provider: '{service}',
description: 'Trigger workflow when Event A occurs',
version: '1.0.0',
icon: {Service}Icon,
subBlocks: buildTriggerSubBlocks({
triggerId: '{service}_event_a',
triggerOptions: {service}TriggerOptions,
includeDropdown: true, // PRIMARY TRIGGER - includes dropdown
setupInstructions: {service}SetupInstructions('Event A'),
extraFields: build{Service}ExtraFields('{service}_event_a'),
}),
outputs: build{Service}Outputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
```
## Step 3: Create Secondary Triggers
Secondary triggers do NOT include the dropdown (it's already in the primary trigger).
```typescript
import { {Service}Icon } from '@/components/icons'
import { buildTriggerSubBlocks } from '@/triggers'
import {
build{Service}ExtraFields,
build{Service}Outputs,
{service}SetupInstructions,
{service}TriggerOptions,
} from '@/triggers/{service}/utils'
import type { TriggerConfig } from '@/triggers/types'
/**
* {Service} Event B Trigger
*/
export const {service}EventBTrigger: TriggerConfig = {
id: '{service}_event_b',
name: '{Service} Event B',
provider: '{service}',
description: 'Trigger workflow when Event B occurs',
version: '1.0.0',
icon: {Service}Icon,
subBlocks: buildTriggerSubBlocks({
triggerId: '{service}_event_b',
triggerOptions: {service}TriggerOptions,
// NO includeDropdown - secondary trigger
setupInstructions: {service}SetupInstructions('Event B'),
extraFields: build{Service}ExtraFields('{service}_event_b'),
}),
outputs: build{Service}Outputs(),
webhook: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
},
}
```
## Step 4: Create index.ts Barrel Export
```typescript
export { {service}EventATrigger } from './event_a'
export { {service}EventBTrigger } from './event_b'
export { {service}EventCTrigger } from './event_c'
export { {service}WebhookTrigger } from './webhook'
```
## Step 5: Register Triggers
### Trigger Registry (`apps/sim/triggers/registry.ts`)
```typescript
// Add import
import {
{service}EventATrigger,
{service}EventBTrigger,
{service}EventCTrigger,
{service}WebhookTrigger,
} from '@/triggers/{service}'
// Add to TRIGGER_REGISTRY
export const TRIGGER_REGISTRY: TriggerRegistry = {
// ... existing triggers ...
{service}_event_a: {service}EventATrigger,
{service}_event_b: {service}EventBTrigger,
{service}_event_c: {service}EventCTrigger,
{service}_webhook: {service}WebhookTrigger,
}
```
## Step 6: Connect Triggers to Block
In the block file (`apps/sim/blocks/blocks/{service}.ts`):
```typescript
import { {Service}Icon } from '@/components/icons'
import { getTrigger } from '@/triggers'
import type { BlockConfig } from '@/blocks/types'
export const {Service}Block: BlockConfig = {
type: '{service}',
name: '{Service}',
// ... other config ...
// Enable triggers and list available trigger IDs
triggers: {
enabled: true,
available: [
'{service}_event_a',
'{service}_event_b',
'{service}_event_c',
'{service}_webhook',
],
},
subBlocks: [
// Regular tool subBlocks first
{ id: 'operation', /* ... */ },
{ id: 'credential', /* ... */ },
// ... other tool fields ...
// Then spread ALL trigger subBlocks
...getTrigger('{service}_event_a').subBlocks,
...getTrigger('{service}_event_b').subBlocks,
...getTrigger('{service}_event_c').subBlocks,
...getTrigger('{service}_webhook').subBlocks,
],
// ... tools config ...
}
```
## Automatic Webhook Registration (Preferred)
If the service's API supports programmatic webhook creation, implement automatic webhook registration instead of requiring users to manually configure webhooks. This provides a much better user experience.
### When to Use Automatic Registration
Check the service's API documentation for endpoints like:
- `POST /webhooks` or `POST /hooks` - Create webhook
- `DELETE /webhooks/{id}` - Delete webhook
Services that support this pattern include: Grain, Lemlist, Calendly, Airtable, Webflow, Typeform, etc.
### Implementation Steps
#### 1. Add API Key to Extra Fields
Update your `build{Service}ExtraFields` function to include an API key field:
```typescript
export function build{Service}ExtraFields(triggerId: string): SubBlockConfig[] {
return [
{
id: 'apiKey',
title: 'API Key',
type: 'short-input',
placeholder: 'Enter your {Service} API key',
description: 'Required to create the webhook in {Service}.',
password: true,
required: true,
mode: 'trigger',
condition: { field: 'selectedTriggerId', value: triggerId },
},
// Other optional fields (e.g., campaign filter, project filter)
{
id: 'projectId',
title: 'Project ID (Optional)',
type: 'short-input',
placeholder: 'Leave empty for all projects',
mode: 'trigger',
condition: { field: 'selectedTriggerId', value: triggerId },
},
]
}
```
#### 2. Update Setup Instructions for Automatic Creation
Change instructions to indicate automatic webhook creation:
```typescript
export function {service}SetupInstructions(eventType: string): string {
const instructions = [
'Enter your {Service} API Key above.',
'You can find your API key in {Service} at <strong>Settings > API</strong>.',
`Click <strong>"Save Configuration"</strong> to automatically create the webhook in {Service} for <strong>${eventType}</strong> events.`,
'The webhook will be automatically deleted when you remove this trigger.',
]
return instructions
.map((instruction, index) =>
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
)
.join('')
}
```
#### 3. Add Webhook Creation to API Route
In `apps/sim/app/api/webhooks/route.ts`, add provider-specific logic after the database save:
```typescript
// --- {Service} specific logic ---
if (savedWebhook && provider === '{service}') {
logger.info(`[${requestId}] {Service} provider detected. Creating webhook subscription.`)
try {
const result = await create{Service}WebhookSubscription(
{
id: savedWebhook.id,
path: savedWebhook.path,
providerConfig: savedWebhook.providerConfig,
},
requestId
)
if (result) {
// Update the webhook record with the external webhook ID
const updatedConfig = {
...(savedWebhook.providerConfig as Record<string, any>),
externalId: result.id,
}
await db
.update(webhook)
.set({
providerConfig: updatedConfig,
updatedAt: new Date(),
})
.where(eq(webhook.id, savedWebhook.id))
savedWebhook.providerConfig = updatedConfig
logger.info(`[${requestId}] Successfully created {Service} webhook`, {
externalHookId: result.id,
webhookId: savedWebhook.id,
})
}
} catch (err) {
logger.error(
`[${requestId}] Error creating {Service} webhook subscription, rolling back webhook`,
err
)
await db.delete(webhook).where(eq(webhook.id, savedWebhook.id))
return NextResponse.json(
{
error: 'Failed to create webhook in {Service}',
details: err instanceof Error ? err.message : 'Unknown error',
},
{ status: 500 }
)
}
}
// --- End {Service} specific logic ---
```
Then add the helper function at the end of the file:
```typescript
async function create{Service}WebhookSubscription(
webhookData: any,
requestId: string
): Promise<{ id: string } | undefined> {
try {
const { path, providerConfig } = webhookData
const { apiKey, triggerId, projectId } = providerConfig || {}
if (!apiKey) {
throw new Error('{Service} API Key is required.')
}
// Map trigger IDs to service event types
const eventTypeMap: Record<string, string | undefined> = {
{service}_event_a: 'eventA',
{service}_event_b: 'eventB',
{service}_webhook: undefined, // Generic - no filter
}
const eventType = eventTypeMap[triggerId]
const notificationUrl = `${getBaseUrl()}/api/webhooks/trigger/${path}`
const requestBody: Record<string, any> = {
url: notificationUrl,
}
if (eventType) {
requestBody.eventType = eventType
}
if (projectId) {
requestBody.projectId = projectId
}
const response = await fetch('https://api.{service}.com/webhooks', {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(requestBody),
})
const responseBody = await response.json()
if (!response.ok) {
const errorMessage = responseBody.message || 'Unknown API error'
let userFriendlyMessage = 'Failed to create webhook in {Service}'
if (response.status === 401) {
userFriendlyMessage = 'Invalid API Key. Please verify and try again.'
} else if (errorMessage) {
userFriendlyMessage = `{Service} error: ${errorMessage}`
}
throw new Error(userFriendlyMessage)
}
return { id: responseBody.id }
} catch (error: any) {
logger.error(`Exception during {Service} webhook creation`, { error: error.message })
throw error
}
}
```
#### 4. Add Webhook Deletion to Provider Subscriptions
In `apps/sim/lib/webhooks/provider-subscriptions.ts`:
1. Add a logger:
```typescript
const {service}Logger = createLogger('{Service}Webhook')
```
2. Add the delete function:
```typescript
export async function delete{Service}Webhook(webhook: any, requestId: string): Promise<void> {
try {
const config = getProviderConfig(webhook)
const apiKey = config.apiKey as string | undefined
const externalId = config.externalId as string | undefined
if (!apiKey || !externalId) {
{service}Logger.warn(`[${requestId}] Missing apiKey or externalId, skipping cleanup`)
return
}
const response = await fetch(`https://api.{service}.com/webhooks/${externalId}`, {
method: 'DELETE',
headers: {
Authorization: `Bearer ${apiKey}`,
},
})
if (!response.ok && response.status !== 404) {
{service}Logger.warn(`[${requestId}] Failed to delete webhook (non-fatal): ${response.status}`)
} else {
{service}Logger.info(`[${requestId}] Successfully deleted webhook ${externalId}`)
}
} catch (error) {
{service}Logger.warn(`[${requestId}] Error deleting webhook (non-fatal)`, error)
}
}
```
3. Add to `cleanupExternalWebhook`:
```typescript
export async function cleanupExternalWebhook(...): Promise<void> {
// ... existing providers ...
} else if (webhook.provider === '{service}') {
await delete{Service}Webhook(webhook, requestId)
}
}
```
### Key Points for Automatic Registration
- **API Key visibility**: Always use `password: true` for API key fields
- **Error handling**: Roll back the database webhook if external creation fails
- **External ID storage**: Save the external webhook ID in `providerConfig.externalId`
- **Graceful cleanup**: Don't fail webhook deletion if cleanup fails (use non-fatal logging)
- **User-friendly errors**: Map HTTP status codes to helpful error messages
## The buildTriggerSubBlocks Helper
This is the generic helper from `@/triggers` that creates consistent trigger subBlocks.
### Function Signature
```typescript
interface BuildTriggerSubBlocksOptions {
triggerId: string // e.g., 'service_event_a'
triggerOptions: Array<{ label: string; id: string }> // Dropdown options
includeDropdown?: boolean // true only for primary trigger
setupInstructions: string // HTML instructions
extraFields?: SubBlockConfig[] // Service-specific fields
webhookPlaceholder?: string // Custom placeholder text
}
function buildTriggerSubBlocks(options: BuildTriggerSubBlocksOptions): SubBlockConfig[]
```
### What It Creates
The helper creates this structure:
1. **Dropdown** (only if `includeDropdown: true`) - Trigger type selector
2. **Webhook URL** - Read-only field with copy button
3. **Extra Fields** - Your service-specific fields (filters, options, etc.)
4. **Save Button** - Activates the trigger
5. **Instructions** - Setup guide for users
All fields automatically have:
- `mode: 'trigger'` - Only shown in trigger mode
- `condition: { field: 'selectedTriggerId', value: triggerId }` - Only shown when this trigger is selected
## Trigger Outputs & Webhook Input Formatting
### Important: Two Sources of Truth
There are two related but separate concerns:
1. **Trigger `outputs`** - Schema/contract defining what fields SHOULD be available. Used by UI for tag dropdown.
2. **`formatWebhookInput`** - Implementation that transforms raw webhook payload into actual data. Located in `apps/sim/lib/webhooks/utils.server.ts`.
**These MUST be aligned.** The fields returned by `formatWebhookInput` should match what's defined in trigger `outputs`. If they differ:
- Tag dropdown shows fields that don't exist (broken variable resolution)
- Or actual data has fields not shown in dropdown (users can't discover them)
### When to Add a formatWebhookInput Handler
- **Simple providers**: If the raw webhook payload structure already matches your outputs, you don't need a handler. The generic fallback returns `body` directly.
- **Complex providers**: If you need to transform, flatten, extract nested data, compute fields, or handle conditional logic, add a handler.
### Adding a Handler
In `apps/sim/lib/webhooks/utils.server.ts`, add a handler block:
```typescript
if (foundWebhook.provider === '{service}') {
// Transform raw webhook body to match trigger outputs
return {
eventType: body.type,
resourceId: body.data?.id || '',
timestamp: body.created_at,
resource: body.data,
}
}
```
**Key rules:**
- Return fields that match your trigger `outputs` definition exactly
- No wrapper objects like `webhook: { data: ... }` or `{service}: { ... }`
- No duplication (don't spread body AND add individual fields)
- Use `null` for missing optional data, not empty objects with empty strings
### Verify Alignment
Run the alignment checker:
```bash
bunx scripts/check-trigger-alignment.ts {service}
```
## Trigger Outputs
Trigger outputs use the same schema as block outputs (NOT tool outputs).
**Supported:**
- `type` and `description` for simple fields
- Nested object structure for complex data
**NOT Supported:**
- `optional: true` (tool outputs only)
- `items` property (tool outputs only)
```typescript
export function buildOutputs(): Record<string, TriggerOutput> {
return {
// Simple fields
eventType: { type: 'string', description: 'Event type' },
timestamp: { type: 'string', description: 'When it occurred' },
// Complex data - use type: 'json'
payload: { type: 'json', description: 'Full event payload' },
// Nested structure
resource: {
id: { type: 'string', description: 'Resource ID' },
name: { type: 'string', description: 'Resource name' },
},
}
}
```
## Generic Webhook Trigger Pattern
For services with many event types, create a generic webhook that accepts all events:
```typescript
export const {service}WebhookTrigger: TriggerConfig = {
id: '{service}_webhook',
name: '{Service} Webhook (All Events)',
// ...
subBlocks: buildTriggerSubBlocks({
triggerId: '{service}_webhook',
triggerOptions: {service}TriggerOptions,
setupInstructions: {service}SetupInstructions('All Events'),
extraFields: [
// Event type filter (optional)
{
id: 'eventTypes',
title: 'Event Types',
type: 'dropdown',
multiSelect: true,
options: [
{ label: 'Event A', id: 'event_a' },
{ label: 'Event B', id: 'event_b' },
],
placeholder: 'Leave empty for all events',
mode: 'trigger',
condition: { field: 'selectedTriggerId', value: '{service}_webhook' },
},
// Plus any other service-specific fields
...build{Service}ExtraFields('{service}_webhook'),
],
}),
}
```
## Checklist Before Finishing
### Utils
- [ ] Created `{service}TriggerOptions` array with all trigger IDs
- [ ] Created `{service}SetupInstructions` function with clear steps
- [ ] Created `build{Service}ExtraFields` for service-specific fields
- [ ] Created output builders for each trigger type
### Triggers
- [ ] Primary trigger has `includeDropdown: true`
- [ ] Secondary triggers do NOT have `includeDropdown`
- [ ] All triggers use `buildTriggerSubBlocks` helper
- [ ] All triggers have proper outputs defined
- [ ] Created `index.ts` barrel export
### Registration
- [ ] All triggers imported in `triggers/registry.ts`
- [ ] All triggers added to `TRIGGER_REGISTRY`
- [ ] Block has `triggers.enabled: true`
- [ ] Block has all trigger IDs in `triggers.available`
- [ ] Block spreads all trigger subBlocks: `...getTrigger('id').subBlocks`
### Automatic Webhook Registration (if supported)
- [ ] Added API key field to `build{Service}ExtraFields` with `password: true`
- [ ] Updated setup instructions for automatic webhook creation
- [ ] Added provider-specific logic to `apps/sim/app/api/webhooks/route.ts`
- [ ] Added `create{Service}WebhookSubscription` helper function
- [ ] Added `delete{Service}Webhook` function to `provider-subscriptions.ts`
- [ ] Added provider to `cleanupExternalWebhook` function
### Webhook Input Formatting
- [ ] Added handler in `apps/sim/lib/webhooks/utils.server.ts` (if custom formatting needed)
- [ ] Handler returns fields matching trigger `outputs` exactly
- [ ] Run `bunx scripts/check-trigger-alignment.ts {service}` to verify alignment
### Testing
- [ ] Run `bun run type-check` to verify no TypeScript errors
- [ ] Restart dev server to pick up new triggers
- [ ] Test trigger UI shows correctly in the block
- [ ] Test automatic webhook creation works (if applicable)

View File

@@ -1,35 +0,0 @@
---
paths:
- "apps/sim/components/emcn/**"
---
# EMCN Components
Import from `@/components/emcn`, never from subpaths (except CSS files).
## CVA vs Direct Styles
**Use CVA when:** 2+ variants (primary/secondary, sm/md/lg)
```tsx
const buttonVariants = cva('base-classes', {
variants: { variant: { default: '...', primary: '...' } }
})
export { Button, buttonVariants }
```
**Use direct className when:** Single consistent style, no variations
```tsx
function Label({ className, ...props }) {
return <Primitive className={cn('style-classes', className)} {...props} />
}
```
## Rules
- Use Radix UI primitives for accessibility
- Export component and variants (if using CVA)
- TSDoc with usage examples
- Consistent tokens: `font-medium`, `text-[12px]`, `rounded-[4px]`
- `transition-colors` for hover states

View File

@@ -1,13 +0,0 @@
# Global Standards
## Logging
Import `createLogger` from `sim/logger`. Use `logger.info`, `logger.warn`, `logger.error` instead of `console.log`.
## Comments
Use TSDoc for documentation. No `====` separators. No non-TSDoc comments.
## Styling
Never update global styles. Keep all styling local to components.
## Package Manager
Use `bun` and `bunx`, not `npm` and `npx`.

View File

@@ -1,56 +0,0 @@
---
paths:
- "apps/sim/**"
---
# Sim App Architecture
## Core Principles
1. **Single Responsibility**: Each component, hook, store has one clear purpose
2. **Composition Over Complexity**: Break down complex logic into smaller pieces
3. **Type Safety First**: TypeScript interfaces for all props, state, return types
4. **Predictable State**: Zustand for global state, useState for UI-only concerns
## Root-Level Structure
```
apps/sim/
├── app/ # Next.js app router (pages, API routes)
├── blocks/ # Block definitions and registry
├── components/ # Shared UI (emcn/, ui/)
├── executor/ # Workflow execution engine
├── hooks/ # Shared hooks (queries/, selectors/)
├── lib/ # App-wide utilities
├── providers/ # LLM provider integrations
├── stores/ # Zustand stores
├── tools/ # Tool definitions
└── triggers/ # Trigger definitions
```
## Feature Organization
Features live under `app/workspace/[workspaceId]/`:
```
feature/
├── components/ # Feature components
├── hooks/ # Feature-scoped hooks
├── utils/ # Feature-scoped utilities (2+ consumers)
├── feature.tsx # Main component
└── page.tsx # Next.js page entry
```
## Naming Conventions
- **Components**: PascalCase (`WorkflowList`)
- **Hooks**: `use` prefix (`useWorkflowOperations`)
- **Files**: kebab-case (`workflow-list.tsx`)
- **Stores**: `stores/feature/store.ts`
- **Constants**: SCREAMING_SNAKE_CASE
- **Interfaces**: PascalCase with suffix (`WorkflowListProps`)
## Utils Rules
- **Never create `utils.ts` for single consumer** - inline it
- **Create `utils.ts` when** 2+ files need the same helper
- **Check existing sources** before duplicating (`lib/` has many utilities)
- **Location**: `lib/` (app-wide) → `feature/utils/` (feature-scoped) → inline (single-use)

View File

@@ -1,48 +0,0 @@
---
paths:
- "apps/sim/**/*.tsx"
---
# Component Patterns
## Structure Order
```typescript
'use client' // Only if using hooks
// Imports (external → internal)
// Constants at module level
const CONFIG = { SPACING: 8 } as const
// Props interface
interface ComponentProps {
requiredProp: string
optionalProp?: boolean
}
export function Component({ requiredProp, optionalProp = false }: ComponentProps) {
// a. Refs
// b. External hooks (useParams, useRouter)
// c. Store hooks
// d. Custom hooks
// e. Local state
// f. useMemo
// g. useCallback
// h. useEffect
// i. Return JSX
}
```
## Rules
1. `'use client'` only when using React hooks
2. Always define props interface
3. Extract constants with `as const`
4. Semantic HTML (`aside`, `nav`, `article`)
5. Optional chain callbacks: `onAction?.(id)`
## Component Extraction
**Extract when:** 50+ lines, used in 2+ files, or has own state/logic
**Keep inline when:** < 10 lines, single use, purely presentational

View File

@@ -1,55 +0,0 @@
---
paths:
- "apps/sim/**/use-*.ts"
- "apps/sim/**/hooks/**/*.ts"
---
# Hook Patterns
## Structure
```typescript
interface UseFeatureProps {
id: string
onSuccess?: (result: Result) => void
}
export function useFeature({ id, onSuccess }: UseFeatureProps) {
// 1. Refs for stable dependencies
const idRef = useRef(id)
const onSuccessRef = useRef(onSuccess)
// 2. State
const [data, setData] = useState<Data | null>(null)
const [isLoading, setIsLoading] = useState(false)
// 3. Sync refs
useEffect(() => {
idRef.current = id
onSuccessRef.current = onSuccess
}, [id, onSuccess])
// 4. Operations (useCallback with empty deps when using refs)
const fetchData = useCallback(async () => {
setIsLoading(true)
try {
const result = await fetch(`/api/${idRef.current}`).then(r => r.json())
setData(result)
onSuccessRef.current?.(result)
} finally {
setIsLoading(false)
}
}, [])
return { data, isLoading, fetchData }
}
```
## Rules
1. Single responsibility per hook
2. Props interface required
3. Refs for stable callback dependencies
4. Wrap returned functions in useCallback
5. Always try/catch async operations
6. Track loading/error states

View File

@@ -1,62 +0,0 @@
---
paths:
- "apps/sim/**/*.ts"
- "apps/sim/**/*.tsx"
---
# Import Patterns
## Absolute Imports
**Always use absolute imports.** Never use relative imports.
```typescript
// ✓ Good
import { useWorkflowStore } from '@/stores/workflows/store'
import { Button } from '@/components/ui/button'
// ✗ Bad
import { useWorkflowStore } from '../../../stores/workflows/store'
```
## Barrel Exports
Use barrel exports (`index.ts`) when a folder has 3+ exports. Import from barrel, not individual files.
```typescript
// ✓ Good
import { Dashboard, Sidebar } from '@/app/workspace/[workspaceId]/logs/components'
// ✗ Bad
import { Dashboard } from '@/app/workspace/[workspaceId]/logs/components/dashboard/dashboard'
```
## No Re-exports
Do not re-export from non-barrel files. Import directly from the source.
```typescript
// ✓ Good - import from where it's declared
import { CORE_TRIGGER_TYPES } from '@/stores/logs/filters/types'
// ✗ Bad - re-exporting in utils.ts then importing from there
import { CORE_TRIGGER_TYPES } from '@/app/workspace/.../utils'
```
## Import Order
1. React/core libraries
2. External libraries
3. UI components (`@/components/emcn`, `@/components/ui`)
4. Utilities (`@/lib/...`)
5. Stores (`@/stores/...`)
6. Feature imports
7. CSS imports
## Type Imports
Use `type` keyword for type-only imports:
```typescript
import type { WorkflowLog } from '@/stores/logs/types'
```

View File

@@ -1,287 +0,0 @@
---
paths:
- "apps/sim/tools/**"
- "apps/sim/blocks/**"
- "apps/sim/triggers/**"
---
# Adding Integrations
## Overview
Adding a new integration typically requires:
1. **Tools** - API operations (`tools/{service}/`)
2. **Block** - UI component (`blocks/blocks/{service}.ts`)
3. **Icon** - SVG icon (`components/icons.tsx`)
4. **Trigger** (optional) - Webhooks/polling (`triggers/{service}/`)
Always look up the service's API docs first.
## 1. Tools (`tools/{service}/`)
```
tools/{service}/
├── index.ts # Export all tools
├── types.ts # Params/response types
├── {action}.ts # Individual tool (e.g., send_message.ts)
└── ...
```
**Tool file structure:**
```typescript
// tools/{service}/{action}.ts
import type { {Service}Params, {Service}Response } from '@/tools/{service}/types'
import type { ToolConfig } from '@/tools/types'
export const {service}{Action}Tool: ToolConfig<{Service}Params, {Service}Response> = {
id: '{service}_{action}',
name: '{Service} {Action}',
description: 'What this tool does',
version: '1.0.0',
oauth: { required: true, provider: '{service}' }, // if OAuth
params: { /* param definitions */ },
request: {
url: '/api/tools/{service}/{action}',
method: 'POST',
headers: () => ({ 'Content-Type': 'application/json' }),
body: (params) => ({ ...params }),
},
transformResponse: async (response) => {
const data = await response.json()
if (!data.success) throw new Error(data.error)
return { success: true, output: data.output }
},
outputs: { /* output definitions */ },
}
```
**Register in `tools/registry.ts`:**
```typescript
import { {service}{Action}Tool } from '@/tools/{service}'
// Add to registry object
{service}_{action}: {service}{Action}Tool,
```
## 2. Block (`blocks/blocks/{service}.ts`)
```typescript
import { {Service}Icon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import type { {Service}Response } from '@/tools/{service}/types'
export const {Service}Block: BlockConfig<{Service}Response> = {
type: '{service}',
name: '{Service}',
description: 'Short description',
longDescription: 'Detailed description',
category: 'tools',
bgColor: '#hexcolor',
icon: {Service}Icon,
subBlocks: [ /* see SubBlock Properties below */ ],
tools: {
access: ['{service}_{action}', ...],
config: {
tool: (params) => `{service}_${params.operation}`,
params: (params) => ({ ...params }),
},
},
inputs: { /* input definitions */ },
outputs: { /* output definitions */ },
}
```
### SubBlock Properties
```typescript
{
id: 'fieldName', // Unique identifier
title: 'Field Label', // UI label
type: 'short-input', // See SubBlock Types below
placeholder: 'Hint text',
required: true, // See Required below
condition: { ... }, // See Condition below
dependsOn: ['otherField'], // See DependsOn below
mode: 'basic', // 'basic' | 'advanced' | 'both' | 'trigger'
}
```
**SubBlock Types:** `short-input`, `long-input`, `dropdown`, `code`, `switch`, `slider`, `oauth-input`, `channel-selector`, `user-selector`, `file-upload`, etc.
### `condition` - Show/hide based on another field
```typescript
// Show when operation === 'send'
condition: { field: 'operation', value: 'send' }
// Show when operation is 'send' OR 'read'
condition: { field: 'operation', value: ['send', 'read'] }
// Show when operation !== 'send'
condition: { field: 'operation', value: 'send', not: true }
// Complex: NOT in list AND another condition
condition: {
field: 'operation',
value: ['list_channels', 'list_users'],
not: true,
and: { field: 'destinationType', value: 'dm', not: true }
}
```
### `required` - Field validation
```typescript
// Always required
required: true
// Conditionally required (same syntax as condition)
required: { field: 'operation', value: 'send' }
```
### `dependsOn` - Clear field when dependencies change
```typescript
// Clear when credential changes
dependsOn: ['credential']
// Clear when authMethod changes AND (credential OR botToken) changes
dependsOn: { all: ['authMethod'], any: ['credential', 'botToken'] }
```
### `mode` - When to show field
- `'basic'` - Only in basic mode (default UI)
- `'advanced'` - Only in advanced mode (manual input)
- `'both'` - Show in both modes (default)
- `'trigger'` - Only when block is used as trigger
### `canonicalParamId` - Link basic/advanced alternatives
Use to map multiple UI inputs to a single logical parameter:
```typescript
// Basic mode: Visual selector
{
id: 'fileSelector',
type: 'file-selector',
mode: 'basic',
canonicalParamId: 'fileId',
required: true,
},
// Advanced mode: Manual input
{
id: 'manualFileId',
type: 'short-input',
mode: 'advanced',
canonicalParamId: 'fileId',
required: true,
},
```
**Critical Rules:**
- `canonicalParamId` must NOT match any subblock's `id`
- `canonicalParamId` must be unique per operation/condition context
- **Required consistency:** All subblocks in a canonical group must have the same `required` status
- **Inputs section:** Must list canonical param IDs (e.g., `fileId`), NOT raw subblock IDs
- **Params function:** Must use canonical param IDs (raw IDs are deleted after canonical transformation)
**Register in `blocks/registry.ts`:**
```typescript
import { {Service}Block } from '@/blocks/blocks/{service}'
// Add to registry object (alphabetically)
{service}: {Service}Block,
```
## 3. Icon (`components/icons.tsx`)
```typescript
export function {Service}Icon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
{/* SVG path from service's brand assets */}
</svg>
)
}
```
## 4. Trigger (`triggers/{service}/`) - Optional
```
triggers/{service}/
├── index.ts # Export all triggers
├── webhook.ts # Webhook handler
├── utils.ts # Shared utilities
└── {event}.ts # Specific event handlers
```
**Register in `triggers/registry.ts`:**
```typescript
import { {service}WebhookTrigger } from '@/triggers/{service}'
// Add to TRIGGER_REGISTRY
{service}_webhook: {service}WebhookTrigger,
```
## File Handling
When integrations handle file uploads/downloads, use `UserFile` objects consistently.
### File Input (Uploads)
1. **Block subBlocks:** Use basic/advanced mode pattern with `canonicalParamId`
2. **Normalize in block config:** Use `normalizeFileInput` from `@/blocks/utils`
3. **Internal API route:** Create route that uses `downloadFileFromStorage` to get file content
4. **Tool routes to internal API:** Don't call external APIs directly with files
```typescript
// In block tools.config:
import { normalizeFileInput } from '@/blocks/utils'
const normalizedFile = normalizeFileInput(
params.uploadFile || params.fileRef || params.fileContent,
{ single: true }
)
if (normalizedFile) params.file = normalizedFile
```
### File Output (Downloads)
Use `FileToolProcessor` in tool `transformResponse` to store files:
```typescript
import { FileToolProcessor } from '@/executor/utils/file-tool-processor'
const processor = new FileToolProcessor(context)
const file = await processor.processFileData({
data: base64Content,
mimeType: 'application/pdf',
filename: 'doc.pdf',
})
```
### Key Helpers
| Helper | Location | Purpose |
|--------|----------|---------|
| `normalizeFileInput` | `@/blocks/utils` | Normalize file params in block config |
| `processFilesToUserFiles` | `@/lib/uploads/utils/file-utils` | Convert raw inputs to UserFile[] |
| `downloadFileFromStorage` | `@/lib/uploads/utils/file-utils.server` | Get Buffer from UserFile |
| `FileToolProcessor` | `@/executor/utils/file-tool-processor` | Process tool output files |
## Checklist
- [ ] Look up API docs for the service
- [ ] Create `tools/{service}/types.ts` with proper types
- [ ] Create tool files for each operation
- [ ] Create `tools/{service}/index.ts` barrel export
- [ ] Register tools in `tools/registry.ts`
- [ ] Add icon to `components/icons.tsx`
- [ ] Create block in `blocks/blocks/{service}.ts`
- [ ] Register block in `blocks/registry.ts`
- [ ] (Optional) Create triggers in `triggers/{service}/`
- [ ] (Optional) Register triggers in `triggers/registry.ts`
- [ ] (If file uploads) Create internal API route with `downloadFileFromStorage`
- [ ] (If file uploads) Use `normalizeFileInput` in block config

View File

@@ -1,66 +0,0 @@
---
paths:
- "apps/sim/hooks/queries/**/*.ts"
---
# React Query Patterns
All React Query hooks live in `hooks/queries/`.
## Query Key Factory
Every query file defines a keys factory:
```typescript
export const entityKeys = {
all: ['entity'] as const,
list: (workspaceId?: string) => [...entityKeys.all, 'list', workspaceId ?? ''] as const,
detail: (id?: string) => [...entityKeys.all, 'detail', id ?? ''] as const,
}
```
## File Structure
```typescript
// 1. Query keys factory
// 2. Types (if needed)
// 3. Private fetch functions
// 4. Exported hooks
```
## Query Hook
```typescript
export function useEntityList(workspaceId?: string, options?: { enabled?: boolean }) {
return useQuery({
queryKey: entityKeys.list(workspaceId),
queryFn: () => fetchEntities(workspaceId as string),
enabled: Boolean(workspaceId) && (options?.enabled ?? true),
staleTime: 60 * 1000,
placeholderData: keepPreviousData,
})
}
```
## Mutation Hook
```typescript
export function useCreateEntity() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async (variables) => { /* fetch POST */ },
onSuccess: () => queryClient.invalidateQueries({ queryKey: entityKeys.all }),
})
}
```
## Optimistic Updates
For optimistic mutations syncing with Zustand, use `createOptimisticMutationHandlers` from `@/hooks/queries/utils/optimistic-mutation`.
## Naming
- **Keys**: `entityKeys`
- **Query hooks**: `useEntity`, `useEntityList`
- **Mutation hooks**: `useCreateEntity`, `useUpdateEntity`
- **Fetch functions**: `fetchEntity` (private)

View File

@@ -1,71 +0,0 @@
---
paths:
- "apps/sim/**/store.ts"
- "apps/sim/**/stores/**/*.ts"
---
# Zustand Store Patterns
Stores live in `stores/`. Complex stores split into `store.ts` + `types.ts`.
## Basic Store
```typescript
import { create } from 'zustand'
import { devtools } from 'zustand/middleware'
import type { FeatureState } from '@/stores/feature/types'
const initialState = { items: [] as Item[], activeId: null as string | null }
export const useFeatureStore = create<FeatureState>()(
devtools(
(set, get) => ({
...initialState,
setItems: (items) => set({ items }),
addItem: (item) => set((state) => ({ items: [...state.items, item] })),
reset: () => set(initialState),
}),
{ name: 'feature-store' }
)
)
```
## Persisted Store
```typescript
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
export const useFeatureStore = create<FeatureState>()(
persist(
(set) => ({
width: 300,
setWidth: (width) => set({ width }),
_hasHydrated: false,
setHasHydrated: (v) => set({ _hasHydrated: v }),
}),
{
name: 'feature-state',
partialize: (state) => ({ width: state.width }),
onRehydrateStorage: () => (state) => state?.setHasHydrated(true),
}
)
)
```
## Rules
1. Use `devtools` middleware (named stores)
2. Use `persist` only when data should survive reload
3. `partialize` to persist only necessary state
4. `_hasHydrated` pattern for persisted stores needing hydration tracking
5. Immutable updates only
6. `set((state) => ...)` when depending on previous state
7. Provide `reset()` action
## Outside React
```typescript
const items = useFeatureStore.getState().items
useFeatureStore.setState({ items: newItems })
```

View File

@@ -1,41 +0,0 @@
---
paths:
- "apps/sim/**/*.tsx"
- "apps/sim/**/*.css"
---
# Styling Rules
## Tailwind
1. **No inline styles** - Use Tailwind classes
2. **No duplicate dark classes** - Skip `dark:` when value matches light mode
3. **Exact values** - `text-[14px]`, `h-[26px]`
4. **Transitions** - `transition-colors` for interactive states
## Conditional Classes
```typescript
import { cn } from '@/lib/utils'
<div className={cn(
'base-classes',
isActive && 'active-classes',
disabled ? 'opacity-60' : 'hover:bg-accent'
)} />
```
## CSS Variables
For dynamic values (widths, heights) synced with stores:
```typescript
// In store
setWidth: (width) => {
set({ width })
document.documentElement.style.setProperty('--sidebar-width', `${width}px`)
}
// In component
<aside style={{ width: 'var(--sidebar-width)' }} />
```

View File

@@ -1,58 +0,0 @@
---
paths:
- "apps/sim/**/*.test.ts"
- "apps/sim/**/*.test.tsx"
---
# Testing Patterns
Use Vitest. Test files: `feature.ts``feature.test.ts`
## Structure
```typescript
/**
* @vitest-environment node
*/
import { databaseMock, loggerMock } from '@sim/testing'
import { describe, expect, it, vi } from 'vitest'
vi.mock('@sim/db', () => databaseMock)
vi.mock('@sim/logger', () => loggerMock)
import { myFunction } from '@/lib/feature'
describe('myFunction', () => {
beforeEach(() => vi.clearAllMocks())
it.concurrent('isolated tests run in parallel', () => { ... })
})
```
## @sim/testing Package
Always prefer over local mocks.
| Category | Utilities |
|----------|-----------|
| **Mocks** | `loggerMock`, `databaseMock`, `setupGlobalFetchMock()` |
| **Factories** | `createSession()`, `createWorkflowRecord()`, `createBlock()`, `createExecutorContext()` |
| **Builders** | `WorkflowBuilder`, `ExecutionContextBuilder` |
| **Assertions** | `expectWorkflowAccessGranted()`, `expectBlockExecuted()` |
## Rules
1. `@vitest-environment node` directive at file top
2. `vi.mock()` calls before importing mocked modules
3. `@sim/testing` utilities over local mocks
4. `it.concurrent` for isolated tests (no shared mutable state)
5. `beforeEach(() => vi.clearAllMocks())` to reset state
## Hoisted Mocks
For mutable mock references:
```typescript
const mockFn = vi.hoisted(() => vi.fn())
vi.mock('@/lib/module', () => ({ myFunction: mockFn }))
mockFn.mockResolvedValue({ data: 'test' })
```

View File

@@ -1,21 +0,0 @@
---
paths:
- "apps/sim/**/*.ts"
- "apps/sim/**/*.tsx"
---
# TypeScript Rules
1. **No `any`** - Use proper types or `unknown` with type guards
2. **Props interface** - Always define for components
3. **Const assertions** - `as const` for constant objects/arrays
4. **Ref types** - Explicit: `useRef<HTMLDivElement>(null)`
5. **Type imports** - `import type { X }` for type-only imports
```typescript
// ✗ Bad
const handleClick = (e: any) => {}
// ✓ Good
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {}
```

View File

@@ -1,7 +0,0 @@
Based on the given area of interest, please:
1. Dig around the codebase in terms of that given area of interest, gather general information such as keywords and architecture overview.
2. Spawn off n=10 (unless specified otherwise) task agents to dig deeper into the codebase in terms of that given area of interest, some of them should be out of the box for variance.
3. Once the task agents are done, use the information to do what the user wants.
If user is in plan mode, use the information to create the plan.

View File

@@ -1,35 +1,45 @@
--- ---
description: EMCN component library patterns description: EMCN component library patterns with CVA
globs: ["apps/sim/components/emcn/**"] globs: ["apps/sim/components/emcn/**"]
--- ---
# EMCN Components # EMCN Component Guidelines
Import from `@/components/emcn`, never from subpaths (except CSS files). ## When to Use CVA vs Direct Styles
## CVA vs Direct Styles **Use CVA (class-variance-authority) when:**
- 2+ visual variants (primary, secondary, outline)
- Multiple sizes or state variations
- Example: Button with variants
**Use CVA when:** 2+ variants (primary/secondary, sm/md/lg) **Use direct className when:**
- Single consistent style
- No variations needed
- Example: Label with one style
## Patterns
**With CVA:**
```tsx ```tsx
const buttonVariants = cva('base-classes', { const buttonVariants = cva('base-classes', {
variants: { variant: { default: '...', primary: '...' } } variants: {
variant: { default: '...', primary: '...' },
size: { sm: '...', md: '...' }
}
}) })
export { Button, buttonVariants } export { Button, buttonVariants }
``` ```
**Use direct className when:** Single consistent style, no variations **Without CVA:**
```tsx ```tsx
function Label({ className, ...props }) { function Label({ className, ...props }) {
return <Primitive className={cn('style-classes', className)} {...props} /> return <Primitive className={cn('single-style-classes', className)} {...props} />
} }
``` ```
## Rules ## Rules
- Use Radix UI primitives for accessibility - Use Radix UI primitives for accessibility
- Export component and variants (if using CVA) - Export component and variants (if using CVA)
- TSDoc with usage examples - TSDoc with usage examples
- Consistent tokens: `font-medium`, `text-[12px]`, `rounded-[4px]` - Consistent tokens: `font-medium`, `text-[12px]`, `rounded-[4px]`
- `transition-colors` for hover states - Always use `transition-colors` for hover states

View File

@@ -8,7 +8,7 @@ alwaysApply: true
You are a professional software engineer. All code must follow best practices: accurate, readable, clean, and efficient. You are a professional software engineer. All code must follow best practices: accurate, readable, clean, and efficient.
## Logging ## Logging
Import `createLogger` from `@sim/logger`. Use `logger.info`, `logger.warn`, `logger.error` instead of `console.log`. Use `logger.info`, `logger.warn`, `logger.error` instead of `console.log`.
## Comments ## Comments
Use TSDoc for documentation. No `====` separators. No non-TSDoc comments. Use TSDoc for documentation. No `====` separators. No non-TSDoc comments.

View File

@@ -10,47 +10,58 @@ globs: ["apps/sim/**"]
2. **Composition Over Complexity**: Break down complex logic into smaller pieces 2. **Composition Over Complexity**: Break down complex logic into smaller pieces
3. **Type Safety First**: TypeScript interfaces for all props, state, return types 3. **Type Safety First**: TypeScript interfaces for all props, state, return types
4. **Predictable State**: Zustand for global state, useState for UI-only concerns 4. **Predictable State**: Zustand for global state, useState for UI-only concerns
5. **Performance by Default**: useMemo, useCallback, refs appropriately
## Root-Level Structure ## File Organization
```
apps/sim/
├── app/ # Next.js app router (pages, API routes)
├── blocks/ # Block definitions and registry
├── components/ # Shared UI (emcn/, ui/)
├── executor/ # Workflow execution engine
├── hooks/ # Shared hooks (queries/, selectors/)
├── lib/ # App-wide utilities
├── providers/ # LLM provider integrations
├── stores/ # Zustand stores
├── tools/ # Tool definitions
└── triggers/ # Trigger definitions
```
## Feature Organization
Features live under `app/workspace/[workspaceId]/`:
``` ```
feature/ feature/
├── components/ # Feature components ├── components/ # Feature components
├── hooks/ # Feature-scoped hooks │ └── sub-feature/ # Sub-feature with own components
├── utils/ # Feature-scoped utilities (2+ consumers) ├── hooks/ # Custom hooks
── feature.tsx # Main component ── feature.tsx # Main component
└── page.tsx # Next.js page entry
``` ```
## Naming Conventions ## Naming Conventions
- **Components**: PascalCase (`WorkflowList`) - **Components**: PascalCase (`WorkflowList`, `TriggerPanel`)
- **Hooks**: `use` prefix (`useWorkflowOperations`) - **Hooks**: camelCase with `use` prefix (`useWorkflowOperations`)
- **Files**: kebab-case (`workflow-list.tsx`) - **Files**: kebab-case matching export (`workflow-list.tsx`)
- **Stores**: `stores/feature/store.ts` - **Stores**: kebab-case in stores/ (`sidebar/store.ts`)
- **Constants**: SCREAMING_SNAKE_CASE - **Constants**: SCREAMING_SNAKE_CASE
- **Interfaces**: PascalCase with suffix (`WorkflowListProps`) - **Interfaces**: PascalCase with suffix (`WorkflowListProps`)
## Utils Rules ## State Management
- **Never create `utils.ts` for single consumer** - inline it **useState**: UI-only concerns (dropdown open, hover, form inputs)
- **Create `utils.ts` when** 2+ files need the same helper **Zustand**: Shared state, persistence, global app state
- **Check existing sources** before duplicating (`lib/` has many utilities) **useRef**: DOM refs, avoiding dependency issues, mutable non-reactive values
- **Location**: `lib/` (app-wide) → `feature/utils/` (feature-scoped) → inline (single-use)
## Component Extraction
**Extract to separate file when:**
- Complex (50+ lines)
- Used across 2+ files
- Has own state/logic
**Keep inline when:**
- Simple (< 10 lines)
- Used in only 1 file
- Purely presentational
**Never import utilities from another component file.** Extract shared helpers to `lib/` or `utils/`.
## Utils Files
**Never create a `utils.ts` file for a single consumer.** Inline the logic directly in the consuming component.
**Create `utils.ts` when:**
- 2+ files import the same helper
**Prefer existing sources of truth:**
- Before duplicating logic, check if a centralized helper already exists (e.g., `lib/logs/get-trigger-options.ts`)
- Import from the source of truth rather than creating wrapper functions
**Location hierarchy:**
- `lib/` — App-wide utilities (auth, billing, core)
- `feature/utils.ts` — Feature-scoped utilities (used by 2+ components in the feature)
- Inline — Single-use helpers (define directly in the component)

View File

@@ -6,43 +6,59 @@ globs: ["apps/sim/**/*.tsx"]
# Component Patterns # Component Patterns
## Structure Order ## Structure Order
```typescript ```typescript
'use client' // Only if using hooks 'use client' // Only if using hooks
// Imports (external → internal) // 1. Imports (external → internal → relative)
// Constants at module level // 2. Constants at module level
const CONFIG = { SPACING: 8 } as const const CONFIG = { SPACING: 8 } as const
// Props interface // 3. Props interface with TSDoc
interface ComponentProps { interface ComponentProps {
/** Description */
requiredProp: string requiredProp: string
optionalProp?: boolean optionalProp?: boolean
} }
// 4. Component with TSDoc
export function Component({ requiredProp, optionalProp = false }: ComponentProps) { export function Component({ requiredProp, optionalProp = false }: ComponentProps) {
// a. Refs // a. Refs
// b. External hooks (useParams, useRouter) // b. External hooks (useParams, useRouter)
// c. Store hooks // c. Store hooks
// d. Custom hooks // d. Custom hooks
// e. Local state // e. Local state
// f. useMemo // f. useMemo computations
// g. useCallback // g. useCallback handlers
// h. useEffect // h. useEffect
// i. Return JSX // i. Return JSX
} }
``` ```
## Rules ## Rules
1. Add `'use client'` when using React hooks
1. `'use client'` only when using React hooks
2. Always define props interface 2. Always define props interface
3. Extract constants with `as const` 3. TSDoc on component: description, @param, @returns
4. Semantic HTML (`aside`, `nav`, `article`) 4. Extract constants with `as const`
5. Optional chain callbacks: `onAction?.(id)` 5. Use Tailwind only, no inline styles
6. Semantic HTML (`aside`, `nav`, `article`)
7. Include ARIA attributes where appropriate
8. Optional chain callbacks: `onAction?.(id)`
## Component Extraction ## Factory Pattern with Caching
**Extract when:** 50+ lines, used in 2+ files, or has own state/logic When generating components for a specific signature (e.g., icons):
**Keep inline when:** < 10 lines, single use, purely presentational ```typescript
const cache = new Map<string, React.ComponentType<{ className?: string }>>()
function getColorIcon(color: string) {
if (cache.has(color)) return cache.get(color)!
const Icon = ({ className }: { className?: string }) => (
<div className={cn(className, 'rounded-[3px]')} style={{ backgroundColor: color, width: 10, height: 10 }} />
)
Icon.displayName = `ColorIcon(${color})`
cache.set(color, Icon)
return Icon
}
```

View File

@@ -6,13 +6,21 @@ globs: ["apps/sim/**/use-*.ts", "apps/sim/**/hooks/**/*.ts"]
# Hook Patterns # Hook Patterns
## Structure ## Structure
```typescript ```typescript
import { createLogger } from '@/lib/logs/console/logger'
const logger = createLogger('useFeatureName')
interface UseFeatureProps { interface UseFeatureProps {
id: string id: string
onSuccess?: (result: Result) => void onSuccess?: (result: Result) => void
} }
/**
* Hook description.
* @param props - Configuration
* @returns State and operations
*/
export function useFeature({ id, onSuccess }: UseFeatureProps) { export function useFeature({ id, onSuccess }: UseFeatureProps) {
// 1. Refs for stable dependencies // 1. Refs for stable dependencies
const idRef = useRef(id) const idRef = useRef(id)
@@ -21,6 +29,7 @@ export function useFeature({ id, onSuccess }: UseFeatureProps) {
// 2. State // 2. State
const [data, setData] = useState<Data | null>(null) const [data, setData] = useState<Data | null>(null)
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState<Error | null>(null)
// 3. Sync refs // 3. Sync refs
useEffect(() => { useEffect(() => {
@@ -28,27 +37,32 @@ export function useFeature({ id, onSuccess }: UseFeatureProps) {
onSuccessRef.current = onSuccess onSuccessRef.current = onSuccess
}, [id, onSuccess]) }, [id, onSuccess])
// 4. Operations (useCallback with empty deps when using refs) // 4. Operations with useCallback
const fetchData = useCallback(async () => { const fetchData = useCallback(async () => {
setIsLoading(true) setIsLoading(true)
try { try {
const result = await fetch(`/api/${idRef.current}`).then(r => r.json()) const result = await fetch(`/api/${idRef.current}`).then(r => r.json())
setData(result) setData(result)
onSuccessRef.current?.(result) onSuccessRef.current?.(result)
} catch (err) {
setError(err as Error)
logger.error('Failed', { error: err })
} finally { } finally {
setIsLoading(false) setIsLoading(false)
} }
}, []) }, []) // Empty deps - using refs
return { data, isLoading, fetchData } // 5. Return grouped by state/operations
return { data, isLoading, error, fetchData }
} }
``` ```
## Rules ## Rules
1. Single responsibility per hook 1. Single responsibility per hook
2. Props interface required 2. Props interface required
3. Refs for stable callback dependencies 3. TSDoc required
4. Wrap returned functions in useCallback 4. Use logger, not console.log
5. Always try/catch async operations 5. Refs for stable callback dependencies
6. Track loading/error states 6. Wrap returned functions in useCallback
7. Always try/catch async operations
8. Track loading/error states

View File

@@ -5,57 +5,33 @@ globs: ["apps/sim/**/*.ts", "apps/sim/**/*.tsx"]
# Import Patterns # Import Patterns
## Absolute Imports ## EMCN Components
Import from `@/components/emcn`, never from subpaths like `@/components/emcn/components/modal/modal`.
**Always use absolute imports.** Never use relative imports. **Exception**: CSS imports use actual file paths: `import '@/components/emcn/components/code/code.css'`
## Feature Components
Import from central folder indexes, not specific subfolders:
```typescript ```typescript
// ✓ Good // ✅ Correct
import { useWorkflowStore } from '@/stores/workflows/store'
import { Button } from '@/components/ui/button'
// ✗ Bad
import { useWorkflowStore } from '../../../stores/workflows/store'
```
## Barrel Exports
Use barrel exports (`index.ts`) when a folder has 3+ exports. Import from barrel, not individual files.
```typescript
// ✓ Good
import { Dashboard, Sidebar } from '@/app/workspace/[workspaceId]/logs/components' import { Dashboard, Sidebar } from '@/app/workspace/[workspaceId]/logs/components'
// ✗ Bad // ❌ Wrong
import { Dashboard } from '@/app/workspace/[workspaceId]/logs/components/dashboard/dashboard' import { Dashboard } from '@/app/workspace/[workspaceId]/logs/components/dashboard'
``` ```
## No Re-exports ## Internal vs External
- **Cross-feature**: Absolute paths through central index
Do not re-export from non-barrel files. Import directly from the source. - **Within feature**: Relative paths (`./components/...`, `../utils`)
```typescript
// ✓ Good - import from where it's declared
import { CORE_TRIGGER_TYPES } from '@/stores/logs/filters/types'
// ✗ Bad - re-exporting in utils.ts then importing from there
import { CORE_TRIGGER_TYPES } from '@/app/workspace/.../utils'
```
## Import Order ## Import Order
1. React/core libraries 1. React/core libraries
2. External libraries 2. External libraries
3. UI components (`@/components/emcn`, `@/components/ui`) 3. UI components (`@/components/emcn`, `@/components/ui`)
4. Utilities (`@/lib/...`) 4. Utilities (`@/lib/...`)
5. Stores (`@/stores/...`) 5. Feature imports from indexes
6. Feature imports 6. Relative imports
7. CSS imports 7. CSS imports
## Type Imports ## Types
Use `type` keyword: `import type { WorkflowLog } from '...'`
Use `type` keyword for type-only imports:
```typescript
import type { WorkflowLog } from '@/stores/logs/types'
```

View File

@@ -1,285 +0,0 @@
---
description: Adding new integrations (tools, blocks, triggers)
globs: ["apps/sim/tools/**", "apps/sim/blocks/**", "apps/sim/triggers/**"]
---
# Adding Integrations
## Overview
Adding a new integration typically requires:
1. **Tools** - API operations (`tools/{service}/`)
2. **Block** - UI component (`blocks/blocks/{service}.ts`)
3. **Icon** - SVG icon (`components/icons.tsx`)
4. **Trigger** (optional) - Webhooks/polling (`triggers/{service}/`)
Always look up the service's API docs first.
## 1. Tools (`tools/{service}/`)
```
tools/{service}/
├── index.ts # Export all tools
├── types.ts # Params/response types
├── {action}.ts # Individual tool (e.g., send_message.ts)
└── ...
```
**Tool file structure:**
```typescript
// tools/{service}/{action}.ts
import type { {Service}Params, {Service}Response } from '@/tools/{service}/types'
import type { ToolConfig } from '@/tools/types'
export const {service}{Action}Tool: ToolConfig<{Service}Params, {Service}Response> = {
id: '{service}_{action}',
name: '{Service} {Action}',
description: 'What this tool does',
version: '1.0.0',
oauth: { required: true, provider: '{service}' }, // if OAuth
params: { /* param definitions */ },
request: {
url: '/api/tools/{service}/{action}',
method: 'POST',
headers: () => ({ 'Content-Type': 'application/json' }),
body: (params) => ({ ...params }),
},
transformResponse: async (response) => {
const data = await response.json()
if (!data.success) throw new Error(data.error)
return { success: true, output: data.output }
},
outputs: { /* output definitions */ },
}
```
**Register in `tools/registry.ts`:**
```typescript
import { {service}{Action}Tool } from '@/tools/{service}'
// Add to registry object
{service}_{action}: {service}{Action}Tool,
```
## 2. Block (`blocks/blocks/{service}.ts`)
```typescript
import { {Service}Icon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import type { {Service}Response } from '@/tools/{service}/types'
export const {Service}Block: BlockConfig<{Service}Response> = {
type: '{service}',
name: '{Service}',
description: 'Short description',
longDescription: 'Detailed description',
category: 'tools',
bgColor: '#hexcolor',
icon: {Service}Icon,
subBlocks: [ /* see SubBlock Properties below */ ],
tools: {
access: ['{service}_{action}', ...],
config: {
tool: (params) => `{service}_${params.operation}`,
params: (params) => ({ ...params }),
},
},
inputs: { /* input definitions */ },
outputs: { /* output definitions */ },
}
```
### SubBlock Properties
```typescript
{
id: 'fieldName', // Unique identifier
title: 'Field Label', // UI label
type: 'short-input', // See SubBlock Types below
placeholder: 'Hint text',
required: true, // See Required below
condition: { ... }, // See Condition below
dependsOn: ['otherField'], // See DependsOn below
mode: 'basic', // 'basic' | 'advanced' | 'both' | 'trigger'
}
```
**SubBlock Types:** `short-input`, `long-input`, `dropdown`, `code`, `switch`, `slider`, `oauth-input`, `channel-selector`, `user-selector`, `file-upload`, etc.
### `condition` - Show/hide based on another field
```typescript
// Show when operation === 'send'
condition: { field: 'operation', value: 'send' }
// Show when operation is 'send' OR 'read'
condition: { field: 'operation', value: ['send', 'read'] }
// Show when operation !== 'send'
condition: { field: 'operation', value: 'send', not: true }
// Complex: NOT in list AND another condition
condition: {
field: 'operation',
value: ['list_channels', 'list_users'],
not: true,
and: { field: 'destinationType', value: 'dm', not: true }
}
```
### `required` - Field validation
```typescript
// Always required
required: true
// Conditionally required (same syntax as condition)
required: { field: 'operation', value: 'send' }
```
### `dependsOn` - Clear field when dependencies change
```typescript
// Clear when credential changes
dependsOn: ['credential']
// Clear when authMethod changes AND (credential OR botToken) changes
dependsOn: { all: ['authMethod'], any: ['credential', 'botToken'] }
```
### `mode` - When to show field
- `'basic'` - Only in basic mode (default UI)
- `'advanced'` - Only in advanced mode (manual input)
- `'both'` - Show in both modes (default)
- `'trigger'` - Only when block is used as trigger
### `canonicalParamId` - Link basic/advanced alternatives
Use to map multiple UI inputs to a single logical parameter:
```typescript
// Basic mode: Visual selector
{
id: 'fileSelector',
type: 'file-selector',
mode: 'basic',
canonicalParamId: 'fileId',
required: true,
},
// Advanced mode: Manual input
{
id: 'manualFileId',
type: 'short-input',
mode: 'advanced',
canonicalParamId: 'fileId',
required: true,
},
```
**Critical Rules:**
- `canonicalParamId` must NOT match any subblock's `id`
- `canonicalParamId` must be unique per operation/condition context
- **Required consistency:** All subblocks in a canonical group must have the same `required` status
- **Inputs section:** Must list canonical param IDs (e.g., `fileId`), NOT raw subblock IDs
- **Params function:** Must use canonical param IDs (raw IDs are deleted after canonical transformation)
**Register in `blocks/registry.ts`:**
```typescript
import { {Service}Block } from '@/blocks/blocks/{service}'
// Add to registry object (alphabetically)
{service}: {Service}Block,
```
## 3. Icon (`components/icons.tsx`)
```typescript
export function {Service}Icon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
{/* SVG path from service's brand assets */}
</svg>
)
}
```
## 4. Trigger (`triggers/{service}/`) - Optional
```
triggers/{service}/
├── index.ts # Export all triggers
├── webhook.ts # Webhook handler
├── utils.ts # Shared utilities
└── {event}.ts # Specific event handlers
```
**Register in `triggers/registry.ts`:**
```typescript
import { {service}WebhookTrigger } from '@/triggers/{service}'
// Add to TRIGGER_REGISTRY
{service}_webhook: {service}WebhookTrigger,
```
## File Handling
When integrations handle file uploads/downloads, use `UserFile` objects consistently.
### File Input (Uploads)
1. **Block subBlocks:** Use basic/advanced mode pattern with `canonicalParamId`
2. **Normalize in block config:** Use `normalizeFileInput` from `@/blocks/utils`
3. **Internal API route:** Create route that uses `downloadFileFromStorage` to get file content
4. **Tool routes to internal API:** Don't call external APIs directly with files
```typescript
// In block tools.config:
import { normalizeFileInput } from '@/blocks/utils'
const normalizedFile = normalizeFileInput(
params.uploadFile || params.fileRef || params.fileContent,
{ single: true }
)
if (normalizedFile) params.file = normalizedFile
```
### File Output (Downloads)
Use `FileToolProcessor` in tool `transformResponse` to store files:
```typescript
import { FileToolProcessor } from '@/executor/utils/file-tool-processor'
const processor = new FileToolProcessor(context)
const file = await processor.processFileData({
data: base64Content,
mimeType: 'application/pdf',
filename: 'doc.pdf',
})
```
### Key Helpers
| Helper | Location | Purpose |
|--------|----------|---------|
| `normalizeFileInput` | `@/blocks/utils` | Normalize file params in block config |
| `processFilesToUserFiles` | `@/lib/uploads/utils/file-utils` | Convert raw inputs to UserFile[] |
| `downloadFileFromStorage` | `@/lib/uploads/utils/file-utils.server` | Get Buffer from UserFile |
| `FileToolProcessor` | `@/executor/utils/file-tool-processor` | Process tool output files |
## Checklist
- [ ] Look up API docs for the service
- [ ] Create `tools/{service}/types.ts` with proper types
- [ ] Create tool files for each operation
- [ ] Create `tools/{service}/index.ts` barrel export
- [ ] Register tools in `tools/registry.ts`
- [ ] Add icon to `components/icons.tsx`
- [ ] Create block in `blocks/blocks/{service}.ts`
- [ ] Register block in `blocks/registry.ts`
- [ ] (Optional) Create triggers in `triggers/{service}/`
- [ ] (Optional) Register triggers in `triggers/registry.ts`
- [ ] (If file uploads) Create internal API route with `downloadFileFromStorage`
- [ ] (If file uploads) Use `normalizeFileInput` in block config

View File

@@ -1,66 +0,0 @@
---
description: React Query patterns for the Sim application
globs: ["apps/sim/hooks/queries/**/*.ts"]
---
# React Query Patterns
All React Query hooks live in `hooks/queries/`.
## Query Key Factory
Every query file defines a keys factory:
```typescript
export const entityKeys = {
all: ['entity'] as const,
list: (workspaceId?: string) => [...entityKeys.all, 'list', workspaceId ?? ''] as const,
detail: (id?: string) => [...entityKeys.all, 'detail', id ?? ''] as const,
}
```
## File Structure
```typescript
// 1. Query keys factory
// 2. Types (if needed)
// 3. Private fetch functions
// 4. Exported hooks
```
## Query Hook
```typescript
export function useEntityList(workspaceId?: string, options?: { enabled?: boolean }) {
return useQuery({
queryKey: entityKeys.list(workspaceId),
queryFn: () => fetchEntities(workspaceId as string),
enabled: Boolean(workspaceId) && (options?.enabled ?? true),
staleTime: 60 * 1000,
placeholderData: keepPreviousData,
})
}
```
## Mutation Hook
```typescript
export function useCreateEntity() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async (variables) => { /* fetch POST */ },
onSuccess: () => queryClient.invalidateQueries({ queryKey: entityKeys.all }),
})
}
```
## Optimistic Updates
For optimistic mutations syncing with Zustand, use `createOptimisticMutationHandlers` from `@/hooks/queries/utils/optimistic-mutation`.
## Naming
- **Keys**: `entityKeys`
- **Query hooks**: `useEntity`, `useEntityList`
- **Mutation hooks**: `useCreateEntity`, `useUpdateEntity`
- **Fetch functions**: `fetchEntity` (private)

View File

@@ -5,66 +5,53 @@ globs: ["apps/sim/**/store.ts", "apps/sim/**/stores/**/*.ts"]
# Zustand Store Patterns # Zustand Store Patterns
Stores live in `stores/`. Complex stores split into `store.ts` + `types.ts`. ## Structure
## Basic Store
```typescript
import { create } from 'zustand'
import { devtools } from 'zustand/middleware'
import type { FeatureState } from '@/stores/feature/types'
const initialState = { items: [] as Item[], activeId: null as string | null }
export const useFeatureStore = create<FeatureState>()(
devtools(
(set, get) => ({
...initialState,
setItems: (items) => set({ items }),
addItem: (item) => set((state) => ({ items: [...state.items, item] })),
reset: () => set(initialState),
}),
{ name: 'feature-store' }
)
)
```
## Persisted Store
```typescript ```typescript
import { create } from 'zustand' import { create } from 'zustand'
import { persist } from 'zustand/middleware' import { persist } from 'zustand/middleware'
interface FeatureState {
// State
items: Item[]
activeId: string | null
// Actions
setItems: (items: Item[]) => void
addItem: (item: Item) => void
clearState: () => void
}
const createInitialState = () => ({
items: [],
activeId: null,
})
export const useFeatureStore = create<FeatureState>()( export const useFeatureStore = create<FeatureState>()(
persist( persist(
(set) => ({ (set) => ({
width: 300, ...createInitialState(),
setWidth: (width) => set({ width }),
_hasHydrated: false, setItems: (items) => set({ items }),
setHasHydrated: (v) => set({ _hasHydrated: v }),
addItem: (item) => set((state) => ({
items: [...state.items, item],
})),
clearState: () => set(createInitialState()),
}), }),
{ {
name: 'feature-state', name: 'feature-state',
partialize: (state) => ({ width: state.width }), partialize: (state) => ({ items: state.items }),
onRehydrateStorage: () => (state) => state?.setHasHydrated(true),
} }
) )
) )
``` ```
## Rules ## Rules
1. Interface includes state and actions
1. Use `devtools` middleware (named stores) 2. Extract config to module constants
2. Use `persist` only when data should survive reload 3. TSDoc on store
3. `partialize` to persist only necessary state 4. Only persist what's needed
4. `_hasHydrated` pattern for persisted stores needing hydration tracking 5. Immutable updates only - never mutate
5. Immutable updates only 6. Use `set((state) => ...)` when depending on previous state
6. `set((state) => ...)` when depending on previous state 7. Provide clear/reset actions
7. Provide `reset()` action
## Outside React
```typescript
const items = useFeatureStore.getState().items
useFeatureStore.setState({ items: newItems })
```

View File

@@ -6,14 +6,13 @@ globs: ["apps/sim/**/*.tsx", "apps/sim/**/*.css"]
# Styling Rules # Styling Rules
## Tailwind ## Tailwind
1. **No inline styles** - Use Tailwind classes exclusively
1. **No inline styles** - Use Tailwind classes 2. **No duplicate dark classes** - Don't add `dark:` when value matches light mode
2. **No duplicate dark classes** - Skip `dark:` when value matches light mode 3. **Exact values** - Use design system values (`text-[14px]`, `h-[25px]`)
3. **Exact values** - `text-[14px]`, `h-[26px]` 4. **Prefer px** - Use `px-[4px]` over `px-1`
4. **Transitions** - `transition-colors` for interactive states 5. **Transitions** - Add `transition-colors` for interactive states
## Conditional Classes ## Conditional Classes
```typescript ```typescript
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
@@ -24,17 +23,25 @@ import { cn } from '@/lib/utils'
)} /> )} />
``` ```
## CSS Variables ## CSS Variables for Dynamic Styles
For dynamic values (widths, heights) synced with stores:
```typescript ```typescript
// In store // In store setter
setWidth: (width) => { setSidebarWidth: (width) => {
set({ width }) set({ sidebarWidth: width })
document.documentElement.style.setProperty('--sidebar-width', `${width}px`) document.documentElement.style.setProperty('--sidebar-width', `${width}px`)
} }
// In component // In component
<aside style={{ width: 'var(--sidebar-width)' }} /> <aside style={{ width: 'var(--sidebar-width)' }} />
``` ```
## Anti-Patterns
```typescript
// ❌ Bad
<div style={{ width: 200 }}>
<div className='text-[var(--text-primary)] dark:text-[var(--text-primary)]'>
// ✅ Good
<div className='w-[200px]'>
<div className='text-[var(--text-primary)]'>
```

View File

@@ -1,57 +0,0 @@
---
description: Testing patterns with Vitest and @sim/testing
globs: ["apps/sim/**/*.test.ts", "apps/sim/**/*.test.tsx"]
---
# Testing Patterns
Use Vitest. Test files: `feature.ts` → `feature.test.ts`
## Structure
```typescript
/**
* @vitest-environment node
*/
import { databaseMock, loggerMock } from '@sim/testing'
import { describe, expect, it, vi } from 'vitest'
vi.mock('@sim/db', () => databaseMock)
vi.mock('@sim/logger', () => loggerMock)
import { myFunction } from '@/lib/feature'
describe('myFunction', () => {
beforeEach(() => vi.clearAllMocks())
it.concurrent('isolated tests run in parallel', () => { ... })
})
```
## @sim/testing Package
Always prefer over local mocks.
| Category | Utilities |
|----------|-----------|
| **Mocks** | `loggerMock`, `databaseMock`, `setupGlobalFetchMock()` |
| **Factories** | `createSession()`, `createWorkflowRecord()`, `createBlock()`, `createExecutorContext()` |
| **Builders** | `WorkflowBuilder`, `ExecutionContextBuilder` |
| **Assertions** | `expectWorkflowAccessGranted()`, `expectBlockExecuted()` |
## Rules
1. `@vitest-environment node` directive at file top
2. `vi.mock()` calls before importing mocked modules
3. `@sim/testing` utilities over local mocks
4. `it.concurrent` for isolated tests (no shared mutable state)
5. `beforeEach(() => vi.clearAllMocks())` to reset state
## Hoisted Mocks
For mutable mock references:
```typescript
const mockFn = vi.hoisted(() => vi.fn())
vi.mock('@/lib/module', () => ({ myFunction: mockFn }))
mockFn.mockResolvedValue({ data: 'test' })
```

View File

@@ -6,15 +6,19 @@ globs: ["apps/sim/**/*.ts", "apps/sim/**/*.tsx"]
# TypeScript Rules # TypeScript Rules
1. **No `any`** - Use proper types or `unknown` with type guards 1. **No `any`** - Use proper types or `unknown` with type guards
2. **Props interface** - Always define for components 2. **Props interface** - Always define, even for simple components
3. **Const assertions** - `as const` for constant objects/arrays 3. **Callback types** - Full signature with params and return type
4. **Ref types** - Explicit: `useRef<HTMLDivElement>(null)` 4. **Generics** - Use for reusable components/hooks
5. **Type imports** - `import type { X }` for type-only imports 5. **Const assertions** - `as const` for constant objects/arrays
6. **Ref types** - Explicit: `useRef<HTMLDivElement>(null)`
## Anti-Patterns
```typescript ```typescript
// Bad // Bad
const handleClick = (e: any) => {} const handleClick = (e: any) => {}
useEffect(() => { doSomething(prop) }, []) // Missing dep
// Good // Good
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {} const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {}
useEffect(() => { doSomething(prop) }, [prop])
``` ```

View File

@@ -1,4 +1,4 @@
FROM oven/bun:1.3.9-alpine FROM oven/bun:1.3.3-alpine
# Install necessary packages for development # Install necessary packages for development
RUN apk add --no-cache \ RUN apk add --no-cache \

View File

@@ -44,7 +44,7 @@ services:
deploy: deploy:
resources: resources:
limits: limits:
memory: 1G memory: 4G
environment: environment:
- NODE_ENV=development - NODE_ENV=development
- DATABASE_URL=postgresql://postgres:postgres@db:5432/simstudio - DATABASE_URL=postgresql://postgres:postgres@db:5432/simstudio

View File

@@ -10,9 +10,6 @@ concurrency:
group: ci-${{ github.ref }} group: ci-${{ github.ref }}
cancel-in-progress: false cancel-in-progress: false
permissions:
contents: read
jobs: jobs:
test-build: test-build:
name: Test and Build name: Test and Build
@@ -30,11 +27,10 @@ jobs:
steps: steps:
- name: Extract version from commit message - name: Extract version from commit message
id: extract id: extract
env:
COMMIT_MSG: ${{ github.event.head_commit.message }}
run: | run: |
COMMIT_MSG="${{ github.event.head_commit.message }}"
# Only tag versions on main branch # Only tag versions on main branch
if [ "$GITHUB_REF" = "refs/heads/main" ] && [[ "$COMMIT_MSG" =~ ^(v[0-9]+\.[0-9]+\.[0-9]+): ]]; then if [ "${{ github.ref }}" = "refs/heads/main" ] && [[ "$COMMIT_MSG" =~ ^(v[0-9]+\.[0-9]+\.[0-9]+): ]]; then
VERSION="${BASH_REMATCH[1]}" VERSION="${BASH_REMATCH[1]}"
echo "version=${VERSION}" >> $GITHUB_OUTPUT echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "is_release=true" >> $GITHUB_OUTPUT echo "is_release=true" >> $GITHUB_OUTPUT
@@ -281,30 +277,3 @@ jobs:
if: needs.check-docs-changes.outputs.docs_changed == 'true' if: needs.check-docs-changes.outputs.docs_changed == 'true'
uses: ./.github/workflows/docs-embeddings.yml uses: ./.github/workflows/docs-embeddings.yml
secrets: inherit secrets: inherit
# Create GitHub Release (only for version commits on main, after all builds complete)
create-release:
name: Create GitHub Release
runs-on: blacksmith-4vcpu-ubuntu-2404
needs: [create-ghcr-manifests, detect-version]
if: needs.detect-version.outputs.is_release == 'true'
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Create release
env:
GH_PAT: ${{ secrets.GITHUB_TOKEN }}
run: bun run scripts/create-single-release.ts ${{ needs.detect-version.outputs.version }}

View File

@@ -4,9 +4,6 @@ on:
workflow_call: workflow_call:
workflow_dispatch: # Allow manual triggering workflow_dispatch: # Allow manual triggering
permissions:
contents: read
jobs: jobs:
process-docs-embeddings: process-docs-embeddings:
name: Process Documentation Embeddings name: Process Documentation Embeddings
@@ -20,7 +17,7 @@ jobs:
- name: Setup Bun - name: Setup Bun
uses: oven-sh/setup-bun@v2 uses: oven-sh/setup-bun@v2
with: with:
bun-version: 1.3.9 bun-version: 1.3.3
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4 uses: actions/setup-node@v4

View File

@@ -1,7 +1,11 @@
name: 'Auto-translate Documentation' name: 'Auto-translate Documentation'
on: on:
workflow_dispatch: # Manual trigger only (scheduled runs disabled) push:
branches: [ staging ]
paths:
- 'apps/docs/content/docs/en/**'
- 'apps/docs/i18n.json'
permissions: permissions:
contents: write contents: write
@@ -16,14 +20,13 @@ jobs:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
ref: staging
token: ${{ secrets.GH_PAT }} token: ${{ secrets.GH_PAT }}
fetch-depth: 0 fetch-depth: 0
- name: Setup Bun - name: Setup Bun
uses: oven-sh/setup-bun@v2 uses: oven-sh/setup-bun@v2
with: with:
bun-version: 1.3.9 bun-version: 1.3.3
- name: Cache Bun dependencies - name: Cache Bun dependencies
uses: actions/cache@v4 uses: actions/cache@v4
@@ -65,11 +68,12 @@ jobs:
title: "feat(i18n): update translations" title: "feat(i18n): update translations"
body: | body: |
## Summary ## Summary
Automated weekly translation updates for documentation. Automated translation updates triggered by changes to documentation.
This PR was automatically created by the scheduled weekly i18n workflow, updating translations for all supported languages using Lingo.dev AI translation engine. This PR was automatically created after content changes were made, updating translations for all supported languages using Lingo.dev AI translation engine.
**Triggered**: Weekly scheduled run **Original trigger**: ${{ github.event.head_commit.message }}
**Commit**: ${{ github.sha }}
**Workflow**: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} **Workflow**: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
## Type of Change ## Type of Change
@@ -103,7 +107,7 @@ jobs:
## Screenshots/Videos ## Screenshots/Videos
<!-- Translation changes are text-based - no visual changes expected --> <!-- Translation changes are text-based - no visual changes expected -->
<!-- Reviewers should check the documentation site renders correctly for all languages --> <!-- Reviewers should check the documentation site renders correctly for all languages -->
branch: auto-translate/weekly-${{ github.run_id }} branch: auto-translate/staging-merge-${{ github.run_id }}
base: staging base: staging
labels: | labels: |
i18n i18n
@@ -122,7 +126,7 @@ jobs:
- name: Setup Bun - name: Setup Bun
uses: oven-sh/setup-bun@v2 uses: oven-sh/setup-bun@v2
with: with:
bun-version: 1.3.9 bun-version: 1.3.3
- name: Cache Bun dependencies - name: Cache Bun dependencies
uses: actions/cache@v4 uses: actions/cache@v4
@@ -141,8 +145,6 @@ jobs:
bun install --frozen-lockfile bun install --frozen-lockfile
- name: Build documentation to verify translations - name: Build documentation to verify translations
env:
DATABASE_URL: postgresql://dummy:dummy@localhost:5432/dummy
run: | run: |
cd apps/docs cd apps/docs
bun run build bun run build
@@ -151,7 +153,7 @@ jobs:
run: | run: |
cd apps/docs cd apps/docs
echo "## Translation Status Report" >> $GITHUB_STEP_SUMMARY echo "## Translation Status Report" >> $GITHUB_STEP_SUMMARY
echo "**Weekly scheduled translation run**" >> $GITHUB_STEP_SUMMARY echo "**Triggered by merge to staging branch**" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY
en_count=$(find content/docs/en -name "*.mdx" | wc -l) en_count=$(find content/docs/en -name "*.mdx" | wc -l)

View File

@@ -4,9 +4,6 @@ on:
workflow_call: workflow_call:
workflow_dispatch: workflow_dispatch:
permissions:
contents: read
jobs: jobs:
migrate: migrate:
name: Apply Database Migrations name: Apply Database Migrations
@@ -19,7 +16,7 @@ jobs:
- name: Setup Bun - name: Setup Bun
uses: oven-sh/setup-bun@v2 uses: oven-sh/setup-bun@v2
with: with:
bun-version: 1.3.9 bun-version: 1.3.3
- name: Cache Bun dependencies - name: Cache Bun dependencies
uses: actions/cache@v4 uses: actions/cache@v4

View File

@@ -6,9 +6,6 @@ on:
paths: paths:
- 'packages/cli/**' - 'packages/cli/**'
permissions:
contents: read
jobs: jobs:
publish-npm: publish-npm:
runs-on: blacksmith-4vcpu-ubuntu-2404 runs-on: blacksmith-4vcpu-ubuntu-2404
@@ -19,7 +16,7 @@ jobs:
- name: Setup Bun - name: Setup Bun
uses: oven-sh/setup-bun@v2 uses: oven-sh/setup-bun@v2
with: with:
bun-version: 1.3.9 bun-version: 1.3.3
- name: Setup Node.js for npm publishing - name: Setup Node.js for npm publishing
uses: actions/setup-node@v4 uses: actions/setup-node@v4

View File

@@ -6,9 +6,6 @@ on:
paths: paths:
- 'packages/python-sdk/**' - 'packages/python-sdk/**'
permissions:
contents: write
jobs: jobs:
publish-pypi: publish-pypi:
runs-on: blacksmith-4vcpu-ubuntu-2404 runs-on: blacksmith-4vcpu-ubuntu-2404

View File

@@ -6,9 +6,6 @@ on:
paths: paths:
- 'packages/ts-sdk/**' - 'packages/ts-sdk/**'
permissions:
contents: write
jobs: jobs:
publish-npm: publish-npm:
runs-on: blacksmith-4vcpu-ubuntu-2404 runs-on: blacksmith-4vcpu-ubuntu-2404
@@ -19,7 +16,7 @@ jobs:
- name: Setup Bun - name: Setup Bun
uses: oven-sh/setup-bun@v2 uses: oven-sh/setup-bun@v2
with: with:
bun-version: 1.3.9 bun-version: 1.3.3
- name: Setup Node.js for npm publishing - name: Setup Node.js for npm publishing
uses: actions/setup-node@v4 uses: actions/setup-node@v4

View File

@@ -4,9 +4,6 @@ on:
workflow_call: workflow_call:
workflow_dispatch: workflow_dispatch:
permissions:
contents: read
jobs: jobs:
test-build: test-build:
name: Test and Build name: Test and Build
@@ -19,63 +16,27 @@ jobs:
- name: Setup Bun - name: Setup Bun
uses: oven-sh/setup-bun@v2 uses: oven-sh/setup-bun@v2
with: with:
bun-version: 1.3.9 bun-version: 1.3.3
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: latest node-version: latest
- name: Mount Bun cache (Sticky Disk) - name: Cache Bun dependencies
uses: useblacksmith/stickydisk@v1 uses: actions/cache@v4
with: with:
key: ${{ github.repository }}-bun-cache path: |
path: ~/.bun/install/cache ~/.bun/install/cache
node_modules
- name: Mount node_modules (Sticky Disk) **/node_modules
uses: useblacksmith/stickydisk@v1 key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
with: restore-keys: |
key: ${{ github.repository }}-node-modules ${{ runner.os }}-bun-
path: ./node_modules
- name: Install dependencies - name: Install dependencies
run: bun install --frozen-lockfile run: bun install --frozen-lockfile
- name: Validate feature flags
run: |
FILE="apps/sim/lib/core/config/feature-flags.ts"
ERRORS=""
echo "Checking for hardcoded boolean feature flags..."
# Use perl for multiline matching to catch both:
# export const isHosted = true
# export const isHosted =
# true
HARDCODED=$(perl -0777 -ne 'while (/export const (is[A-Za-z]+)\s*=\s*\n?\s*(true|false)\b/g) { print " $1 = $2\n" }' "$FILE")
if [ -n "$HARDCODED" ]; then
ERRORS="${ERRORS}\n❌ Feature flags must not be hardcoded to boolean literals!\n\nFound hardcoded flags:\n${HARDCODED}\n\nFeature flags should derive their values from environment variables.\n"
fi
echo "Checking feature flag naming conventions..."
# Check that all export const (except functions) start with 'is'
# This finds exports like "export const someFlag" that don't start with "is" or "get"
BAD_NAMES=$(grep -E "^export const [a-z]" "$FILE" | grep -vE "^export const (is|get)" | sed 's/export const \([a-zA-Z]*\).*/ \1/')
if [ -n "$BAD_NAMES" ]; then
ERRORS="${ERRORS}\n❌ Feature flags must use 'is' prefix for boolean flags!\n\nFound incorrectly named flags:\n${BAD_NAMES}\n\nExample: 'hostedMode' should be 'isHostedMode'\n"
fi
if [ -n "$ERRORS" ]; then
echo ""
echo -e "$ERRORS"
exit 1
fi
echo "✅ All feature flags are properly configured"
- name: Lint code - name: Lint code
run: bun run lint:check run: bun run lint:check
@@ -87,19 +48,6 @@ jobs:
ENCRYPTION_KEY: '7cf672e460e430c1fba707575c2b0e2ad5a99dddf9b7b7e3b5646e630861db1c' # dummy key for CI only ENCRYPTION_KEY: '7cf672e460e430c1fba707575c2b0e2ad5a99dddf9b7b7e3b5646e630861db1c' # dummy key for CI only
run: bun run test 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 - name: Build application
env: env:
NODE_OPTIONS: '--no-warnings' NODE_OPTIONS: '--no-warnings'

323
CLAUDE.md
View File

@@ -1,314 +1,47 @@
# Sim Development Guidelines # Expert Programming Standards
You are a professional software engineer. All code must follow best practices: accurate, readable, clean, and efficient. **You are tasked with implementing solutions that follow best practices. You MUST be accurate, elegant, and efficient as an expert programmer.**
## Global Standards ---
- **Logging**: Import `createLogger` from `@sim/logger`. Use `logger.info`, `logger.warn`, `logger.error` instead of `console.log` # Role
- **Comments**: Use TSDoc for documentation. No `====` separators. No non-TSDoc comments
- **Styling**: Never update global styles. Keep all styling local to components
- **Package Manager**: Use `bun` and `bunx`, not `npm` and `npx`
## Architecture You are a professional software engineer. All code you write MUST follow best practices, ensuring accuracy, quality, readability, and cleanliness. You MUST make FOCUSED EDITS that are EFFICIENT and ELEGANT.
### Core Principles ## Logs
1. Single Responsibility: Each component, hook, store has one clear purpose
2. Composition Over Complexity: Break down complex logic into smaller pieces
3. Type Safety First: TypeScript interfaces for all props, state, return types
4. Predictable State: Zustand for global state, useState for UI-only concerns
### Root Structure ENSURE that you use the logger.info and logger.warn and logger.error instead of the console.log whenever you want to display logs.
```
apps/sim/
├── app/ # Next.js app router (pages, API routes)
├── blocks/ # Block definitions and registry
├── components/ # Shared UI (emcn/, ui/)
├── executor/ # Workflow execution engine
├── hooks/ # Shared hooks (queries/, selectors/)
├── lib/ # App-wide utilities
├── providers/ # LLM provider integrations
├── stores/ # Zustand stores
├── tools/ # Tool definitions
└── triggers/ # Trigger definitions
```
### Naming Conventions ## Comments
- Components: PascalCase (`WorkflowList`)
- Hooks: `use` prefix (`useWorkflowOperations`)
- Files: kebab-case (`workflow-list.tsx`)
- Stores: `stores/feature/store.ts`
- Constants: SCREAMING_SNAKE_CASE
- Interfaces: PascalCase with suffix (`WorkflowListProps`)
## Imports You must use TSDOC for comments. Do not use ==== for comments to separate sections. Do not leave any comments that are not TSDOC.
**Always use absolute imports.** Never use relative imports. ## Global Styles
```typescript You should not update the global styles unless it is absolutely necessary. Keep all styling local to components and files.
// ✓ Good
import { useWorkflowStore } from '@/stores/workflows/store'
// ✗ Bad ## Bun
import { useWorkflowStore } from '../../../stores/workflows/store'
```
Use barrel exports (`index.ts`) when a folder has 3+ exports. Do not re-export from non-barrel files; import directly from the source. Use bun and bunx not npm and npx.
### Import Order ## Code Quality
1. React/core libraries
2. External libraries
3. UI components (`@/components/emcn`, `@/components/ui`)
4. Utilities (`@/lib/...`)
5. Stores (`@/stores/...`)
6. Feature imports
7. CSS imports
Use `import type { X }` for type-only imports. - Write clean, maintainable code that follows the project's existing patterns
- Prefer composition over inheritance
## TypeScript - Keep functions small and focused on a single responsibility
- Use meaningful variable and function names
1. No `any` - Use proper types or `unknown` with type guards - Handle errors gracefully and provide useful error messages
2. Always define props interface for components - Write type-safe code with proper TypeScript types
3. `as const` for constant objects/arrays
4. Explicit ref types: `useRef<HTMLDivElement>(null)`
## Components
```typescript
'use client' // Only if using hooks
const CONFIG = { SPACING: 8 } as const
interface ComponentProps {
requiredProp: string
optionalProp?: boolean
}
export function Component({ requiredProp, optionalProp = false }: ComponentProps) {
// Order: refs → external hooks → store hooks → custom hooks → state → useMemo → useCallback → useEffect → return
}
```
Extract when: 50+ lines, used in 2+ files, or has own state/logic. Keep inline when: < 10 lines, single use, purely presentational.
## Hooks
```typescript
interface UseFeatureProps { id: string }
export function useFeature({ id }: UseFeatureProps) {
const idRef = useRef(id)
const [data, setData] = useState<Data | null>(null)
useEffect(() => { idRef.current = id }, [id])
const fetchData = useCallback(async () => { ... }, []) // Empty deps when using refs
return { data, fetchData }
}
```
## Zustand Stores
Stores live in `stores/`. Complex stores split into `store.ts` + `types.ts`.
```typescript
import { create } from 'zustand'
import { devtools } from 'zustand/middleware'
const initialState = { items: [] as Item[] }
export const useFeatureStore = create<FeatureState>()(
devtools(
(set, get) => ({
...initialState,
setItems: (items) => set({ items }),
reset: () => set(initialState),
}),
{ name: 'feature-store' }
)
)
```
Use `devtools` middleware. Use `persist` only when data should survive reload with `partialize` to persist only necessary state.
## React Query
All React Query hooks live in `hooks/queries/`.
```typescript
export const entityKeys = {
all: ['entity'] as const,
list: (workspaceId?: string) => [...entityKeys.all, 'list', workspaceId ?? ''] as const,
}
export function useEntityList(workspaceId?: string) {
return useQuery({
queryKey: entityKeys.list(workspaceId),
queryFn: () => fetchEntities(workspaceId as string),
enabled: Boolean(workspaceId),
staleTime: 60 * 1000,
placeholderData: keepPreviousData,
})
}
```
## Styling
Use Tailwind only, no inline styles. Use `cn()` from `@/lib/utils` for conditional classes.
```typescript
<div className={cn('base-classes', isActive && 'active-classes')} />
```
## EMCN Components
Import from `@/components/emcn`, never from subpaths (except CSS files). Use CVA when 2+ variants exist.
## Testing ## Testing
Use Vitest. Test files: `feature.ts``feature.test.ts` - Write tests for new functionality when appropriate
- Ensure existing tests pass before completing work
- Follow the project's testing conventions
```typescript ## Performance
/**
* @vitest-environment node
*/
import { databaseMock, loggerMock } from '@sim/testing'
import { describe, expect, it, vi } from 'vitest'
vi.mock('@sim/db', () => databaseMock) - Consider performance implications of your code
vi.mock('@sim/logger', () => loggerMock) - Avoid unnecessary re-renders in React components
- Use appropriate data structures and algorithms
import { myFunction } from '@/lib/feature' - Profile and optimize when necessary
describe('feature', () => {
beforeEach(() => vi.clearAllMocks())
it.concurrent('runs in parallel', () => { ... })
})
```
Use `@sim/testing` mocks/factories over local test data. See `.cursor/rules/sim-testing.mdc` for details.
## Utils Rules
- Never create `utils.ts` for single consumer - inline it
- Create `utils.ts` when 2+ files need the same helper
- Check existing sources in `lib/` before duplicating
## Adding Integrations
New integrations require: **Tools****Block****Icon** → (optional) **Trigger**
Always look up the service's API docs first.
### 1. Tools (`tools/{service}/`)
```
tools/{service}/
├── index.ts # Barrel export
├── types.ts # Params/response types
└── {action}.ts # Tool implementation
```
**Tool structure:**
```typescript
export const serviceTool: ToolConfig<Params, Response> = {
id: 'service_action',
name: 'Service Action',
description: '...',
version: '1.0.0',
oauth: { required: true, provider: 'service' },
params: { /* ... */ },
request: { url: '/api/tools/service/action', method: 'POST', ... },
transformResponse: async (response) => { /* ... */ },
outputs: { /* ... */ },
}
```
Register in `tools/registry.ts`.
### 2. Block (`blocks/blocks/{service}.ts`)
```typescript
export const ServiceBlock: BlockConfig = {
type: 'service',
name: 'Service',
description: '...',
category: 'tools',
bgColor: '#hexcolor',
icon: ServiceIcon,
subBlocks: [ /* see SubBlock Properties */ ],
tools: { access: ['service_action'], config: { tool: (p) => `service_${p.operation}` } },
inputs: { /* ... */ },
outputs: { /* ... */ },
}
```
Register in `blocks/registry.ts` (alphabetically).
**SubBlock Properties:**
```typescript
{
id: 'field', title: 'Label', type: 'short-input', placeholder: '...',
required: true, // or condition object
condition: { field: 'op', value: 'send' }, // show/hide
dependsOn: ['credential'], // clear when dep changes
mode: 'basic', // 'basic' | 'advanced' | 'both' | 'trigger'
}
```
**condition examples:**
- `{ field: 'op', value: 'send' }` - show when op === 'send'
- `{ field: 'op', value: ['a','b'] }` - show when op is 'a' OR 'b'
- `{ field: 'op', value: 'x', not: true }` - show when op !== 'x'
- `{ field: 'op', value: 'x', not: true, and: { field: 'type', value: 'dm', not: true } }` - complex
**dependsOn:** `['field']` or `{ all: ['a'], any: ['b', 'c'] }`
**File Input Pattern (basic/advanced mode):**
```typescript
// Basic: file-upload UI
{ id: 'uploadFile', type: 'file-upload', canonicalParamId: 'file', mode: 'basic' },
// Advanced: reference from other blocks
{ id: 'fileRef', type: 'short-input', canonicalParamId: 'file', mode: 'advanced' },
```
In `tools.config.tool`, normalize with:
```typescript
import { normalizeFileInput } from '@/blocks/utils'
const file = normalizeFileInput(params.uploadFile || params.fileRef, { single: true })
if (file) params.file = file
```
For file uploads, create an internal API route (`/api/tools/{service}/upload`) that uses `downloadFileFromStorage` to get file content from `UserFile` objects.
### 3. Icon (`components/icons.tsx`)
```typescript
export function ServiceIcon(props: SVGProps<SVGSVGElement>) {
return <svg {...props}>/* SVG from brand assets */</svg>
}
```
### 4. Trigger (`triggers/{service}/`) - Optional
```
triggers/{service}/
├── index.ts # Barrel export
├── webhook.ts # Webhook handler
└── {event}.ts # Event-specific handlers
```
Register in `triggers/registry.ts`.
### Integration Checklist
- [ ] Look up API docs
- [ ] Create `tools/{service}/` with types and tools
- [ ] Register tools in `tools/registry.ts`
- [ ] Add icon to `components/icons.tsx`
- [ ] Create block in `blocks/blocks/{service}.ts`
- [ ] Register block in `blocks/registry.ts`
- [ ] (Optional) Create and register triggers
- [ ] (If file uploads) Create internal API route with `downloadFileFromStorage`
- [ ] (If file uploads) Use `normalizeFileInput` in block config

View File

@@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier same "printed page" as the copyright notice for easier
identification within third-party archives. identification within third-party archives.
Copyright 2026 Sim Studio, Inc. Copyright 2025 Sim Studio, Inc.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

2
NOTICE
View File

@@ -1,4 +1,4 @@
Sim Studio Sim Studio
Copyright 2026 Sim Studio Copyright 2025 Sim Studio
This product includes software developed for the Sim project. This product includes software developed for the Sim project.

142
README.md
View File

@@ -9,14 +9,10 @@
<p align="center"> <p align="center">
<a href="https://sim.ai" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/badge/sim.ai-6F3DFA" alt="Sim.ai"></a> <a href="https://sim.ai" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/badge/sim.ai-6F3DFA" alt="Sim.ai"></a>
<a href="https://discord.gg/Hr4UWYEcTT" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/badge/Discord-Join%20Server-5865F2?logo=discord&logoColor=white" alt="Discord"></a> <a href="https://discord.gg/Hr4UWYEcTT" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/badge/Discord-Join%20Server-5865F2?logo=discord&logoColor=white" alt="Discord"></a>
<a href="https://x.com/simdotai" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/twitter/follow/simdotai?style=social" alt="Twitter"></a> <a href="https://x.com/simdotai" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/twitter/follow/simstudioai?style=social" alt="Twitter"></a>
<a href="https://docs.sim.ai" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/badge/Docs-6F3DFA.svg" alt="Documentation"></a> <a href="https://docs.sim.ai" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/badge/Docs-6F3DFA.svg" alt="Documentation"></a>
</p> </p>
<p align="center">
<a href="https://deepwiki.com/simstudioai/sim" target="_blank" rel="noopener noreferrer"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a> <a href="https://cursor.com/link/prompt?text=Help%20me%20set%20up%20Sim%20locally.%20Follow%20these%20steps%3A%0A%0A1.%20First%2C%20verify%20Docker%20is%20installed%20and%20running%3A%0A%20%20%20docker%20--version%0A%20%20%20docker%20info%0A%0A2.%20Clone%20the%20repository%3A%0A%20%20%20git%20clone%20https%3A%2F%2Fgithub.com%2Fsimstudioai%2Fsim.git%0A%20%20%20cd%20sim%0A%0A3.%20Start%20the%20services%20with%20Docker%20Compose%3A%0A%20%20%20docker%20compose%20-f%20docker-compose.prod.yml%20up%20-d%0A%0A4.%20Wait%20for%20all%20containers%20to%20be%20healthy%20(this%20may%20take%201-2%20minutes)%3A%0A%20%20%20docker%20compose%20-f%20docker-compose.prod.yml%20ps%0A%0A5.%20Verify%20the%20app%20is%20accessible%20at%20http%3A%2F%2Flocalhost%3A3000%0A%0AIf%20there%20are%20any%20errors%2C%20help%20me%20troubleshoot%20them.%20Common%20issues%3A%0A-%20Port%203000%2C%203002%2C%20or%205432%20already%20in%20use%0A-%20Docker%20not%20running%0A-%20Insufficient%20memory%20(needs%2012GB%2B%20RAM)%0A%0AFor%20local%20AI%20models%20with%20Ollama%2C%20use%20this%20instead%20of%20step%203%3A%0A%20%20%20docker%20compose%20-f%20docker-compose.ollama.yml%20--profile%20setup%20up%20-d"><img src="https://img.shields.io/badge/Set%20Up%20with-Cursor-000000?logo=cursor&logoColor=white" alt="Set Up with Cursor"></a>
</p>
### Build Workflows with Ease ### Build Workflows with Ease
Design agent workflows visually on a canvas—connect agents, tools, and blocks, then run them instantly. Design agent workflows visually on a canvas—connect agents, tools, and blocks, then run them instantly.
@@ -64,11 +60,17 @@ Docker must be installed and running on your machine.
### Self-hosted: Docker Compose ### Self-hosted: Docker Compose
```bash ```bash
git clone https://github.com/simstudioai/sim.git && cd sim # Clone the repository
git clone https://github.com/simstudioai/sim.git
# Navigate to the project directory
cd sim
# Start Sim
docker compose -f docker-compose.prod.yml up -d docker compose -f docker-compose.prod.yml up -d
``` ```
Open [http://localhost:3000](http://localhost:3000) Access the application at [http://localhost:3000/](http://localhost:3000/)
#### Using Local Models with Ollama #### Using Local Models with Ollama
@@ -89,17 +91,33 @@ docker compose -f docker-compose.ollama.yml exec ollama ollama pull llama3.1:8b
#### Using an External Ollama Instance #### Using an External Ollama Instance
If Ollama is running on your host machine, use `host.docker.internal` instead of `localhost`: If you already have Ollama running on your host machine (outside Docker), you need to configure the `OLLAMA_URL` to use `host.docker.internal` instead of `localhost`:
```bash ```bash
# Docker Desktop (macOS/Windows)
OLLAMA_URL=http://host.docker.internal:11434 docker compose -f docker-compose.prod.yml up -d OLLAMA_URL=http://host.docker.internal:11434 docker compose -f docker-compose.prod.yml up -d
# Linux (add extra_hosts or use host IP)
docker compose -f docker-compose.prod.yml up -d # Then set OLLAMA_URL to your host's IP
``` ```
On Linux, use your host's IP address or add `extra_hosts: ["host.docker.internal:host-gateway"]` to the compose file. **Why?** When running inside Docker, `localhost` refers to the container itself, not your host machine. `host.docker.internal` is a special DNS name that resolves to the host.
For Linux users, you can either:
- Use your host machine's actual IP address (e.g., `http://192.168.1.100:11434`)
- Add `extra_hosts: ["host.docker.internal:host-gateway"]` to the simstudio service in your compose file
#### Using vLLM #### Using vLLM
Sim supports [vLLM](https://docs.vllm.ai/) for self-hosted models. Set `VLLM_BASE_URL` and optionally `VLLM_API_KEY` in your environment. Sim also supports [vLLM](https://docs.vllm.ai/) for self-hosted models with OpenAI-compatible API:
```bash
# Set these environment variables
VLLM_BASE_URL=http://your-vllm-server:8000
VLLM_API_KEY=your_optional_api_key # Only if your vLLM instance requires auth
```
When running with Docker, use `host.docker.internal` if vLLM is on your host machine (same as Ollama above).
### Self-hosted: Dev Containers ### Self-hosted: Dev Containers
@@ -110,9 +128,13 @@ Sim supports [vLLM](https://docs.vllm.ai/) for self-hosted models. Set `VLLM_BAS
### Self-hosted: Manual Setup ### Self-hosted: Manual Setup
**Requirements:** [Bun](https://bun.sh/), [Node.js](https://nodejs.org/) v20+, PostgreSQL 12+ with [pgvector](https://github.com/pgvector/pgvector) **Requirements:**
- [Bun](https://bun.sh/) runtime
- PostgreSQL 12+ with [pgvector extension](https://github.com/pgvector/pgvector) (required for AI embeddings)
1. Clone and install: **Note:** Sim uses vector embeddings for AI features like knowledge bases and semantic search, which requires the `pgvector` PostgreSQL extension.
1. Clone and install dependencies:
```bash ```bash
git clone https://github.com/simstudioai/sim.git git clone https://github.com/simstudioai/sim.git
@@ -122,33 +144,74 @@ bun install
2. Set up PostgreSQL with pgvector: 2. Set up PostgreSQL with pgvector:
You need PostgreSQL with the `vector` extension for embedding support. Choose one option:
**Option A: Using Docker (Recommended)**
```bash ```bash
docker run --name simstudio-db -e POSTGRES_PASSWORD=your_password -e POSTGRES_DB=simstudio -p 5432:5432 -d pgvector/pgvector:pg17 # Start PostgreSQL with pgvector extension
docker run --name simstudio-db \
-e POSTGRES_PASSWORD=your_password \
-e POSTGRES_DB=simstudio \
-p 5432:5432 -d \
pgvector/pgvector:pg17
``` ```
Or install manually via the [pgvector guide](https://github.com/pgvector/pgvector#installation). **Option B: Manual Installation**
- Install PostgreSQL 12+ and the pgvector extension
- See [pgvector installation guide](https://github.com/pgvector/pgvector#installation)
3. Configure environment: 3. Set up environment:
```bash ```bash
cp apps/sim/.env.example apps/sim/.env cd apps/sim
cp packages/db/.env.example packages/db/.env cp .env.example .env # Configure with required variables (DATABASE_URL, BETTER_AUTH_SECRET, BETTER_AUTH_URL)
# Edit both .env files to set DATABASE_URL="postgresql://postgres:your_password@localhost:5432/simstudio"
``` ```
4. Run migrations: Update your `.env` file with the database URL:
```bash
DATABASE_URL="postgresql://postgres:your_password@localhost:5432/simstudio"
```
4. Set up the database:
First, configure the database package environment:
```bash
cd packages/db
cp .env.example .env
```
Update your `packages/db/.env` file with the database URL:
```bash
DATABASE_URL="postgresql://postgres:your_password@localhost:5432/simstudio"
```
Then run the migrations:
```bash
bunx drizzle-kit migrate --config=./drizzle.config.ts
```
5. Start the development servers:
**Recommended approach - run both servers together (from project root):**
```bash ```bash
cd packages/db && bunx drizzle-kit migrate --config=./drizzle.config.ts bun run dev:full
``` ```
5. Start development servers: This starts both the main Next.js application and the realtime socket server required for full functionality.
**Alternative - run servers separately:**
Next.js app (from project root):
```bash ```bash
bun run dev:full # Starts both Next.js app and realtime socket server bun run dev
``` ```
Or run separately: `bun run dev` (Next.js) and `cd apps/sim && bun run dev:sockets` (realtime). Realtime socket server (from `apps/sim` directory in a separate terminal):
```bash
cd apps/sim
bun run dev:sockets
```
## Copilot API Keys ## Copilot API Keys
@@ -159,7 +222,7 @@ Copilot is a Sim-managed service. To use Copilot on a self-hosted instance:
## Environment Variables ## Environment Variables
Key environment variables for self-hosted deployments. See [`.env.example`](apps/sim/.env.example) for defaults or [`env.ts`](apps/sim/lib/core/config/env.ts) for the full list. Key environment variables for self-hosted deployments (see `apps/sim/.env.example` for full list):
| Variable | Required | Description | | Variable | Required | Description |
|----------|----------|-------------| |----------|----------|-------------|
@@ -167,11 +230,36 @@ Key environment variables for self-hosted deployments. See [`.env.example`](apps
| `BETTER_AUTH_SECRET` | Yes | Auth secret (`openssl rand -hex 32`) | | `BETTER_AUTH_SECRET` | Yes | Auth secret (`openssl rand -hex 32`) |
| `BETTER_AUTH_URL` | Yes | Your app URL (e.g., `http://localhost:3000`) | | `BETTER_AUTH_URL` | Yes | Your app URL (e.g., `http://localhost:3000`) |
| `NEXT_PUBLIC_APP_URL` | Yes | Public app URL (same as above) | | `NEXT_PUBLIC_APP_URL` | Yes | Public app URL (same as above) |
| `ENCRYPTION_KEY` | Yes | Encrypts environment variables (`openssl rand -hex 32`) | | `ENCRYPTION_KEY` | Yes | Encryption key (`openssl rand -hex 32`) |
| `INTERNAL_API_SECRET` | Yes | Encrypts internal API routes (`openssl rand -hex 32`) | | `OLLAMA_URL` | No | Ollama server URL (default: `http://localhost:11434`) |
| `API_ENCRYPTION_KEY` | Yes | Encrypts API keys (`openssl rand -hex 32`) | | `VLLM_BASE_URL` | No | vLLM server URL for self-hosted models |
| `COPILOT_API_KEY` | No | API key from sim.ai for Copilot features | | `COPILOT_API_KEY` | No | API key from sim.ai for Copilot features |
## Troubleshooting
### Ollama models not showing in dropdown (Docker)
If you're running Ollama on your host machine and Sim in Docker, change `OLLAMA_URL` from `localhost` to `host.docker.internal`:
```bash
OLLAMA_URL=http://host.docker.internal:11434 docker compose -f docker-compose.prod.yml up -d
```
See [Using an External Ollama Instance](#using-an-external-ollama-instance) for details.
### Database connection issues
Ensure PostgreSQL has the pgvector extension installed. When using Docker, wait for the database to be healthy before running migrations.
### Port conflicts
If ports 3000, 3002, or 5432 are in use, configure alternatives:
```bash
# Custom ports
NEXT_PUBLIC_APP_URL=http://localhost:3100 POSTGRES_PORT=5433 docker compose up -d
```
## Tech Stack ## Tech Stack
- **Framework**: [Next.js](https://nextjs.org/) (App Router) - **Framework**: [Next.js](https://nextjs.org/) (App Router)

View File

@@ -1,6 +1,4 @@
import type React from 'react'
import { findNeighbour } from 'fumadocs-core/page-tree' import { findNeighbour } from 'fumadocs-core/page-tree'
import { Pre } from 'fumadocs-ui/components/codeblock'
import defaultMdxComponents from 'fumadocs-ui/mdx' import defaultMdxComponents from 'fumadocs-ui/mdx'
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/page' import { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/page'
import { ChevronLeft, ChevronRight } from 'lucide-react' import { ChevronLeft, ChevronRight } from 'lucide-react'
@@ -12,17 +10,15 @@ import { LLMCopyButton } from '@/components/page-actions'
import { StructuredData } from '@/components/structured-data' import { StructuredData } from '@/components/structured-data'
import { CodeBlock } from '@/components/ui/code-block' import { CodeBlock } from '@/components/ui/code-block'
import { Heading } from '@/components/ui/heading' import { Heading } from '@/components/ui/heading'
import { type PageData, source } from '@/lib/source' import { source } from '@/lib/source'
export default async function Page(props: { params: Promise<{ slug?: string[]; lang: string }> }) { export default async function Page(props: { params: Promise<{ slug?: string[]; lang: string }> }) {
const params = await props.params const params = await props.params
const page = source.getPage(params.slug, params.lang) const page = source.getPage(params.slug, params.lang)
if (!page) notFound() if (!page) notFound()
const data = page.data as PageData const MDX = page.data.body
const MDX = data.body
const baseUrl = 'https://docs.sim.ai' const baseUrl = 'https://docs.sim.ai'
const markdownContent = await data.getText('processed')
const pageTreeRecord = source.pageTree as Record<string, any> const pageTreeRecord = source.pageTree as Record<string, any>
const pageTree = const pageTree =
@@ -55,7 +51,7 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l
if (index === urlParts.length - 1) { if (index === urlParts.length - 1) {
breadcrumbs.push({ breadcrumbs.push({
name: data.title, name: page.data.title,
url: `${baseUrl}${page.url}`, url: `${baseUrl}${page.url}`,
}) })
} else { } else {
@@ -172,21 +168,26 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l
return ( return (
<> <>
<StructuredData <StructuredData
title={data.title} title={page.data.title}
description={data.description || ''} description={page.data.description || ''}
url={`${baseUrl}${page.url}`} url={`${baseUrl}${page.url}`}
lang={params.lang} lang={params.lang}
breadcrumb={breadcrumbs} breadcrumb={breadcrumbs}
/> />
<DocsPage <DocsPage
toc={data.toc} toc={page.data.toc}
full={data.full} full={page.data.full}
breadcrumb={{ breadcrumb={{
enabled: false, enabled: false,
}} }}
tableOfContent={{ tableOfContent={{
style: 'clerk', style: 'clerk',
enabled: true, enabled: true,
header: (
<div key='toc-header' className='mb-2 font-medium text-sm'>
On this page
</div>
),
footer: <TOCFooter />, footer: <TOCFooter />,
single: false, single: false,
}} }}
@@ -202,40 +203,24 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l
<div className='relative mt-6 sm:mt-0'> <div className='relative mt-6 sm:mt-0'>
<div className='absolute top-1 right-0 flex items-center gap-2'> <div className='absolute top-1 right-0 flex items-center gap-2'>
<div className='hidden sm:flex'> <div className='hidden sm:flex'>
<LLMCopyButton content={markdownContent} /> <LLMCopyButton markdownUrl={`${page.url}.mdx`} />
</div> </div>
<PageNavigationArrows previous={neighbours?.previous} next={neighbours?.next} /> <PageNavigationArrows previous={neighbours?.previous} next={neighbours?.next} />
</div> </div>
<DocsTitle>{data.title}</DocsTitle> <DocsTitle>{page.data.title}</DocsTitle>
<DocsDescription>{data.description}</DocsDescription> <DocsDescription>{page.data.description}</DocsDescription>
</div> </div>
<DocsBody> <DocsBody>
<MDX <MDX
components={{ components={{
...defaultMdxComponents, ...defaultMdxComponents,
pre: (props: React.HTMLAttributes<HTMLPreElement>) => ( CodeBlock,
<CodeBlock {...props}> h1: (props) => <Heading as='h1' {...props} />,
<Pre>{props.children}</Pre> h2: (props) => <Heading as='h2' {...props} />,
</CodeBlock> h3: (props) => <Heading as='h3' {...props} />,
), h4: (props) => <Heading as='h4' {...props} />,
h1: (props: React.HTMLAttributes<HTMLHeadingElement>) => ( h5: (props) => <Heading as='h5' {...props} />,
<Heading as='h1' {...props} /> h6: (props) => <Heading as='h6' {...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> </DocsBody>
@@ -255,16 +240,16 @@ export async function generateMetadata(props: {
const page = source.getPage(params.slug, params.lang) const page = source.getPage(params.slug, params.lang)
if (!page) notFound() if (!page) notFound()
const data = page.data as PageData
const baseUrl = 'https://docs.sim.ai' const baseUrl = 'https://docs.sim.ai'
const fullUrl = `${baseUrl}${page.url}` const fullUrl = `${baseUrl}${page.url}`
const ogImageUrl = `${baseUrl}/api/og?title=${encodeURIComponent(data.title)}` const description = page.data.description || ''
const ogImageUrl = `${baseUrl}/api/og?title=${encodeURIComponent(page.data.title)}&category=DOCUMENTATION${description ? `&description=${encodeURIComponent(description)}` : ''}`
return { return {
title: data.title, title: page.data.title,
description: description:
data.description || 'Sim visual workflow builder for AI applications documentation', page.data.description || 'Sim visual workflow builder for AI applications documentation',
keywords: [ keywords: [
'AI workflow builder', 'AI workflow builder',
'visual workflow editor', 'visual workflow editor',
@@ -273,16 +258,16 @@ export async function generateMetadata(props: {
'AI agents', 'AI agents',
'no-code AI', 'no-code AI',
'drag and drop workflows', 'drag and drop workflows',
data.title?.toLowerCase().split(' '), page.data.title?.toLowerCase().split(' '),
] ]
.flat() .flat()
.filter(Boolean), .filter(Boolean),
authors: [{ name: 'Sim Team' }], authors: [{ name: 'Sim Team' }],
category: 'Developer Tools', category: 'Developer Tools',
openGraph: { openGraph: {
title: data.title, title: page.data.title,
description: description:
data.description || 'Sim visual workflow builder for AI applications documentation', page.data.description || 'Sim visual workflow builder for AI applications documentation',
url: fullUrl, url: fullUrl,
siteName: 'Sim Documentation', siteName: 'Sim Documentation',
type: 'article', type: 'article',
@@ -295,15 +280,15 @@ export async function generateMetadata(props: {
url: ogImageUrl, url: ogImageUrl,
width: 1200, width: 1200,
height: 630, height: 630,
alt: data.title, alt: page.data.title,
}, },
], ],
}, },
twitter: { twitter: {
card: 'summary_large_image', card: 'summary_large_image',
title: data.title, title: page.data.title,
description: description:
data.description || 'Sim visual workflow builder for AI applications documentation', page.data.description || 'Sim visual workflow builder for AI applications documentation',
images: [ogImageUrl], images: [ogImageUrl],
creator: '@simdotai', creator: '@simdotai',
site: '@simdotai', site: '@simdotai',

View File

@@ -3,14 +3,13 @@ import { defineI18nUI } from 'fumadocs-ui/i18n'
import { DocsLayout } from 'fumadocs-ui/layouts/docs' import { DocsLayout } from 'fumadocs-ui/layouts/docs'
import { RootProvider } from 'fumadocs-ui/provider/next' import { RootProvider } from 'fumadocs-ui/provider/next'
import { Geist_Mono, Inter } from 'next/font/google' import { Geist_Mono, Inter } from 'next/font/google'
import Script from 'next/script' import Image from 'next/image'
import { import {
SidebarFolder, SidebarFolder,
SidebarItem, SidebarItem,
SidebarSeparator, SidebarSeparator,
} from '@/components/docs-layout/sidebar-components' } from '@/components/docs-layout/sidebar-components'
import { Navbar } from '@/components/navbar/navbar' import { Navbar } from '@/components/navbar/navbar'
import { SimLogoFull } from '@/components/ui/sim-logo'
import { i18n } from '@/lib/i18n' import { i18n } from '@/lib/i18n'
import { source } from '@/lib/source' import { source } from '@/lib/source'
import '../global.css' import '../global.css'
@@ -18,13 +17,11 @@ import '../global.css'
const inter = Inter({ const inter = Inter({
subsets: ['latin'], subsets: ['latin'],
variable: '--font-geist-sans', variable: '--font-geist-sans',
display: 'swap',
}) })
const geistMono = Geist_Mono({ const geistMono = Geist_Mono({
subsets: ['latin'], subsets: ['latin'],
variable: '--font-geist-mono', variable: '--font-geist-mono',
display: 'swap',
}) })
const { provider } = defineI18nUI(i18n, { const { provider } = defineI18nUI(i18n, {
@@ -96,15 +93,25 @@ export default async function Layout({ children, params }: LayoutProps) {
type='application/ld+json' type='application/ld+json'
dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }} dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}
/> />
{/* OneDollarStats Analytics - CDN script handles everything automatically */}
<script defer src='https://assets.onedollarstats.com/stonks.js' />
</head> </head>
<body className='flex min-h-screen flex-col font-sans'> <body className='flex min-h-screen flex-col font-sans'>
<Script src='https://assets.onedollarstats.com/stonks.js' strategy='lazyOnload' />
<RootProvider i18n={provider(lang)}> <RootProvider i18n={provider(lang)}>
<Navbar /> <Navbar />
<DocsLayout <DocsLayout
tree={source.pageTree[lang]} tree={source.pageTree[lang]}
nav={{ nav={{
title: <SimLogoFull className='h-7 w-auto' />, title: (
<Image
src='/static/logo.png'
alt='Sim'
width={72}
height={28}
className='h-7 w-auto'
priority
/>
),
}} }}
sidebar={{ sidebar={{
defaultOpenLevel: 0, defaultOpenLevel: 0,

View File

@@ -1,23 +0,0 @@
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-[#47d991] to-[#33c482] 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>
)
}

View File

@@ -33,42 +33,19 @@ async function loadGoogleFont(font: string, weights: string, text: string): Prom
throw new Error('Failed to load font data') throw new Error('Failed to load font data')
} }
/**
* Sim logo with icon and "Sim" text for OG image.
*/
function SimLogoFull() {
return (
<svg height='28' viewBox='720 440 1020 320' fill='none'>
{/* Green icon - top left shape with cutout */}
<path
fillRule='evenodd'
clipRule='evenodd'
d='M875.791 577.171C875.791 581.922 873.911 586.483 870.576 589.842L870.098 590.323C866.764 593.692 862.234 595.575 857.517 595.575H750.806C740.978 595.575 733 603.6 733 613.498V728.902C733 738.799 740.978 746.826 750.806 746.826H865.382C875.209 746.826 883.177 738.799 883.177 728.902V620.853C883.177 616.448 884.912 612.222 888.008 609.104C891.093 605.997 895.29 604.249 899.664 604.249H1008.16C1017.99 604.249 1025.96 596.224 1025.96 586.327V470.923C1025.96 461.025 1017.99 453 1008.16 453H893.586C883.759 453 875.791 461.025 875.791 470.923V577.171ZM910.562 477.566H991.178C996.922 477.566 1001.57 482.254 1001.57 488.029V569.22C1001.57 574.995 996.922 579.683 991.178 579.683H910.562C904.828 579.683 900.173 574.995 900.173 569.22V488.029C900.173 482.254 904.828 477.566 910.562 477.566Z'
fill='#33C482'
/>
{/* Green icon - bottom right square */}
<path
d='M1008.3 624.59H923.113C912.786 624.59 904.414 633.022 904.414 643.423V728.171C904.414 738.572 912.786 747.004 923.113 747.004H1008.3C1018.63 747.004 1027 738.572 1027 728.171V643.423C1027 633.022 1018.63 624.59 1008.3 624.59Z'
fill='#33C482'
/>
{/* "Sim" text - white for dark background */}
<path
d='M1210.54 515.657C1226.65 515.657 1240.59 518.51 1252.31 524.257H1252.31C1264.3 529.995 1273.63 538.014 1280.26 548.319H1280.26C1287.19 558.635 1290.78 570.899 1291.08 585.068L1291.1 586.089H1249.11L1249.09 585.115C1248.8 574.003 1245.18 565.493 1238.32 559.451C1231.45 553.399 1221.79 550.308 1209.21 550.308C1196.3 550.308 1186.48 553.113 1179.61 558.588C1172.76 564.046 1169.33 571.499 1169.33 581.063C1169.33 588.092 1171.88 593.978 1177.01 598.783C1182.17 603.618 1189.99 607.399 1200.56 610.061H1200.56L1238.77 619.451C1257.24 623.65 1271.21 630.571 1280.57 640.293L1281.01 640.739C1290.13 650.171 1294.64 662.97 1294.64 679.016C1294.64 692.923 1290.88 705.205 1283.34 715.822L1283.33 715.834C1275.81 726.134 1265.44 734.14 1252.26 739.866L1252.25 739.871C1239.36 745.302 1224.12 748 1206.54 748C1180.9 748 1160.36 741.696 1145.02 728.984C1129.67 716.258 1122 699.269 1122 678.121V677.121H1163.99V678.121C1163.99 688.869 1167.87 697.367 1175.61 703.722L1176.34 704.284C1184.04 709.997 1194.37 712.902 1207.43 712.902C1222.13 712.902 1233.3 710.087 1241.07 704.588C1248.8 698.812 1252.64 691.21 1252.64 681.699C1252.64 674.769 1250.5 669.057 1246.25 664.49L1246.23 664.478L1246.22 664.464C1242.28 659.929 1234.83 656.119 1223.64 653.152L1185.43 644.208L1185.42 644.204C1166.05 639.407 1151.49 632.035 1141.83 622.012L1141.83 622.006L1141.82 622C1132.43 611.94 1127.78 598.707 1127.78 582.405C1127.78 568.81 1131.23 556.976 1138.17 546.949L1138.18 546.941L1138.19 546.933C1145.41 536.936 1155.18 529.225 1167.48 523.793L1167.48 523.79C1180.07 518.36 1194.43 515.657 1210.54 515.657ZM1323.39 521.979C1331.68 525.008 1337.55 526.482 1343.51 526.482C1349.48 526.482 1355.64 525.005 1364.49 521.973L1365.82 521.52V742.633H1322.05V521.489L1323.39 521.979ZM1642.01 515.657C1667.11 515.657 1686.94 523.031 1701.39 537.876C1715.83 552.716 1723 572.968 1723 598.507V742.633H1680.12V608.794C1680.12 591.666 1675.72 578.681 1667.07 569.681L1667.06 569.669L1667.04 569.656C1658.67 560.359 1647.26 555.675 1632.68 555.675C1622.47 555.675 1613.47 558.022 1605.64 562.69L1605.63 562.696C1598.11 567.064 1592.17 573.475 1587.8 581.968C1583.44 590.448 1581.25 600.424 1581.25 611.925V742.633H1537.92V608.347C1537.92 591.208 1533.67 578.376 1525.31 569.68L1525.31 569.674L1525.3 569.668C1516.93 560.664 1505.52 556.122 1490.93 556.122C1480.72 556.122 1471.72 558.469 1463.89 563.138L1463.88 563.144C1456.36 567.511 1450.41 573.922 1446.05 582.415L1446.05 582.422L1446.04 582.428C1441.69 590.602 1439.5 600.423 1439.5 611.925V742.633H1395.72V521.919H1435.05V554.803C1439.92 544.379 1447.91 535.465 1458.37 528.356C1470.71 519.875 1485.58 515.657 1502.93 515.657C1522.37 515.657 1538.61 520.931 1551.55 531.538C1560.38 538.771 1567.1 547.628 1571.72 558.091C1576.05 547.619 1582.83 538.757 1592.07 531.524C1605.61 520.93 1622.28 515.657 1642.01 515.657ZM1343.49 452C1351.45 452 1358.23 454.786 1363.75 460.346C1369.27 465.905 1372.04 472.721 1372.04 480.73C1372.04 488.452 1369.27 495.254 1363.77 501.096L1363.76 501.105L1363.75 501.115C1358.23 506.675 1351.45 509.461 1343.49 509.461C1335.81 509.461 1329.05 506.669 1323.25 501.134L1323.23 501.115L1323.21 501.096C1317.71 495.254 1314.94 488.452 1314.94 480.73C1314.94 472.721 1317.7 465.905 1323.23 460.346L1323.24 460.337L1323.25 460.327C1329.05 454.792 1335.81 452 1343.49 452Z'
fill='#fafafa'
/>
</svg>
)
}
/** /**
* Generates dynamic Open Graph images for documentation pages. * Generates dynamic Open Graph images for documentation pages.
* Style matches Cursor docs: dark background, title at top, logo bottom-left, domain bottom-right.
*/ */
export async function GET(request: NextRequest) { export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url) const { searchParams } = new URL(request.url)
const title = searchParams.get('title') || 'Documentation' const title = searchParams.get('title') || 'Documentation'
const category = searchParams.get('category') || 'DOCUMENTATION'
const description = searchParams.get('description') || ''
const allText = `${title}docs.sim.ai` 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) const fontData = await loadGoogleFont('Geist', '400;500;600', allText)
return new ImageResponse( return new ImageResponse(
@@ -78,40 +55,103 @@ export async function GET(request: NextRequest) {
width: '100%', width: '100%',
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
justifyContent: 'space-between', background: 'linear-gradient(315deg, #1e1e3f 0%, #1a1a2e 40%, #0f0f0f 100%)',
padding: '56px 64px', position: 'relative',
background: '#121212', // Dark mode background matching docs (hsla 0, 0%, 7%)
fontFamily: 'Geist', fontFamily: 'Geist',
}} }}
> >
{/* Title at top */} {/* Background texture */}
<span <img
src={backgroundImageUrl}
alt=''
style={{ style={{
fontSize: getTitleFontSize(title), position: 'absolute',
fontWeight: 500, top: 0,
color: '#fafafa', // Light text matching docs left: 0,
lineHeight: 1.2, width: '100%',
letterSpacing: '-0.02em', height: '100%',
objectFit: 'cover',
opacity: 0.04,
}} }}
> />
{title}
</span>
{/* Footer: icon left, domain right */} {/* Subtle purple glow from bottom right */}
<div
style={{
position: 'absolute',
bottom: 0,
right: 0,
width: '50%',
height: '100%',
background:
'radial-gradient(ellipse at bottom right, rgba(112, 31, 252, 0.1) 0%, transparent 50%)',
display: 'flex',
}}
/>
{/* Content */}
<div <div
style={{ style={{
display: 'flex', display: 'flex',
flexDirection: 'column',
padding: '56px 72px',
height: '100%',
justifyContent: 'space-between', justifyContent: 'space-between',
alignItems: 'center',
width: '100%',
}} }}
> >
<SimLogoFull /> {/* Logo */}
<img src={`${baseUrl}/static/logo.png`} alt='sim' height={32} />
{/* Category + Title + Description */}
<div
style={{
display: 'flex',
flexDirection: 'column',
gap: 12,
}}
>
<span
style={{
fontSize: 15,
fontWeight: 600,
color: '#802fff',
letterSpacing: '0.02em',
}}
>
{category}
</span>
<span
style={{
fontSize: getTitleFontSize(title),
fontWeight: 600,
color: '#ffffff',
lineHeight: 1.1,
letterSpacing: '-0.02em',
}}
>
{title}
</span>
{description && (
<span
style={{
fontSize: 18,
fontWeight: 400,
color: '#a1a1aa',
lineHeight: 1.4,
marginTop: 4,
}}
>
{description.length > 100 ? `${description.slice(0, 100)}...` : description}
</span>
)}
</div>
{/* Footer */}
<span <span
style={{ style={{
fontSize: 20, fontSize: 15,
fontWeight: 400, fontWeight: 500,
color: '#71717a', color: '#52525b',
}} }}
> >
docs.sim.ai docs.sim.ai

View File

@@ -1,211 +1,16 @@
import { sql } from 'drizzle-orm' import { createFromSource } from 'fumadocs-core/search/server'
import { type NextRequest, NextResponse } from 'next/server' import { source } from '@/lib/source'
import { db, docsEmbeddings } from '@/lib/db'
import { generateSearchEmbedding } from '@/lib/embeddings'
export const runtime = 'nodejs' export const revalidate = 3600 // Revalidate every hour
export const revalidate = 0
/** export const { GET } = createFromSource(source, {
* Hybrid search API endpoint localeMap: {
* - English: Vector embeddings + keyword search en: { language: 'english' },
* - Other languages: Keyword search only es: { language: 'spanish' },
*/ fr: { language: 'french' },
export async function GET(request: NextRequest) { de: { language: 'german' },
try { // ja and zh are not supported by the stemmer library, so we'll skip language config for them
const searchParams = request.nextUrl.searchParams ja: {},
const query = searchParams.get('query') || searchParams.get('q') || '' zh: {},
const locale = searchParams.get('locale') || 'en' },
const limit = Number.parseInt(searchParams.get('limit') || '10', 10) })
if (!query || query.trim().length === 0) {
return NextResponse.json([])
}
const candidateLimit = limit * 3
const similarityThreshold = 0.6
const localeMap: Record<string, string> = {
en: 'english',
es: 'spanish',
fr: 'french',
de: 'german',
ja: 'simple', // PostgreSQL doesn't have Japanese support, use simple
zh: 'simple', // PostgreSQL doesn't have Chinese support, use simple
}
const tsConfig = localeMap[locale] || 'simple'
const useVectorSearch = locale === 'en'
let vectorResults: Array<{
chunkId: string
chunkText: string
sourceDocument: string
sourceLink: string
headerText: string
headerLevel: number
similarity: number
searchType: string
}> = []
if (useVectorSearch) {
const queryEmbedding = await generateSearchEmbedding(query)
vectorResults = await db
.select({
chunkId: docsEmbeddings.chunkId,
chunkText: docsEmbeddings.chunkText,
sourceDocument: docsEmbeddings.sourceDocument,
sourceLink: docsEmbeddings.sourceLink,
headerText: docsEmbeddings.headerText,
headerLevel: docsEmbeddings.headerLevel,
similarity: sql<number>`1 - (${docsEmbeddings.embedding} <=> ${JSON.stringify(queryEmbedding)}::vector)`,
searchType: sql<string>`'vector'`,
})
.from(docsEmbeddings)
.where(
sql`1 - (${docsEmbeddings.embedding} <=> ${JSON.stringify(queryEmbedding)}::vector) >= ${similarityThreshold}`
)
.orderBy(sql`${docsEmbeddings.embedding} <=> ${JSON.stringify(queryEmbedding)}::vector`)
.limit(candidateLimit)
}
const keywordResults = await db
.select({
chunkId: docsEmbeddings.chunkId,
chunkText: docsEmbeddings.chunkText,
sourceDocument: docsEmbeddings.sourceDocument,
sourceLink: docsEmbeddings.sourceLink,
headerText: docsEmbeddings.headerText,
headerLevel: docsEmbeddings.headerLevel,
similarity: sql<number>`ts_rank(${docsEmbeddings.chunkTextTsv}, plainto_tsquery(${tsConfig}, ${query}))`,
searchType: sql<string>`'keyword'`,
})
.from(docsEmbeddings)
.where(sql`${docsEmbeddings.chunkTextTsv} @@ plainto_tsquery(${tsConfig}, ${query})`)
.orderBy(
sql`ts_rank(${docsEmbeddings.chunkTextTsv}, plainto_tsquery(${tsConfig}, ${query})) DESC`
)
.limit(candidateLimit)
const knownLocales = ['en', 'es', 'fr', 'de', 'ja', 'zh']
const vectorRankMap = new Map<string, number>()
vectorResults.forEach((r, idx) => vectorRankMap.set(r.chunkId, idx + 1))
const keywordRankMap = new Map<string, number>()
keywordResults.forEach((r, idx) => keywordRankMap.set(r.chunkId, idx + 1))
const allChunkIds = new Set([
...vectorResults.map((r) => r.chunkId),
...keywordResults.map((r) => r.chunkId),
])
const k = 60
type ResultWithRRF = (typeof vectorResults)[0] & { rrfScore: number }
const scoredResults: ResultWithRRF[] = []
for (const chunkId of allChunkIds) {
const vectorRank = vectorRankMap.get(chunkId) ?? Number.POSITIVE_INFINITY
const keywordRank = keywordRankMap.get(chunkId) ?? Number.POSITIVE_INFINITY
const rrfScore = 1 / (k + vectorRank) + 1 / (k + keywordRank)
const result =
vectorResults.find((r) => r.chunkId === chunkId) ||
keywordResults.find((r) => r.chunkId === chunkId)
if (result) {
scoredResults.push({ ...result, rrfScore })
}
}
scoredResults.sort((a, b) => b.rrfScore - a.rrfScore)
const localeFilteredResults = scoredResults.filter((result) => {
const firstPart = result.sourceDocument.split('/')[0]
if (knownLocales.includes(firstPart)) {
return firstPart === locale
}
return locale === 'en'
})
const queryLower = query.toLowerCase()
const getTitleBoost = (result: ResultWithRRF): number => {
const fileName = result.sourceDocument
.replace('.mdx', '')
.split('/')
.pop()
?.toLowerCase()
?.replace(/_/g, ' ')
if (fileName === queryLower) return 0.01
if (fileName?.includes(queryLower)) return 0.005
return 0
}
localeFilteredResults.sort((a, b) => {
return b.rrfScore + getTitleBoost(b) - (a.rrfScore + getTitleBoost(a))
})
const pageMap = new Map<string, ResultWithRRF>()
for (const result of localeFilteredResults) {
const pageKey = result.sourceDocument
const existing = pageMap.get(pageKey)
if (!existing || result.rrfScore > existing.rrfScore) {
pageMap.set(pageKey, result)
}
}
const deduplicatedResults = Array.from(pageMap.values())
.sort((a, b) => b.rrfScore + getTitleBoost(b) - (a.rrfScore + getTitleBoost(a)))
.slice(0, limit)
const searchResults = deduplicatedResults.map((result) => {
const title = result.headerText || result.sourceDocument.replace('.mdx', '')
const pathParts = result.sourceDocument
.replace('.mdx', '')
.split('/')
.filter((part) => part !== 'index' && !knownLocales.includes(part))
.map((part) => {
return part
.replace(/_/g, ' ')
.split(' ')
.map((word) => {
const acronyms = [
'api',
'mcp',
'sdk',
'url',
'http',
'json',
'xml',
'html',
'css',
'ai',
]
if (acronyms.includes(word.toLowerCase())) {
return word.toUpperCase()
}
return word.charAt(0).toUpperCase() + word.slice(1)
})
.join(' ')
})
return {
id: result.chunkId,
type: 'page' as const,
url: result.sourceLink,
content: title,
breadcrumbs: pathParts,
}
})
return NextResponse.json(searchResults)
} catch (error) {
console.error('Semantic search error:', error)
return NextResponse.json([])
}
}

View File

@@ -9,20 +9,11 @@ body {
} }
@theme { @theme {
--color-fd-primary: #33c482; /* Green from Sim logo */ --color-fd-primary: #802fff; /* Purple from control-bar component */
--font-geist-sans: var(--font-geist-sans); --font-geist-sans: var(--font-geist-sans);
--font-geist-mono: var(--font-geist-mono); --font-geist-mono: var(--font-geist-mono);
} }
/* Ensure primary color is set in both light and dark modes */
:root {
--color-fd-primary: #33c482;
}
.dark {
--color-fd-primary: #33c482;
}
/* Font family utilities */ /* Font family utilities */
.font-sans { .font-sans {
font-family: var(--font-geist-sans), ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, font-family: var(--font-geist-sans), ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
@@ -43,7 +34,7 @@ body {
:root { :root {
--fd-border: transparent !important; --fd-border: transparent !important;
--fd-border-sidebar: transparent !important; --fd-border-sidebar: transparent !important;
--fd-nav-height: 65px; /* Custom navbar height (h-16 = 64px + 1px border) */ --fd-nav-height: 64px; /* Custom navbar height (h-16 = 4rem = 64px) */
/* Content container width used to center main content */ /* Content container width used to center main content */
--spacing-fd-container: 1400px; --spacing-fd-container: 1400px;
/* Edge gutter = leftover space on each side of centered container */ /* Edge gutter = leftover space on each side of centered container */
@@ -128,28 +119,15 @@ aside#nd-sidebar {
} }
} }
/* Hide TOC popover on tablet/medium screens (768px - 1279px) */
/* Keeps it visible on mobile (<768px) for easy navigation */
/* Desktop (>=1280px) already hides it via fumadocs xl:hidden */
@media (min-width: 768px) and (max-width: 1279px) {
#nd-docs-layout {
--fd-toc-popover-height: 0px !important;
}
[data-toc-popover] {
display: none !important;
}
}
/* Desktop only: Apply custom navbar offset, sidebar width and margin offsets */ /* Desktop only: Apply custom navbar offset, sidebar width and margin offsets */
/* On mobile, let fumadocs handle the layout natively */ /* On mobile, let fumadocs handle the layout natively */
@media (min-width: 1024px) { @media (min-width: 1024px) {
:root { :root {
--fd-banner-height: 65px !important; /* 64px navbar + 1px border */ --fd-banner-height: 64px !important;
} }
#nd-docs-layout { #nd-docs-layout {
--fd-docs-height: calc(100dvh - 65px) !important; /* 64px navbar + 1px border */ --fd-docs-height: calc(100dvh - 64px) !important;
--fd-sidebar-width: 300px !important; --fd-sidebar-width: 300px !important;
margin-left: var(--sidebar-offset) !important; margin-left: var(--sidebar-offset) !important;
margin-right: var(--toc-offset) !important; margin-right: var(--toc-offset) !important;
@@ -236,19 +214,19 @@ html:not(.dark) #nd-sidebar button:not([aria-label*="ollapse"]):not([aria-label*
letter-spacing: 0.05em !important; letter-spacing: 0.05em !important;
} }
/* Override active state */ /* Override active state (NO PURPLE) */
#nd-sidebar a[data-active="true"], #nd-sidebar a[data-active="true"],
#nd-sidebar button[data-active="true"], #nd-sidebar button[data-active="true"],
#nd-sidebar a.bg-fd-primary\/10, #nd-sidebar a.bg-fd-primary\/10,
#nd-sidebar a.text-fd-primary, #nd-sidebar a.text-fd-primary,
#nd-sidebar a[class*="bg-fd-primary"], #nd-sidebar a[class*="bg-fd-primary"],
#nd-sidebar a[class*="text-fd-primary"], #nd-sidebar a[class*="text-fd-primary"],
/* Override custom sidebar green classes */ /* Override custom sidebar purple classes */
#nd-sidebar #nd-sidebar
a.bg-emerald-50\/80, a.bg-purple-50\/80,
#nd-sidebar a.text-emerald-600, #nd-sidebar a.text-purple-600,
#nd-sidebar a[class*="bg-emerald"], #nd-sidebar a[class*="bg-purple"],
#nd-sidebar a[class*="text-emerald"] { #nd-sidebar a[class*="text-purple"] {
background-image: none !important; background-image: none !important;
} }
@@ -259,10 +237,10 @@ html.dark #nd-sidebar a.bg-fd-primary\/10,
html.dark #nd-sidebar a.text-fd-primary, html.dark #nd-sidebar a.text-fd-primary,
html.dark #nd-sidebar a[class*="bg-fd-primary"], html.dark #nd-sidebar a[class*="bg-fd-primary"],
html.dark #nd-sidebar a[class*="text-fd-primary"], html.dark #nd-sidebar a[class*="text-fd-primary"],
html.dark #nd-sidebar a.bg-emerald-50\/80, html.dark #nd-sidebar a.bg-purple-50\/80,
html.dark #nd-sidebar a.text-emerald-600, html.dark #nd-sidebar a.text-purple-600,
html.dark #nd-sidebar a[class*="bg-emerald"], html.dark #nd-sidebar a[class*="bg-purple"],
html.dark #nd-sidebar a[class*="text-emerald"] { html.dark #nd-sidebar a[class*="text-purple"] {
background-color: rgba(255, 255, 255, 0.15) !important; background-color: rgba(255, 255, 255, 0.15) !important;
color: rgba(255, 255, 255, 1) !important; color: rgba(255, 255, 255, 1) !important;
} }
@@ -274,10 +252,10 @@ html:not(.dark) #nd-sidebar a.bg-fd-primary\/10,
html:not(.dark) #nd-sidebar a.text-fd-primary, html:not(.dark) #nd-sidebar a.text-fd-primary,
html:not(.dark) #nd-sidebar a[class*="bg-fd-primary"], html:not(.dark) #nd-sidebar a[class*="bg-fd-primary"],
html:not(.dark) #nd-sidebar a[class*="text-fd-primary"], html:not(.dark) #nd-sidebar a[class*="text-fd-primary"],
html:not(.dark) #nd-sidebar a.bg-emerald-50\/80, html:not(.dark) #nd-sidebar a.bg-purple-50\/80,
html:not(.dark) #nd-sidebar a.text-emerald-600, html:not(.dark) #nd-sidebar a.text-purple-600,
html:not(.dark) #nd-sidebar a[class*="bg-emerald"], html:not(.dark) #nd-sidebar a[class*="bg-purple"],
html:not(.dark) #nd-sidebar a[class*="text-emerald"] { html:not(.dark) #nd-sidebar a[class*="text-purple"] {
background-color: rgba(0, 0, 0, 0.07) !important; background-color: rgba(0, 0, 0, 0.07) !important;
color: rgba(0, 0, 0, 0.9) !important; color: rgba(0, 0, 0, 0.9) !important;
} }
@@ -295,8 +273,8 @@ html:not(.dark) #nd-sidebar button:hover:not([data-active="true"]) {
} }
/* Dark mode - ensure active/selected items don't change on hover */ /* Dark mode - ensure active/selected items don't change on hover */
html.dark #nd-sidebar a.bg-emerald-50\/80:hover, html.dark #nd-sidebar a.bg-purple-50\/80:hover,
html.dark #nd-sidebar a[class*="bg-emerald"]:hover, html.dark #nd-sidebar a[class*="bg-purple"]:hover,
html.dark #nd-sidebar a[data-active="true"]:hover, html.dark #nd-sidebar a[data-active="true"]:hover,
html.dark #nd-sidebar button[data-active="true"]:hover { html.dark #nd-sidebar button[data-active="true"]:hover {
background-color: rgba(255, 255, 255, 0.15) !important; background-color: rgba(255, 255, 255, 0.15) !important;
@@ -304,8 +282,8 @@ html.dark #nd-sidebar button[data-active="true"]:hover {
} }
/* Light mode - ensure active/selected items don't change on hover */ /* Light mode - ensure active/selected items don't change on hover */
html:not(.dark) #nd-sidebar a.bg-emerald-50\/80:hover, html:not(.dark) #nd-sidebar a.bg-purple-50\/80:hover,
html:not(.dark) #nd-sidebar a[class*="bg-emerald"]:hover, html:not(.dark) #nd-sidebar a[class*="bg-purple"]:hover,
html:not(.dark) #nd-sidebar a[data-active="true"]:hover, html:not(.dark) #nd-sidebar a[data-active="true"]:hover,
html:not(.dark) #nd-sidebar button[data-active="true"]:hover { html:not(.dark) #nd-sidebar button[data-active="true"]:hover {
background-color: rgba(0, 0, 0, 0.07) !important; background-color: rgba(0, 0, 0, 0.07) !important;
@@ -377,24 +355,16 @@ aside[data-sidebar] > *:not([data-sidebar-viewport]) {
button[aria-label="Toggle Sidebar"], button[aria-label="Toggle Sidebar"],
button[aria-label="Collapse Sidebar"], button[aria-label="Collapse Sidebar"],
/* Hide nav title/logo in sidebar on desktop - target all possible locations */ /* Hide nav title/logo in sidebar on desktop - target all possible locations */
/* Lower specificity selectors first (attribute selectors) */
[data-sidebar-header],
[data-sidebar] [data-title],
aside[data-sidebar] a[href="/"], aside[data-sidebar] a[href="/"],
aside[data-sidebar] a[href="/"] img, aside[data-sidebar] a[href="/"] img,
aside[data-sidebar] > a:first-child, aside[data-sidebar] > a:first-child,
aside[data-sidebar] > div > a:first-child, aside[data-sidebar] > div > a:first-child,
aside[data-sidebar] img[alt="Sim"], aside[data-sidebar] img[alt="Sim"],
aside[data-sidebar] svg[aria-label="Sim"], [data-sidebar-header],
/* Higher specificity selectors (ID selectors) */ [data-sidebar] [data-title],
#nd-sidebar
a[href="/"],
#nd-sidebar a[href="/"] img,
#nd-sidebar a[href="/"] svg,
#nd-sidebar > a:first-child, #nd-sidebar > a:first-child,
#nd-sidebar > div:first-child > a:first-child, #nd-sidebar > div:first-child > a:first-child,
#nd-sidebar img[alt="Sim"], #nd-sidebar img[alt="Sim"],
#nd-sidebar svg[aria-label="Sim"],
/* Hide theme toggle at bottom of sidebar on desktop */ /* Hide theme toggle at bottom of sidebar on desktop */
#nd-sidebar #nd-sidebar
> footer, > footer,
@@ -532,15 +502,6 @@ pre code .line {
color: var(--color-fd-primary); color: var(--color-fd-primary);
} }
/* ============================================
TOC (Table of Contents) Styling
============================================ */
/* Remove the thin border-left on nested TOC items (keeps main indicator only) */
#nd-toc a[style*="padding-inline-start"] {
border-left: none !important;
}
/* Add bottom spacing to prevent abrupt page endings */ /* Add bottom spacing to prevent abrupt page endings */
[data-content] { [data-content] {
padding-top: 1.5rem !important; padding-top: 1.5rem !important;

View File

@@ -58,7 +58,7 @@ export const metadata = {
'Comprehensive documentation for Sim - the visual workflow builder for AI applications. Create powerful AI agents, automation workflows, and data processing pipelines.', 'Comprehensive documentation for Sim - the visual workflow builder for AI applications. Create powerful AI agents, automation workflows, and data processing pipelines.',
images: [ images: [
{ {
url: 'https://docs.sim.ai/api/og?title=Sim%20Documentation', url: 'https://docs.sim.ai/api/og?title=Sim%20Documentation&category=DOCUMENTATION',
width: 1200, width: 1200,
height: 630, height: 630,
alt: 'Sim Documentation', alt: 'Sim Documentation',
@@ -72,7 +72,7 @@ export const metadata = {
'Comprehensive documentation for Sim - the visual workflow builder for AI applications.', 'Comprehensive documentation for Sim - the visual workflow builder for AI applications.',
creator: '@simdotai', creator: '@simdotai',
site: '@simdotai', site: '@simdotai',
images: ['https://docs.sim.ai/api/og?title=Sim%20Documentation'], images: ['https://docs.sim.ai/api/og?title=Sim%20Documentation&category=DOCUMENTATION'],
}, },
robots: { robots: {
index: true, index: true,

View File

@@ -6,10 +6,7 @@ import { source } from '@/lib/source'
export const revalidate = false export const revalidate = false
export async function GET( export async function GET(_req: NextRequest, { params }: { params: Promise<{ slug?: string[] }> }) {
_request: NextRequest,
{ params }: { params: Promise<{ slug?: string[] }> }
) {
const { slug } = await params const { slug } = await params
let lang: (typeof i18n.languages)[number] = i18n.defaultLanguage let lang: (typeof i18n.languages)[number] = i18n.defaultLanguage

View File

@@ -44,7 +44,7 @@ export function SidebarItem({ item }: { item: Item }) {
'lg:text-gray-600 lg:dark:text-gray-400', 'lg:text-gray-600 lg:dark:text-gray-400',
!active && 'lg:hover:bg-gray-100/60 lg:dark:hover:bg-gray-800/40', !active && 'lg:hover:bg-gray-100/60 lg:dark:hover:bg-gray-800/40',
active && active &&
'lg:bg-emerald-50/80 lg:font-normal lg:text-emerald-600 lg:dark:bg-emerald-900/15 lg:dark:text-emerald-400' 'lg:bg-purple-50/80 lg:font-normal lg:text-purple-600 lg:dark:bg-purple-900/15 lg:dark:text-purple-400'
)} )}
> >
{item.name} {item.name}
@@ -79,7 +79,7 @@ export function SidebarFolder({ item, children }: { item: Folder; children: Reac
'lg:text-gray-600 lg:dark:text-gray-400', 'lg:text-gray-600 lg:dark:text-gray-400',
!active && 'lg:hover:bg-gray-100/60 lg:dark:hover:bg-gray-800/40', !active && 'lg:hover:bg-gray-100/60 lg:dark:hover:bg-gray-800/40',
active && active &&
'lg:bg-emerald-50/80 lg:font-normal lg:text-emerald-600 lg:dark:bg-emerald-900/15 lg:dark:text-emerald-400' 'lg:bg-purple-50/80 lg:font-normal lg:text-purple-600 lg:dark:bg-purple-900/15 lg:dark:text-purple-400'
)} )}
> >
{item.name} {item.name}
@@ -104,7 +104,7 @@ export function SidebarFolder({ item, children }: { item: Folder; children: Reac
'lg:text-gray-800 lg:dark:text-gray-200', 'lg:text-gray-800 lg:dark:text-gray-200',
!active && 'lg:hover:bg-gray-100/60 lg:dark:hover:bg-gray-800/40', !active && 'lg:hover:bg-gray-100/60 lg:dark:hover:bg-gray-800/40',
active && active &&
'lg:bg-emerald-50/80 lg:text-emerald-600 lg:dark:bg-emerald-900/15 lg:dark:text-emerald-400' 'lg:bg-purple-50/80 lg:text-purple-600 lg:dark:bg-purple-900/15 lg:dark:text-purple-400'
)} )}
> >
{item.name} {item.name}

View File

@@ -23,7 +23,7 @@ export function TOCFooter() {
rel='noopener noreferrer' rel='noopener noreferrer'
onMouseEnter={() => setIsHovered(true)} onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)} onMouseLeave={() => setIsHovered(false)}
className='group mt-2 inline-flex h-8 w-fit items-center justify-center gap-1 whitespace-nowrap rounded-[10px] border border-[#2AAD6C] bg-gradient-to-b from-[#3ED990] to-[#2AAD6C] px-3 pr-[10px] pl-[12px] font-medium text-sm text-white shadow-[inset_0_2px_4px_0_#5EE8A8] outline-none transition-all hover:shadow-lg focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50' className='group mt-2 inline-flex h-8 w-fit items-center justify-center gap-1 whitespace-nowrap rounded-[10px] border border-[#6F3DFA] bg-gradient-to-b from-[#8357FF] to-[#6F3DFA] px-3 pr-[10px] pl-[12px] font-medium text-sm text-white shadow-[inset_0_2px_4px_0_#9B77FF] outline-none transition-all hover:shadow-lg focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50'
aria-label='Get started with Sim - Sign up for free' aria-label='Get started with Sim - Sign up for free'
> >
<span>Get started</span> <span>Get started</span>

File diff suppressed because one or more lines are too long

View File

@@ -1,14 +1,20 @@
'use client' 'use client'
import Image from 'next/image'
import Link from 'next/link' import Link from 'next/link'
import { LanguageDropdown } from '@/components/ui/language-dropdown' import { LanguageDropdown } from '@/components/ui/language-dropdown'
import { SearchTrigger } from '@/components/ui/search-trigger' import { SearchTrigger } from '@/components/ui/search-trigger'
import { SimLogoFull } from '@/components/ui/sim-logo'
import { ThemeToggle } from '@/components/ui/theme-toggle' import { ThemeToggle } from '@/components/ui/theme-toggle'
export function Navbar() { export function Navbar() {
return ( return (
<nav className='sticky top-0 z-50 border-border/50 border-b bg-background/80 backdrop-blur-md backdrop-saturate-150'> <nav
className='sticky top-0 z-50 border-border/50 border-b'
style={{
backdropFilter: 'blur(25px) saturate(180%)',
WebkitBackdropFilter: 'blur(25px) saturate(180%)',
}}
>
{/* Desktop: Single row layout */} {/* Desktop: Single row layout */}
<div className='hidden h-16 w-full items-center lg:flex'> <div className='hidden h-16 w-full items-center lg:flex'>
<div <div
@@ -21,7 +27,13 @@ export function Navbar() {
{/* Left cluster: logo */} {/* Left cluster: logo */}
<div className='flex items-center'> <div className='flex items-center'>
<Link href='/' className='flex min-w-[100px] items-center'> <Link href='/' className='flex min-w-[100px] items-center'>
<SimLogoFull className='h-7 w-auto' /> <Image
src='/static/logo.png'
alt='Sim'
width={72}
height={28}
className='h-7 w-auto'
/>
</Link> </Link>
</div> </div>

View File

@@ -1,13 +1,45 @@
'use client' 'use client'
import { useState } from 'react'
import { useCopyButton } from 'fumadocs-ui/utils/use-copy-button' import { useCopyButton } from 'fumadocs-ui/utils/use-copy-button'
import { Check, Copy } from 'lucide-react' import { Check, Copy } from 'lucide-react'
export function LLMCopyButton({ content }: { content: string }) { const cache = new Map<string, string>()
const [checked, onClick] = useCopyButton(() => navigator.clipboard.writeText(content))
export function LLMCopyButton({
markdownUrl,
}: {
/**
* A URL to fetch the raw Markdown/MDX content of page
*/
markdownUrl: string
}) {
const [isLoading, setLoading] = useState(false)
const [checked, onClick] = useCopyButton(async () => {
const cached = cache.get(markdownUrl)
if (cached) return navigator.clipboard.writeText(cached)
setLoading(true)
try {
await navigator.clipboard.write([
new ClipboardItem({
'text/plain': fetch(markdownUrl).then(async (res) => {
const content = await res.text()
cache.set(markdownUrl, content)
return content
}),
}),
])
} finally {
setLoading(false)
}
})
return ( return (
<button <button
disabled={isLoading}
onClick={onClick} onClick={onClick}
className='flex cursor-pointer items-center gap-1.5 rounded-lg border border-border/40 bg-background px-2.5 py-2 text-muted-foreground/60 text-sm leading-none transition-all hover:border-border hover:bg-accent/50 hover:text-muted-foreground' className='flex cursor-pointer items-center gap-1.5 rounded-lg border border-border/40 bg-background px-2.5 py-2 text-muted-foreground/60 text-sm leading-none transition-all hover:border-border hover:bg-accent/50 hover:text-muted-foreground'
aria-label={checked ? 'Copied to clipboard' : 'Copy page content'} aria-label={checked ? 'Copied to clipboard' : 'Copy page content'}

View File

@@ -1,87 +0,0 @@
'use client'
import { useState } from 'react'
import { cn, getAssetUrl } from '@/lib/utils'
import { Lightbox } from './lightbox'
interface ActionImageProps {
src: string
alt: string
enableLightbox?: boolean
}
interface ActionVideoProps {
src: string
alt: string
enableLightbox?: boolean
}
export function ActionImage({ src, alt, enableLightbox = true }: ActionImageProps) {
const [isLightboxOpen, setIsLightboxOpen] = useState(false)
const handleClick = () => {
if (enableLightbox) {
setIsLightboxOpen(true)
}
}
return (
<>
<img
src={src}
alt={alt}
onClick={handleClick}
className={cn(
'inline-block w-full max-w-[200px] rounded border border-neutral-200 dark:border-neutral-700',
enableLightbox && 'cursor-pointer transition-opacity hover:opacity-90'
)}
/>
{enableLightbox && (
<Lightbox
isOpen={isLightboxOpen}
onClose={() => setIsLightboxOpen(false)}
src={src}
alt={alt}
type='image'
/>
)}
</>
)
}
export function ActionVideo({ src, alt, enableLightbox = true }: ActionVideoProps) {
const [isLightboxOpen, setIsLightboxOpen] = useState(false)
const resolvedSrc = getAssetUrl(src)
const handleClick = () => {
if (enableLightbox) {
setIsLightboxOpen(true)
}
}
return (
<>
<video
src={resolvedSrc}
autoPlay
loop
muted
playsInline
onClick={handleClick}
className={cn(
'inline-block w-full max-w-[200px] rounded border border-neutral-200 dark:border-neutral-700',
enableLightbox && 'cursor-pointer transition-opacity hover:opacity-90'
)}
/>
{enableLightbox && (
<Lightbox
isOpen={isLightboxOpen}
onClose={() => setIsLightboxOpen(false)}
src={src}
alt={alt}
type='video'
/>
)}
</>
)
}

View File

@@ -17,16 +17,23 @@ export function CodeBlock(props: React.ComponentProps<typeof FumadocsCodeBlock>)
return ( return (
<FumadocsCodeBlock <FumadocsCodeBlock
{...props} {...props}
Actions={({ className }) => ( Actions={({ children, className }) => (
<div className={cn('empty:hidden', className)}> <div className={cn('empty:hidden', className)}>
{/* Custom copy button */}
<button <button
type='button' type='button'
aria-label={copied ? 'Copied Text' : 'Copy Text'} aria-label={copied ? 'Copied Text' : 'Copy Text'}
onClick={(e) => { onClick={(e) => {
const pre = (e.currentTarget as HTMLElement).closest('figure')?.querySelector('pre') const pre = (e.currentTarget as HTMLElement)
.closest('.nd-codeblock')
?.querySelector('pre')
if (pre) handleCopy(pre.textContent || '') if (pre) handleCopy(pre.textContent || '')
}} }}
className='cursor-pointer rounded-md p-2 text-muted-foreground transition-colors hover:text-foreground' className={cn(
'cursor-pointer rounded-md p-2 transition-all',
'border border-border bg-background/80 hover:bg-muted',
'backdrop-blur-sm'
)}
> >
<span className='flex items-center justify-center'> <span className='flex items-center justify-center'>
{copied ? ( {copied ? (

View File

@@ -4,54 +4,42 @@
import type { ComponentType, SVGProps } from 'react' import type { ComponentType, SVGProps } from 'react'
import { import {
A2AIcon,
AhrefsIcon, AhrefsIcon,
AirtableIcon, AirtableIcon,
AirweaveIcon,
ApifyIcon, ApifyIcon,
ApolloIcon, ApolloIcon,
ArxivIcon, ArxivIcon,
AsanaIcon, AsanaIcon,
BrainIcon, BrainIcon,
BrowserUseIcon, BrowserUseIcon,
CalComIcon,
CalendlyIcon, CalendlyIcon,
CirclebackIcon,
ClayIcon, ClayIcon,
ClerkIcon,
ConfluenceIcon, ConfluenceIcon,
CursorIcon, CursorIcon,
DatadogIcon, DatadogIcon,
DiscordIcon, DiscordIcon,
DocumentIcon, DocumentIcon,
DropboxIcon, DropboxIcon,
DsPyIcon,
DuckDuckGoIcon, DuckDuckGoIcon,
DynamoDBIcon, DynamoDBIcon,
ElasticsearchIcon, ElasticsearchIcon,
ElevenLabsIcon, ElevenLabsIcon,
EnrichSoIcon,
ExaAIIcon, ExaAIIcon,
EyeIcon, EyeIcon,
FirecrawlIcon, FirecrawlIcon,
FirefliesIcon,
GithubIcon, GithubIcon,
GitLabIcon, GitLabIcon,
GmailIcon, GmailIcon,
GoogleBooksIcon,
GoogleCalendarIcon, GoogleCalendarIcon,
GoogleDocsIcon, GoogleDocsIcon,
GoogleDriveIcon, GoogleDriveIcon,
GoogleFormsIcon, GoogleFormsIcon,
GoogleGroupsIcon, GoogleGroupsIcon,
GoogleIcon, GoogleIcon,
GoogleMapsIcon,
GoogleSheetsIcon, GoogleSheetsIcon,
GoogleSlidesIcon, GoogleSlidesIcon,
GoogleVaultIcon, GoogleVaultIcon,
GrafanaIcon, GrafanaIcon,
GrainIcon,
GreptileIcon,
HubspotIcon, HubspotIcon,
HuggingFaceIcon, HuggingFaceIcon,
HunterIOIcon, HunterIOIcon,
@@ -60,16 +48,12 @@ import {
IntercomIcon, IntercomIcon,
JinaAIIcon, JinaAIIcon,
JiraIcon, JiraIcon,
JiraServiceManagementIcon,
KalshiIcon, KalshiIcon,
LangsmithIcon,
LemlistIcon,
LinearIcon, LinearIcon,
LinkedInIcon, LinkedInIcon,
LinkupIcon, LinkupIcon,
MailchimpIcon, MailchimpIcon,
MailgunIcon, MailgunIcon,
MailServerIcon,
Mem0Icon, Mem0Icon,
MicrosoftExcelIcon, MicrosoftExcelIcon,
MicrosoftOneDriveIcon, MicrosoftOneDriveIcon,
@@ -81,7 +65,6 @@ import {
MySQLIcon, MySQLIcon,
Neo4jIcon, Neo4jIcon,
NotionIcon, NotionIcon,
OnePasswordIcon,
OpenAIIcon, OpenAIIcon,
OutlookIcon, OutlookIcon,
PackageSearchIcon, PackageSearchIcon,
@@ -92,11 +75,9 @@ import {
PolymarketIcon, PolymarketIcon,
PostgresIcon, PostgresIcon,
PosthogIcon, PosthogIcon,
PulseIcon,
QdrantIcon, QdrantIcon,
RDSIcon, RDSIcon,
RedditIcon, RedditIcon,
ReductoIcon,
ResendIcon, ResendIcon,
S3Icon, S3Icon,
SalesforceIcon, SalesforceIcon,
@@ -104,12 +85,11 @@ import {
SendgridIcon, SendgridIcon,
SentryIcon, SentryIcon,
SerperIcon, SerperIcon,
ServiceNowIcon,
SftpIcon, SftpIcon,
ShopifyIcon, ShopifyIcon,
SimilarwebIcon,
SlackIcon, SlackIcon,
SmtpIcon, SmtpIcon,
SpotifyIcon,
SQSIcon, SQSIcon,
SshIcon, SshIcon,
STTIcon, STTIcon,
@@ -118,8 +98,6 @@ import {
SupabaseIcon, SupabaseIcon,
TavilyIcon, TavilyIcon,
TelegramIcon, TelegramIcon,
TextractIcon,
TinybirdIcon,
TranslateIcon, TranslateIcon,
TrelloIcon, TrelloIcon,
TTSIcon, TTSIcon,
@@ -141,137 +119,116 @@ import {
type IconComponent = ComponentType<SVGProps<SVGSVGElement>> type IconComponent = ComponentType<SVGProps<SVGSVGElement>>
export const blockTypeToIconMap: Record<string, IconComponent> = { export const blockTypeToIconMap: Record<string, IconComponent> = {
a2a: A2AIcon,
ahrefs: AhrefsIcon,
airtable: AirtableIcon,
airweave: AirweaveIcon,
apify: ApifyIcon,
apollo: ApolloIcon,
arxiv: ArxivIcon,
asana: AsanaIcon,
browser_use: BrowserUseIcon,
calcom: CalComIcon,
calendly: CalendlyIcon,
circleback: CirclebackIcon,
clay: ClayIcon,
clerk: ClerkIcon,
confluence_v2: ConfluenceIcon,
cursor_v2: CursorIcon,
datadog: DatadogIcon,
discord: DiscordIcon,
dropbox: DropboxIcon,
dspy: DsPyIcon,
duckduckgo: DuckDuckGoIcon,
dynamodb: DynamoDBIcon,
elasticsearch: ElasticsearchIcon,
elevenlabs: ElevenLabsIcon,
enrich: EnrichSoIcon,
exa: ExaAIIcon,
file_v3: DocumentIcon,
firecrawl: FirecrawlIcon,
fireflies_v2: FirefliesIcon,
github_v2: GithubIcon,
gitlab: GitLabIcon,
gmail_v2: GmailIcon,
google_books: GoogleBooksIcon,
google_calendar_v2: GoogleCalendarIcon,
google_docs: GoogleDocsIcon,
google_drive: GoogleDriveIcon,
google_forms: GoogleFormsIcon,
google_groups: GoogleGroupsIcon,
google_maps: GoogleMapsIcon,
google_search: GoogleIcon,
google_sheets_v2: GoogleSheetsIcon,
google_slides_v2: GoogleSlidesIcon,
google_vault: GoogleVaultIcon,
grafana: GrafanaIcon,
grain: GrainIcon,
greptile: GreptileIcon,
hubspot: HubspotIcon,
huggingface: HuggingFaceIcon,
hunter: HunterIOIcon,
image_generator: ImageIcon,
imap: MailServerIcon,
incidentio: IncidentioIcon,
intercom_v2: IntercomIcon,
jina: JinaAIIcon,
jira: JiraIcon,
jira_service_management: JiraServiceManagementIcon,
kalshi_v2: KalshiIcon,
knowledge: PackageSearchIcon,
langsmith: LangsmithIcon,
lemlist: LemlistIcon,
linear: LinearIcon,
linkedin: LinkedInIcon,
linkup: LinkupIcon,
mailchimp: MailchimpIcon,
mailgun: MailgunIcon,
mem0: Mem0Icon,
memory: BrainIcon,
microsoft_excel_v2: MicrosoftExcelIcon,
microsoft_planner: MicrosoftPlannerIcon,
microsoft_teams: MicrosoftTeamsIcon,
mistral_parse_v3: MistralIcon,
mongodb: MongoDBIcon,
mysql: MySQLIcon,
neo4j: Neo4jIcon,
notion_v2: NotionIcon,
onedrive: MicrosoftOneDriveIcon,
onepassword: OnePasswordIcon,
openai: OpenAIIcon,
outlook: OutlookIcon,
parallel_ai: ParallelIcon,
perplexity: PerplexityIcon,
pinecone: PineconeIcon,
pipedrive: PipedriveIcon,
polymarket: PolymarketIcon,
postgresql: PostgresIcon,
posthog: PosthogIcon,
pulse_v2: PulseIcon,
qdrant: QdrantIcon,
rds: RDSIcon,
reddit: RedditIcon,
reducto_v2: ReductoIcon,
resend: ResendIcon,
s3: S3Icon,
salesforce: SalesforceIcon,
search: SearchIcon,
sendgrid: SendgridIcon,
sentry: SentryIcon,
serper: SerperIcon,
servicenow: ServiceNowIcon,
sftp: SftpIcon,
sharepoint: MicrosoftSharepointIcon,
shopify: ShopifyIcon,
similarweb: SimilarwebIcon,
slack: SlackIcon,
smtp: SmtpIcon,
sqs: SQSIcon,
ssh: SshIcon,
stagehand: StagehandIcon,
stripe: StripeIcon,
stt_v2: STTIcon,
supabase: SupabaseIcon,
tavily: TavilyIcon,
telegram: TelegramIcon,
textract_v2: TextractIcon,
tinybird: TinybirdIcon,
translate: TranslateIcon,
trello: TrelloIcon,
tts: TTSIcon,
twilio_sms: TwilioIcon,
twilio_voice: TwilioIcon,
typeform: TypeformIcon,
video_generator_v2: VideoIcon,
vision_v2: EyeIcon,
wealthbox: WealthboxIcon,
webflow: WebflowIcon,
whatsapp: WhatsAppIcon,
wikipedia: WikipediaIcon,
wordpress: WordpressIcon,
x: xIcon,
youtube: YouTubeIcon,
zendesk: ZendeskIcon,
zep: ZepIcon,
zoom: ZoomIcon, 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,
} }

View File

@@ -1,9 +1,8 @@
'use client' 'use client'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { Check, ChevronDown } from 'lucide-react' import { Check, ChevronRight } from 'lucide-react'
import { useParams, usePathname, useRouter } from 'next/navigation' import { useParams, usePathname, useRouter } from 'next/navigation'
import { cn } from '@/lib/utils'
const languages = { const languages = {
en: { name: 'English', flag: '🇺🇸' }, en: { name: 'English', flag: '🇺🇸' },
@@ -16,7 +15,6 @@ const languages = {
export function LanguageDropdown() { export function LanguageDropdown() {
const [isOpen, setIsOpen] = useState(false) const [isOpen, setIsOpen] = useState(false)
const [hoveredIndex, setHoveredIndex] = useState<number>(-1)
const pathname = usePathname() const pathname = usePathname()
const params = useParams() const params = useParams()
const router = useRouter() const router = useRouter()
@@ -73,15 +71,6 @@ export function LanguageDropdown() {
return () => window.removeEventListener('keydown', onKey) return () => window.removeEventListener('keydown', onKey)
}, [isOpen]) }, [isOpen])
// Reset hovered index when popover closes
useEffect(() => {
if (!isOpen) {
setHoveredIndex(-1)
}
}, [isOpen])
const languageEntries = Object.entries(languages)
return ( return (
<div className='relative'> <div className='relative'>
<button <button
@@ -93,14 +82,14 @@ export function LanguageDropdown() {
aria-haspopup='listbox' aria-haspopup='listbox'
aria-expanded={isOpen} aria-expanded={isOpen}
aria-controls='language-menu' aria-controls='language-menu'
className='flex cursor-pointer items-center gap-1.5 rounded-[6px] px-3 py-2 font-normal text-[0.9375rem] text-foreground/60 leading-[1.4] transition-colors hover:bg-foreground/8 hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring' className='flex cursor-pointer items-center gap-1.5 rounded-xl px-3 py-2 font-normal text-[0.9375rem] text-foreground/60 leading-[1.4] transition-colors hover:bg-foreground/8 hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring'
style={{ style={{
fontFamily: fontFamily:
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif', '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
}} }}
> >
<span>{languages[currentLang as keyof typeof languages]?.name}</span> <span>{languages[currentLang as keyof typeof languages]?.name}</span>
<ChevronDown className={cn('h-3.5 w-3.5 transition-transform', isOpen && 'rotate-180')} /> <ChevronRight className='h-3.5 w-3.5' />
</button> </button>
{isOpen && ( {isOpen && (
@@ -109,37 +98,29 @@ export function LanguageDropdown() {
<div <div
id='language-menu' id='language-menu'
role='listbox' role='listbox'
className='absolute top-full right-0 z-[1001] mt-2 max-h-[400px] min-w-[160px] overflow-auto rounded-[6px] bg-white px-[6px] py-[6px] shadow-lg dark:bg-neutral-900' className='absolute top-full right-0 z-[1001] mt-1 max-h-[75vh] w-56 overflow-auto rounded-xl border border-border/50 bg-white shadow-2xl md:w-44 md:bg-background/95 md:backdrop-blur-md dark:bg-neutral-950 md:dark:bg-background/95'
> >
{languageEntries.map(([code, lang], index) => { {Object.entries(languages).map(([code, lang]) => (
const isSelected = currentLang === code <button
const isHovered = hoveredIndex === index key={code}
onClick={(e) => {
return ( e.preventDefault()
<button e.stopPropagation()
key={code} handleLanguageChange(code)
onClick={(e) => { }}
e.preventDefault() role='option'
e.stopPropagation() aria-selected={currentLang === code}
handleLanguageChange(code) className={`flex w-full cursor-pointer items-center gap-3 px-3 py-3 text-base transition-colors first:rounded-t-xl last:rounded-b-xl hover:bg-muted/80 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring md:gap-2 md:px-2.5 md:py-2 md:text-sm ${
}} currentLang === code ? 'bg-muted/60 font-medium text-primary' : 'text-foreground'
onMouseEnter={() => setHoveredIndex(index)} }`}
onMouseLeave={() => setHoveredIndex(-1)} >
role='option' <span className='text-base md:text-sm'>{lang.flag}</span>
aria-selected={isSelected} <span className='leading-none'>{lang.name}</span>
className={cn( {currentLang === code && (
'flex h-[26px] w-full min-w-0 cursor-pointer items-center gap-[8px] rounded-[6px] px-[6px] text-[13px] transition-colors', <Check className='ml-auto h-4 w-4 text-primary md:h-3.5 md:w-3.5' />
'text-neutral-700 dark:text-neutral-200', )}
isHovered && 'bg-neutral-100 dark:bg-neutral-800', </button>
'focus:outline-none' ))}
)}
>
<span className='text-[13px]'>{lang.flag}</span>
<span className='flex-1 text-left leading-none'>{lang.name}</span>
{isSelected && <Check className='ml-auto h-3.5 w-3.5' />}
</button>
)
})}
</div> </div>
</> </>
)} )}

View File

@@ -1,108 +0,0 @@
'use client'
import { cn } from '@/lib/utils'
interface SimLogoProps {
className?: string
}
/**
* Sim logo with icon and text.
* The icon stays green (#33C482), text adapts to light/dark mode.
*/
export function SimLogo({ className }: SimLogoProps) {
return (
<svg
viewBox='720 440 320 320'
fill='none'
xmlns='http://www.w3.org/2000/svg'
className={cn('h-7 w-auto', className)}
aria-label='Sim'
>
{/* Green icon - top left shape with cutout */}
<path
fillRule='evenodd'
clipRule='evenodd'
d='M875.791 577.171C875.791 581.922 873.911 586.483 870.576 589.842L870.098 590.323C866.764 593.692 862.234 595.575 857.517 595.575H750.806C740.978 595.575 733 603.6 733 613.498V728.902C733 738.799 740.978 746.826 750.806 746.826H865.382C875.209 746.826 883.177 738.799 883.177 728.902V620.853C883.177 616.448 884.912 612.222 888.008 609.104C891.093 605.997 895.29 604.249 899.664 604.249H1008.16C1017.99 604.249 1025.96 596.224 1025.96 586.327V470.923C1025.96 461.025 1017.99 453 1008.16 453H893.586C883.759 453 875.791 461.025 875.791 470.923V577.171ZM910.562 477.566H991.178C996.922 477.566 1001.57 482.254 1001.57 488.029V569.22C1001.57 574.995 996.922 579.683 991.178 579.683H910.562C904.828 579.683 900.173 574.995 900.173 569.22V488.029C900.173 482.254 904.828 477.566 910.562 477.566Z'
fill='#33C482'
/>
{/* Green icon - bottom right square */}
<path
d='M1008.3 624.59H923.113C912.786 624.59 904.414 633.022 904.414 643.423V728.171C904.414 738.572 912.786 747.004 923.113 747.004H1008.3C1018.63 747.004 1027 738.572 1027 728.171V643.423C1027 633.022 1018.63 624.59 1008.3 624.59Z'
fill='#33C482'
/>
{/* Gradient overlay on bottom right square */}
<path
d='M1008.3 624.199H923.113C912.786 624.199 904.414 632.631 904.414 643.033V727.78C904.414 738.181 912.786 746.612 923.113 746.612H1008.3C1018.63 746.612 1027 738.181 1027 727.78V643.033C1027 632.631 1018.63 624.199 1008.3 624.199Z'
fill='url(#sim-logo-gradient)'
fillOpacity='0.2'
/>
<defs>
<linearGradient
id='sim-logo-gradient'
x1='904.414'
y1='624.199'
x2='978.836'
y2='698.447'
gradientUnits='userSpaceOnUse'
>
<stop />
<stop offset='1' stopOpacity='0' />
</linearGradient>
</defs>
</svg>
)
}
/**
* Full Sim logo with icon and "Sim" text.
* The icon stays green (#33C482), text adapts to light/dark mode.
*/
export function SimLogoFull({ className }: SimLogoProps) {
return (
<svg
viewBox='720 440 1020 320'
fill='none'
xmlns='http://www.w3.org/2000/svg'
className={cn('h-7 w-auto', className)}
aria-label='Sim'
>
{/* Green icon - top left shape with cutout */}
<path
fillRule='evenodd'
clipRule='evenodd'
d='M875.791 577.171C875.791 581.922 873.911 586.483 870.576 589.842L870.098 590.323C866.764 593.692 862.234 595.575 857.517 595.575H750.806C740.978 595.575 733 603.6 733 613.498V728.902C733 738.799 740.978 746.826 750.806 746.826H865.382C875.209 746.826 883.177 738.799 883.177 728.902V620.853C883.177 616.448 884.912 612.222 888.008 609.104C891.093 605.997 895.29 604.249 899.664 604.249H1008.16C1017.99 604.249 1025.96 596.224 1025.96 586.327V470.923C1025.96 461.025 1017.99 453 1008.16 453H893.586C883.759 453 875.791 461.025 875.791 470.923V577.171ZM910.562 477.566H991.178C996.922 477.566 1001.57 482.254 1001.57 488.029V569.22C1001.57 574.995 996.922 579.683 991.178 579.683H910.562C904.828 579.683 900.173 574.995 900.173 569.22V488.029C900.173 482.254 904.828 477.566 910.562 477.566Z'
fill='#33C482'
/>
{/* Green icon - bottom right square */}
<path
d='M1008.3 624.59H923.113C912.786 624.59 904.414 633.022 904.414 643.423V728.171C904.414 738.572 912.786 747.004 923.113 747.004H1008.3C1018.63 747.004 1027 738.572 1027 728.171V643.423C1027 633.022 1018.63 624.59 1008.3 624.59Z'
fill='#33C482'
/>
{/* Gradient overlay on bottom right square */}
<path
d='M1008.3 624.199H923.113C912.786 624.199 904.414 632.631 904.414 643.033V727.78C904.414 738.181 912.786 746.612 923.113 746.612H1008.3C1018.63 746.612 1027 738.181 1027 727.78V643.033C1027 632.631 1018.63 624.199 1008.3 624.199Z'
fill='url(#sim-logo-full-gradient)'
fillOpacity='0.2'
/>
{/* "Sim" text - adapts to light/dark mode via currentColor */}
<path
d='M1210.54 515.657C1226.65 515.657 1240.59 518.51 1252.31 524.257H1252.31C1264.3 529.995 1273.63 538.014 1280.26 548.319H1280.26C1287.19 558.635 1290.78 570.899 1291.08 585.068L1291.1 586.089H1249.11L1249.09 585.115C1248.8 574.003 1245.18 565.493 1238.32 559.451C1231.45 553.399 1221.79 550.308 1209.21 550.308C1196.3 550.308 1186.48 553.113 1179.61 558.588C1172.76 564.046 1169.33 571.499 1169.33 581.063C1169.33 588.092 1171.88 593.978 1177.01 598.783C1182.17 603.618 1189.99 607.399 1200.56 610.061H1200.56L1238.77 619.451C1257.24 623.65 1271.21 630.571 1280.57 640.293L1281.01 640.739C1290.13 650.171 1294.64 662.97 1294.64 679.016C1294.64 692.923 1290.88 705.205 1283.34 715.822L1283.33 715.834C1275.81 726.134 1265.44 734.14 1252.26 739.866L1252.25 739.871C1239.36 745.302 1224.12 748 1206.54 748C1180.9 748 1160.36 741.696 1145.02 728.984C1129.67 716.258 1122 699.269 1122 678.121V677.121H1163.99V678.121C1163.99 688.869 1167.87 697.367 1175.61 703.722L1176.34 704.284C1184.04 709.997 1194.37 712.902 1207.43 712.902C1222.13 712.902 1233.3 710.087 1241.07 704.588C1248.8 698.812 1252.64 691.21 1252.64 681.699C1252.64 674.769 1250.5 669.057 1246.25 664.49L1246.23 664.478L1246.22 664.464C1242.28 659.929 1234.83 656.119 1223.64 653.152L1185.43 644.208L1185.42 644.204C1166.05 639.407 1151.49 632.035 1141.83 622.012L1141.83 622.006L1141.82 622C1132.43 611.94 1127.78 598.707 1127.78 582.405C1127.78 568.81 1131.23 556.976 1138.17 546.949L1138.18 546.941L1138.19 546.933C1145.41 536.936 1155.18 529.225 1167.48 523.793L1167.48 523.79C1180.07 518.36 1194.43 515.657 1210.54 515.657ZM1323.39 521.979C1331.68 525.008 1337.55 526.482 1343.51 526.482C1349.48 526.482 1355.64 525.005 1364.49 521.973L1365.82 521.52V742.633H1322.05V521.489L1323.39 521.979ZM1642.01 515.657C1667.11 515.657 1686.94 523.031 1701.39 537.876C1715.83 552.716 1723 572.968 1723 598.507V742.633H1680.12V608.794C1680.12 591.666 1675.72 578.681 1667.07 569.681L1667.06 569.669L1667.04 569.656C1658.67 560.359 1647.26 555.675 1632.68 555.675C1622.47 555.675 1613.47 558.022 1605.64 562.69L1605.63 562.696C1598.11 567.064 1592.17 573.475 1587.8 581.968C1583.44 590.448 1581.25 600.424 1581.25 611.925V742.633H1537.92V608.347C1537.92 591.208 1533.67 578.376 1525.31 569.68L1525.31 569.674L1525.3 569.668C1516.93 560.664 1505.52 556.122 1490.93 556.122C1480.72 556.122 1471.72 558.469 1463.89 563.138L1463.88 563.144C1456.36 567.511 1450.41 573.922 1446.05 582.415L1446.05 582.422L1446.04 582.428C1441.69 590.602 1439.5 600.423 1439.5 611.925V742.633H1395.72V521.919H1435.05V554.803C1439.92 544.379 1447.91 535.465 1458.37 528.356C1470.71 519.875 1485.58 515.657 1502.93 515.657C1522.37 515.657 1538.61 520.931 1551.55 531.538C1560.38 538.771 1567.1 547.628 1571.72 558.091C1576.05 547.619 1582.83 538.757 1592.07 531.524C1605.61 520.93 1622.28 515.657 1642.01 515.657ZM1343.49 452C1351.45 452 1358.23 454.786 1363.75 460.346C1369.27 465.905 1372.04 472.721 1372.04 480.73C1372.04 488.452 1369.27 495.254 1363.77 501.096L1363.76 501.105L1363.75 501.115C1358.23 506.675 1351.45 509.461 1343.49 509.461C1335.81 509.461 1329.05 506.669 1323.25 501.134L1323.23 501.115L1323.21 501.096C1317.71 495.254 1314.94 488.452 1314.94 480.73C1314.94 472.721 1317.7 465.905 1323.23 460.346L1323.24 460.337L1323.25 460.327C1329.05 454.792 1335.81 452 1343.49 452Z'
className='fill-neutral-900 dark:fill-white'
/>
<defs>
<linearGradient
id='sim-logo-full-gradient'
x1='904.414'
y1='624.199'
x2='978.836'
y2='698.447'
gradientUnits='userSpaceOnUse'
>
<stop />
<stop offset='1' stopOpacity='0' />
</linearGradient>
</defs>
</svg>
)
}

View File

@@ -152,9 +152,3 @@ Input → Agent (Google Search, Notion) → Function (Compile Report)
- **Sei spezifisch in System-Prompts**: Definiere die Rolle, den Ton und die Einschränkungen des Agenten klar. Je spezifischer deine Anweisungen sind, desto besser kann der Agent seinen vorgesehenen Zweck erfüllen. - **Sei spezifisch in System-Prompts**: Definiere die Rolle, den Ton und die Einschränkungen des Agenten klar. Je spezifischer deine Anweisungen sind, desto besser kann der Agent seinen vorgesehenen Zweck erfüllen.
- **Wähle die richtige Temperatureinstellung**: Verwende niedrigere Temperatureinstellungen (0-0,3), wenn Genauigkeit wichtig ist, oder erhöhe die Temperatur (0,7-2,0) für kreativere oder vielfältigere Antworten - **Wähle die richtige Temperatureinstellung**: Verwende niedrigere Temperatureinstellungen (0-0,3), wenn Genauigkeit wichtig ist, oder erhöhe die Temperatur (0,7-2,0) für kreativere oder vielfältigere Antworten
- **Nutze Tools effektiv**: Integriere Tools, die den Zweck des Agenten ergänzen und seine Fähigkeiten erweitern. Sei selektiv bei der Auswahl der Tools, um den Agenten nicht zu überfordern. Für Aufgaben mit wenig Überschneidung verwende einen anderen Agent-Block für die besten Ergebnisse. - **Nutze Tools effektiv**: Integriere Tools, die den Zweck des Agenten ergänzen und seine Fähigkeiten erweitern. Sei selektiv bei der Auswahl der Tools, um den Agenten nicht zu überfordern. Für Aufgaben mit wenig Überschneidung verwende einen anderen Agent-Block für die besten Ergebnisse.
## Best Practices
- **Seien Sie spezifisch in System-Prompts**: Definieren Sie die Rolle, den Ton und die Grenzen des Agenten klar. Je spezifischer Ihre Anweisungen sind, desto besser kann der Agent seinen beabsichtigten Zweck erfüllen.
- **Wählen Sie die richtige Temperatureinstellung**: Verwenden Sie niedrigere Temperatureinstellungen (00,3), wenn Genauigkeit wichtig ist, oder erhöhen Sie die Temperatur (0,72,0) für kreativere oder vielfältigere Antworten
- **Nutzen Sie Tools effektiv**: Integrieren Sie Tools, die den Zweck des Agenten ergänzen und seine Fähigkeiten erweitern. Seien Sie selektiv bei der Auswahl der Tools, um den Agenten nicht zu überfordern. Verwenden Sie für Aufgaben mit geringer Überschneidung einen weiteren Agent-Block für die besten Ergebnisse.

View File

@@ -255,57 +255,3 @@ console.log(`${processedItems} gültige Elemente verarbeitet`);
- **Setzen Sie vernünftige Grenzen**: Halten Sie die Anzahl der Iterationen in einem vernünftigen Rahmen, um lange Ausführungszeiten zu vermeiden - **Setzen Sie vernünftige Grenzen**: Halten Sie die Anzahl der Iterationen in einem vernünftigen Rahmen, um lange Ausführungszeiten zu vermeiden
- **Verwenden Sie ForEach für Sammlungen**: Verwenden Sie beim Verarbeiten von Arrays oder Objekten ForEach anstelle von For-Schleifen - **Verwenden Sie ForEach für Sammlungen**: Verwenden Sie beim Verarbeiten von Arrays oder Objekten ForEach anstelle von For-Schleifen
- **Behandeln Sie Fehler elegant**: Erwägen Sie, Fehlerbehandlung innerhalb von Schleifen hinzuzufügen, um robuste Workflows zu gewährleisten - **Behandeln Sie Fehler elegant**: Erwägen Sie, Fehlerbehandlung innerhalb von Schleifen hinzuzufügen, um robuste Workflows zu gewährleisten
## Eingaben und Ausgaben
<Tabs items={['Configuration', 'Variables', 'Results']}>
<Tab>
<ul className="list-disc space-y-2 pl-6">
<li>
<strong>Schleifentyp</strong>: Wählen Sie zwischen 'for', 'forEach', 'while' oder 'doWhile'
</li>
<li>
<strong>Iterationen</strong>: Anzahl der Ausführungen (für for-Schleifen)
</li>
<li>
<strong>Sammlung</strong>: Array oder Objekt zum Durchlaufen (für forEach-Schleifen)
</li>
<li>
<strong>Bedingung</strong>: Boolescher Ausdruck zur Auswertung (für while/do-while-Schleifen)
</li>
</ul>
</Tab>
<Tab>
Verfügbar **innerhalb** der Schleife:
<ul className="list-disc space-y-2 pl-6">
<li>
<strong>{"<loop.index>"}</strong>: Aktuelle Iterationsnummer (0-basiert)
</li>
<li>
<strong>{"<loop.currentItem>"}</strong>: Aktuell verarbeitetes Element (nur forEach)
</li>
<li>
<strong>{"<loop.items>"}</strong>: Vollständige Sammlung (nur forEach)
</li>
</ul>
</Tab>
<Tab>
<ul className="list-disc space-y-2 pl-6">
<li>
<strong>{"<blockname.results>"}</strong>: Array aller Iterationsergebnisse (Zugriff über Blocknamen)
</li>
<li>
<strong>Struktur</strong>: Ergebnisse behalten die Iterationsreihenfolge bei
</li>
<li>
<strong>Zugriff</strong>: Verfügbar in Blöcken nach Abschluss der Schleife
</li>
</ul>
</Tab>
</Tabs>
## Best Practices
- **Setzen Sie vernünftige Grenzen**: Halten Sie die Iterationsanzahl angemessen, um lange Ausführungszeiten zu vermeiden
- **Verwenden Sie ForEach für Sammlungen**: Verwenden Sie beim Verarbeiten von Arrays oder Objekten ForEach anstelle von For-Schleifen
- **Behandeln Sie Fehler elegant**: Erwägen Sie, Fehlerbehandlung innerhalb von Schleifen hinzuzufügen, um robuste Workflows zu gewährleisten

View File

@@ -214,51 +214,3 @@ Wann Sie welche Methode verwenden sollten:
- **Nur unabhängige Operationen**: Stellen Sie sicher, dass Operationen nicht voneinander abhängen - **Nur unabhängige Operationen**: Stellen Sie sicher, dass Operationen nicht voneinander abhängen
- **Ratenbegrenzungen berücksichtigen**: Fügen Sie Verzögerungen oder Drosselungen für API-intensive Workflows hinzu - **Ratenbegrenzungen berücksichtigen**: Fügen Sie Verzögerungen oder Drosselungen für API-intensive Workflows hinzu
- **Fehlerbehandlung**: Jede Instanz sollte ihre eigenen Fehler angemessen behandeln - **Fehlerbehandlung**: Jede Instanz sollte ihre eigenen Fehler angemessen behandeln
## Eingaben und Ausgaben
<Tabs items={['Konfiguration', 'Variablen', 'Ergebnisse']}>
<Tab>
<ul className="list-disc space-y-2 pl-6">
<li>
<strong>Parallel-Typ</strong>: Wählen Sie zwischen „count" oder „collection"
</li>
<li>
<strong>Anzahl</strong>: Anzahl der auszuführenden Instanzen (anzahlbasiert)
</li>
<li>
<strong>Collection</strong>: Array oder Objekt zur Verteilung (sammlungsbasiert)
</li>
</ul>
</Tab>
<Tab>
Verfügbar **innerhalb** der Parallelverarbeitung:
<ul className="list-disc space-y-2 pl-6">
<li>
<strong>{"<parallel.index>"}</strong>: Instanznummer (0-basiert)
</li>
<li>
<strong>{"<parallel.currentItem>"}</strong>: Element für diese Instanz (nur sammlungsbasiert)
</li>
<li>
<strong>{"<parallel.items>"}</strong>: Vollständige Sammlung (nur sammlungsbasiert)
</li>
</ul>
</Tab>
<Tab>
<ul className="list-disc space-y-2 pl-6">
<li>
<strong>{"<blockname.results>"}</strong>: Array aller Instanzergebnisse (Zugriff über Blockname)
</li>
<li>
<strong>Zugriff</strong>: Verfügbar in Blöcken nach Abschluss der Parallelverarbeitung
</li>
</ul>
</Tab>
</Tabs>
## Best Practices
- **Nur unabhängige Operationen**: Stellen Sie sicher, dass Operationen nicht voneinander abhängen
- **Rate Limits beachten**: Fügen Sie Verzögerungen oder Drosselung für API-intensive Workflows hinzu
- **Fehlerbehandlung**: Jede Instanz sollte ihre eigenen Fehler ordnungsgemäß behandeln

View File

@@ -100,18 +100,3 @@ Input (Lead) → Router → Agent (Enterprise Sales) or Workflow (Self-serve)
- **Mit verschiedenen Eingaben testen**: Stellen Sie sicher, dass der Router verschiedene Eingabetypen, Grenzfälle und unerwartete Inhalte verarbeiten kann - **Mit verschiedenen Eingaben testen**: Stellen Sie sicher, dass der Router verschiedene Eingabetypen, Grenzfälle und unerwartete Inhalte verarbeiten kann
- **Routing-Leistung überwachen**: Überprüfen Sie Routing-Entscheidungen regelmäßig und verfeinern Sie Kriterien basierend auf tatsächlichen Nutzungsmustern - **Routing-Leistung überwachen**: Überprüfen Sie Routing-Entscheidungen regelmäßig und verfeinern Sie Kriterien basierend auf tatsächlichen Nutzungsmustern
- **Geeignete Modelle auswählen**: Verwenden Sie Modelle mit starken Argumentationsfähigkeiten für komplexe Routing-Entscheidungen - **Geeignete Modelle auswählen**: Verwenden Sie Modelle mit starken Argumentationsfähigkeiten für komplexe Routing-Entscheidungen
Wenn der Router keine geeignete Route für den gegebenen Kontext ermitteln kann, leitet er stattdessen zum **Fehlerpfad** weiter, anstatt willkürlich eine Route auszuwählen. Dies geschieht, wenn:
- Der Kontext keiner der definierten Routenbeschreibungen eindeutig entspricht
- Die KI feststellt, dass keine der verfügbaren Routen geeignet ist
## Best Practices
- **Klare Routenbeschreibungen verfassen**: Jede Routenbeschreibung sollte klar erklären, wann diese Route ausgewählt werden sollte. Seien Sie spezifisch bezüglich der Kriterien.
- **Routen gegenseitig ausschließend gestalten**: Stellen Sie nach Möglichkeit sicher, dass sich Routenbeschreibungen nicht überschneiden, um mehrdeutige Routing-Entscheidungen zu vermeiden.
- **Einen Fehlerpfad verbinden**: Behandeln Sie Fälle, in denen keine Route passt, indem Sie einen Fehlerbehandler für ein elegantes Fallback-Verhalten verbinden.
- **Aussagekräftige Routentitel verwenden**: Routentitel erscheinen im Workflow-Canvas, machen Sie sie daher für bessere Lesbarkeit aussagekräftig.
- **Mit verschiedenen Eingaben testen**: Stellen Sie sicher, dass der Router verschiedene Eingabetypen, Grenzfälle und unerwartete Inhalte verarbeitet.
- **Routing-Performance überwachen**: Überprüfen Sie Routing-Entscheidungen regelmäßig und verfeinern Sie Routenbeschreibungen basierend auf tatsächlichen Nutzungsmustern.
- **Geeignete Modelle wählen**: Verwenden Sie Modelle mit starken Reasoning-Fähigkeiten für komplexe Routing-Entscheidungen.

View File

@@ -1,89 +0,0 @@
---
title: Webhook
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Image } from '@/components/ui/image'
Der Webhook-Block sendet HTTP-POST-Anfragen an externe Webhook-Endpunkte mit automatischen Webhook-Headern und optionaler HMAC-Signierung.
<div className="flex justify-center">
<Image
src="/static/blocks/webhook.png"
alt="Webhook-Block"
width={500}
height={400}
className="my-6"
/>
</div>
## Konfiguration
### Webhook-URL
Der Ziel-Endpunkt für Ihre Webhook-Anfrage. Unterstützt sowohl statische URLs als auch dynamische Werte aus anderen Blöcken.
### Payload
JSON-Daten, die im Anfrage-Body gesendet werden. Verwenden Sie den KI-Zauberstab, um Payloads zu generieren oder auf Workflow-Variablen zu verweisen:
```json
{
"event": "workflow.completed",
"data": {
"result": "<agent.content>",
"timestamp": "<function.result>"
}
}
```
### Signierungsgeheimnis
Optionales Geheimnis für die HMAC-SHA256-Payload-Signierung. Wenn angegeben, wird ein `X-Webhook-Signature`Header hinzugefügt:
```
X-Webhook-Signature: t=1704067200000,v1=5d41402abc4b2a76b9719d911017c592...
```
Um Signaturen zu verifizieren, berechnen Sie `HMAC-SHA256(secret, "${timestamp}.${body}")` und vergleichen Sie mit dem `v1`Wert.
### Zusätzliche Header
Benutzerdefinierte Schlüssel-Wert-Header, die in die Anfrage aufgenommen werden. Diese überschreiben alle automatischen Header mit demselben Namen.
## Automatische Header
Jede Anfrage enthält automatisch diese Header:
| Header | Beschreibung |
|--------|-------------|
| `Content-Type` | `application/json` |
| `X-Webhook-Timestamp` | Unix-Zeitstempel in Millisekunden |
| `X-Delivery-ID` | Eindeutige UUID für diese Zustellung |
| `Idempotency-Key` | Identisch mit `X-Delivery-ID` zur Deduplizierung |
## Ausgaben
| Ausgabe | Typ | Beschreibung |
|--------|------|-------------|
| `data` | json | Antwort-Body vom Endpunkt |
| `status` | number | HTTP-Statuscode |
| `headers` | object | Antwort-Header |
## Beispiel-Anwendungsfälle
**Externe Dienste benachrichtigen** - Workflow-Ergebnisse an Slack, Discord oder benutzerdefinierte Endpunkte senden
```
Agent → Function (format) → Webhook (notify)
```
**Externe Workflows auslösen** - Prozesse in anderen Systemen starten, wenn Bedingungen erfüllt sind
```
Condition (check) → Webhook (trigger) → Response
```
<Callout>
Der Webhook-Block verwendet immer POST. Für andere HTTP-Methoden oder mehr Kontrolle verwenden Sie den [API-Block](/blocks/api).
</Callout>

View File

@@ -111,24 +111,26 @@ Verschiedene Blocktypen erzeugen unterschiedliche Ausgabestrukturen. Hier ist, w
```json ```json
{ {
"content": "Original content passed through",
"conditionResult": true, "conditionResult": true,
"selectedPath": { "selectedPath": {
"blockId": "2acd9007-27e8-4510-a487-73d3b825e7c1", "blockId": "2acd9007-27e8-4510-a487-73d3b825e7c1",
"blockType": "agent", "blockType": "agent",
"blockTitle": "Follow-up Agent" "blockTitle": "Follow-up Agent"
}, },
"selectedOption": "condition-1" "selectedConditionId": "condition-1"
} }
``` ```
### Ausgabefelder des Condition-Blocks ### Ausgabefelder des Condition-Blocks
- **content**: Der ursprüngliche, durchgeleitete Inhalt
- **conditionResult**: Boolesches Ergebnis der Bedingungsauswertung - **conditionResult**: Boolesches Ergebnis der Bedingungsauswertung
- **selectedPath**: Informationen über den ausgewählten Pfad - **selectedPath**: Informationen über den ausgewählten Pfad
- **blockId**: ID des nächsten Blocks im ausgewählten Pfad - **blockId**: ID des nächsten Blocks im ausgewählten Pfad
- **blockType**: Typ des nächsten Blocks - **blockType**: Typ des nächsten Blocks
- **blockTitle**: Titel des nächsten Blocks - **blockTitle**: Titel des nächsten Blocks
- **selectedOption**: ID der ausgewählten Bedingung - **selectedConditionId**: ID der ausgewählten Bedingung
</Tab> </Tab>
<Tab> <Tab>

View File

@@ -169,175 +169,3 @@ copilotCost = (inputTokens × inputPrice + outputTokens × (outputPrice × 1.5))
<Callout type="info"> <Callout type="info">
Modellpreise werden pro Million Tokens angegeben. Die Berechnung teilt durch 1.000.000, um die tatsächlichen Kosten zu ermitteln. Siehe <a href="/execution/costs">die Seite zur Kostenberechnung</a> für Hintergründe und Beispiele. Modellpreise werden pro Million Tokens angegeben. Die Berechnung teilt durch 1.000.000, um die tatsächlichen Kosten zu ermitteln. Siehe <a href="/execution/costs">die Seite zur Kostenberechnung</a> für Hintergründe und Beispiele.
</Callout> </Callout>
Fahre mit der Maus über eine deiner Nachrichten und klicke auf **Bearbeiten**, um sie zu ändern und erneut zu senden. Dies ist nützlich, um deine Eingaben zu verfeinern.
### Nachrichtenwarteschlange
Wenn du eine Nachricht sendest, während Copilot noch antwortet, wird sie in die Warteschlange gestellt. Du kannst:
- Warteschlangennachrichten im erweiterbaren Warteschlangenpanel anzeigen
- Eine Nachricht aus der Warteschlange sofort senden (bricht die aktuelle Antwort ab)
- Nachrichten aus der Warteschlange entfernen
## Dateianhänge
Klicke auf das Anhang-Symbol, um Dateien mit deiner Nachricht hochzuladen. Unterstützte Dateitypen umfassen:
- Bilder (Vorschau-Thumbnails werden angezeigt)
- PDFs
- Textdateien, JSON, XML
- Andere Dokumentformate
Dateien werden als anklickbare Thumbnails angezeigt, die in einem neuen Tab geöffnet werden.
## Checkpoints & Änderungen
Wenn Copilot Änderungen an deinem Workflow vornimmt, speichert es Checkpoints, damit du bei Bedarf zurückkehren kannst.
### Checkpoints anzeigen
Fahre mit der Maus über eine Copilot-Nachricht und klicke auf das Checkpoints-Symbol, um gespeicherte Workflow-Zustände für diese Nachricht anzuzeigen.
### Änderungen rückgängig machen
Klicke bei jedem Checkpoint auf **Rückgängig machen**, um deinen Workflow auf diesen Zustand zurückzusetzen. Ein Bestätigungsdialog warnt dich, dass diese Aktion nicht rückgängig gemacht werden kann.
### Änderungen akzeptieren
Wenn Copilot Änderungen vorschlägt, kannst du:
- **Akzeptieren**: Die vorgeschlagenen Änderungen anwenden (`Mod+Shift+Enter`)
- **Ablehnen**: Die Änderungen verwerfen und deinen aktuellen Workflow beibehalten
## Denkblöcke
Bei komplexen Anfragen kann Copilot seinen Denkprozess in erweiterbaren Denkblöcken anzeigen:
- Blöcke werden automatisch erweitert, während Copilot denkt
- Klicken zum manuellen Erweitern/Reduzieren
- Zeigt die Dauer des Denkprozesses an
- Hilft dir zu verstehen, wie Copilot zu seiner Lösung gekommen ist
## Optionsauswahl
Wenn Copilot mehrere Optionen präsentiert, kannst du auswählen mit:
| Steuerung | Aktion |
|---------|--------|
| **1-9** | Option nach Nummer auswählen |
| **Pfeiltaste auf/ab** | Zwischen Optionen navigieren |
| **Eingabetaste** | Hervorgehobene Option auswählen |
Ausgewählte Optionen sind hervorgehoben; nicht ausgewählte Optionen erscheinen durchgestrichen.
## Tastenkombinationen
| Tastenkombination | Aktion |
|----------|--------|
| `@` | Kontextmenü öffnen |
| `/` | Slash-Befehle öffnen |
| `Arrow Up/Down` | Menüelemente navigieren |
| `Enter` | Menüelement auswählen |
| `Esc` | Menüs schließen |
| `Mod+Shift+Enter` | Copilot-Änderungen akzeptieren |
## Nutzungslimits
Die Copilot-Nutzung wird pro Token des zugrunde liegenden LLM abgerechnet. Wenn Sie Ihr Nutzungslimit erreichen, fordert Copilot Sie auf, Ihr Limit zu erhöhen. Sie können die Nutzung in Schritten (50 $, 100 $) von Ihrer aktuellen Basis aus hinzufügen.
<Callout type="info">
Siehe die [Seite zur Kostenberechnung](/execution/costs) für Abrechnungsdetails.
</Callout>
## Copilot MCP
Sie können Copilot als MCP-Server in Ihrem bevorzugten Editor oder AI-Client verwenden. Damit können Sie Sim-Workflows direkt aus Tools wie Cursor, Claude Code, Claude Desktop und VS Code erstellen, testen, bereitstellen und verwalten.
### Generieren eines Copilot-API-Schlüssels
Um sich mit dem Copilot-MCP-Server zu verbinden, benötigen Sie einen **Copilot-API-Schlüssel**:
1. Gehen Sie zu [sim.ai](https://sim.ai) und melden Sie sich an
2. Navigieren Sie zu **Einstellungen** → **Copilot**
3. Klicken Sie auf **API-Schlüssel generieren**
4. Kopieren Sie den Schlüssel er wird nur einmal angezeigt
Der Schlüssel sieht aus wie `sk-sim-copilot-...`. Sie werden ihn in der folgenden Konfiguration verwenden.
### Cursor
Fügen Sie Folgendes zu Ihrer `.cursor/mcp.json` (Projektebene) oder den globalen Cursor-MCP-Einstellungen hinzu:
```json
{
"mcpServers": {
"sim-copilot": {
"url": "https://www.sim.ai/api/mcp/copilot",
"headers": {
"X-API-Key": "YOUR_COPILOT_API_KEY"
}
}
}
}
```
Ersetzen Sie `YOUR_COPILOT_API_KEY` durch den oben generierten Schlüssel.
### Claude Code
Führen Sie den folgenden Befehl aus, um den Copilot MCP-Server hinzuzufügen:
```bash
claude mcp add sim-copilot \
--transport http \
https://www.sim.ai/api/mcp/copilot \
--header "X-API-Key: YOUR_COPILOT_API_KEY"
```
Ersetzen Sie `YOUR_COPILOT_API_KEY` durch Ihren Schlüssel.
### Claude Desktop
Claude Desktop benötigt [`mcp-remote`](https://www.npmjs.com/package/mcp-remote), um sich mit HTTP-basierten MCP-Servern zu verbinden. Fügen Sie Folgendes zu Ihrer Claude Desktop-Konfigurationsdatei hinzu (`~/Library/Application Support/Claude/claude_desktop_config.json` unter macOS):
```json
{
"mcpServers": {
"sim-copilot": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://www.sim.ai/api/mcp/copilot",
"--header",
"X-API-Key: YOUR_COPILOT_API_KEY"
]
}
}
}
```
Ersetzen Sie `YOUR_COPILOT_API_KEY` durch Ihren Schlüssel.
### VS Code
Fügen Sie Folgendes zu Ihrer VS Code `settings.json` oder Workspace `.vscode/settings.json` hinzu:
```json
{
"mcp": {
"servers": {
"sim-copilot": {
"type": "http",
"url": "https://www.sim.ai/api/mcp/copilot",
"headers": {
"X-API-Key": "YOUR_COPILOT_API_KEY"
}
}
}
}
}
```
Ersetzen Sie `YOUR_COPILOT_API_KEY` durch Ihren Schlüssel.
<Callout type="info">
Für selbst gehostete Deployments ersetzen Sie `https://www.sim.ai` durch Ihre selbst gehostete Sim-URL.
</Callout>

View File

@@ -1,114 +0,0 @@
---
title: Enterprise
description: Enterprise-Funktionen für Organisationen mit erweiterten
Sicherheits- und Compliance-Anforderungen
---
import { Callout } from 'fumadocs-ui/components/callout'
Sim Enterprise bietet erweiterte Funktionen für Organisationen mit erhöhten Sicherheits-, Compliance- und Verwaltungsanforderungen.
---
## Bring Your Own Key (BYOK)
Verwenden Sie Ihre eigenen API-Schlüssel für KI-Modellanbieter anstelle der gehosteten Schlüssel von Sim.
### Unterstützte Anbieter
| Anbieter | Verwendung |
|----------|-------|
| OpenAI | Knowledge Base-Embeddings, Agent-Block |
| Anthropic | Agent-Block |
| Google | Agent-Block |
| Mistral | Knowledge Base OCR |
### Einrichtung
1. Navigieren Sie zu **Einstellungen** → **BYOK** in Ihrem Workspace
2. Klicken Sie auf **Schlüssel hinzufügen** für Ihren Anbieter
3. Geben Sie Ihren API-Schlüssel ein und speichern Sie
<Callout type="warn">
BYOK-Schlüssel werden verschlüsselt gespeichert. Nur Organisationsadministratoren und -inhaber können Schlüssel verwalten.
</Callout>
Wenn konfiguriert, verwenden Workflows Ihren Schlüssel anstelle der gehosteten Schlüssel von Sim. Bei Entfernung wechseln Workflows automatisch zu den gehosteten Schlüsseln zurück.
---
## Single Sign-On (SSO)
Enterprise-Authentifizierung mit SAML 2.0- und OIDC-Unterstützung für zentralisiertes Identitätsmanagement.
### Unterstützte Anbieter
- Okta
- Azure AD / Entra ID
- Google Workspace
- OneLogin
- Jeder SAML 2.0- oder OIDC-Anbieter
### Einrichtung
1. Navigieren Sie zu **Einstellungen** → **SSO** in Ihrem Workspace
2. Wählen Sie Ihren Identitätsanbieter
3. Konfigurieren Sie die Verbindung mithilfe der Metadaten Ihres IdP
4. Aktivieren Sie SSO für Ihre Organisation
<Callout type="info">
Sobald SSO aktiviert ist, authentifizieren sich Teammitglieder über Ihren Identitätsanbieter anstelle von E-Mail/Passwort.
</Callout>
---
## Self-Hosted
Für selbst gehostete Bereitstellungen können Enterprise-Funktionen über Umgebungsvariablen aktiviert werden:
| Variable | Beschreibung |
|----------|-------------|
| `SSO_ENABLED`, `NEXT_PUBLIC_SSO_ENABLED` | Single Sign-On mit SAML/OIDC |
| `CREDENTIAL_SETS_ENABLED`, `NEXT_PUBLIC_CREDENTIAL_SETS_ENABLED` | Polling-Gruppen für E-Mail-Trigger |
| `DISABLE_INVITATIONS`, `NEXT_PUBLIC_DISABLE_INVITATIONS` | Workspace-/Organisations-Einladungen global deaktivieren |
<Callout type="warn">
BYOK ist nur im gehosteten Sim verfügbar. Selbst gehostete Deployments konfigurieren AI-Provider-Schlüssel direkt über Umgebungsvariablen.
</Callout>
Wenn die Abrechnung deaktiviert ist, verwenden Sie die Admin-API zur Verwaltung von Organisationen:
```bash
# Create an organization
curl -X POST https://your-instance/api/v1/admin/organizations \
-H "x-admin-key: YOUR_ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "My Organization", "ownerId": "user-id-here"}'
# Add a member
curl -X POST https://your-instance/api/v1/admin/organizations/{orgId}/members \
-H "x-admin-key: YOUR_ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"userId": "user-id-here", "role": "admin"}'
```
### Workspace-Mitglieder
Wenn Einladungen deaktiviert sind, verwenden Sie die Admin-API zur direkten Verwaltung von Workspace-Mitgliedschaften:
```bash
# Add a user to a workspace
curl -X POST https://your-instance/api/v1/admin/workspaces/{workspaceId}/members \
-H "x-admin-key: YOUR_ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"userId": "user-id-here", "permissions": "write"}'
# Remove a user from a workspace
curl -X DELETE "https://your-instance/api/v1/admin/workspaces/{workspaceId}/members?userId=user-id-here" \
-H "x-admin-key: YOUR_ADMIN_API_KEY"
```
### Hinweise
- Die Aktivierung von `ACCESS_CONTROL_ENABLED` aktiviert automatisch Organisationen, da die Zugriffskontrolle eine Organisationsmitgliedschaft erfordert.
- Wenn `DISABLE_INVITATIONS` gesetzt ist, können Benutzer keine Einladungen versenden. Verwenden Sie stattdessen die Admin-API zur Verwaltung von Workspace- und Organisationsmitgliedschaften.

View File

@@ -49,40 +49,40 @@ Die Modellaufschlüsselung zeigt:
<Tabs items={['Hosted Models', 'Bring Your Own API Key']}> <Tabs items={['Hosted Models', 'Bring Your Own API Key']}>
<Tab> <Tab>
**Hosted Models** - Sim bietet API-Schlüssel mit einem 1,4-fachen Preismultiplikator für Agent-Blöcke: **Gehostete Modelle** - Sim stellt API-Schlüssel mit einem 2,5-fachen Preismultiplikator bereit:
**OpenAI** **OpenAI**
| Modell | Basispreis (Eingabe/Ausgabe) | Hosted-Preis (Eingabe/Ausgabe) | | Modell | Basispreis (Eingabe/Ausgabe) | Gehosteter Preis (Eingabe/Ausgabe) |
|-------|---------------------------|----------------------------| |-------|---------------------------|----------------------------|
| GPT-5.1 | $1.25 / $10.00 | $1.75 / $14.00 | | GPT-5.1 | $1,25 / $10,00 | $3,13 / $25,00 |
| GPT-5 | $1.25 / $10.00 | $1.75 / $14.00 | | GPT-5 | $1,25 / $10,00 | $3,13 / $25,00 |
| GPT-5 Mini | $0.25 / $2.00 | $0.35 / $2.80 | | GPT-5 Mini | $0,25 / $2,00 | $0,63 / $5,00 |
| GPT-5 Nano | $0.05 / $0.40 | $0.07 / $0.56 | | GPT-5 Nano | $0,05 / $0,40 | $0,13 / $1,00 |
| GPT-4o | $2.50 / $10.00 | $3.50 / $14.00 | | GPT-4o | $2,50 / $10,00 | $6,25 / $25,00 |
| GPT-4.1 | $2.00 / $8.00 | $2.80 / $11.20 | | GPT-4.1 | $2,00 / $8,00 | $5,00 / $20,00 |
| GPT-4.1 Mini | $0.40 / $1.60 | $0.56 / $2.24 | | GPT-4.1 Mini | $0,40 / $1,60 | $1,00 / $4,00 |
| GPT-4.1 Nano | $0.10 / $0.40 | $0.14 / $0.56 | | GPT-4.1 Nano | $0,10 / $0,40 | $0,25 / $1,00 |
| o1 | $15.00 / $60.00 | $21.00 / $84.00 | | o1 | $15,00 / $60,00 | $37,50 / $150,00 |
| o3 | $2.00 / $8.00 | $2.80 / $11.20 | | o3 | $2,00 / $8,00 | $5,00 / $20,00 |
| o4 Mini | $1.10 / $4.40 | $1.54 / $6.16 | | o4 Mini | $1,10 / $4,40 | $2,75 / $11,00 |
**Anthropic** **Anthropic**
| Modell | Basispreis (Eingabe/Ausgabe) | Hosted-Preis (Eingabe/Ausgabe) | | Modell | Basispreis (Eingabe/Ausgabe) | Gehosteter Preis (Eingabe/Ausgabe) |
|-------|---------------------------|----------------------------| |-------|---------------------------|----------------------------|
| Claude Opus 4.5 | $5.00 / $25.00 | $7.00 / $35.00 | | Claude Opus 4.5 | $5,00 / $25,00 | $12,50 / $62,50 |
| Claude Opus 4.1 | $15.00 / $75.00 | $21.00 / $105.00 | | Claude Opus 4.1 | $15,00 / $75,00 | $37,50 / $187,50 |
| Claude Sonnet 4.5 | $3.00 / $15.00 | $4.20 / $21.00 | | Claude Sonnet 4.5 | $3,00 / $15,00 | $7,50 / $37,50 |
| Claude Sonnet 4.0 | $3.00 / $15.00 | $4.20 / $21.00 | | Claude Sonnet 4.0 | $3,00 / $15,00 | $7,50 / $37,50 |
| Claude Haiku 4.5 | $1.00 / $5.00 | $1.40 / $7.00 | | Claude Haiku 4.5 | $1,00 / $5,00 | $2,50 / $12,50 |
**Google** **Google**
| Modell | Basispreis (Eingabe/Ausgabe) | Hosted-Preis (Eingabe/Ausgabe) | | Modell | Basispreis (Eingabe/Ausgabe) | Gehosteter Preis (Eingabe/Ausgabe) |
|-------|---------------------------|----------------------------| |-------|---------------------------|----------------------------|
| Gemini 3 Pro Preview | $2.00 / $12.00 | $2.80 / $16.80 | | Gemini 3 Pro Preview | $2,00 / $12,00 | $5,00 / $30,00 |
| Gemini 2.5 Pro | $1.25 / $10.00 | $1.75 / $14.00 | | Gemini 2.5 Pro | $0,15 / $0,60 | $0,38 / $1,50 |
| Gemini 2.5 Flash | $0.30 / $2.50 | $0.42 / $3.50 | | Gemini 2.5 Flash | $0,15 / $0,60 | $0,38 / $1,50 |
*Der 1,4-fache Multiplikator deckt Infrastruktur- und API-Verwaltungskosten ab.* *Der 2,5-fache Multiplikator deckt Infrastruktur- und API-Verwaltungskosten ab.*
</Tab> </Tab>
<Tab> <Tab>
@@ -105,32 +105,28 @@ Die Modellaufschlüsselung zeigt:
Die angezeigten Preise entsprechen den Tarifen vom 10. September 2025. Überprüfen Sie die Dokumentation der Anbieter für aktuelle Preise. Die angezeigten Preise entsprechen den Tarifen vom 10. September 2025. Überprüfen Sie die Dokumentation der Anbieter für aktuelle Preise.
</Callout> </Callout>
## Bring Your Own Key (BYOK)
Sie können Ihre eigenen API-Schlüssel für gehostete Modelle (OpenAI, Anthropic, Google, Mistral) unter **Einstellungen → BYOK** verwenden, um Basispreise zu zahlen. Schlüssel werden verschlüsselt und gelten arbeitsbereichsweit.
## Strategien zur Kostenoptimierung ## Strategien zur Kostenoptimierung
- **Modellauswahl**: Wählen Sie Modelle basierend auf der Aufgabenkomplexität. Einfache Aufgaben können GPT-4.1-nano verwenden, während komplexes Reasoning o1 oder Claude Opus erfordern könnte. - **Modellauswahl**: Wählen Sie Modelle basierend auf der Komplexität der Aufgabe. Einfache Aufgaben können GPT-4.1-nano verwenden, während komplexes Denken möglicherweise o1 oder Claude Opus erfordert.
- **Prompt Engineering**: Gut strukturierte, prägnante Prompts reduzieren den Token-Verbrauch ohne Qualitätsverlust. - **Prompt-Engineering**: Gut strukturierte, präzise Prompts reduzieren den Token-Verbrauch ohne Qualitätseinbußen.
- **Lokale Modelle**: Verwenden Sie Ollama oder VLLM für unkritische Aufgaben, um API-Kosten vollständig zu eliminieren. - **Lokale Modelle**: Verwenden Sie Ollama oder VLLM für unkritische Aufgaben, um API-Kosten vollständig zu eliminieren.
- **Caching und Wiederverwendung**: Speichern Sie häufig verwendete Ergebnisse in Variablen oder Dateien, um wiederholte AI-Modellaufrufe zu vermeiden. - **Caching und Wiederverwendung**: Speichern Sie häufig verwendete Ergebnisse in Variablen oder Dateien, um wiederholte KI-Modellaufrufe zu vermeiden.
- **Batch-Verarbeitung**: Verarbeiten Sie mehrere Elemente in einer einzigen AI-Anfrage, anstatt einzelne Aufrufe zu tätigen. - **Batch-Verarbeitung**: Verarbeiten Sie mehrere Elemente in einer einzigen KI-Anfrage anstatt einzelne Aufrufe zu tätigen.
## Nutzungsüberwachung ## Nutzungsüberwachung
Überwachen Sie Ihre Nutzung und Abrechnung unter Einstellungen → Abonnement: Überwachen Sie Ihre Nutzung und Abrechnung unter Einstellungen → Abonnement:
- **Aktuelle Nutzung**: Echtzeit-Nutzung und Kosten für den aktuellen Zeitraum - **Aktuelle Nutzung**: Echtzeit-Nutzung und -Kosten für den aktuellen Zeitraum
- **Nutzungslimits**: Plan-Limits mit visuellen Fortschrittsindikatoren - **Nutzungslimits**: Plangrenzen mit visuellen Fortschrittsanzeigen
- **Abrechnungsdetails**: Prognostizierte Gebühren und Mindestverpflichtungen - **Abrechnungsdetails**: Prognostizierte Gebühren und Mindestverpflichtungen
- **Plan-Verwaltung**: Upgrade-Optionen und Abrechnungsverlauf - **Planverwaltung**: Upgrade-Optionen und Abrechnungsverlauf
### Programmatisches Nutzungs-Tracking ### Programmatische Nutzungsverfolgung
Sie können Ihre aktuelle Nutzung und Limits programmatisch über die API abfragen: Sie können Ihre aktuelle Nutzung und Limits programmatisch über die API abfragen:
**Endpoint:** **Endpunkt:**
```text ```text
GET /api/users/me/usage-limits GET /api/users/me/usage-limits
@@ -176,110 +172,69 @@ curl -X GET -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" htt
``` ```
**Rate-Limit-Felder:** **Rate-Limit-Felder:**
- `requestsPerMinute`: Dauerhaftes Rate-Limit (Tokens werden mit dieser Rate aufgefüllt) - `requestsPerMinute`: Dauerhafte Rate-Begrenzung (Tokens werden mit dieser Rate aufgefüllt)
- `maxBurst`: Maximale Tokens, die Sie akkumulieren können (Burst-Kapazität) - `maxBurst`: Maximale Tokens, die Sie ansammeln können (Burst-Kapazität)
- `remaining`: Aktuell verfügbare Tokens (kann bis zu `maxBurst` betragen) - `remaining`: Aktuell verfügbare Tokens (können bis zu `maxBurst` sein)
**Antwortfelder:** **Antwortfelder:**
- `currentPeriodCost` spiegelt die Nutzung im aktuellen Abrechnungszeitraum wider - `currentPeriodCost` spiegelt die Nutzung in der aktuellen Abrechnungsperiode wider
- `limit` wird aus individuellen Limits (Free/Pro) oder gepoolten Organisationslimits (Team/Enterprise) abgeleitet - `limit` wird von individuellen Limits (Free/Pro) oder gepoolten Organisationslimits (Team/Enterprise) abgeleitet
- `plan` ist der Plan mit der höchsten Priorität, der Ihrem Benutzer zugeordnet ist - `plan` ist der aktive Plan mit der höchsten Priorität, der mit Ihrem Benutzer verknüpft ist
## Plan-Limits ## Plan-Limits
Verschiedene Abonnement-Pläne haben unterschiedliche Nutzungslimits: Verschiedene Abonnementpläne haben unterschiedliche Nutzungslimits:
| Plan | Monatliches Nutzungslimit | Ratenlimits (pro Minute) | | Plan | Monatliches Nutzungslimit | Rate-Limits (pro Minute) |
|------|-------------------|-------------------------| |------|-------------------|-------------------------|
| **Free** | 20 $ | 5 sync, 10 async | | **Free** | $10 | 5 sync, 10 async |
| **Pro** | 100 $ | 10 sync, 50 async | | **Pro** | $100 | 10 sync, 50 async |
| **Team** | 500 $ (gemeinsam) | 50 sync, 100 async | | **Team** | $500 (gepoolt) | 50 sync, 100 async |
| **Enterprise** | Individuell | Individuell | | **Enterprise** | Individuell | Individuell |
## Abrechnungsmodell ## Abrechnungsmodell
Sim verwendet ein **Basis-Abonnement + Mehrverbrauch**-Abrechnungsmodell: Sim verwendet ein **Basisabonnement + Mehrverbrauch**-Abrechnungsmodell:
### So funktioniert es ### Wie es funktioniert
**Pro-Plan (20 $/Monat):** **Pro-Plan ($20/Monat):**
- Monatsabonnement beinhaltet 20 $ Nutzung - Monatliches Abonnement beinhaltet $20 Nutzung
- Nutzung unter 20 $ → Keine zusätzlichen Gebühren - Nutzung unter $20 → Keine zusätzlichen Kosten
- Nutzung über 20 $ → Mehrverbrauch am Monatsende zahlen - Nutzung über $20 → Zahlen Sie den Mehrverbrauch am Monatsende
- Beispiel: 35 $ Nutzung = 20 $ (Abonnement) + 15 $ (Mehrverbrauch) - Beispiel: $35 Nutzung = $20 (Abonnement) + $15 (Mehrverbrauch)
**Team-Plan (40 $/Platz/Monat):** **Team-Plan ($40/Benutzer/Monat):**
- Gemeinsame Nutzung über alle Teammitglieder - Gepoolte Nutzung für alle Teammitglieder
- Mehrverbrauch wird aus der gesamten Team-Nutzung berechnet - Mehrverbrauch wird aus der Gesamtnutzung des Teams berechnet
- Organisationsinhaber erhält eine Rechnung - Organisationsinhaber erhält eine Rechnung
**Enterprise-Pläne:** **Enterprise-Pläne:**
- Fester Monatspreis, kein Mehrverbrauch - Fester monatlicher Preis, kein Mehrverbrauch
- Individuelle Nutzungslimits gemäß Vereinbarung - Individuelle Nutzungslimits gemäß Vereinbarung
### Schwellenwert-Abrechnung ### Schwellenwert-Abrechnung
Wenn der nicht abgerechnete Mehrverbrauch 50 $ erreicht, rechnet Sim automatisch den gesamten nicht abgerechneten Betrag ab. Wenn der nicht abgerechnete Mehrverbrauch $50 erreicht, berechnet Sim automatisch den gesamten nicht abgerechneten Betrag.
**Beispiel:** **Beispiel:**
- Tag 10: 70 $ Mehrverbrauch → 70 $ sofort abrechnen - Tag 10: $70 Mehrverbrauch → Sofortige Abrechnung von $70
- Tag 15: Zusätzliche 35 $ Nutzung (105 $ gesamt) → Bereits abgerechnet, keine Aktion - Tag 15: Zusätzliche $35 Nutzung ($105 insgesamt) → Bereits abgerechnet, keine Aktion
- Tag 20: Weitere 50 $ Nutzung (155 $ gesamt, 85 $ nicht abgerechnet) → 85 $ sofort abrechnen - Tag 20: Weitere $50 Nutzung ($155 insgesamt, $85 nicht abgerechnet) → Sofortige Abrechnung von $85
Dies verteilt große Mehrverbrauchsgebühren über den Monat, anstatt einer großen Rechnung am Periodenende. Dies verteilt große Überziehungsgebühren über den Monat, anstatt eine große Rechnung am Ende des Abrechnungszeitraums zu erhalten.
## Best Practices für Kostenmanagement ## Best Practices für Kostenmanagement
1. **Regelmäßig überwachen**: Überprüfen Sie Ihr Nutzungs-Dashboard häufig, um Überraschungen zu vermeiden 1. **Regelmäßig überwachen**: Überprüfen Sie Ihr Nutzungs-Dashboard häufig, um Überraschungen zu vermeiden
2. **Budgets festlegen**: Nutzen Sie Plan-Limits als Leitplanken für Ihre Ausgaben 2. **Budgets festlegen**: Nutzen Sie Planlimits als Leitplanken für Ihre Ausgaben
3. **Workflows optimieren**: Überprüfen Sie kostenintensive Ausführungen und optimieren Sie Prompts oder Modellauswahl 3. **Workflows optimieren**: Überprüfen Sie kostenintensive Ausführungen und optimieren Sie Prompts oder Modellauswahl
4. **Passende Modelle verwenden**: Passen Sie die Modellkomplexität an die Aufgabenanforderungen an 4. **Passende Modelle verwenden**: Passen Sie die Modellkomplexität an die Aufgabenanforderungen an
5. **Ähnliche Aufgaben bündeln**: Kombinieren Sie mehrere Anfragen, wenn möglich, um Overhead zu reduzieren 5. **Ähnliche Aufgaben bündeln**: Kombinieren Sie wenn möglich mehrere Anfragen, um den Overhead zu reduzieren
## Nächste Schritte ## Nächste Schritte
- Überprüfen Sie Ihre aktuelle Nutzung unter [Einstellungen → Abonnement](https://sim.ai/settings/subscription) - Überprüfen Sie Ihre aktuelle Nutzung unter [Einstellungen → Abonnement](https://sim.ai/settings/subscription)
- Erfahren Sie mehr über [Protokollierung](/execution/logging), um Ausführungsdetails zu verfolgen - Erfahren Sie mehr über [Protokollierung](/execution/logging), um Ausführungsdetails zu verfolgen
- Entdecken Sie die [externe API](/execution/api) für programmatische Kostenüberwachung - Erkunden Sie die [Externe API](/execution/api) für programmatische Kostenüberwachung
- Sehen Sie sich [Workflow-Optimierungstechniken](/blocks) an, um Kosten zu reduzieren - Sehen Sie sich [Workflow-Optimierungstechniken](/blocks) an, um Kosten zu reduzieren
**Pro-Tarif (20 $/Monat):**
- Monatliches Abonnement beinhaltet 20 $ Nutzung
- Nutzung unter 20 $ → Keine zusätzlichen Gebühren
- Nutzung über 20 $ → Mehrverbrauch wird am Monatsende abgerechnet
- Beispiel: 35 $ Nutzung = 20 $ (Abonnement) + 15 $ (Mehrverbrauch)
**Team-Tarif (40 $/Platz/Monat):**
- Gemeinsame Nutzung über alle Teammitglieder hinweg
- Mehrverbrauch wird aus der gesamten Teamnutzung berechnet
- Der Organisationsinhaber erhält eine Rechnung
**Enterprise-Tarife:**
- Fester Monatspreis, keine Mehrverbräuche
- Individuelle Nutzungslimits gemäß Vereinbarung
### Schwellenwertabrechnung
Wenn der nicht abgerechnete Mehrverbrauch 50 $ erreicht, rechnet Sim automatisch den gesamten nicht abgerechneten Betrag ab.
**Beispiel:**
- Tag 10: 70 $ Mehrverbrauch → 70 $ sofort abrechnen
- Tag 15: Weitere 35 $ Nutzung (105 $ gesamt) → Bereits abgerechnet, keine Aktion
- Tag 20: Weitere 50 $ Nutzung (155 $ gesamt, 85 $ nicht abgerechnet) → 85 $ sofort abrechnen
Dies verteilt hohe Mehrverbrauchsgebühren über den Monat hinweg, anstatt einer großen Rechnung am Periodenende.
## Best Practices für das Kostenmanagement
1. **Regelmäßig überwachen**: Überprüfen Sie Ihr Nutzungs-Dashboard häufig, um Überraschungen zu vermeiden
2. **Budgets festlegen**: Nutzen Sie Tariflimits als Leitplanken für Ihre Ausgaben
3. **Workflows optimieren**: Überprüfen Sie kostenintensive Ausführungen und optimieren Sie Prompts oder Modellauswahl
4. **Passende Modelle verwenden**: Stimmen Sie die Modellkomplexität auf die Aufgabenanforderungen ab
5. **Ähnliche Aufgaben bündeln**: Kombinieren Sie mehrere Anfragen, wenn möglich, um den Overhead zu reduzieren
## Nächste Schritte
- Überprüfen Sie Ihre aktuelle Nutzung unter [Einstellungen → Abonnement](https://sim.ai/settings/subscription)
- Erfahren Sie mehr über [Protokollierung](/execution/logging), um Ausführungsdetails zu verfolgen
- Erkunden Sie die [externe API](/execution/api) für programmatische Kostenüberwachung
- Informieren Sie sich über [Workflow-Optimierungstechniken](/blocks), um Kosten zu reduzieren

View File

@@ -1,172 +0,0 @@
---
title: Dateien übergeben
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
Sim macht es einfach, mit Dateien in Ihren Workflows zu arbeiten. Blöcke können Dateien empfangen, verarbeiten und nahtlos an andere Blöcke weitergeben.
## Dateiobjekte
Wenn Blöcke Dateien ausgeben (wie Gmail-Anhänge, generierte Bilder oder geparste Dokumente), geben sie ein standardisiertes Dateiobjekt zurück:
```json
{
"name": "report.pdf",
"url": "https://...",
"base64": "JVBERi0xLjQK...",
"type": "application/pdf",
"size": 245678
}
```
Sie können auf alle diese Eigenschaften zugreifen, wenn Sie auf Dateien aus vorherigen Blöcken verweisen.
## Der Datei-Block
Der **Datei-Block** ist der universelle Einstiegspunkt für Dateien in Ihren Workflows. Er akzeptiert Dateien aus jeder Quelle und gibt standardisierte Dateiobjekte aus, die mit allen Integrationen funktionieren.
**Eingaben:**
- **Hochgeladene Dateien** - Dateien direkt per Drag & Drop oder Auswahl hinzufügen
- **Externe URLs** - Jede öffentlich zugängliche Datei-URL
- **Dateien von anderen Blöcken** - Dateien von Gmail-Anhängen, Slack-Downloads usw. übergeben
**Ausgaben:**
- Eine Liste von `UserFile`-Objekten mit konsistenter Struktur (`name`, `url`, `base64`, `type`, `size`)
- `combinedContent` - Extrahierter Textinhalt aus allen Dateien (für Dokumente)
**Beispielverwendung:**
```
// Get all files from the File block
<file.files>
// Get the first file
<file.files[0]>
// Get combined text content from parsed documents
<file.combinedContent>
```
Der Datei-Block führt automatisch folgende Aktionen aus:
- Erkennt Dateitypen aus URLs und Erweiterungen
- Extrahiert Text aus PDFs, CSVs und Dokumenten
- Generiert Base64-Kodierung für Binärdateien
- Erstellt vorsignierte URLs für sicheren Zugriff
Verwenden Sie den Datei-Block, wenn Sie Dateien aus verschiedenen Quellen normalisieren müssen, bevor Sie sie an andere Blöcke wie Vision, STT oder E-Mail-Integrationen übergeben.
## Dateien zwischen Blöcken übergeben
Verweisen Sie auf Dateien aus vorherigen Blöcken über das Tag-Dropdown. Klicken Sie in ein beliebiges Dateieingabefeld und geben Sie `<` ein, um verfügbare Ausgaben anzuzeigen.
**Häufige Muster:**
```
// Single file from a block
<gmail.attachments[0]>
// Pass the whole file object
<file_parser.files[0]>
// Access specific properties
<gmail.attachments[0].name>
<gmail.attachments[0].base64>
```
Die meisten Blöcke akzeptieren das vollständige Dateiobjekt und extrahieren automatisch, was sie benötigen. Sie müssen `base64` oder `url` in den meisten Fällen nicht manuell extrahieren.
## Workflows mit Dateien auslösen
Wenn Sie einen Workflow über die API aufrufen, der Dateieingaben erwartet, fügen Sie Dateien in Ihre Anfrage ein:
<Tabs items={['Base64', 'URL']}>
<Tab value="Base64">
```bash
curl -X POST "https://sim.ai/api/workflows/YOUR_WORKFLOW_ID/execute" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"document": {
"name": "report.pdf",
"base64": "JVBERi0xLjQK...",
"type": "application/pdf"
}
}'
```
</Tab>
<Tab value="URL">
```bash
curl -X POST "https://sim.ai/api/workflows/YOUR_WORKFLOW_ID/execute" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"document": {
"name": "report.pdf",
"url": "https://example.com/report.pdf",
"type": "application/pdf"
}
}'
```
</Tab>
</Tabs>
Der Start-Block des Workflows sollte ein Eingabefeld haben, das für den Empfang des Dateiparameters konfiguriert ist.
## Dateien in API-Antworten empfangen
Wenn ein Workflow Dateien ausgibt, sind diese in der Antwort enthalten:
```json
{
"success": true,
"output": {
"generatedFile": {
"name": "output.png",
"url": "https://...",
"base64": "iVBORw0KGgo...",
"type": "image/png",
"size": 34567
}
}
}
```
Verwenden Sie `url` für direkte Downloads oder `base64` für Inline-Verarbeitung.
## Blöcke, die mit Dateien arbeiten
**Dateieingaben:**
- **File** - Dokumente, Bilder und Textdateien parsen
- **Vision** - Bilder mit KI-Modellen analysieren
- **Mistral Parser** - Text aus PDFs extrahieren
**Dateiausgaben:**
- **Gmail** - E-Mail-Anhänge
- **Slack** - Heruntergeladene Dateien
- **TTS** - Generierte Audiodateien
- **Video Generator** - Generierte Videos
- **Image Generator** - Generierte Bilder
**Dateispeicherung:**
- **Supabase** - Upload/Download aus dem Speicher
- **S3** - AWS S3-Operationen
- **Google Drive** - Drive-Dateioperationen
- **Dropbox** - Dropbox-Dateioperationen
<Callout type="info">
Dateien sind automatisch für nachgelagerte Blöcke verfügbar. Die Ausführungs-Engine übernimmt die gesamte Dateiübertragung und Formatkonvertierung.
</Callout>
## Best Practices
1. **Dateiobjekte direkt verwenden** - Übergeben Sie das vollständige Dateiobjekt, anstatt einzelne Eigenschaften zu extrahieren. Blöcke übernehmen die Konvertierung automatisch.
2. **Dateitypen prüfen** - Stellen Sie sicher, dass der Dateityp mit dem übereinstimmt, was der empfangende Block erwartet. Der Vision-Block benötigt Bilder, der File-Block verarbeitet Dokumente.
3. **Dateigröße beachten** Große Dateien erhöhen die Ausführungszeit. Bei sehr großen Dateien sollten Sie Storage-Blöcke (S3, Supabase) für die Zwischenspeicherung verwenden.

View File

@@ -1,142 +0,0 @@
---
title: Formular-Bereitstellung
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
Stellen Sie Ihren Workflow als einbettbares Formular bereit, das Benutzer auf Ihrer Website ausfüllen oder per Link teilen können. Formularübermittlungen lösen Ihren Workflow mit dem `form` Trigger-Typ aus.
## Übersicht
Die Formular-Bereitstellung verwandelt das Eingabeformat Ihres Workflows in ein responsives Formular, das:
- Per Direktlink geteilt werden kann (z. B. `https://sim.ai/form/my-survey`)
- Mit einem iframe in jede Website eingebettet werden kann
Wenn ein Benutzer das Formular absendet, wird Ihr Workflow mit den Formulardaten ausgelöst.
<Callout type="info">
Formulare leiten ihre Felder vom Eingabeformat des Start-Blocks Ihres Workflows ab. Jedes Feld wird zu einer Formulareingabe mit dem entsprechenden Typ.
</Callout>
## Erstellen eines Formulars
1. Öffnen Sie Ihren Workflow und klicken Sie auf **Bereitstellen**
2. Wählen Sie den Tab **Formular**
3. Konfigurieren Sie:
- **URL**: Eindeutige Kennung (z. B. `contact-form` → `sim.ai/form/contact-form`)
- **Titel**: Formularüberschrift
- **Beschreibung**: Optionaler Untertitel
- **Formularfelder**: Passen Sie Beschriftungen und Beschreibungen für jedes Feld an
- **Authentifizierung**: Öffentlich, passwortgeschützt oder E-Mail-Whitelist
- **Dankesnachricht**: Wird nach der Übermittlung angezeigt
4. Klicken Sie auf **Starten**
## Feldzuordnung
| Eingabeformat-Typ | Formularfeld |
|------------------|------------|
| `string` | Texteingabe |
| `number` | Zahleneingabe |
| `boolean` | Umschalter |
| `object` | JSON-Editor |
| `array` | JSON-Array-Editor |
| `files` | Datei-Upload |
## Zugriffskontrolle
| Modus | Beschreibung |
|------|-------------|
| **Öffentlich** | Jeder mit dem Link kann absenden |
| **Passwort** | Benutzer müssen ein Passwort eingeben |
| **E-Mail-Whitelist** | Nur angegebene E-Mails/Domains können absenden |
Für E-Mail-Whitelist:
- Exakt: `user@example.com`
- Domain: `@example.com` (alle E-Mails von der Domain)
## Einbettung
### Direkter Link
```
https://sim.ai/form/your-identifier
```
### Iframe
```html
<iframe
src="https://sim.ai/form/your-identifier"
width="100%"
height="600"
frameborder="0"
title="Form"
></iframe>
```
## API-Übermittlung
Formulare programmatisch übermitteln:
<Tabs items={['cURL', 'TypeScript']}>
<Tab value="cURL">
```bash
curl -X POST https://sim.ai/api/form/your-identifier \
-H "Content-Type: application/json" \
-d '{
"formData": {
"name": "John Doe",
"email": "john@example.com"
}
}'
```
</Tab>
<Tab value="TypeScript">
```typescript
const response = await fetch('https://sim.ai/api/form/your-identifier', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
formData: {
name: 'John Doe',
email: 'john@example.com'
}
})
});
const result = await response.json();
// { success: true, data: { executionId: '...' } }
```
</Tab>
</Tabs>
### Geschützte Formulare
Für passwortgeschützte Formulare:
```bash
curl -X POST https://sim.ai/api/form/your-identifier \
-H "Content-Type: application/json" \
-d '{ "password": "secret", "formData": { "name": "John" } }'
```
Für E-Mail-geschützte Formulare:
```bash
curl -X POST https://sim.ai/api/form/your-identifier \
-H "Content-Type: application/json" \
-d '{ "email": "allowed@example.com", "formData": { "name": "John" } }'
```
## Fehlerbehebung
**"Keine Eingabefelder konfiguriert"** - Fügen Sie Eingabeformat-Felder zu Ihrem Start-Block hinzu.
**Formular lädt nicht im Iframe** - Überprüfen Sie, ob die CSP Ihrer Website Iframes von `sim.ai` erlaubt.
**Übermittlungen schlagen fehl** - Überprüfen Sie, ob die Kennung korrekt ist und erforderliche Felder ausgefüllt sind.

View File

@@ -1,60 +0,0 @@
---
title: Tastaturkürzel
description: Meistern Sie die Workflow-Arbeitsfläche mit Tastaturkürzeln und Maussteuerung
---
import { Callout } from 'fumadocs-ui/components/callout'
Beschleunigen Sie die Erstellung Ihrer Workflows mit diesen Tastaturkürzeln und Maussteuerungen. Alle Tastenkombinationen funktionieren, wenn die Arbeitsfläche fokussiert ist (nicht beim Tippen in einem Eingabefeld).
<Callout type="info">
**Mod** bezieht sich auf `Cmd` unter macOS und `Ctrl` unter Windows/Linux.
</Callout>
## Arbeitsflächen-Steuerung
### Maussteuerung
| Aktion | Steuerung |
|--------|---------|
| Arbeitsfläche verschieben | Linksziehen auf leerer Fläche |
| Arbeitsfläche verschieben | Scrollen oder Trackpad |
| Mehrere Blöcke auswählen | Rechtsziehen zum Aufziehen eines Auswahlrahmens |
| Block ziehen | Linksziehen auf Block-Kopfzeile |
| Zur Auswahl hinzufügen | `Mod` + Klick auf Blöcke |
### Workflow-Aktionen
| Tastenkombination | Aktion |
|----------|--------|
| `Mod` + `Enter` | Workflow ausführen (oder abbrechen, falls aktiv) |
| `Mod` + `Z` | Rückgängig |
| `Mod` + `Shift` + `Z` | Wiederholen |
| `Mod` + `C` | Ausgewählte Blöcke kopieren |
| `Mod` + `V` | Blöcke einfügen |
| `Delete` oder `Backspace` | Ausgewählte Blöcke oder Verbindungen löschen |
| `Shift` + `L` | Arbeitsfläche automatisch anordnen |
## Panel-Navigation
Diese Tastenkombinationen wechseln zwischen den Panel-Tabs auf der rechten Seite der Arbeitsfläche.
| Tastenkombination | Aktion |
|----------|--------|
| `Mod` + `F` | Toolbar-Suche fokussieren |
## Globale Navigation
| Tastenkombination | Aktion |
|----------|--------|
| `Mod` + `K` | Suche öffnen |
| `Mod` + `Shift` + `A` | Neuen Agenten-Workflow hinzufügen |
| `Mod` + `Y` | Zu Vorlagen gehen |
| `Mod` + `L` | Zu Logs gehen |
## Dienstprogramm
| Tastenkombination | Aktion |
|----------|--------|
| `Mod` + `D` | Terminal-Konsole leeren |
| `Mod` + `E` | Benachrichtigungen löschen |

View File

@@ -35,87 +35,81 @@ Sobald Ihre Dokumente verarbeitet sind, können Sie die einzelnen Chunks anzeige
<Image src="/static/knowledgebase/knowledgebase.png" alt="Dokumentchunk-Ansicht mit verarbeiteten Inhalten" width={800} height={500} /> <Image src="/static/knowledgebase/knowledgebase.png" alt="Dokumentchunk-Ansicht mit verarbeiteten Inhalten" width={800} height={500} />
### Chunk-Konfiguration ### Chunk-Konfiguration
- **Standardgröße der Chunks**: 1.024 Zeichen
- **Konfigurierbarer Bereich**: 100-4.000 Zeichen pro Chunk
- **Intelligente Überlappung**: Standardmäßig 200 Zeichen zur Kontexterhaltung
- **Hierarchische Aufteilung**: Respektiert Dokumentstruktur (Abschnitte, Absätze, Sätze)
Beim Erstellen einer Wissensdatenbank können Sie konfigurieren, wie Dokumente in Chunks aufgeteilt werden: ### Bearbeitungsfunktionen
| Einstellung | Einheit | Standard | Bereich | Beschreibung |
|---------|------|---------|-------|-------------|
| **Maximale Chunk-Größe** | Tokens | 1.024 | 100-4.000 | Maximale Größe jedes Chunks (1 Token ≈ 4 Zeichen) |
| **Minimale Chunk-Größe** | Zeichen | 1 | 1-2.000 | Minimale Chunk-Größe, um winzige Fragmente zu vermeiden |
| **Überlappung** | Zeichen | 200 | 0-500 | Kontextüberlappung zwischen aufeinanderfolgenden Chunks |
- **Hierarchische Aufteilung**: Berücksichtigt die Dokumentstruktur (Abschnitte, Absätze, Sätze)
### Bearbeitungsmöglichkeiten
- **Chunk-Inhalt bearbeiten**: Textinhalt einzelner Chunks ändern - **Chunk-Inhalt bearbeiten**: Textinhalt einzelner Chunks ändern
- **Chunk-Grenzen anpassen**: Chunks nach Bedarf zusammenführen oder aufteilen - **Chunk-Grenzen anpassen**: Chunks bei Bedarf zusammenführen oder teilen
- **Metadaten hinzufügen**: Chunks mit zusätzlichem Kontext anreichern - **Metadaten hinzufügen**: Chunks mit zusätzlichem Kontext anreichern
- **Massenoperationen**: Mehrere Chunks effizient verwalten - **Massenoperationen**: Effiziente Verwaltung mehrerer Chunks
## Erweiterte PDF-Verarbeitung ## Erweiterte PDF-Verarbeitung
Für PDF-Dokumente bietet Sim erweiterte Verarbeitungsfunktionen: Für PDF-Dokumente bietet Sim erweiterte Verarbeitungsfunktionen:
### OCR-Unterstützung ### OCR-Unterstützung
Wenn mit Azure oder [Mistral OCR](https://docs.mistral.ai/ocr/) konfiguriert: Bei Konfiguration mit Azure oder [Mistral OCR](https://docs.mistral.ai/ocr/):
- **Verarbeitung gescannter Dokumente**: Text aus bildbasierten PDFs extrahieren - **Verarbeitung gescannter Dokumente**: Text aus bildbasierten PDFs extrahieren
- **Verarbeitung gemischter Inhalte**: PDFs mit Text und Bildern verarbeiten - **Umgang mit gemischten Inhalten**: Verarbeitung von PDFs mit Text und Bildern
- **Hohe Genauigkeit**: Fortschrittliche KI-Modelle gewährleisten präzise Textextraktion - **Hohe Genauigkeit**: Fortschrittliche KI-Modelle gewährleisten präzise Textextraktion
## Verwendung des Knowledge-Blocks in Workflows ## Verwendung des Wissensblocks in Workflows
Sobald Ihre Dokumente verarbeitet sind, können Sie sie in Ihren KI-Workflows über den Knowledge-Block verwenden. Dies ermöglicht Retrieval-Augmented Generation (RAG), wodurch Ihre KI-Agenten auf Ihre Dokumentinhalte zugreifen und darüber nachdenken können, um genauere, kontextbezogene Antworten zu liefern. Sobald Ihre Dokumente verarbeitet sind, können Sie sie in Ihren KI-Workflows über den Wissensblock nutzen. Dies ermöglicht Retrieval-Augmented Generation (RAG), wodurch Ihre KI-Agenten auf Ihre Dokumentinhalte zugreifen und darüber nachdenken können, um genauere, kontextbezogene Antworten zu liefern.
<Image src="/static/knowledgebase/knowledgebase-2.png" alt="Verwendung des Knowledge-Blocks in Workflows" width={800} height={500} /> <Image src="/static/knowledgebase/knowledgebase-2.png" alt="Verwendung des Wissensblocks in Workflows" width={800} height={500} />
### Knowledge-Block-Funktionen ### Funktionen des Wissensblocks
- **Semantische Suche**: Relevante Inhalte mithilfe natürlichsprachlicher Abfragen finden - **Semantische Suche**: Relevante Inhalte mit natürlichsprachlichen Abfragen finden
- **Kontextintegration**: Relevante Chunks automatisch in Agenten-Prompts einbinden - **Kontextintegration**: Automatisches Einbinden relevanter Chunks in Agenten-Prompts
- **Dynamisches Abrufen**: Suche erfolgt in Echtzeit während der Workflow-Ausführung - **Dynamischer Abruf**: Suche erfolgt in Echtzeit während der Workflow-Ausführung
- **Relevanz-Bewertung**: Ergebnisse nach semantischer Ähnlichkeit sortiert - **Relevanzbewertung**: Ergebnisse nach semantischer Ähnlichkeit geordnet
### Integrationsoptionen ### Integrationsoptionen
- **System-Prompts**: Stellen Sie Ihren KI-Agenten Kontext bereit - **System-Prompts**: Kontext für Ihre KI-Agenten bereitstellen
- **Dynamischer Kontext**: Suchen und fügen Sie relevante Informationen während Konversationen hinzu - **Dynamischer Kontext**: Suche und Einbindung relevanter Informationen während Gesprächen
- **Multi-Dokument-Suche**: Durchsuchen Sie Ihre gesamte Wissensdatenbank - **Dokumentübergreifende Suche**: Abfrage über Ihre gesamte Wissensdatenbank
- **Gefilterte Suche**: Kombinieren Sie mit Tags für präzises Abrufen von Inhalten - **Gefilterte Suche**: Kombination mit Tags für präzisen Inhaltsabruf
## Vektor-Suchtechnologie ## Vektorsuchtechnologie
Sim verwendet Vektorsuche, die von [pgvector](https://github.com/pgvector/pgvector) unterstützt wird, um die Bedeutung und den Kontext Ihrer Inhalte zu verstehen: Sim verwendet Vektorsuche, die von [pgvector](https://github.com/pgvector/pgvector) unterstützt wird, um die Bedeutung und den Kontext Ihrer Inhalte zu verstehen:
### Semantisches Verständnis ### Semantisches Verständnis
- **Kontextuelle Suche**: Findet relevante Inhalte, auch wenn exakte Schlüsselwörter nicht übereinstimmen - **Kontextuelle Suche**: Findet relevante Inhalte, auch wenn exakte Schlüsselwörter nicht übereinstimmen
- **Konzeptbasiertes Abrufen**: Versteht Beziehungen zwischen Ideen - **Konzeptbasierte Abfrage**: Versteht Beziehungen zwischen Ideen
- **Mehrsprachige Unterstützung**: Funktioniert über verschiedene Sprachen hinweg - **Mehrsprachige Unterstützung**: Funktioniert über verschiedene Sprachen hinweg
- **Synonymerkennung**: Findet verwandte Begriffe und Konzepte - **Synonymerkennung**: Findet verwandte Begriffe und Konzepte
### Suchfunktionen ### Suchfunktionen
- **Natürlichsprachige Abfragen**: Stellen Sie Fragen in einfachem Deutsch - **Natürlichsprachige Abfragen**: Stellen Sie Fragen in natürlicher Sprache
- **Ähnlichkeitssuche**: Finden Sie konzeptionell ähnliche Inhalte - **Ähnlichkeitssuche**: Finden Sie konzeptionell ähnliche Inhalte
- **Hybride Suche**: Kombiniert Vektor- und traditionelle Schlüsselwortsuche - **Hybridsuche**: Kombiniert Vektor- und traditionelle Schlüsselwortsuche
- **Konfigurierbare Ergebnisse**: Steuern Sie die Anzahl und Relevanzschwelle der Ergebnisse - **Konfigurierbare Ergebnisse**: Steuern Sie die Anzahl und den Relevanz-Schwellenwert der Ergebnisse
## Dokumentenverwaltung ## Dokumentenverwaltung
### Organisationsfunktionen ### Organisationsfunktionen
- **Massen-Upload**: Laden Sie mehrere Dateien gleichzeitig über die asynchrone API hoch - **Massenupload**: Laden Sie mehrere Dateien gleichzeitig über die asynchrone API hoch
- **Verarbeitungsstatus**: Echtzeit-Updates zur Dokumentenverarbeitung - **Verarbeitungsstatus**: Echtzeit-Updates zum Dokumentenverarbeitungsprozess
- **Suchen und filtern**: Finden Sie Dokumente schnell in großen Sammlungen - **Suchen und Filtern**: Finden Sie Dokumente schnell in großen Sammlungen
- **Metadaten-Tracking**: Automatische Erfassung von Dateiinformationen und Verarbeitungsdetails - **Metadaten-Tracking**: Automatische Erfassung von Dateiinformationen und Verarbeitungsdetails
### Sicherheit und Datenschutz ### Sicherheit und Datenschutz
- **Sichere Speicherung**: Dokumente werden mit Sicherheit auf Unternehmensniveau gespeichert - **Sichere Speicherung**: Dokumente werden mit Sicherheit auf Unternehmensniveau gespeichert
- **Zugriffskontrolle**: Workspace-basierte Berechtigungen - **Zugriffskontrolle**: Workspace-basierte Berechtigungen
- **Verarbeitungsisolierung**: Jeder Workspace hat isolierte Dokumentenverarbeitung - **Verarbeitungsisolierung**: Jeder Workspace hat eine isolierte Dokumentenverarbeitung
- **Datenaufbewahrung**: Konfigurieren Sie Richtlinien zur Dokumentenaufbewahrung - **Datenaufbewahrung**: Konfigurieren Sie Richtlinien zur Dokumentenaufbewahrung
## Erste Schritte ## Erste Schritte
1. **Navigieren Sie zu Ihrer Wissensdatenbank**: Zugriff über Ihre Workspace-Seitenleiste 1. **Navigieren Sie zu Ihrer Wissensdatenbank**: Zugriff über Ihre Workspace-Seitenleiste
2. **Dokumente hochladen**: Ziehen und ablegen oder Dateien zum Hochladen auswählen 2. **Dokumente hochladen**: Drag & Drop oder wählen Sie Dateien zum Hochladen aus
3. **Verarbeitung überwachen**: Beobachten Sie, wie Dokumente verarbeitet und in Abschnitte unterteilt werden 3. **Verarbeitung überwachen**: Beobachten Sie, wie Dokumente verarbeitet und in Chunks aufgeteilt werden
4. **Abschnitte erkunden**: Zeigen Sie die verarbeiteten Inhalte an und bearbeiten Sie sie 4. **Chunks erkunden**: Sehen und bearbeiten Sie die verarbeiteten Inhalte
5. **Zu Workflows hinzufügen**: Verwenden Sie den Knowledge-Block, um mit Ihren KI-Agenten zu integrieren 5. **Zu Workflows hinzufügen**: Verwenden Sie den Wissensblock, um ihn in Ihre KI-Agenten zu integrieren
Die Wissensdatenbank verwandelt Ihre statischen Dokumente in eine intelligente, durchsuchbare Ressource, die Ihre KI-Workflows für fundiertere und kontextbezogene Antworten nutzen können. Die Wissensdatenbank verwandelt Ihre statischen Dokumente in eine intelligente, durchsuchbare Ressource, die Ihre KI-Workflows für fundiertere und kontextbezogenere Antworten nutzen können.

View File

@@ -1,108 +0,0 @@
---
title: Workflows als MCP bereitstellen
description: Stellen Sie Ihre Workflows als MCP-Tools für externe KI-Assistenten
und Anwendungen bereit
---
import { Video } from '@/components/ui/video'
import { Callout } from 'fumadocs-ui/components/callout'
Stellen Sie Ihre Workflows als MCP-Tools bereit, um sie für externe KI-Assistenten wie Claude Desktop, Cursor und andere MCP-kompatible Clients zugänglich zu machen. Dies verwandelt Ihre Workflows in aufrufbare Tools, die von überall aus aufgerufen werden können.
## MCP-Server erstellen und verwalten
MCP-Server gruppieren Ihre Workflow-Tools zusammen. Erstellen und verwalten Sie sie in den Workspace-Einstellungen:
<div className="mx-auto w-full overflow-hidden rounded-lg">
<Video src="mcp/mcp-server.mp4" width={700} height={450} />
</div>
1. Navigieren Sie zu **Einstellungen → MCP-Server**
2. Klicken Sie auf **Server erstellen**
3. Geben Sie einen Namen und eine optionale Beschreibung ein
4. Kopieren Sie die Server-URL zur Verwendung in Ihren MCP-Clients
5. Zeigen Sie alle zum Server hinzugefügten Tools an und verwalten Sie diese
## Einen Workflow als Tool hinzufügen
Sobald Ihr Workflow bereitgestellt ist, können Sie ihn als MCP-Tool verfügbar machen:
<div className="mx-auto w-full overflow-hidden rounded-lg">
<Video src="mcp/mcp-deploy-tool.mp4" width={700} height={450} />
</div>
1. Öffnen Sie Ihren bereitgestellten Workflow
2. Klicken Sie auf **Bereitstellen** und wechseln Sie zum Tab **MCP**
3. Konfigurieren Sie den Tool-Namen und die Beschreibung
4. Fügen Sie Beschreibungen für jeden Parameter hinzu (hilft der KI, Eingaben zu verstehen)
5. Wählen Sie aus, zu welchen MCP-Servern es hinzugefügt werden soll
<Callout type="info">
Der Workflow muss bereitgestellt sein, bevor er als MCP-Tool hinzugefügt werden kann.
</Callout>
## Tool-Konfiguration
### Tool-Name
Verwenden Sie Kleinbuchstaben, Zahlen und Unterstriche. Der Name sollte beschreibend sein und den MCP-Namenskonventionen folgen (z. B. `search_documents`, `send_email`).
### Beschreibung
Schreiben Sie eine klare Beschreibung dessen, was das Tool tut. Dies hilft KI-Assistenten zu verstehen, wann das Tool verwendet werden soll.
### Parameter
Die Eingabeformatfelder deines Workflows werden zu Tool-Parametern. Füge jedem Parameter Beschreibungen hinzu, um KI-Assistenten zu helfen, korrekte Werte bereitzustellen.
## MCP-Clients verbinden
Verwende die Server-URL aus den Einstellungen, um externe Anwendungen zu verbinden:
### Claude Desktop
Füge dies zu deiner Claude Desktop-Konfiguration hinzu (`~/Library/Application Support/Claude/claude_desktop_config.json`):
```json
{
"mcpServers": {
"my-sim-workflows": {
"command": "npx",
"args": ["-y", "mcp-remote", "YOUR_SERVER_URL"]
}
}
}
```
### Cursor
Füge die Server-URL in den MCP-Einstellungen von Cursor mit demselben mcp-remote-Muster hinzu.
<Callout type="warn">
Füge deinen API-Key-Header (`X-API-Key`) für authentifizierten Zugriff hinzu, wenn du mcp-remote oder andere HTTP-basierte MCP-Transporte verwendest.
</Callout>
## Server-Verwaltung
In der Server-Detailansicht unter **Einstellungen → MCP-Server** können Sie:
- **Tools anzeigen**: Alle Workflows sehen, die einem Server hinzugefügt wurden
- **URL kopieren**: Die Server-URL für MCP-Clients abrufen
- **Workflows hinzufügen**: Weitere bereitgestellte Workflows als Tools hinzufügen
- **Tools entfernen**: Workflows vom Server entfernen
- **Server löschen**: Den gesamten Server und alle seine Tools entfernen
## So funktioniert es
Wenn ein MCP-Client dein Tool aufruft:
1. Die Anfrage wird an deiner MCP-Server-URL empfangen
2. Sim validiert die Anfrage und ordnet Parameter den Workflow-Eingaben zu
3. Der bereitgestellte Workflow wird mit den angegebenen Eingaben ausgeführt
4. Die Ergebnisse werden an den MCP-Client zurückgegeben
Workflows werden mit derselben Bereitstellungsversion wie API-Aufrufe ausgeführt, was konsistentes Verhalten gewährleistet.
## Berechtigungsanforderungen
| Aktion | Erforderliche Berechtigung |
|--------|-------------------|
| MCP-Server erstellen | **Admin** |
| Workflows zu Servern hinzufügen | **Write** oder **Admin** |
| MCP-Server anzeigen | **Read**, **Write** oder **Admin** |
| MCP-Server löschen | **Admin** |

View File

@@ -1,10 +1,8 @@
--- ---
title: MCP-Tools verwenden title: MCP (Model Context Protocol)
description: Externe Tools und Dienste über das Model Context Protocol verbinden
--- ---
import { Image } from '@/components/ui/image' import { Image } from '@/components/ui/image'
import { Video } from '@/components/ui/video'
import { Callout } from 'fumadocs-ui/components/callout' import { Callout } from 'fumadocs-ui/components/callout'
Das Model Context Protocol ([MCP](https://modelcontextprotocol.com/)) ermöglicht es Ihnen, externe Tools und Dienste über ein standardisiertes Protokoll zu verbinden, wodurch Sie APIs und Dienste direkt in Ihre Workflows integrieren können. Mit MCP können Sie die Fähigkeiten von Sim erweitern, indem Sie benutzerdefinierte Integrationen hinzufügen, die nahtlos mit Ihren Agenten und Workflows zusammenarbeiten. Das Model Context Protocol ([MCP](https://modelcontextprotocol.com/)) ermöglicht es Ihnen, externe Tools und Dienste über ein standardisiertes Protokoll zu verbinden, wodurch Sie APIs und Dienste direkt in Ihre Workflows integrieren können. Mit MCP können Sie die Fähigkeiten von Sim erweitern, indem Sie benutzerdefinierte Integrationen hinzufügen, die nahtlos mit Ihren Agenten und Workflows zusammenarbeiten.
@@ -22,8 +20,14 @@ MCP ist ein offener Standard, der es KI-Assistenten ermöglicht, sich sicher mit
MCP-Server stellen Sammlungen von Tools bereit, die Ihre Agenten nutzen können. Konfigurieren Sie diese in den Workspace-Einstellungen: MCP-Server stellen Sammlungen von Tools bereit, die Ihre Agenten nutzen können. Konfigurieren Sie diese in den Workspace-Einstellungen:
<div className="mx-auto w-full overflow-hidden rounded-lg"> <div className="flex justify-center">
<Video src="mcp/settings-mcp-tools.mp4" width={700} height={450} /> <Image
src="/static/blocks/mcp-1.png"
alt="Konfiguration eines MCP-Servers in den Einstellungen"
width={700}
height={450}
className="my-6"
/>
</div> </div>
1. Navigieren Sie zu Ihren Workspace-Einstellungen 1. Navigieren Sie zu Ihren Workspace-Einstellungen
@@ -36,18 +40,14 @@ MCP-Server stellen Sammlungen von Tools bereit, die Ihre Agenten nutzen können.
Sie können MCP-Server auch direkt über die Symbolleiste in einem Agent-Block für eine schnelle Einrichtung konfigurieren. Sie können MCP-Server auch direkt über die Symbolleiste in einem Agent-Block für eine schnelle Einrichtung konfigurieren.
</Callout> </Callout>
### Tools aktualisieren ## Verwendung von MCP-Tools in Agenten
Klicken Sie bei einem Server auf **Aktualisieren**, um die neuesten Tool-Schemas abzurufen und alle Agent-Blöcke, die diese Tools verwenden, automatisch mit den neuen Parameterdefinitionen zu aktualisieren. Sobald MCP-Server konfiguriert sind, werden ihre Tools innerhalb Ihrer Agent-Blöcke verfügbar:
## MCP-Tools in Agents verwenden
Sobald MCP-Server konfiguriert sind, werden ihre Tools in Ihren Agent-Blöcken verfügbar:
<div className="flex justify-center"> <div className="flex justify-center">
<Image <Image
src="/static/blocks/mcp-2.png" src="/static/blocks/mcp-2.png"
alt="Using MCP Tool in Agent Block" alt="Verwendung eines MCP-Tools im Agent-Block"
width={700} width={700}
height={450} height={450}
className="my-6" className="my-6"
@@ -55,25 +55,25 @@ Sobald MCP-Server konfiguriert sind, werden ihre Tools in Ihren Agent-Blöcken v
</div> </div>
1. Öffnen Sie einen **Agent**-Block 1. Öffnen Sie einen **Agent**-Block
2. Im Bereich **Tools** sehen Sie die verfügbaren MCP-Tools 2. Im Abschnitt **Tools** sehen Sie die verfügbaren MCP-Tools
3. Wählen Sie die Tools aus, die der Agent verwenden soll 3. Wählen Sie die Tools aus, die der Agent verwenden soll
4. Der Agent kann nun während der Ausführung auf diese Tools zugreifen 4. Der Agent kann nun während der Ausführung auf diese Tools zugreifen
## Eigenständiger MCP-Tool-Block ## Eigenständiger MCP-Tool-Block
Für eine präzisere Steuerung können Sie den dedizierten MCP-Tool-Block verwenden, um bestimmte MCP-Tools auszuführen: Für eine genauere Kontrolle können Sie den dedizierten MCP-Tool-Block verwenden, um bestimmte MCP-Tools auszuführen:
<div className="flex justify-center"> <div className="flex justify-center">
<Image <Image
src="/static/blocks/mcp-3.png" src="/static/blocks/mcp-3.png"
alt="Standalone MCP Tool Block" alt="Eigenständiger MCP-Tool-Block"
width={700} width={700}
height={450} height={450}
className="my-6" className="my-6"
/> />
</div> </div>
Der MCP-Tool-Block ermöglicht Ihnen: Der MCP-Tool-Block ermöglicht es Ihnen:
- Jedes konfigurierte MCP-Tool direkt auszuführen - Jedes konfigurierte MCP-Tool direkt auszuführen
- Spezifische Parameter an das Tool zu übergeben - Spezifische Parameter an das Tool zu übergeben
- Die Ausgabe des Tools in nachfolgenden Workflow-Schritten zu verwenden - Die Ausgabe des Tools in nachfolgenden Workflow-Schritten zu verwenden
@@ -81,9 +81,9 @@ Der MCP-Tool-Block ermöglicht Ihnen:
### Wann MCP-Tool vs. Agent verwenden ### Wann MCP-Tool vs. Agent verwenden
**Verwenden Sie Agent mit MCP-Tools, wenn:** **Verwenden Sie einen Agenten mit MCP-Tools, wenn:**
- Sie möchten, dass die KI entscheidet, welche Tools verwendet werden - Sie möchten, dass die KI entscheidet, welche Tools zu verwenden sind
- Sie komplexes Reasoning darüber benötigen, wann und wie Tools verwendet werden - Sie komplexe Überlegungen benötigen, wann und wie Tools eingesetzt werden sollen
- Sie eine natürlichsprachliche Interaktion mit den Tools wünschen - Sie eine natürlichsprachliche Interaktion mit den Tools wünschen
**Verwenden Sie den MCP-Tool-Block, wenn:** **Verwenden Sie den MCP-Tool-Block, wenn:**
@@ -93,7 +93,7 @@ Der MCP-Tool-Block ermöglicht Ihnen:
## Berechtigungsanforderungen ## Berechtigungsanforderungen
Die MCP-Funktionalität erfordert spezifische Workspace-Berechtigungen: MCP-Funktionalität erfordert spezifische Workspace-Berechtigungen:
| Aktion | Erforderliche Berechtigung | | Aktion | Erforderliche Berechtigung |
|--------|-------------------| |--------|-------------------|
@@ -105,7 +105,7 @@ Die MCP-Funktionalität erfordert spezifische Workspace-Berechtigungen:
## Häufige Anwendungsfälle ## Häufige Anwendungsfälle
### Datenbankintegration ### Datenbankintegration
Verbinden Sie sich mit Datenbanken, um Daten in Ihren Workflows abzufragen, einzufügen oder zu aktualisieren. Verbinden Sie sich mit Datenbanken, um Daten innerhalb Ihrer Workflows abzufragen, einzufügen oder zu aktualisieren.
### API-Integrationen ### API-Integrationen
Greifen Sie auf externe APIs und Webdienste zu, die keine integrierten Sim-Integrationen haben. Greifen Sie auf externe APIs und Webdienste zu, die keine integrierten Sim-Integrationen haben.
@@ -113,8 +113,8 @@ Greifen Sie auf externe APIs und Webdienste zu, die keine integrierten Sim-Integ
### Dateisystemzugriff ### Dateisystemzugriff
Lesen, schreiben und bearbeiten Sie Dateien auf lokalen oder entfernten Dateisystemen. Lesen, schreiben und bearbeiten Sie Dateien auf lokalen oder entfernten Dateisystemen.
### Individuelle Geschäftslogik ### Benutzerdefinierte Geschäftslogik
Führen Sie benutzerdefinierte Skripte oder Tools aus, die spezifisch für die Anforderungen Ihrer Organisation sind. Führen Sie benutzerdefinierte Skripte oder Tools aus, die auf die Bedürfnisse Ihrer Organisation zugeschnitten sind.
### Echtzeit-Datenzugriff ### Echtzeit-Datenzugriff
Rufen Sie Live-Daten von externen Systemen während der Workflow-Ausführung ab. Rufen Sie Live-Daten von externen Systemen während der Workflow-Ausführung ab.
@@ -128,12 +128,12 @@ Rufen Sie Live-Daten von externen Systemen während der Workflow-Ausführung ab.
## Fehlerbehebung ## Fehlerbehebung
### MCP-Server wird nicht angezeigt ### MCP-Server erscheint nicht
- Überprüfen Sie, ob die Serverkonfiguration korrekt ist - Überprüfen Sie, ob die Serverkonfiguration korrekt ist
- Prüfen Sie, ob Sie über die erforderlichen Berechtigungen verfügen - Prüfen Sie, ob Sie die erforderlichen Berechtigungen haben
- Stellen Sie sicher, dass der MCP-Server läuft und erreichbar ist - Stellen Sie sicher, dass der MCP-Server läuft und zugänglich ist
### Tool-Ausführungsfehler ### Fehler bei der Tool-Ausführung
- Überprüfen Sie, ob die Tool-Parameter korrekt formatiert sind - Überprüfen Sie, ob die Tool-Parameter korrekt formatiert sind
- Prüfen Sie die MCP-Server-Logs auf Fehlermeldungen - Prüfen Sie die MCP-Server-Logs auf Fehlermeldungen
- Stellen Sie sicher, dass die erforderliche Authentifizierung konfiguriert ist - Stellen Sie sicher, dass die erforderliche Authentifizierung konfiguriert ist
@@ -141,4 +141,4 @@ Rufen Sie Live-Daten von externen Systemen während der Workflow-Ausführung ab.
### Berechtigungsfehler ### Berechtigungsfehler
- Bestätigen Sie Ihre Workspace-Berechtigungsstufe - Bestätigen Sie Ihre Workspace-Berechtigungsstufe
- Prüfen Sie, ob der MCP-Server zusätzliche Authentifizierung erfordert - Prüfen Sie, ob der MCP-Server zusätzliche Authentifizierung erfordert
- Überprüfen Sie, ob der Server ordnungsgemäß für Ihren Workspace konfiguriert ist - Stellen Sie sicher, dass der Server für Ihren Workspace richtig konfiguriert ist

View File

@@ -1,394 +0,0 @@
---
title: Kurzreferenz
description: Wesentliche Aktionen zum Navigieren und Verwenden des Sim-Workflow-Editors
---
import { Callout } from 'fumadocs-ui/components/callout'
import { ActionImage, ActionVideo } from '@/components/ui/action-media'
Eine schnelle Übersicht für alltägliche Aktionen im Sim-Workflow-Editor. Für Tastaturkürzel siehe [Tastaturkürzel](/keyboard-shortcuts).
<Callout type="info">
**Mod** bezieht sich auf `Cmd` unter macOS und `Ctrl` unter Windows/Linux.
</Callout>
## Arbeitsbereiche
<table>
<thead>
<tr><th>Aktion</th><th>Wie</th><th>Vorschau</th></tr>
</thead>
<tbody>
<tr>
<td>Arbeitsbereich erstellen</td>
<td>Arbeitsbereich-Dropdown anklicken → **Neuer Arbeitsbereich**</td>
<td><ActionVideo src="quick-reference/create-workspace.mp4" alt="Arbeitsbereich erstellen" /></td>
</tr>
<tr>
<td>Arbeitsbereiche wechseln</td>
<td>Arbeitsbereich-Dropdown anklicken → Arbeitsbereich auswählen</td>
<td><ActionVideo src="quick-reference/switch-workspace.mp4" alt="Arbeitsbereiche wechseln" /></td>
</tr>
<tr>
<td>Teammitglieder einladen</td>
<td>Seitenleiste → **Einladen**</td>
<td><ActionVideo src="quick-reference/invite.mp4" alt="Teammitglieder einladen" /></td>
</tr>
<tr>
<td>Arbeitsbereich umbenennen</td>
<td>Rechtsklick auf Arbeitsbereich → **Umbenennen**</td>
<td rowSpan={4}><ActionImage src="/static/quick-reference/workspace-context-menu.png" alt="Arbeitsbereich-Kontextmenü" /></td>
</tr>
<tr>
<td>Arbeitsbereich duplizieren</td>
<td>Rechtsklick auf Arbeitsbereich → **Duplizieren**</td>
</tr>
<tr>
<td>Arbeitsbereich exportieren</td>
<td>Rechtsklick auf Arbeitsbereich → **Exportieren**</td>
</tr>
<tr>
<td>Arbeitsbereich löschen</td>
<td>Rechtsklick auf Arbeitsbereich → **Löschen**</td>
</tr>
</tbody>
</table>
## Workflows
<table>
<thead>
<tr><th>Aktion</th><th>Wie</th><th>Vorschau</th></tr>
</thead>
<tbody>
<tr>
<td>Workflow erstellen</td>
<td>**+**-Schaltfläche in der Seitenleiste anklicken</td>
<td><ActionImage src="/static/quick-reference/create-workflow.png" alt="Workflow erstellen" /></td>
</tr>
<tr>
<td>Workflows neu anordnen / verschieben</td>
<td>Workflow nach oben/unten oder auf einen Ordner ziehen</td>
<td><ActionVideo src="quick-reference/reordering.mp4" alt="Workflows neu anordnen" /></td>
</tr>
<tr>
<td>Workflow importieren</td>
<td>Import-Schaltfläche in der Seitenleiste anklicken → Datei auswählen</td>
<td><ActionImage src="/static/quick-reference/import-workflow.png" alt="Workflow importieren" /></td>
</tr>
<tr>
<td>Mehrere Workflows auswählen</td>
<td>`Mod+Click` oder `Shift+Click` Workflows in der Seitenleiste</td>
<td><ActionVideo src="quick-reference/multiselect.mp4" alt="Mehrere Workflows auswählen" /></td>
</tr>
<tr>
<td>In neuem Tab öffnen</td>
<td>Rechtsklick auf Workflow → **In neuem Tab öffnen**</td>
<td rowSpan={6}><ActionImage src="/static/quick-reference/workflow-context-menu.png" alt="Workflow-Kontextmenü" /></td>
</tr>
<tr>
<td>Workflow umbenennen</td>
<td>Rechtsklick auf Workflow → **Umbenennen**</td>
</tr>
<tr>
<td>Workflow-Farbe zuweisen</td>
<td>Rechtsklick auf Workflow → **Farbe ändern**</td>
</tr>
<tr>
<td>Workflow duplizieren</td>
<td>Rechtsklick auf Workflow → **Duplizieren**</td>
</tr>
<tr>
<td>Workflow exportieren</td>
<td>Rechtsklick auf Workflow → **Exportieren**</td>
</tr>
<tr>
<td>Workflow löschen</td>
<td>Rechtsklick auf Workflow → **Löschen**</td>
</tr>
<tr>
<td>Ordner umbenennen</td>
<td>Rechtsklick auf Ordner → **Umbenennen**</td>
<td rowSpan={6}><ActionImage src="/static/quick-reference/folder-context-menu.png" alt="Ordner-Kontextmenü" /></td>
</tr>
<tr>
<td>Workflow in Ordner erstellen</td>
<td>Rechtsklick auf Ordner → **Workflow erstellen**</td>
</tr>
<tr>
<td>Ordner in Ordner erstellen</td>
<td>Rechtsklick auf Ordner → **Ordner erstellen**</td>
</tr>
<tr>
<td>Ordner duplizieren</td>
<td>Rechtsklick auf Ordner → **Duplizieren**</td>
</tr>
<tr>
<td>Ordner exportieren</td>
<td>Rechtsklick auf Ordner → **Exportieren**</td>
</tr>
<tr>
<td>Ordner löschen</td>
<td>Rechtsklick auf Ordner → **Löschen**</td>
</tr>
</tbody>
</table>
## Blöcke
<table>
<thead>
<tr><th>Aktion</th><th>Wie</th><th>Vorschau</th></tr>
</thead>
<tbody>
<tr>
<td>Block hinzufügen</td>
<td>Aus Toolbar-Panel ziehen oder Rechtsklick auf Canvas → **Block hinzufügen**</td>
<td><ActionVideo src="quick-reference/add-block.mp4" alt="Block hinzufügen" /></td>
</tr>
<tr>
<td>Mehrere Blöcke auswählen</td>
<td>`Mod+Click` zusätzliche Blöcke oder Shift-Ziehen für Auswahlrahmen</td>
<td><ActionVideo src="quick-reference/multiselect-blocks.mp4" alt="Mehrere Blöcke auswählen" /></td>
</tr>
<tr>
<td>Blöcke kopieren</td>
<td>`Mod+C` mit ausgewählten Blöcken</td>
<td rowSpan={2}><ActionVideo src="quick-reference/copy-paste.mp4" alt="Blöcke kopieren und einfügen" /></td>
</tr>
<tr>
<td>Blöcke einfügen</td>
<td>`Mod+V` zum Einfügen kopierter Blöcke</td>
</tr>
<tr>
<td>Blöcke duplizieren</td>
<td>Rechtsklick → **Duplizieren**</td>
<td><ActionVideo src="quick-reference/duplicate-block.mp4" alt="Blöcke duplizieren" /></td>
</tr>
<tr>
<td>Blöcke löschen</td>
<td>`Delete` oder `Backspace` Taste oder Rechtsklick → **Löschen**</td>
<td><ActionImage src="/static/quick-reference/delete-block.png" alt="Block löschen" /></td>
</tr>
<tr>
<td>Block umbenennen</td>
<td>Auf Blocknamen im Header klicken oder im Editor-Panel bearbeiten</td>
<td><ActionVideo src="quick-reference/rename-block.mp4" alt="Block umbenennen" /></td>
</tr>
<tr>
<td>Block aktivieren/deaktivieren</td>
<td>Rechtsklick → **Aktivieren/Deaktivieren**</td>
<td><ActionImage src="/static/quick-reference/disable-block.png" alt="Block deaktivieren" /></td>
</tr>
<tr>
<td>Block sperren/entsperren</td>
<td>Über Block hovern → Auf Schloss-Symbol klicken (nur Admin)</td>
<td><ActionImage src="/static/quick-reference/lock-block.png" alt="Block sperren" /></td>
</tr>
<tr>
<td>Handle-Ausrichtung umschalten</td>
<td>Rechtsklick → **Handles umschalten**</td>
<td><ActionVideo src="quick-reference/toggle-handles.mp4" alt="Handle-Ausrichtung umschalten" /></td>
</tr>
<tr>
<td>Block konfigurieren</td>
<td>Block auswählen → Editor-Panel rechts verwenden</td>
<td><ActionVideo src="quick-reference/configure-block.mp4" alt="Block konfigurieren" /></td>
</tr>
</tbody>
</table>
## Verbindungen
<table>
<thead>
<tr><th>Aktion</th><th>Wie</th><th>Vorschau</th></tr>
</thead>
<tbody>
<tr>
<td>Verbindung erstellen</td>
<td>Vom Ausgangs-Handle zum Eingangs-Handle ziehen</td>
<td><ActionVideo src="quick-reference/connect-blocks.mp4" alt="Blöcke verbinden" /></td>
</tr>
<tr>
<td>Verbindung löschen</td>
<td>Auf Kante klicken zum Auswählen → `Delete` Taste</td>
<td><ActionVideo src="quick-reference/delete-connection.mp4" alt="Verbindung löschen" /></td>
</tr>
<tr>
<td>Ausgabe in anderem Block verwenden</td>
<td>Verbindungs-Tag in Eingabefeld ziehen</td>
<td><ActionVideo src="quick-reference/connection-tag.mp4" alt="Verbindungs-Tag verwenden" /></td>
</tr>
</tbody>
</table>
## Panels und Ansichten
<table>
<thead>
<tr><th>Aktion</th><th>Wie</th><th>Vorschau</th></tr>
</thead>
<tbody>
<tr>
<td>Symbolleiste durchsuchen</td>
<td>`Mod+F`</td>
<td><ActionVideo src="quick-reference/search-toolbar.mp4" alt="Symbolleiste durchsuchen" /></td>
</tr>
<tr>
<td>Alles durchsuchen</td>
<td>`Mod+K`</td>
<td><ActionImage src="/static/quick-reference/search-everything.png" alt="Alles durchsuchen" /></td>
</tr>
<tr>
<td>Manuellen Modus umschalten</td>
<td>Klicken Sie auf die Umschalt-Schaltfläche, um zwischen manuell und Selektor zu wechseln</td>
<td><ActionImage src="/static/quick-reference/toggle-manual-mode.png" alt="Manuellen Modus umschalten" /></td>
</tr>
<tr>
<td>Seitenleiste ein-/ausklappen</td>
<td>Klicken Sie auf die Einklappen-Schaltfläche in der Seitenleiste</td>
<td><ActionVideo src="quick-reference/collapse-sidebar.mp4" alt="Seitenleiste einklappen" /></td>
</tr>
</tbody>
</table>
## Ausführen und Testen
<table>
<thead>
<tr><th>Aktion</th><th>Wie</th><th>Vorschau</th></tr>
</thead>
<tbody>
<tr>
<td>Workflow ausführen</td>
<td>Klicken Sie auf die Schaltfläche Workflow ausführen oder `Mod+Enter`</td>
<td><ActionImage src="/static/quick-reference/run-workflow.png" alt="Workflow ausführen" /></td>
</tr>
<tr>
<td>Workflow stoppen</td>
<td>Klicken Sie auf die Stopp-Schaltfläche oder `Mod+Enter` während der Ausführung</td>
<td><ActionImage src="/static/quick-reference/stop-workflow.png" alt="Workflow stoppen" /></td>
</tr>
<tr>
<td>Mit Chat testen</td>
<td>Verwenden Sie das Chat-Panel auf der rechten Seite</td>
<td><ActionImage src="/static/quick-reference/test-chat.png" alt="Mit Chat testen" /></td>
</tr>
<tr>
<td>Ausgabe zum Anzeigen auswählen</td>
<td>Klicken Sie auf das Dropdown-Menü im Chat-Panel → Wählen Sie Block-Ausgabe aus</td>
<td><ActionImage src="/static/quick-reference/output-select.png" alt="Ausgabe zum Anzeigen auswählen" /></td>
</tr>
<tr>
<td>Chat-Verlauf löschen</td>
<td>Klicken Sie auf die Löschen-Schaltfläche im Chat-Panel</td>
<td><ActionImage src="/static/quick-reference/clear-chat.png" alt="Chat-Verlauf löschen" /></td>
</tr>
<tr>
<td>Ab Block ausführen</td>
<td>Bewegen Sie den Mauszeiger über den Block → Klicken Sie auf die Wiedergabe-Schaltfläche oder Rechtsklick → **Ab Block ausführen**</td>
<td><ActionImage src="/static/quick-reference/run-from-block.png" alt="Ab Block ausführen" /></td>
</tr>
<tr>
<td>Bis Block ausführen</td>
<td>Rechtsklick auf Block → **Bis Block ausführen**</td>
<td><ActionImage src="/static/quick-reference/run-until-block.png" alt="Bis Block ausführen" /></td>
</tr>
<tr>
<td>Ausführungsprotokolle anzeigen</td>
<td>Öffnen Sie das Terminal-Panel unten oder `Mod+L`</td>
<td><ActionImage src="/static/quick-reference/terminal.png" alt="Terminal für Ausführungsprotokolle" /></td>
</tr>
<tr>
<td>Protokolle filtern</td>
<td>Klicken Sie auf das Filter-Symbol im Terminal → Filtern Sie nach Block oder Status</td>
<td><ActionImage src="/static/quick-reference/filter-block.png" alt="Protokolle nach Block filtern" /></td>
</tr>
<tr>
<td>Protokolle durchsuchen</td>
<td>Verwenden Sie das Suchfeld im Terminal oder Rechtsklick auf Protokolleintrag → **Suchen**</td>
<td><ActionImage src="/static/quick-reference/terminal-search.png" alt="Protokolle durchsuchen" /></td>
</tr>
<tr>
<td>Protokolleintrag kopieren</td>
<td>Zwischenablage-Symbol oder Rechtsklick auf Protokolleintrag → **Kopieren**</td>
<td><ActionImage src="/static/quick-reference/copy-log.png" alt="Protokolleintrag kopieren" /></td>
</tr>
<tr>
<td>Terminal leeren</td>
<td>Papierkorb-Symbol oder `Mod+D`</td>
<td><ActionImage src="/static/quick-reference/clear-terminal.png" alt="Terminal leeren" /></td>
</tr>
</tbody>
</table>
## Bereitstellung
<table>
<thead>
<tr><th>Aktion</th><th>Wie</th><th>Vorschau</th></tr>
</thead>
<tbody>
<tr>
<td>Workflow bereitstellen</td>
<td>Klicken Sie auf die Schaltfläche **Bereitstellen** im Panel</td>
<td><ActionImage src="/static/quick-reference/deploy.png" alt="Workflow bereitstellen" /></td>
</tr>
<tr>
<td>Bereitstellung aktualisieren</td>
<td>Klicken Sie auf **Aktualisieren**, wenn Änderungen erkannt werden</td>
<td><ActionImage src="/static/quick-reference/update-deployment.png" alt="Bereitstellung aktualisieren" /></td>
</tr>
<tr>
<td>Bereitstellungsstatus anzeigen</td>
<td>Überprüfen Sie die Statusanzeige (Live/Aktualisieren/Bereitstellen) im Tab „Bereitstellen"</td>
<td><ActionImage src="/static/quick-reference/view-deployment.png" alt="Bereitstellungsstatus anzeigen" /></td>
</tr>
<tr>
<td>Bereitstellung zurücksetzen</td>
<td>Greifen Sie auf frühere Versionen im Tab „Bereitstellen" zu → **Zu Live befördern**</td>
<td><ActionImage src="/static/quick-reference/promote-deployment.png" alt="Bereitstellung zu Live befördern" /></td>
</tr>
<tr>
<td>Versionsbeschreibung hinzufügen</td>
<td>Tab „Bereitstellen" → Klicken Sie auf das Beschreibungssymbol → Beschreibung hinzufügen oder generieren</td>
<td><ActionVideo src="quick-reference/deployment-description.mp4" alt="Versionsbeschreibung für Bereitstellung hinzufügen" /></td>
</tr>
<tr>
<td>API-Endpunkt kopieren</td>
<td>Tab „Bereitstellen" → API → API-cURL kopieren</td>
<td><ActionImage src="/static/quick-reference/copy-api.png" alt="API-Endpunkt kopieren" /></td>
</tr>
</tbody>
</table>
## Variablen
<table>
<thead>
<tr><th>Aktion</th><th>Wie</th><th>Vorschau</th></tr>
</thead>
<tbody>
<tr>
<td>Workflow-Variable hinzufügen / bearbeiten / löschen</td>
<td>Panel → Variablen → **Variable hinzufügen**, zum Bearbeiten klicken oder Löschsymbol verwenden</td>
<td><ActionImage src="/static/quick-reference/variables.png" alt="Variablen-Panel" /></td>
</tr>
<tr>
<td>Umgebungsvariable hinzufügen</td>
<td>Einstellungen → **Umgebungsvariablen** → **Hinzufügen**</td>
<td><ActionImage src="/static/quick-reference/add-env-variable.png" alt="Umgebungsvariable hinzufügen" /></td>
</tr>
<tr>
<td>Auf Workflow-Variable verweisen</td>
<td>Verwenden Sie die Syntax `<blockName.itemName>` in Block-Eingaben</td>
<td><ActionImage src="/static/quick-reference/variable-reference.png" alt="Auf Workflow-Variable verweisen" /></td>
</tr>
<tr>
<td>Auf Umgebungsvariable verweisen</td>
<td>Verwenden Sie die Syntax `{{ENV_VAR}}` in Block-Eingaben</td>
<td><ActionImage src="/static/quick-reference/env-variable-reference.png" alt="Auf Umgebungsvariable verweisen" /></td>
</tr>
</tbody>
</table>

View File

@@ -7,10 +7,10 @@ import { Card, Cards } from 'fumadocs-ui/components/card'
import { Step, Steps } from 'fumadocs-ui/components/steps' import { Step, Steps } from 'fumadocs-ui/components/steps'
import { Tab, Tabs } from 'fumadocs-ui/components/tabs' import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
Das offizielle Python SDK für Sim ermöglicht es Ihnen, Workflows programmatisch aus Ihren Python-Anwendungen heraus mit dem offiziellen Python SDK auszuführen. Das offizielle Python SDK für Sim ermöglicht es Ihnen, Workflows programmatisch aus Ihren Python-Anwendungen mithilfe des offiziellen Python SDKs auszuführen.
<Callout type="info"> <Callout type="info">
Das Python SDK unterstützt Python 3.8+ mit Unterstützung für asynchrone Ausführung, automatischer Ratenbegrenzung mit exponentiellem Backoff und Nutzungsverfolgung. Das Python SDK unterstützt Python 3.8+ mit asynchroner Ausführungsunterstützung, automatischer Ratenbegrenzung mit exponentiellem Backoff und Nutzungsverfolgung.
</Callout> </Callout>
## Installation ## Installation
@@ -75,16 +75,16 @@ result = client.execute_workflow(
- `input_data` (dict, optional): Eingabedaten, die an den Workflow übergeben werden - `input_data` (dict, optional): Eingabedaten, die an den Workflow übergeben werden
- `timeout` (float, optional): Timeout in Sekunden (Standard: 30.0) - `timeout` (float, optional): Timeout in Sekunden (Standard: 30.0)
- `stream` (bool, optional): Streaming-Antworten aktivieren (Standard: False) - `stream` (bool, optional): Streaming-Antworten aktivieren (Standard: False)
- `selected_outputs` (list[str], optional): Block-Ausgaben zum Streamen im Format `blockName.attribute` (z. B. `["agent1.content"]`) - `selected_outputs` (list[str], optional): Block-Ausgaben, die im `blockName.attribute`Format gestreamt werden sollen (z.B. `["agent1.content"]`)
- `async_execution` (bool, optional): Asynchron ausführen (Standard: False) - `async_execution` (bool, optional): Asynchron ausführen (Standard: False)
**Rückgabewert:** `WorkflowExecutionResult | AsyncExecutionResult` **Rückgabe:** `WorkflowExecutionResult | AsyncExecutionResult`
Wenn `async_execution=True`, wird sofort mit einer Task-ID zum Polling zurückgegeben. Andernfalls wird auf die Fertigstellung gewartet. Wenn `async_execution=True`, wird sofort mit einer Task-ID zum Abfragen zurückgegeben. Andernfalls wird auf den Abschluss gewartet.
##### get_workflow_status() ##### get_workflow_status()
Ruft den Status eines Workflows ab (Deployment-Status usw.). Den Status eines Workflows abrufen (Bereitstellungsstatus usw.).
```python ```python
status = client.get_workflow_status("workflow-id") status = client.get_workflow_status("workflow-id")
@@ -98,7 +98,7 @@ print("Is deployed:", status.is_deployed)
##### validate_workflow() ##### validate_workflow()
Überprüft, ob ein Workflow zur Ausführung bereit ist. Überprüfen, ob ein Workflow für die Ausführung bereit ist.
```python ```python
is_ready = client.validate_workflow("workflow-id") is_ready = client.validate_workflow("workflow-id")
@@ -114,7 +114,7 @@ if is_ready:
##### get_job_status() ##### get_job_status()
Ruft den Status einer asynchronen Job-Ausführung ab. Den Status einer asynchronen Job-Ausführung abrufen.
```python ```python
status = client.get_job_status("task-id-from-async-execution") status = client.get_job_status("task-id-from-async-execution")
@@ -131,7 +131,7 @@ if status["status"] == "completed":
**Antwortfelder:** **Antwortfelder:**
- `success` (bool): Ob die Anfrage erfolgreich war - `success` (bool): Ob die Anfrage erfolgreich war
- `taskId` (str): Die Task-ID - `taskId` (str): Die Task-ID
- `status` (str): Einer von `'queued'`, `'processing'`, `'completed'`, `'failed'`, `'cancelled'` - `status` (str): Einer der Werte `'queued'`, `'processing'`, `'completed'`, `'failed'`, `'cancelled'`
- `metadata` (dict): Enthält `startedAt`, `completedAt` und `duration` - `metadata` (dict): Enthält `startedAt`, `completedAt` und `duration`
- `output` (any, optional): Die Workflow-Ausgabe (wenn abgeschlossen) - `output` (any, optional): Die Workflow-Ausgabe (wenn abgeschlossen)
- `error` (any, optional): Fehlerdetails (wenn fehlgeschlagen) - `error` (any, optional): Fehlerdetails (wenn fehlgeschlagen)
@@ -139,7 +139,7 @@ if status["status"] == "completed":
##### execute_with_retry() ##### execute_with_retry()
Führt einen Workflow mit automatischer Wiederholung bei Rate-Limit-Fehlern unter Verwendung von exponentiellem Backoff aus. Einen Workflow mit automatischer Wiederholung bei Ratenbegrenzungsfehlern unter Verwendung von exponentiellem Backoff ausführen.
```python ```python
result = client.execute_with_retry( result = client.execute_with_retry(
@@ -161,13 +161,13 @@ result = client.execute_with_retry(
- `selected_outputs` (list, optional): Block-Ausgaben zum Streamen - `selected_outputs` (list, optional): Block-Ausgaben zum Streamen
- `async_execution` (bool, optional): Asynchron ausführen - `async_execution` (bool, optional): Asynchron ausführen
- `max_retries` (int, optional): Maximale Anzahl von Wiederholungen (Standard: 3) - `max_retries` (int, optional): Maximale Anzahl von Wiederholungen (Standard: 3)
- `initial_delay` (float, optional): Anfangsverzögerung in Sekunden (Standard: 1.0) - `initial_delay` (float, optional): Anfängliche Verzögerung in Sekunden (Standard: 1.0)
- `max_delay` (float, optional): Maximale Verzögerung in Sekunden (Standard: 30.0) - `max_delay` (float, optional): Maximale Verzögerung in Sekunden (Standard: 30.0)
- `backoff_multiplier` (float, optional): Backoff-Multiplikator (Standard: 2.0) - `backoff_multiplier` (float, optional): Backoff-Multiplikator (Standard: 2.0)
**Rückgabe:** `WorkflowExecutionResult | AsyncExecutionResult` **Rückgabewert:** `WorkflowExecutionResult | AsyncExecutionResult`
Die Wiederholungslogik verwendet exponentielles Backoff (1s → 2s → 4s → 8s...) mit ±25% Jitter, um Thundering Herd zu verhindern. Wenn die API einen `retry-after`-Header bereitstellt, wird dieser stattdessen verwendet. Die Wiederholungslogik verwendet exponentielles Backoff (1s → 2s → 4s → 8s...) mit ±25% Jitter, um den Thundering-Herd-Effekt zu vermeiden. Wenn die API einen `retry-after` Header bereitstellt, wird dieser stattdessen verwendet.
##### get_rate_limit_info() ##### get_rate_limit_info()
@@ -185,7 +185,7 @@ if rate_limit_info:
##### get_usage_limits() ##### get_usage_limits()
Ruft aktuelle Nutzungslimits und Kontingentinformationen für Ihr Konto ab. Ruft aktuelle Nutzungslimits und Kontingentinformationen für dein Konto ab.
```python ```python
limits = client.get_usage_limits() limits = client.get_usage_limits()
@@ -320,9 +320,9 @@ class SimStudioError(Exception):
**Häufige Fehlercodes:** **Häufige Fehlercodes:**
- `UNAUTHORIZED`: Ungültiger API-Schlüssel - `UNAUTHORIZED`: Ungültiger API-Schlüssel
- `TIMEOUT`: Zeitüberschreitung der Anfrage - `TIMEOUT`: Zeitüberschreitung bei der Anfrage
- `RATE_LIMIT_EXCEEDED`: Ratenlimit überschritten - `RATE_LIMIT_EXCEEDED`: Ratengrenze überschritten
- `USAGE_LIMIT_EXCEEDED`: Nutzungslimit überschritten - `USAGE_LIMIT_EXCEEDED`: Nutzungsgrenze überschritten
- `EXECUTION_ERROR`: Workflow-Ausführung fehlgeschlagen - `EXECUTION_ERROR`: Workflow-Ausführung fehlgeschlagen
## Beispiele ## Beispiele
@@ -334,7 +334,7 @@ class SimStudioError(Exception):
Richten Sie den SimStudioClient mit Ihrem API-Schlüssel ein. Richten Sie den SimStudioClient mit Ihrem API-Schlüssel ein.
</Step> </Step>
<Step title="Workflow validieren"> <Step title="Workflow validieren">
Prüfen Sie, ob der Workflow bereitgestellt und zur Ausführung bereit ist. Prüfen Sie, ob der Workflow bereitgestellt und für die Ausführung bereit ist.
</Step> </Step>
<Step title="Workflow ausführen"> <Step title="Workflow ausführen">
Führen Sie den Workflow mit Ihren Eingabedaten aus. Führen Sie den Workflow mit Ihren Eingabedaten aus.
@@ -386,7 +386,7 @@ Behandeln Sie verschiedene Fehlertypen, die während der Workflow-Ausführung au
from simstudio import SimStudioClient, SimStudioError from simstudio import SimStudioClient, SimStudioError
import os import os
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY")) client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def execute_with_error_handling(): def execute_with_error_handling():
try: try:
@@ -409,20 +409,11 @@ def execute_with_error_handling():
raise raise
``` ```
### Verwendung des Context-Managers ### Verwendung des Kontextmanagers
Verwenden Sie den Client als Context-Manager, um die Ressourcenbereinigung automatisch zu handhaben: Verwenden Sie den Client als Kontextmanager, um die Ressourcenbereinigung automatisch zu handhaben:
```python ---CODE-PLACEHOLDER-ef99d3dd509e04865d5b6b0e0e03d3f8---
from simstudio import SimStudioClient
import os
# Using context manager to automatically close the session
with SimStudioClient(api_key=os.getenv("SIM_API_KEY")) as client:
result = client.execute_workflow("workflow-id")
print("Result:", result)
# Session is automatically closed here
```
### Batch-Workflow-Ausführung ### Batch-Workflow-Ausführung
@@ -475,7 +466,7 @@ for result in results:
### Asynchrone Workflow-Ausführung ### Asynchrone Workflow-Ausführung
Führen Sie Workflows asynchron für langwierige Aufgaben aus: Führen Sie Workflows asynchron für lang laufende Aufgaben aus:
```python ```python
import os import os
@@ -519,9 +510,9 @@ def execute_async():
execute_async() execute_async()
``` ```
### Ratenlimitierung und Wiederholung ### Rate-Limiting und Wiederholungsversuche
Behandeln Sie Ratenbegrenzungen automatisch mit exponentiellem Backoff: Behandle Rate-Limits automatisch mit exponentiellem Backoff:
```python ```python
import os import os
@@ -558,7 +549,7 @@ execute_with_retry_handling()
### Nutzungsüberwachung ### Nutzungsüberwachung
Überwachen Sie die Nutzung und Limits Ihres Kontos: Überwache deine Kontonutzung und -limits:
```python ```python
import os import os
@@ -602,13 +593,13 @@ check_usage()
### Streaming-Workflow-Ausführung ### Streaming-Workflow-Ausführung
Führen Sie Workflows mit Echtzeit-Streaming-Antworten aus: Führe Workflows mit Echtzeit-Streaming-Antworten aus:
```python ```python
from simstudio import SimStudioClient from simstudio import SimStudioClient
import os import os
client = SimStudioClient(api_key=os.getenv("SIM_API_KEY")) client = SimStudioClient(api_key=os.getenv("SIM_API_KEY"))
def execute_with_streaming(): def execute_with_streaming():
"""Execute workflow with streaming enabled.""" """Execute workflow with streaming enabled."""
@@ -628,7 +619,7 @@ def execute_with_streaming():
execute_with_streaming() execute_with_streaming()
``` ```
Die Streaming-Antwort folgt dem Server-Sent-Events- (SSE-) Format: Die Streaming-Antwort folgt dem Server-Sent Events (SSE) Format:
``` ```
data: {"blockId":"7b7735b9-19e5-4bd6-818b-46aae2596e9f","chunk":"One"} data: {"blockId":"7b7735b9-19e5-4bd6-818b-46aae2596e9f","chunk":"One"}
@@ -697,9 +688,9 @@ if __name__ == '__main__':
app.run(debug=True) app.run(debug=True)
``` ```
### Umgebungs­konfiguration ### Umgebungskonfiguration
Konfigurieren Sie den Client mit Umgebungsvariablen: Konfiguriere den Client mit Umgebungsvariablen:
<Tabs items={['Development', 'Production']}> <Tabs items={['Development', 'Production']}>
<Tab value="Development"> <Tab value="Development">
@@ -736,27 +727,27 @@ Konfigurieren Sie den Client mit Umgebungsvariablen:
</Tab> </Tab>
</Tabs> </Tabs>
## Ihren API-Schlüssel erhalten ## API-Schlüssel erhalten
<Steps> <Steps>
<Step title="Bei Sim anmelden"> <Step title="Bei Sim anmelden">
Navigieren Sie zu [Sim](https://sim.ai) und melden Sie sich in Ihrem Konto an. Navigiere zu [Sim](https://sim.ai) und melde dich bei deinem Konto an.
</Step> </Step>
<Step title="Workflow öffnen"> <Step title="Öffne deinen Workflow">
Navigieren Sie zu dem Workflow, den Sie programmatisch ausführen möchten. Navigiere zu dem Workflow, den du programmatisch ausführen möchtest.
</Step> </Step>
<Step title="Workflow bereitstellen"> <Step title="Deploye deinen Workflow">
Klicken Sie auf "Bereitstellen", um Ihren Workflow bereitzustellen, falls dies noch nicht geschehen ist. Klicke auf "Deploy", um deinen Workflow zu deployen, falls dies noch nicht geschehen ist.
</Step> </Step>
<Step title="API-Schlüssel erstellen oder auswählen"> <Step title="Erstelle oder wähle einen API-Schlüssel">
Wählen oder erstellen Sie während des Bereitstellungsprozesses einen API-Schlüssel. Wähle während des Deployment-Prozesses einen API-Schlüssel aus oder erstelle einen neuen.
</Step> </Step>
<Step title="API-Schlüssel kopieren"> <Step title="Kopiere den API-Schlüssel">
Kopieren Sie den API-Schlüssel, um ihn in Ihrer Python-Anwendung zu verwenden. Kopiere den API-Schlüssel zur Verwendung in deiner Python-Anwendung.
</Step> </Step>
</Steps> </Steps>
## Voraussetzungen ## Anforderungen
- Python 3.8+ - Python 3.8+
- requests >= 2.25.0 - requests >= 2.25.0

View File

@@ -1,6 +1,6 @@
--- ---
title: Docker title: Docker
description: Sim mit Docker Compose bereitstellen description: Sim Studio mit Docker Compose bereitstellen
--- ---
import { Tab, Tabs } from 'fumadocs-ui/components/tabs' import { Tab, Tabs } from 'fumadocs-ui/components/tabs'

View File

@@ -1,6 +1,6 @@
--- ---
title: Umgebungsvariablen title: Umgebungsvariablen
description: Konfigurationsreferenz für Sim description: Konfigurationsreferenz für Sim Studio
--- ---
import { Callout } from 'fumadocs-ui/components/callout' import { Callout } from 'fumadocs-ui/components/callout'

View File

@@ -1,29 +1,21 @@
--- ---
title: Self-Hosting title: Self-Hosting
description: Stellen Sie Sim auf Ihrer eigenen Infrastruktur bereit description: Stellen Sie Sim Studio auf Ihrer eigenen Infrastruktur bereit
--- ---
import { Card, Cards } from 'fumadocs-ui/components/card' import { Card, Cards } from 'fumadocs-ui/components/card'
import { Callout } from 'fumadocs-ui/components/callout' import { Callout } from 'fumadocs-ui/components/callout'
Stellen Sie Sim auf Ihrer eigenen Infrastruktur mit Docker oder Kubernetes bereit. Stellen Sie Sim Studio auf Ihrer eigenen Infrastruktur mit Docker oder Kubernetes bereit.
## Anforderungen ## Anforderungen
| Ressource | Klein | Standard | Produktion | | Ressource | Minimum | Empfohlen |
|----------|-------|----------|------------| |----------|---------|-------------|
| CPU | 2 Kerne | 4 Kerne | 8+ Kerne | | CPU | 2 Kerne | 4+ Kerne |
| RAM | 12 GB | 16 GB | 32+ GB | | RAM | 12 GB | 16+ GB |
| Speicher | 20 GB SSD | 50 GB SSD | 100+ GB SSD | | Speicher | 20 GB SSD | 50+ GB SSD |
| Docker | 20.10+ | 20.10+ | Neueste Version | | Docker | 20.10+ | Neueste Version |
**Klein**: Entwicklung, Tests, Einzelnutzer (1-5 Nutzer)
**Standard**: Teams (5-50 Nutzer), moderate Arbeitslasten
**Produktion**: Große Teams (50+ Nutzer), Hochverfügbarkeit, intensive Workflow-Ausführung
<Callout type="info">
Die Ressourcenanforderungen werden durch Workflow-Ausführung (isolated-vm Sandboxing), Dateiverarbeitung (In-Memory-Dokumentenparsing) und Vektoroperationen (pgvector) bestimmt. Arbeitsspeicher ist typischerweise der limitierende Faktor, nicht CPU. Produktionsdaten zeigen, dass die Hauptanwendung durchschnittlich 4-8 GB und bei hoher Last bis zu 12 GB benötigt.
</Callout>
## Schnellstart ## Schnellstart
@@ -56,10 +48,3 @@ docker compose -f docker-compose.prod.yml up -d
| realtime | 3002 | WebSocket-Server | | realtime | 3002 | WebSocket-Server |
| db | 5432 | PostgreSQL mit pgvector | | db | 5432 | PostgreSQL mit pgvector |
| migrations | - | Datenbank-Migrationen (werden einmal ausgeführt) | | migrations | - | Datenbank-Migrationen (werden einmal ausgeführt) |
| Komponente | Port | Beschreibung |
|-----------|------|-------------|
| simstudio | 3000 | Hauptanwendung |
| realtime | 3002 | WebSocket-Server |
| db | 5432 | PostgreSQL mit pgvector |
| migrations | - | Datenbankmigrationen (wird einmal ausgeführt) |

View File

@@ -1,6 +1,6 @@
--- ---
title: Kubernetes title: Kubernetes
description: Sim mit Helm bereitstellen description: Sim Studio mit Helm bereitstellen
--- ---
import { Tab, Tabs } from 'fumadocs-ui/components/tabs' import { Tab, Tabs } from 'fumadocs-ui/components/tabs'

View File

@@ -1,6 +1,6 @@
--- ---
title: Cloud-Plattformen title: Cloud-Plattformen
description: Sim auf Cloud-Plattformen bereitstellen description: Sim Studio auf Cloud-Plattformen bereitstellen
--- ---
import { Tab, Tabs } from 'fumadocs-ui/components/tabs' import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
@@ -64,7 +64,7 @@ sudo usermod -aG docker $USER
docker --version docker --version
``` ```
### Sim bereitstellen ### Sim Studio bereitstellen
```bash ```bash
git clone https://github.com/simstudioai/sim.git && cd sim git clone https://github.com/simstudioai/sim.git && cd sim

View File

@@ -1,134 +0,0 @@
---
title: Agent-Fähigkeiten
---
import { Callout } from 'fumadocs-ui/components/callout'
Agent-Fähigkeiten sind wiederverwendbare Anweisungspakete, die Ihren KI-Agenten spezialisierte Funktionen verleihen. Basierend auf dem offenen [Agent Skills](https://agentskills.io)-Format ermöglichen Fähigkeiten Ihnen, Fachwissen, Arbeitsabläufe und Best Practices zu erfassen, die Agenten bei Bedarf laden können.
## Wie Fähigkeiten funktionieren
Fähigkeiten nutzen **progressive Offenlegung**, um den Kontext des Agenten schlank zu halten:
1. **Entdeckung** — Nur Fähigkeitsnamen und Beschreibungen werden in den System-Prompt des Agenten aufgenommen (~50-100 Token jeweils)
2. **Aktivierung** — Wenn der Agent entscheidet, dass eine Fähigkeit relevant ist, ruft er das `load_skill`-Tool auf, um die vollständigen Anweisungen in den Kontext zu laden
3. **Ausführung** — Der Agent folgt den geladenen Anweisungen, um die Aufgabe zu erledigen
Das bedeutet, Sie können viele Fähigkeiten an einen Agenten anhängen, ohne dessen Kontextfenster aufzublähen. Der Agent lädt nur das, was er benötigt.
## Fähigkeiten erstellen
Gehen Sie zu **Einstellungen** und wählen Sie **Fähigkeiten** im Bereich Tools aus.
![Manage Skills](/static/skills/manage-skills.png)
Klicken Sie auf **Hinzufügen**, um eine neue Fähigkeit mit drei Feldern zu erstellen:
| Feld | Beschreibung |
|-------|-------------|
| **Name** | Eine Kennung im Kebab-Case-Format (z. B. `sql-expert`, `code-reviewer`). Maximal 64 Zeichen. |
| **Beschreibung** | Eine kurze Erklärung, was die Fähigkeit tut und wann sie verwendet werden soll. Dies liest der Agent, um zu entscheiden, ob er die Fähigkeit aktiviert. Maximal 1024 Zeichen. |
| **Inhalt** | Die vollständigen Fähigkeitsanweisungen in Markdown. Diese werden geladen, wenn der Agent die Fähigkeit aktiviert. |
<Callout type="info">
Die Beschreibung ist entscheidend — sie ist das Einzige, was der Agent sieht, bevor er entscheidet, eine Fähigkeit zu laden. Seien Sie spezifisch darüber, wann und warum die Fähigkeit verwendet werden sollte.
</Callout>
### Gute Skill-Inhalte schreiben
Skill-Inhalte folgen denselben Konventionen wie [SKILL.md-Dateien](https://agentskills.io/specification):
```markdown
# SQL Expert
## When to use this skill
Use when the user asks you to write, optimize, or debug SQL queries.
## Instructions
1. Always ask which database engine (PostgreSQL, MySQL, SQLite)
2. Use CTEs over subqueries for readability
3. Add index recommendations when relevant
4. Explain query plans for optimization requests
## Common Patterns
...
```
**Empfohlene Struktur:**
- **Wann verwenden** — Spezifische Auslöser und Szenarien
- **Anweisungen** — Schritt-für-Schritt-Anleitung mit nummerierten Listen
- **Beispiele** — Eingabe-/Ausgabe-Beispiele, die das erwartete Verhalten zeigen
- **Häufige Muster** — Wiederverwendbare Ansätze für häufige Aufgaben
- **Sonderfälle** — Fallstricke und besondere Überlegungen
Halten Sie Skills fokussiert und unter 500 Zeilen. Wenn ein Skill zu groß wird, teilen Sie ihn in mehrere spezialisierte Skills auf.
## Skills zu einem Agenten hinzufügen
Öffnen Sie einen beliebigen **Agent**-Block und finden Sie das **Skills**-Dropdown unterhalb des Tool-Bereichs. Wählen Sie die Skills aus, auf die der Agent Zugriff haben soll.
![Skill hinzufügen](/static/skills/add-skill.png)
Ausgewählte Skills erscheinen als Karten, die Sie anklicken können, um sie zu bearbeiten oder zu entfernen.
### Was zur Laufzeit passiert
Wenn der Workflow ausgeführt wird:
1. Der System-Prompt des Agenten enthält einen `<available_skills>`-Abschnitt, der Name und Beschreibung jedes Skills auflistet
2. Ein `load_skill`-Tool wird automatisch zu den verfügbaren Tools des Agenten hinzugefügt
3. Wenn der Agent feststellt, dass ein Skill für die aktuelle Aufgabe relevant ist, ruft er `load_skill` mit dem Skill-Namen auf
4. Der vollständige Skill-Inhalt wird als Tool-Antwort zurückgegeben und gibt dem Agenten detaillierte Anweisungen
Dies funktioniert über alle unterstützten LLM-Anbieter hinweg — das `load_skill`-Tool verwendet standardmäßiges Tool-Calling, sodass keine anbieterspezifische Konfiguration erforderlich ist.
## Häufige Anwendungsfälle
Skills sind besonders wertvoll, wenn Agenten spezialisiertes Wissen oder mehrstufige Workflows benötigen:
**Domain-Expertise**
- `api-integration-expert` — Best Practices für den Aufruf spezifischer APIs (Authentifizierung, Rate Limiting, Fehlerbehandlung)
- `data-transformation` — ETL-Muster, Datenbereinigung und Validierungsregeln
- `code-reviewer` — Code-Review-Richtlinien spezifisch für die Standards Ihres Teams
**Workflow-Vorlagen**
- `bug-investigation` — Schritt-für-Schritt-Debugging-Methodik (reproduzieren → isolieren → testen → beheben)
- `feature-implementation` — Entwicklungs-Workflow von Anforderungen bis zur Bereitstellung
- `document-generator` — Vorlagen und Formatierungsregeln für technische Dokumentation
**Unternehmensspezifisches Wissen**
- `our-architecture` — Systemarchitekturdiagramme, Service-Abhängigkeiten und Bereitstellungsprozesse
- `style-guide` — Markenrichtlinien, Schreibstil, UI/UX-Muster
- `customer-onboarding` — Standardverfahren und häufige Kundenfragen
**Wann Skills vs. Agentenanweisungen verwendet werden sollten:**
- Verwenden Sie **Skills** für Wissen, das über mehrere Workflows hinweg gilt oder sich häufig ändert
- Verwenden Sie **Agentenanweisungen** für aufgabenspezifischen Kontext, der für einen einzelnen Agenten einzigartig ist
## Best Practices
**Effektive Beschreibungen schreiben**
- **Seien Sie spezifisch und keyword-reich** — Statt "Hilft bei SQL", schreiben Sie "Optimierte SQL-Abfragen für PostgreSQL, MySQL und SQLite schreiben, einschließlich Index-Empfehlungen und Abfrageplan-Analyse"
- **Aktivierungstrigger einbeziehen** — Erwähnen Sie spezifische Wörter oder Phrasen, die den Skill auslösen sollten (z. B. "Verwenden, wenn der Benutzer PDFs, Formulare oder Dokumentenextraktion erwähnt")
- **Unter 200 Wörtern halten** — Agenten scannen Beschreibungen schnell; jedes Wort zählt
**Skill-Umfang und Organisation**
- **Ein Skill pro Domäne** — Ein fokussierter `sql-expert`-Skill funktioniert besser als ein breiter `database-everything`-Skill
- **Auf 5-10 Skills pro Agent begrenzen** — Mehr Skills = mehr Entscheidungsaufwand; klein anfangen und bei Bedarf erweitern
- **Große Skills aufteilen** — Wenn ein Skill 500 Zeilen überschreitet, in fokussierte Sub-Skills aufteilen
**Inhaltsstruktur**
- **Markdown-Formatierung verwenden** — Überschriften, Listen und Code-Blöcke helfen Agenten beim Parsen und Befolgen von Anweisungen
- **Beispiele bereitstellen** — Input/Output-Paare zeigen, damit Agenten das erwartete Verhalten verstehen
- **Explizit über Sonderfälle sein** — Gehen Sie nicht davon aus, dass Agenten spezielle Behandlung ableiten werden
**Testen und Iteration**
- **Aktivierung testen** — Führen Sie Ihren Workflow aus und überprüfen Sie, ob der Agent die Skill lädt, wenn erwartet
- **Auf Fehlalarme prüfen** — Stellen Sie sicher, dass Skills nicht aktiviert werden, wenn sie es nicht sollten
- **Beschreibungen verfeinern** — Wenn eine Skill nicht geladen wird, wenn sie benötigt wird, fügen Sie der Beschreibung weitere Schlüsselwörter hinzu
## Mehr erfahren
- [Agent Skills Spezifikation](https://agentskills.io) — Das offene Format für portable Agent-Skills
- [Beispiel-Skills](https://github.com/anthropics/skills) — Community-Skill-Beispiele durchsuchen
- [Best Practices](https://agentskills.io/what-are-skills) — Effektive Skills schreiben

View File

@@ -1,207 +0,0 @@
---
title: A2A
description: Interagiere mit externen A2A-kompatiblen Agenten
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="a2a"
color="#4151B5"
/>
{/* MANUAL-CONTENT-START:intro */}
Das A2A-Protokoll (Agent-to-Agent) ermöglicht es Sim, mit externen KI-Agenten und Systemen zu interagieren, die A2A-kompatible APIs implementieren. Mit A2A kannst du Sims Automatisierungen und Workflows mit Remote-Agenten verbinden wie LLM-gestützten Bots, Microservices und anderen KI-basierten Tools unter Verwendung eines standardisierten Nachrichtenformats.
Mit den A2A-Tools in Sim kannst du:
- **Nachrichten an externe Agenten senden**: Kommuniziere direkt mit Remote-Agenten und übermittle Prompts, Befehle oder Daten.
- **Antworten empfangen und streamen**: Erhalte strukturierte Antworten, Artefakte oder Echtzeit-Updates vom Agenten, während die Aufgabe fortschreitet.
- **Gespräche oder Aufgaben fortsetzen**: Führe mehrstufige Konversationen oder Workflows fort, indem du auf Aufgaben- und Kontext-IDs verweist.
- **Drittanbieter-KI und Automatisierung integrieren**: Nutze externe A2A-kompatible Dienste als Teil deiner Sim-Workflows.
Diese Funktionen ermöglichen es dir, fortgeschrittene Workflows zu erstellen, die Sims native Fähigkeiten mit der Intelligenz und Automatisierung externer KIs oder benutzerdefinierter Agenten kombinieren. Um A2A-Integrationen zu nutzen, benötigst du die Endpunkt-URL des externen Agenten und, falls erforderlich, einen API-Schlüssel oder Zugangsdaten.
{/* MANUAL-CONTENT-END */}
## Nutzungsanleitung
Verwende das A2A-Protokoll (Agent-to-Agent), um mit externen KI-Agenten zu interagieren.
## Tools
### `a2a_send_message`
Sende eine Nachricht an einen externen A2A-kompatiblen Agenten.
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `agentUrl` | string | Ja | Die A2A-Agenten-Endpunkt-URL |
| `message` | string | Ja | Nachricht, die an den Agenten gesendet werden soll |
| `taskId` | string | Nein | Aufgaben-ID zum Fortsetzen einer bestehenden Aufgabe |
| `contextId` | string | Nein | Kontext-ID für Gesprächskontinuität |
| `data` | string | Nein | Strukturierte Daten, die mit der Nachricht einbezogen werden sollen \(JSON-String\) |
| `files` | array | Nein | Dateien, die mit der Nachricht einbezogen werden sollen |
| `apiKey` | string | Nein | API-Schlüssel für die Authentifizierung |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `content` | string | Textantwort-Inhalt vom Agenten |
| `taskId` | string | Eindeutige Aufgabenkennung |
| `contextId` | string | Gruppiert zusammenhängende Aufgaben/Nachrichten |
| `state` | string | Aktueller Lebenszyklus-Status \(working, completed, failed, canceled, rejected, input_required, auth_required\) |
| `artifacts` | array | Ausgabe-Artefakte der Aufgabe |
| `history` | array | Gesprächsverlauf \(Message-Array\) |
### `a2a_get_task`
Abfrage des Status einer bestehenden A2A-Aufgabe.
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `agentUrl` | string | Ja | Die A2A-Agenten-Endpunkt-URL |
| `taskId` | string | Ja | Abzufragende Aufgaben-ID |
| `apiKey` | string | Nein | API-Schlüssel für die Authentifizierung |
| `historyLength` | number | Nein | Anzahl der einzubeziehenden Verlaufsnachrichten |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `taskId` | string | Eindeutige Aufgabenkennung |
| `contextId` | string | Gruppiert zusammenhängende Aufgaben/Nachrichten |
| `state` | string | Aktueller Lebenszyklus-Status \(working, completed, failed, canceled, rejected, input_required, auth_required\) |
| `artifacts` | array | Ausgabe-Artefakte der Aufgabe |
| `history` | array | Gesprächsverlauf \(Message-Array\) |
### `a2a_cancel_task`
Abbrechen einer laufenden A2A-Aufgabe.
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `agentUrl` | string | Ja | Die A2A-Agenten-Endpunkt-URL |
| `taskId` | string | Ja | Abzubrechende Aufgaben-ID |
| `apiKey` | string | Nein | API-Schlüssel für die Authentifizierung |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `cancelled` | boolean | Ob die Stornierung erfolgreich war |
| `state` | string | Aktueller Lebenszyklus-Status \(working, completed, failed, canceled, rejected, input_required, auth_required\) |
### `a2a_get_agent_card`
Ruft die Agent Card (Discovery-Dokument) für einen A2A-Agenten ab.
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `agentUrl` | string | Ja | Die Endpunkt-URL des A2A-Agenten |
| `apiKey` | string | Nein | API-Schlüssel für die Authentifizierung \(falls erforderlich\) |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `name` | string | Anzeigename des Agenten |
| `description` | string | Zweck/Fähigkeiten des Agenten |
| `url` | string | Service-Endpunkt-URL |
| `provider` | object | Details zur Ersteller-Organisation |
| `capabilities` | object | Feature-Support-Matrix |
| `skills` | array | Verfügbare Operationen |
| `version` | string | Vom Agenten unterstützte A2A-Protokollversion |
| `defaultInputModes` | array | Standard-Eingabe-Inhaltstypen, die vom Agenten akzeptiert werden |
| `defaultOutputModes` | array | Standard-Ausgabe-Inhaltstypen, die vom Agenten produziert werden |
### `a2a_resubscribe`
Stellt die Verbindung zu einem laufenden A2A-Task-Stream nach einer Verbindungsunterbrechung wieder her.
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `agentUrl` | string | Ja | Die Endpunkt-URL des A2A-Agenten |
| `taskId` | string | Ja | Task-ID, zu der erneut abonniert werden soll |
| `apiKey` | string | Nein | API-Schlüssel für die Authentifizierung |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `taskId` | string | Eindeutige Aufgabenkennung |
| `contextId` | string | Gruppiert zusammenhängende Aufgaben/Nachrichten |
| `state` | string | Aktueller Lebenszyklusstatus \(working, completed, failed, canceled, rejected, input_required, auth_required\) |
| `isRunning` | boolean | Ob die Aufgabe noch läuft |
| `artifacts` | array | Ausgabeartefakte der Aufgabe |
| `history` | array | Gesprächsverlauf \(Message-Array\) |
### `a2a_set_push_notification`
Konfigurieren Sie einen Webhook, um Benachrichtigungen über Aufgabenaktualisierungen zu erhalten.
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `agentUrl` | string | Ja | Die A2A-Agent-Endpunkt-URL |
| `taskId` | string | Ja | Aufgaben-ID, für die Benachrichtigungen konfiguriert werden sollen |
| `webhookUrl` | string | Ja | HTTPS-Webhook-URL zum Empfang von Benachrichtigungen |
| `token` | string | Nein | Token zur Webhook-Validierung |
| `apiKey` | string | Nein | API-Schlüssel zur Authentifizierung |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `url` | string | HTTPS-Webhook-URL für Benachrichtigungen |
| `token` | string | Authentifizierungstoken zur Webhook-Validierung |
| `success` | boolean | Ob der Vorgang erfolgreich war |
### `a2a_get_push_notification`
Rufen Sie die Push-Benachrichtigungs-Webhook-Konfiguration für eine Aufgabe ab.
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `agentUrl` | string | Ja | Die A2A-Agent-Endpunkt-URL |
| `taskId` | string | Ja | Aufgaben-ID, für die die Benachrichtigungskonfiguration abgerufen werden soll |
| `apiKey` | string | Nein | API-Schlüssel zur Authentifizierung |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `token` | string | Authentifizierungstoken für Webhook-Validierung |
| `exists` | boolean | Ob die Ressource existiert |
### `a2a_delete_push_notification`
Löscht die Push-Benachrichtigungs-Webhook-Konfiguration für eine Aufgabe.
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `agentUrl` | string | Ja | Die A2A-Agent-Endpunkt-URL |
| `taskId` | string | Ja | Aufgaben-ID, für die die Benachrichtigungskonfiguration gelöscht werden soll |
| `pushNotificationConfigId` | string | Nein | Push-Benachrichtigungskonfigurations-ID zum Löschen \(optional - Server kann aus taskId ableiten\) |
| `apiKey` | string | Nein | API-Schlüssel für Authentifizierung |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `success` | boolean | Ob die Operation erfolgreich war |

View File

@@ -193,3 +193,8 @@ Erhalte eine Liste defekter Backlinks, die auf eine Zieldomäne oder URL verweis
| Parameter | Typ | Beschreibung | | Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- | | --------- | ---- | ----------- |
| `brokenBacklinks` | array | Liste defekter Backlinks | | `brokenBacklinks` | array | Liste defekter Backlinks |
## Hinweise
- Kategorie: `tools`
- Typ: `ahrefs`

View File

@@ -123,3 +123,8 @@ Mehrere vorhandene Datensätze in einer Airtable-Tabelle aktualisieren
| Parameter | Typ | Beschreibung | | Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- | | --------- | ---- | ----------- |
| `records` | json | Array der aktualisierten Airtable-Datensätze | | `records` | json | Array der aktualisierten Airtable-Datensätze |
## Hinweise
- Kategorie: `tools`
- Typ: `airtable`

View File

@@ -1,63 +0,0 @@
---
title: Airweave
description: Durchsuchen Sie Ihre synchronisierten Datensammlungen
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="airweave"
color="#6366F1"
/>
{/* MANUAL-CONTENT-START:intro */}
[Airweave](https://airweave.ai/) ist eine KI-gestützte semantische Suchplattform, die Ihnen hilft, Wissen über alle Ihre synchronisierten Datenquellen hinweg zu entdecken und abzurufen. Airweave wurde für moderne Teams entwickelt und ermöglicht schnelle, relevante Suchergebnisse mithilfe neuraler, hybrider oder schlüsselwortbasierter Strategien, die auf Ihre Bedürfnisse zugeschnitten sind.
Mit Airweave können Sie:
- **Intelligenter suchen**: Verwenden Sie natürlichsprachliche Abfragen, um Informationen aufzudecken, die in Ihren verbundenen Tools und Datenbanken gespeichert sind
- **Ihre Daten vereinheitlichen**: Greifen Sie nahtlos auf Inhalte aus Quellen wie Code, Dokumenten, Chat, E-Mails, Cloud-Dateien und mehr zu
- **Abruf anpassen**: Wählen Sie zwischen hybriden (semantisch + Schlüsselwort), neuralen oder Schlüsselwort-Suchstrategien für optimale Ergebnisse
- **Recall steigern**: Erweitern Sie Suchanfragen mit KI, um umfassendere Antworten zu finden
- **Ergebnisse mit KI neu ordnen**: Priorisieren Sie die relevantesten Antworten mit leistungsstarken Sprachmodellen
- **Sofortige Antworten erhalten**: Generieren Sie klare, KI-gestützte Antworten, die aus Ihren Daten synthetisiert werden
In Sim ermöglicht die Airweave-Integration Ihren Agenten, alle Daten Ihrer Organisation über ein einziges Tool zu durchsuchen, zusammenzufassen und Erkenntnisse zu extrahieren. Nutzen Sie Airweave, um umfassenden, kontextbezogenen Wissensabruf in Ihren Workflows zu ermöglichen sei es beim Beantworten von Fragen, Erstellen von Zusammenfassungen oder Unterstützen dynamischer Entscheidungsfindung.
{/* MANUAL-CONTENT-END */}
## Nutzungsanweisungen
Durchsuchen Sie Ihre synchronisierten Datenquellen mit Airweave. Unterstützt semantische Suche mit hybriden, neuralen oder schlüsselwortbasierten Abrufstrategien. Optional können KI-gestützte Antworten aus Suchergebnissen generiert werden.
## Tools
### `airweave_search`
Durchsuchen Sie Ihre synchronisierten Datensammlungen mit Airweave. Unterstützt semantische Suche mit hybriden, neuralen oder schlüsselwortbasierten Abrufstrategien. Optional können KI-gestützte Antworten aus Suchergebnissen generiert werden.
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Ja | Airweave API-Schlüssel für die Authentifizierung |
| `collectionId` | string | Ja | Die lesbare ID der zu durchsuchenden Sammlung |
| `query` | string | Ja | Der Suchanfragetext |
| `limit` | number | Nein | Maximale Anzahl der zurückzugebenden Ergebnisse \(Standard: 100\) |
| `retrievalStrategy` | string | Nein | Abrufstrategie: hybrid \(Standard\), neural oder keyword |
| `expandQuery` | boolean | Nein | Abfragevariationen generieren, um die Trefferquote zu verbessern |
| `rerank` | boolean | Nein | Ergebnisse für verbesserte Relevanz mithilfe von LLM neu ordnen |
| `generateAnswer` | boolean | Nein | Eine natürlichsprachliche Antwort auf die Abfrage generieren |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `results` | array | Suchergebnisse mit Inhalten, Scores und Metadaten aus Ihren synchronisierten Daten |
| ↳ `entity_id` | string | Eindeutige Kennung für die Suchergebnisentität |
| ↳ `source_name` | string | Name der Datenquelle \(z. B. "GitHub", "Slack"\) |
| ↳ `md_content` | string | Markdown-formatierter Inhalt des Ergebnisses |
| ↳ `score` | number | Relevanz-Score aus der Suche |
| ↳ `metadata` | object | Zusätzliche Metadaten, die mit dem Ergebnis verknüpft sind |
| ↳ `breadcrumbs` | array | Navigationspfad zum Ergebnis innerhalb seiner Quelle |
| ↳ `url` | string | URL zum Originalinhalt |
| `completion` | string | KI-generierte Antwort auf die Abfrage \(wenn generateAnswer aktiviert ist\) |

View File

@@ -81,3 +81,8 @@ Führe einen APIFY-Aktor asynchron mit Polling für lang laufende Aufgaben aus
| `status` | string | Laufstatus \(SUCCEEDED, FAILED, usw.\) | | `status` | string | Laufstatus \(SUCCEEDED, FAILED, usw.\) |
| `datasetId` | string | Dataset-ID mit Ergebnissen | | `datasetId` | string | Dataset-ID mit Ergebnissen |
| `items` | array | Dataset-Elemente \(falls abgeschlossen\) | | `items` | array | Dataset-Elemente \(falls abgeschlossen\) |
## Hinweise
- Kategorie: `tools`
- Typ: `apify`

View File

@@ -567,3 +567,8 @@ Liste des Teams abrufen
| --------- | ---- | ----------- | | --------- | ---- | ----------- |
| `email_accounts` | json | Array von Team-E-Mail-Konten, die in Apollo verknüpft sind | | `email_accounts` | json | Array von Team-E-Mail-Konten, die in Apollo verknüpft sind |
| `metadata` | json | Metadaten einschließlich der Gesamtanzahl von E-Mail-Konten | | `metadata` | json | Metadaten einschließlich der Gesamtanzahl von E-Mail-Konten |
## Notizen
- Kategorie: `tools`
- Typ: `apollo`

View File

@@ -82,3 +82,8 @@ Suche nach Artikeln eines bestimmten Autors auf ArXiv.
| Parameter | Typ | Beschreibung | | Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- | | --------- | ---- | ----------- |
| `authorPapers` | json | Array von Publikationen, die vom angegebenen Autor verfasst wurden | | `authorPapers` | json | Array von Publikationen, die vom angegebenen Autor verfasst wurden |
## Hinweise
- Kategorie: `tools`
- Typ: `arxiv`

View File

@@ -163,16 +163,3 @@ Einen Kommentar (Story) zu einer Asana-Aufgabe hinzufügen
- Kategorie: `tools` - Kategorie: `tools`
- Typ: `asana` - Typ: `asana`
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `success` | boolean | Erfolgsstatus der Operation |
| `ts` | string | Zeitstempel der Antwort |
| `gid` | string | Global eindeutige Kennung des Kommentars |
| `text` | string | Textinhalt des Kommentars |
| `created_at` | string | Erstellungszeitstempel des Kommentars |
| `created_by` | object | Details zum Kommentarautor |
| ↳ `gid` | string | Autor-GID |
| ↳ `name` | string | Name des Autors |

View File

@@ -53,3 +53,8 @@ Führt eine Browser-Automatisierungsaufgabe mit BrowserUse aus
| `success` | boolean | Status der Aufgabenfertigstellung | | `success` | boolean | Status der Aufgabenfertigstellung |
| `output` | json | Ausgabedaten der Aufgabe | | `output` | json | Ausgabedaten der Aufgabe |
| `steps` | json | Ausgeführte Schritte | | `steps` | json | Ausgeführte Schritte |
## Hinweise
- Kategorie: `tools`
- Typ: `browser_use`

View File

@@ -1,785 +0,0 @@
---
title: Cal Com
description: Verwalten Sie Cal.com-Buchungen, Veranstaltungstypen, Zeitpläne und
Verfügbarkeiten
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="calcom"
color="#FFFFFE"
/>
{/* MANUAL-CONTENT-START:intro */}
[Cal.com](https://cal.com/) ist eine flexible und quelloffene Planungsplattform, die es einfach macht, Termine, Buchungen, Veranstaltungstypen und Teamverfügbarkeiten zu verwalten.
Mit Cal.com können Sie:
- **Planung automatisieren**: Ermöglichen Sie Nutzern, Ihre verfügbaren Zeitfenster einzusehen und Meetings automatisch zu buchen, ohne E-Mail-Pingpong.
- **Veranstaltungen verwalten**: Erstellen und passen Sie Veranstaltungstypen, Dauern und Regeln für Einzel- oder Gruppenmeetings an.
- **Kalender integrieren**: Verbinden Sie sich nahtlos mit Google, Outlook, Apple oder anderen Kalenderanbietern, um Doppelbuchungen zu vermeiden.
- **Teilnehmer und Gäste verwalten**: Erfassen Sie Teilnehmerinformationen, verwalten Sie Gäste und versenden Sie Einladungen oder Erinnerungen.
- **Verfügbarkeit steuern**: Definieren Sie individuelle Arbeitszeiten, Pufferzeiten und Storno-/Umbuchungsregeln.
- **Workflows automatisieren**: Lösen Sie benutzerdefinierte Aktionen über Webhooks aus, wenn eine Buchung erstellt, storniert oder umgebucht wird.
In Sim ermöglicht die Cal.com-Integration Ihren Agenten, Meetings zu buchen, Verfügbarkeiten zu prüfen, Veranstaltungstypen zu verwalten und Planungsaufgaben programmatisch zu automatisieren. Dies hilft Agenten, Meetings zu koordinieren, Buchungen im Namen von Nutzern zu versenden, Zeitpläne zu prüfen oder auf Buchungsereignisse zu reagieren alles ohne manuelle Eingriffe. Durch die Verbindung von Sim mit Cal.com erschließen Sie hochautomatisierte und intelligente Planungs-Workflows, die sich nahtlos in Ihre umfassenderen Automatisierungsanforderungen integrieren lassen.
{/* MANUAL-CONTENT-END */}
## Nutzungsanleitung
Integrieren Sie Cal.com in Ihren Workflow. Erstellen und verwalten Sie Buchungen, Veranstaltungstypen, Zeitpläne und prüfen Sie Verfügbarkeitsfenster. Unterstützt das Erstellen, Auflisten, Umbuchen und Stornieren von Buchungen sowie die Verwaltung von Veranstaltungstypen und Zeitplänen. Kann auch Workflows basierend auf Cal.com-Webhook-Ereignissen auslösen (Buchung erstellt, storniert, umgebucht). Verbinden Sie Ihr Cal.com-Konto über OAuth.
## Tools
### `calcom_create_booking`
Eine neue Buchung auf Cal.com erstellen
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `eventTypeId` | number | Ja | Die ID des zu buchenden Ereignistyps |
| `start` | string | Ja | Startzeit im UTC ISO 8601-Format \(z. B. 2024-01-15T09:00:00Z\) |
| `attendee` | object | Ja | Teilnehmerinformationsobjekt mit Name, E-Mail, Zeitzone und optionaler Telefonnummer \(zusammengestellt aus einzelnen Teilnehmerfeldern\) |
| `guests` | array | Nein | Array von Gast-E-Mail-Adressen |
| `items` | string | Nein | Gast-E-Mail-Adresse |
| `lengthInMinutes` | number | Nein | Dauer der Buchung in Minuten \(überschreibt die Standardeinstellung des Ereignistyps\) |
| `metadata` | object | Nein | Benutzerdefinierte Metadaten, die an die Buchung angehängt werden |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `status` | string | Antwortstatus |
| `data` | object | Details der erstellten Buchung |
| ↳ `eventType` | object | Details des Ereignistyps |
| ↳ `id` | number | Ereignistyp-ID |
| ↳ `slug` | string | Ereignistyp-Slug |
| ↳ `attendees` | array | Liste der Teilnehmer |
| ↳ `name` | string | Name des Teilnehmers |
| ↳ `email` | string | Tatsächliche E-Mail-Adresse des Teilnehmers |
| ↳ `displayEmail` | string | Öffentlich angezeigte E-Mail \(kann von der tatsächlichen E-Mail abweichen\) |
| ↳ `timeZone` | string | Zeitzone des Teilnehmers \(IANA-Format\) |
| ↳ `phoneNumber` | string | Telefonnummer des Teilnehmers |
| ↳ `language` | string | Sprachpräferenz des Teilnehmers \(ISO-Code\) |
| ↳ `absent` | boolean | Ob der Teilnehmer abwesend war |
| ↳ `hosts` | array | Liste der Gastgeber |
| ↳ `id` | number | Benutzer-ID des Gastgebers |
| ↳ `name` | string | Anzeigename des Gastgebers |
| ↳ `email` | string | Tatsächliche E-Mail-Adresse des Gastgebers |
| ↳ `displayEmail` | string | Öffentlich angezeigte E-Mail \(kann von der tatsächlichen E-Mail abweichen\) |
| ↳ `username` | string | Cal.com-Benutzername des Gastgebers |
| ↳ `timeZone` | string | Zeitzone des Gastgebers \(IANA-Format\) |
| ↳ `id` | number | Numerische Buchungs-ID |
| ↳ `uid` | string | Eindeutige Kennung für die Buchung |
| ↳ `title` | string | Titel der Buchung |
| ↳ `status` | string | Buchungsstatus \(z. B. akzeptiert, ausstehend, storniert\) |
| ↳ `start` | string | Startzeit im ISO 8601-Format |
| ↳ `end` | string | Endzeit im ISO 8601-Format |
| ↳ `duration` | number | Dauer in Minuten |
| ↳ `eventTypeId` | number | Ereignistyp-ID |
| ↳ `meetingUrl` | string | URL zum Beitritt zum Meeting |
| ↳ `location` | string | Ort der Buchung |
| ↳ `absentHost` | boolean | Ob der Gastgeber abwesend war |
| ↳ `guests` | array | Gast-E-Mail-Adressen |
| ↳ `bookingFieldsResponses` | json | Benutzerdefinierte Buchungsfeldantworten \(dynamische Schlüssel basierend auf der Ereignistyp-Konfiguration\) |
| ↳ `metadata` | json | Benutzerdefinierte Metadaten, die an die Buchung angehängt sind \(dynamische Schlüssel-Wert-Paare\) |
| ↳ `icsUid` | string | ICS-Kalender-UID |
| ↳ `createdAt` | string | Zeitpunkt der Erstellung der Buchung |
### `calcom_get_booking`
Details einer bestimmten Buchung anhand ihrer UID abrufen
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `bookingUid` | string | Ja | Eindeutige Kennung \(UID\) der Buchung |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `status` | string | Antwortstatus |
| `data` | object | Buchungsdetails |
| ↳ `eventType` | object | Details zum Ereignistyp |
| ↳ `id` | number | Ereignistyp-ID |
| ↳ `slug` | string | Ereignistyp-Slug |
| ↳ `attendees` | array | Liste der Teilnehmer |
| ↳ `name` | string | Name des Teilnehmers |
| ↳ `email` | string | Tatsächliche E-Mail-Adresse des Teilnehmers |
| ↳ `displayEmail` | string | Öffentlich angezeigte E-Mail \(kann von der tatsächlichen E-Mail abweichen\) |
| ↳ `timeZone` | string | Zeitzone des Teilnehmers \(IANA-Format\) |
| ↳ `phoneNumber` | string | Telefonnummer des Teilnehmers |
| ↳ `language` | string | Sprachpräferenz des Teilnehmers \(ISO-Code\) |
| ↳ `absent` | boolean | Ob der Teilnehmer abwesend war |
| ↳ `hosts` | array | Liste der Gastgeber |
| ↳ `id` | number | Benutzer-ID des Gastgebers |
| ↳ `name` | string | Anzeigename des Gastgebers |
| ↳ `email` | string | Tatsächliche E-Mail-Adresse des Gastgebers |
| ↳ `displayEmail` | string | Öffentlich angezeigte E-Mail \(kann von der tatsächlichen E-Mail abweichen\) |
| ↳ `username` | string | Cal.com-Benutzername des Gastgebers |
| ↳ `timeZone` | string | Zeitzone des Gastgebers \(IANA-Format\) |
| ↳ `id` | number | Numerische Buchungs-ID |
| ↳ `uid` | string | Eindeutige Kennung für die Buchung |
| ↳ `title` | string | Titel der Buchung |
| ↳ `description` | string | Beschreibung der Buchung |
| ↳ `status` | string | Buchungsstatus \(z. B. akzeptiert, ausstehend, storniert\) |
| ↳ `start` | string | Startzeit im ISO-8601-Format |
| ↳ `end` | string | Endzeit im ISO-8601-Format |
| ↳ `duration` | number | Dauer in Minuten |
| ↳ `eventTypeId` | number | Ereignistyp-ID |
| ↳ `meetingUrl` | string | URL zum Beitritt zum Meeting |
| ↳ `location` | string | Ort der Buchung |
| ↳ `absentHost` | boolean | Ob der Gastgeber abwesend war |
| ↳ `guests` | array | E-Mail-Adressen der Gäste |
| ↳ `bookingFieldsResponses` | json | Benutzerdefinierte Buchungsfeld-Antworten \(dynamische Schlüssel basierend auf der Ereignistyp-Konfiguration\) |
| ↳ `metadata` | json | Benutzerdefinierte Metadaten, die der Buchung angehängt sind \(dynamische Schlüssel-Wert-Paare\) |
| ↳ `rating` | number | Buchungsbewertung |
| ↳ `icsUid` | string | ICS-Kalender-UID |
| ↳ `cancellationReason` | string | Grund für die Stornierung, falls storniert |
| ↳ `reschedulingReason` | string | Grund für die Umbuchung, falls umgebucht |
| ↳ `rescheduledFromUid` | string | Ursprüngliche Buchungs-UID, falls diese Buchung umgebucht wurde |
| ↳ `rescheduledToUid` | string | Neue Buchungs-UID nach Umbuchung |
| ↳ `cancelledByEmail` | string | E-Mail der Person, die die Buchung storniert hat |
| ↳ `rescheduledByEmail` | string | E-Mail der Person, die die Buchung umgebucht hat |
| ↳ `createdAt` | string | Zeitpunkt der Erstellung der Buchung |
| ↳ `updatedAt` | string | Zeitpunkt der letzten Aktualisierung der Buchung |
### `calcom_list_bookings`
Alle Buchungen mit optionalem Statusfilter auflisten
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `status` | string | Nein | Buchungen nach Status filtern: upcoming, recurring, past, cancelled oder unconfirmed |
| `take` | number | Nein | Anzahl der zurückzugebenden Buchungen \(Paginierungslimit\) |
| `skip` | number | Nein | Anzahl der zu überspringenden Buchungen \(Paginierungsoffset\) |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `status` | string | Antwortstatus |
| `data` | array | Array von Buchungen |
| ↳ `eventType` | object | Details zum Ereignistyp |
| ↳ `id` | number | Ereignistyp-ID |
| ↳ `slug` | string | Ereignistyp-Slug |
| ↳ `attendees` | array | Liste der Teilnehmer |
| ↳ `name` | string | Name des Teilnehmers |
| ↳ `email` | string | Tatsächliche E-Mail-Adresse des Teilnehmers |
| ↳ `displayEmail` | string | Öffentlich angezeigte E-Mail \(kann von der tatsächlichen E-Mail abweichen\) |
| ↳ `timeZone` | string | Zeitzone des Teilnehmers \(IANA-Format\) |
| ↳ `phoneNumber` | string | Telefonnummer des Teilnehmers |
| ↳ `language` | string | Sprachpräferenz des Teilnehmers \(ISO-Code\) |
| ↳ `absent` | boolean | Ob der Teilnehmer abwesend war |
| ↳ `hosts` | array | Liste der Gastgeber |
| ↳ `id` | number | Benutzer-ID des Gastgebers |
| ↳ `name` | string | Anzeigename des Gastgebers |
| ↳ `email` | string | Tatsächliche E-Mail-Adresse des Gastgebers |
| ↳ `displayEmail` | string | Öffentlich angezeigte E-Mail \(kann von der tatsächlichen E-Mail abweichen\) |
| ↳ `username` | string | Cal.com-Benutzername des Gastgebers |
| ↳ `timeZone` | string | Zeitzone des Gastgebers \(IANA-Format\) |
| ↳ `id` | number | Numerische Buchungs-ID |
| ↳ `uid` | string | Eindeutige Kennung für die Buchung |
| ↳ `title` | string | Titel der Buchung |
| ↳ `description` | string | Beschreibung der Buchung |
| ↳ `status` | string | Buchungsstatus \(z. B. accepted, pending, cancelled\) |
| ↳ `start` | string | Startzeit im ISO-8601-Format |
| ↳ `end` | string | Endzeit im ISO-8601-Format |
| ↳ `duration` | number | Dauer in Minuten |
| ↳ `eventTypeId` | number | Ereignistyp-ID |
| ↳ `meetingUrl` | string | URL zum Beitritt zum Meeting |
| ↳ `location` | string | Ort der Buchung |
| ↳ `absentHost` | boolean | Ob der Gastgeber abwesend war |
| ↳ `guests` | array | E-Mail-Adressen der Gäste |
| ↳ `bookingFieldsResponses` | json | Antworten auf benutzerdefinierte Buchungsfelder \(dynamische Schlüssel basierend auf der Ereignistyp-Konfiguration\) |
| ↳ `metadata` | json | Benutzerdefinierte Metadaten, die der Buchung zugeordnet sind \(dynamische Schlüssel-Wert-Paare\) |
| ↳ `rating` | number | Buchungsbewertung |
| ↳ `icsUid` | string | ICS-Kalender-UID |
| ↳ `cancellationReason` | string | Grund für die Stornierung, falls storniert |
| ↳ `cancelledByEmail` | string | E-Mail der Person, die die Buchung storniert hat |
| ↳ `reschedulingReason` | string | Grund für die Umbuchung, falls umgebucht |
| ↳ `rescheduledByEmail` | string | E-Mail der Person, die die Buchung umgebucht hat |
| ↳ `rescheduledFromUid` | string | Ursprüngliche Buchungs-UID, falls diese Buchung umgebucht wurde |
| ↳ `rescheduledToUid` | string | Neue Buchungs-UID nach Umbuchung |
| ↳ `createdAt` | string | Zeitpunkt der Erstellung der Buchung |
| ↳ `updatedAt` | string | Zeitpunkt der letzten Aktualisierung der Buchung |
| `pagination` | object | Paginierungs-Metadaten |
| ↳ `totalItems` | number | Gesamtanzahl der Elemente |
| ↳ `remainingItems` | number | Verbleibende Elemente nach der aktuellen Seite |
| ↳ `returnedItems` | number | Anzahl der in dieser Antwort zurückgegebenen Elemente |
| ↳ `itemsPerPage` | number | Elemente pro Seite |
| ↳ `currentPage` | number | Aktuelle Seitennummer |
| ↳ `totalPages` | number | Gesamtanzahl der Seiten |
| ↳ `hasNextPage` | boolean | Ob es eine nächste Seite gibt |
| ↳ `hasPreviousPage` | boolean | Ob es eine vorherige Seite gibt |
### `calcom_cancel_booking`
Eine bestehende Buchung stornieren
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `bookingUid` | string | Ja | Eindeutige Kennung \(UID\) der zu stornierenden Buchung |
| `cancellationReason` | string | Nein | Grund für die Stornierung der Buchung |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `status` | string | Antwortstatus |
| `data` | object | Details der stornierten Buchung |
| ↳ `eventType` | object | Details des Ereignistyps |
| ↳ `id` | number | Ereignistyp-ID |
| ↳ `slug` | string | Ereignistyp-Slug |
| ↳ `attendees` | array | Liste der Teilnehmer |
| ↳ `name` | string | Name des Teilnehmers |
| ↳ `email` | string | Tatsächliche E-Mail-Adresse des Teilnehmers |
| ↳ `displayEmail` | string | Öffentlich angezeigte E-Mail \(kann von der tatsächlichen E-Mail abweichen\) |
| ↳ `timeZone` | string | Zeitzone des Teilnehmers \(IANA-Format\) |
| ↳ `phoneNumber` | string | Telefonnummer des Teilnehmers |
| ↳ `language` | string | Sprachpräferenz des Teilnehmers \(ISO-Code\) |
| ↳ `absent` | boolean | Ob der Teilnehmer abwesend war |
| ↳ `hosts` | array | Liste der Gastgeber |
| ↳ `id` | number | Benutzer-ID des Gastgebers |
| ↳ `name` | string | Anzeigename des Gastgebers |
| ↳ `email` | string | Tatsächliche E-Mail-Adresse des Gastgebers |
| ↳ `displayEmail` | string | Öffentlich angezeigte E-Mail \(kann von der tatsächlichen E-Mail abweichen\) |
| ↳ `username` | string | Cal.com-Benutzername des Gastgebers |
| ↳ `timeZone` | string | Zeitzone des Gastgebers \(IANA-Format\) |
| ↳ `id` | number | Numerische Buchungs-ID |
| ↳ `uid` | string | Eindeutige Kennung für die Buchung |
| ↳ `title` | string | Titel der Buchung |
| ↳ `cancellationReason` | string | Grund für die Stornierung, falls storniert |
| ↳ `cancelledByEmail` | string | E-Mail der Person, die die Buchung storniert hat |
| ↳ `start` | string | Startzeit im ISO-8601-Format |
| ↳ `end` | string | Endzeit im ISO-8601-Format |
| ↳ `duration` | number | Dauer in Minuten |
| ↳ `eventTypeId` | number | Ereignistyp-ID |
| ↳ `location` | string | Ort der Buchung |
| ↳ `metadata` | json | Benutzerdefinierte Metadaten, die der Buchung zugeordnet sind \(dynamische Schlüssel-Wert-Paare\) |
| ↳ `createdAt` | string | Zeitpunkt der Erstellung der Buchung |
| ↳ `status` | string | Buchungsstatus \(sollte storniert sein\) |
### `calcom_reschedule_booking`
Eine bestehende Buchung auf einen neuen Zeitpunkt verschieben
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `bookingUid` | string | Ja | Eindeutige Kennung \(UID\) der zu verschiebenden Buchung |
| `start` | string | Ja | Neue Startzeit im UTC ISO 8601-Format \(z. B. 2024-01-15T09:00:00Z\) |
| `reschedulingReason` | string | Nein | Grund für die Verschiebung der Buchung |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `status` | string | Antwortstatus |
| `data` | object | Details der verschobenen Buchung |
| ↳ `eventType` | object | Details des Ereignistyps |
| ↳ `id` | number | Ereignistyp-ID |
| ↳ `slug` | string | Ereignistyp-Slug |
| ↳ `attendees` | array | Liste der Teilnehmer |
| ↳ `name` | string | Name des Teilnehmers |
| ↳ `email` | string | Tatsächliche E-Mail-Adresse des Teilnehmers |
| ↳ `displayEmail` | string | Öffentlich angezeigte E-Mail \(kann von der tatsächlichen E-Mail abweichen\) |
| ↳ `timeZone` | string | Zeitzone des Teilnehmers \(IANA-Format\) |
| ↳ `phoneNumber` | string | Telefonnummer des Teilnehmers |
| ↳ `language` | string | Sprachpräferenz des Teilnehmers \(ISO-Code\) |
| ↳ `absent` | boolean | Ob der Teilnehmer abwesend war |
| ↳ `hosts` | array | Liste der Gastgeber |
| ↳ `id` | number | Benutzer-ID des Gastgebers |
| ↳ `name` | string | Anzeigename des Gastgebers |
| ↳ `email` | string | Tatsächliche E-Mail-Adresse des Gastgebers |
| ↳ `displayEmail` | string | Öffentlich angezeigte E-Mail \(kann von der tatsächlichen E-Mail abweichen\) |
| ↳ `username` | string | Cal.com-Benutzername des Gastgebers |
| ↳ `timeZone` | string | Zeitzone des Gastgebers \(IANA-Format\) |
| ↳ `id` | number | Numerische Buchungs-ID |
| ↳ `title` | string | Titel der Buchung |
| ↳ `status` | string | Buchungsstatus \(z. B. akzeptiert, ausstehend, storniert\) |
| ↳ `reschedulingReason` | string | Grund für die Verschiebung, falls verschoben |
| ↳ `rescheduledFromUid` | string | Ursprüngliche Buchungs-UID, falls diese Buchung verschoben wurde |
| ↳ `rescheduledByEmail` | string | E-Mail der Person, die die Buchung verschoben hat |
| ↳ `duration` | number | Dauer in Minuten |
| ↳ `eventTypeId` | number | Ereignistyp-ID |
| ↳ `meetingUrl` | string | URL zum Beitreten des Meetings |
| ↳ `location` | string | Ort der Buchung |
| ↳ `guests` | array | E-Mail-Adressen der Gäste |
| ↳ `metadata` | json | Benutzerdefinierte Metadaten, die an die Buchung angehängt sind \(dynamische Schlüssel-Wert-Paare\) |
| ↳ `icsUid` | string | ICS-Kalender-UID |
| ↳ `createdAt` | string | Zeitpunkt der Erstellung der Buchung |
| ↳ `uid` | string | Eindeutige Kennung für die neue Buchung |
| ↳ `start` | string | Neue Startzeit im ISO 8601-Format |
| ↳ `end` | string | Neue Endzeit im ISO 8601-Format |
### `calcom_confirm_booking`
Eine ausstehende Buchung bestätigen, die eine Bestätigung erfordert
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `bookingUid` | string | Ja | Eindeutige Kennung \(UID\) der zu bestätigenden Buchung |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `status` | string | Antwortstatus |
| `data` | object | Details der bestätigten Buchung |
| ↳ `eventType` | object | Details des Ereignistyps |
| ↳ `id` | number | Ereignistyp-ID |
| ↳ `slug` | string | Ereignistyp-Slug |
| ↳ `attendees` | array | Liste der Teilnehmer |
| ↳ `name` | string | Name des Teilnehmers |
| ↳ `email` | string | Tatsächliche E-Mail-Adresse des Teilnehmers |
| ↳ `displayEmail` | string | Öffentlich angezeigte E-Mail \(kann von der tatsächlichen E-Mail abweichen\) |
| ↳ `timeZone` | string | Zeitzone des Teilnehmers \(IANA-Format\) |
| ↳ `phoneNumber` | string | Telefonnummer des Teilnehmers |
| ↳ `language` | string | Sprachpräferenz des Teilnehmers \(ISO-Code\) |
| ↳ `absent` | boolean | Ob der Teilnehmer abwesend war |
| ↳ `hosts` | array | Liste der Gastgeber |
| ↳ `id` | number | Benutzer-ID des Gastgebers |
| ↳ `name` | string | Anzeigename des Gastgebers |
| ↳ `email` | string | Tatsächliche E-Mail-Adresse des Gastgebers |
| ↳ `displayEmail` | string | Öffentlich angezeigte E-Mail \(kann von der tatsächlichen E-Mail abweichen\) |
| ↳ `username` | string | Cal.com-Benutzername des Gastgebers |
| ↳ `timeZone` | string | Zeitzone des Gastgebers \(IANA-Format\) |
| ↳ `id` | number | Numerische Buchungs-ID |
| ↳ `uid` | string | Eindeutige Kennung für die Buchung |
| ↳ `title` | string | Titel der Buchung |
| ↳ `start` | string | Startzeit im ISO-8601-Format |
| ↳ `end` | string | Endzeit im ISO-8601-Format |
| ↳ `duration` | number | Dauer in Minuten |
| ↳ `eventTypeId` | number | Ereignistyp-ID |
| ↳ `meetingUrl` | string | URL zum Beitreten des Meetings |
| ↳ `location` | string | Ort der Buchung |
| ↳ `guests` | array | E-Mail-Adressen der Gäste |
| ↳ `metadata` | json | Benutzerdefinierte Metadaten, die der Buchung angehängt sind \(dynamische Schlüssel-Wert-Paare\) |
| ↳ `icsUid` | string | ICS-Kalender-UID |
| ↳ `createdAt` | string | Zeitpunkt der Erstellung der Buchung |
| ↳ `status` | string | Buchungsstatus \(sollte akzeptiert/bestätigt sein\) |
### `calcom_decline_booking`
Eine ausstehende Buchungsanfrage ablehnen
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `bookingUid` | string | Ja | Eindeutige Kennung (UID) der abzulehnenden Buchung |
| `reason` | string | Nein | Grund für die Ablehnung der Buchung |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `status` | string | Antwortstatus |
| `data` | object | Details der abgelehnten Buchung |
| ↳ `eventType` | object | Details des Ereignistyps |
| ↳ `id` | number | Ereignistyp-ID |
| ↳ `slug` | string | Ereignistyp-Slug |
| ↳ `attendees` | array | Liste der Teilnehmer |
| ↳ `name` | string | Name des Teilnehmers |
| ↳ `email` | string | Tatsächliche E-Mail-Adresse des Teilnehmers |
| ↳ `displayEmail` | string | Öffentlich angezeigte E-Mail (kann von der tatsächlichen E-Mail abweichen) |
| ↳ `timeZone` | string | Zeitzone des Teilnehmers (IANA-Format) |
| ↳ `phoneNumber` | string | Telefonnummer des Teilnehmers |
| ↳ `language` | string | Sprachpräferenz des Teilnehmers (ISO-Code) |
| ↳ `absent` | boolean | Ob der Teilnehmer abwesend war |
| ↳ `hosts` | array | Liste der Gastgeber |
| ↳ `id` | number | Benutzer-ID des Gastgebers |
| ↳ `name` | string | Anzeigename des Gastgebers |
| ↳ `email` | string | Tatsächliche E-Mail-Adresse des Gastgebers |
| ↳ `displayEmail` | string | Öffentlich angezeigte E-Mail (kann von der tatsächlichen E-Mail abweichen) |
| ↳ `username` | string | Cal.com-Benutzername des Gastgebers |
| ↳ `timeZone` | string | Zeitzone des Gastgebers (IANA-Format) |
| ↳ `id` | number | Numerische Buchungs-ID |
| ↳ `uid` | string | Eindeutige Kennung für die Buchung |
| ↳ `title` | string | Titel der Buchung |
| ↳ `cancellationReason` | string | Grund für die Stornierung, falls storniert |
| ↳ `start` | string | Startzeit im ISO-8601-Format |
| ↳ `end` | string | Endzeit im ISO-8601-Format |
| ↳ `duration` | number | Dauer in Minuten |
| ↳ `eventTypeId` | number | Ereignistyp-ID |
| ↳ `location` | string | Ort der Buchung |
| ↳ `metadata` | json | Benutzerdefinierte Metadaten, die der Buchung angehängt sind (dynamische Schlüssel-Wert-Paare) |
| ↳ `createdAt` | string | Zeitpunkt der Erstellung der Buchung |
| ↳ `status` | string | Buchungsstatus (sollte storniert/abgelehnt sein) |
### `calcom_create_event_type`
Einen neuen Ereignistyp in Cal.com erstellen
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `title` | string | Ja | Titel des Ereignistyps |
| `slug` | string | Ja | Eindeutiger Slug für die Ereignistyp-URL |
| `lengthInMinutes` | number | Ja | Dauer des Ereignisses in Minuten |
| `description` | string | Nein | Beschreibung des Ereignistyps |
| `slotInterval` | number | Nein | Intervall zwischen verfügbaren Buchungsslots in Minuten |
| `minimumBookingNotice` | number | Nein | Erforderliche Mindestvorlaufzeit vor der Buchung in Minuten |
| `beforeEventBuffer` | number | Nein | Pufferzeit vor dem Ereignis in Minuten |
| `afterEventBuffer` | number | Nein | Pufferzeit nach dem Ereignis in Minuten |
| `scheduleId` | number | Nein | ID des Zeitplans für die Verfügbarkeit |
| `disableGuests` | boolean | Nein | Ob das Hinzufügen von Gästen zu Buchungen deaktiviert ist |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `status` | string | Antwortstatus |
| `data` | object | Details des erstellten Ereignistyps |
| ↳ `id` | number | Ereignistyp-ID |
| ↳ `title` | string | Ereignistyp-Titel |
| ↳ `slug` | string | Ereignistyp-Slug |
| ↳ `description` | string | Ereignistyp-Beschreibung |
| ↳ `lengthInMinutes` | number | Dauer in Minuten |
| ↳ `slotInterval` | number | Slot-Intervall in Minuten |
| ↳ `minimumBookingNotice` | number | Mindestvorlaufzeit für Buchung in Minuten |
| ↳ `beforeEventBuffer` | number | Puffer vor Ereignis in Minuten |
| ↳ `afterEventBuffer` | number | Puffer nach Ereignis in Minuten |
| ↳ `scheduleId` | number | Zeitplan-ID |
| ↳ `disableGuests` | boolean | Ob Gäste deaktiviert sind |
| ↳ `createdAt` | string | ISO-Zeitstempel der Erstellung |
| ↳ `updatedAt` | string | ISO-Zeitstempel der letzten Aktualisierung |
### `calcom_get_event_type`
Detaillierte Informationen über einen bestimmten Ereignistyp abrufen
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `eventTypeId` | number | Ja | Ereignistyp-ID zum Abrufen |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `status` | string | Antwortstatus |
| `data` | object | Details zum Ereignistyp |
| ↳ `id` | number | Ereignistyp-ID |
| ↳ `title` | string | Titel des Ereignistyps |
| ↳ `slug` | string | Slug des Ereignistyps |
| ↳ `description` | string | Beschreibung des Ereignistyps |
| ↳ `lengthInMinutes` | number | Dauer in Minuten |
| ↳ `slotInterval` | number | Zeitfensterintervall in Minuten |
| ↳ `minimumBookingNotice` | number | Mindestvorlaufzeit für Buchungen in Minuten |
| ↳ `beforeEventBuffer` | number | Puffer vor dem Ereignis in Minuten |
| ↳ `afterEventBuffer` | number | Puffer nach dem Ereignis in Minuten |
| ↳ `scheduleId` | number | Zeitplan-ID |
| ↳ `disableGuests` | boolean | Ob Gäste deaktiviert sind |
| ↳ `createdAt` | string | ISO-Zeitstempel der Erstellung |
| ↳ `updatedAt` | string | ISO-Zeitstempel der letzten Aktualisierung |
### `calcom_list_event_types`
Eine Liste aller Ereignistypen abrufen
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `sortCreatedAt` | string | Nein | Sortierung nach Erstellungsdatum: "asc" oder "desc" |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `status` | string | Antwortstatus |
| `data` | array | Array von Ereignistypen |
| ↳ `id` | number | Ereignistyp-ID |
| ↳ `title` | string | Ereignistyp-Titel |
| ↳ `slug` | string | Ereignistyp-Slug |
| ↳ `description` | string | Ereignistyp-Beschreibung |
| ↳ `lengthInMinutes` | number | Dauer in Minuten |
| ↳ `slotInterval` | number | Zeitfensterintervall in Minuten |
| ↳ `minimumBookingNotice` | number | Mindestvorlaufzeit für Buchungen in Minuten |
| ↳ `beforeEventBuffer` | number | Pufferzeit vor dem Ereignis in Minuten |
| ↳ `afterEventBuffer` | number | Pufferzeit nach dem Ereignis in Minuten |
| ↳ `scheduleId` | number | Zeitplan-ID |
| ↳ `disableGuests` | boolean | Ob Gäste deaktiviert sind |
| ↳ `createdAt` | string | ISO-Zeitstempel der Erstellung |
| ↳ `updatedAt` | string | ISO-Zeitstempel der letzten Aktualisierung |
### `calcom_update_event_type`
Einen bestehenden Ereignistyp in Cal.com aktualisieren
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `eventTypeId` | number | Ja | Zu aktualisierende Ereignistyp-ID \(z. B. 12345\) |
| `title` | string | Nein | Titel des Ereignistyps |
| `slug` | string | Nein | Eindeutiger Slug für die Ereignistyp-URL |
| `lengthInMinutes` | number | Nein | Dauer des Ereignisses in Minuten |
| `description` | string | Nein | Beschreibung des Ereignistyps |
| `slotInterval` | number | Nein | Intervall zwischen verfügbaren Buchungszeitfenstern in Minuten |
| `minimumBookingNotice` | number | Nein | Erforderliche Mindestvorlaufzeit vor der Buchung in Minuten |
| `beforeEventBuffer` | number | Nein | Pufferzeit vor dem Ereignis in Minuten |
| `afterEventBuffer` | number | Nein | Pufferzeit nach dem Ereignis in Minuten |
| `scheduleId` | number | Nein | ID des Zeitplans für die Verfügbarkeit |
| `disableGuests` | boolean | Nein | Ob das Hinzufügen von Gästen zu Buchungen deaktiviert werden soll |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `status` | string | Antwortstatus |
| `data` | object | Aktualisierte Details des Ereignistyps |
| ↳ `id` | number | Ereignistyp-ID |
| ↳ `title` | string | Titel des Ereignistyps |
| ↳ `slug` | string | Slug des Ereignistyps |
| ↳ `description` | string | Beschreibung des Ereignistyps |
| ↳ `lengthInMinutes` | number | Dauer in Minuten |
| ↳ `slotInterval` | number | Zeitfensterintervall in Minuten |
| ↳ `minimumBookingNotice` | number | Mindestvorlaufzeit für Buchungen in Minuten |
| ↳ `beforeEventBuffer` | number | Puffer vor dem Ereignis in Minuten |
| ↳ `afterEventBuffer` | number | Puffer nach dem Ereignis in Minuten |
| ↳ `scheduleId` | number | Zeitplan-ID |
| ↳ `disableGuests` | boolean | Ob Gäste deaktiviert sind |
| ↳ `createdAt` | string | ISO-Zeitstempel der Erstellung |
| ↳ `updatedAt` | string | ISO-Zeitstempel der letzten Aktualisierung |
### `calcom_delete_event_type`
Einen Ereignistyp aus Cal.com löschen
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `eventTypeId` | number | Ja | Zu löschende Ereignistyp-ID |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `status` | string | Antwortstatus |
| `data` | object | Details des gelöschten Ereignistyps |
| ↳ `id` | number | Ereignistyp-ID |
| ↳ `lengthInMinutes` | number | Dauer in Minuten |
| ↳ `title` | string | Titel des Ereignistyps |
| ↳ `slug` | string | Slug des Ereignistyps |
### `calcom_create_schedule`
Einen neuen Verfügbarkeitsplan in Cal.com erstellen
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `name` | string | Ja | Name des Plans |
| `timeZone` | string | Ja | Zeitzone für den Plan \(z. B. America/New_York\) |
| `isDefault` | boolean | Ja | Ob dieser Plan als Standard festgelegt werden soll |
| `availability` | array | Nein | Verfügbarkeitsintervalle für den Plan |
| `items` | object | Nein | Verfügbarkeitsintervall |
| `properties` | array | Nein | Wochentage \(Montag, Dienstag, Mittwoch, Donnerstag, Freitag, Samstag, Sonntag\) |
| `days` | array | Nein | Wochentage \(Montag, Dienstag, Mittwoch, Donnerstag, Freitag, Samstag, Sonntag\) |
| `startTime` | string | Nein | Startzeit im Format HH:MM |
| `endTime` | string | Nein | Endzeit im Format HH:MM |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `status` | string | Antwortstatus |
| `data` | object | Erstellte Plandaten |
| ↳ `id` | number | Plan-ID |
| ↳ `ownerId` | number | Benutzer-ID des Eigentümers |
| ↳ `name` | string | Planname |
| ↳ `timeZone` | string | Zeitzone \(z. B. America/New_York\) |
| ↳ `isDefault` | boolean | Ob dies der Standardplan ist |
| ↳ `availability` | array | Verfügbarkeitsfenster |
| ↳ `days` | array | Wochentage \(Montag, Dienstag usw.\) |
| ↳ `startTime` | string | Startzeit im Format HH:MM |
| ↳ `endTime` | string | Endzeit im Format HH:MM |
| ↳ `overrides` | array | Datumsspezifische Verfügbarkeitsüberschreibungen |
| ↳ `date` | string | Datum im Format JJJJ-MM-TT |
| ↳ `startTime` | string | Startzeit im Format HH:MM |
| ↳ `endTime` | string | Endzeit im Format HH:MM |
### `calcom_get_schedule`
Einen bestimmten Zeitplan anhand der ID von Cal.com abrufen
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `scheduleId` | string | Ja | ID des abzurufenden Zeitplans |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `status` | string | Antwortstatus |
| `data` | object | Zeitplandaten |
| ↳ `id` | number | Zeitplan-ID |
| ↳ `ownerId` | number | Benutzer-ID des Eigentümers |
| ↳ `name` | string | Zeitplanname |
| ↳ `timeZone` | string | Zeitzone \(z. B. America/New_York\) |
| ↳ `isDefault` | boolean | Ob dies der Standardzeitplan ist |
| ↳ `availability` | array | Verfügbarkeitsfenster |
| ↳ `days` | array | Wochentage \(Montag, Dienstag usw.\) |
| ↳ `startTime` | string | Startzeit im Format HH:MM |
| ↳ `endTime` | string | Endzeit im Format HH:MM |
| ↳ `overrides` | array | Datumsspezifische Verfügbarkeitsüberschreibungen |
| ↳ `date` | string | Datum im Format JJJJ-MM-TT |
| ↳ `startTime` | string | Startzeit im Format HH:MM |
| ↳ `endTime` | string | Endzeit im Format HH:MM |
### `calcom_list_schedules`
Alle Verfügbarkeitszeitpläne von Cal.com auflisten
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `status` | string | Antwortstatus |
| `data` | array | Array von Zeitplanobjekten |
| ↳ `id` | number | Zeitplan-ID |
| ↳ `ownerId` | number | Benutzer-ID des Eigentümers |
| ↳ `name` | string | Zeitplanname |
| ↳ `timeZone` | string | Zeitzone \(z. B. America/New_York\) |
| ↳ `isDefault` | boolean | Ob dies der Standardzeitplan ist |
| ↳ `availability` | array | Verfügbarkeitsfenster |
| ↳ `days` | array | Wochentage \(Montag, Dienstag usw.\) |
| ↳ `startTime` | string | Startzeit im Format HH:MM |
| ↳ `endTime` | string | Endzeit im Format HH:MM |
| ↳ `overrides` | array | Datumsspezifische Verfügbarkeitsüberschreibungen |
| ↳ `date` | string | Datum im Format JJJJ-MM-TT |
| ↳ `startTime` | string | Startzeit im Format HH:MM |
| ↳ `endTime` | string | Endzeit im Format HH:MM |
### `calcom_update_schedule`
Einen bestehenden Zeitplan in Cal.com aktualisieren
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `scheduleId` | string | Ja | ID des zu aktualisierenden Zeitplans |
| `name` | string | Nein | Neuer Name für den Zeitplan |
| `timeZone` | string | Nein | Neue Zeitzone für den Zeitplan \(z. B. America/New_York\) |
| `isDefault` | boolean | Nein | Ob dieser Zeitplan als Standard festgelegt werden soll |
| `availability` | array | Nein | Neue Verfügbarkeitsintervalle für den Zeitplan |
| `items` | object | Nein | Verfügbarkeitsintervall |
| `properties` | array | Nein | Wochentage \(Montag, Dienstag, Mittwoch, Donnerstag, Freitag, Samstag, Sonntag\) |
| `days` | array | Nein | Wochentage \(Montag, Dienstag, Mittwoch, Donnerstag, Freitag, Samstag, Sonntag\) |
| `startTime` | string | Nein | Startzeit im Format HH:MM |
| `endTime` | string | Nein | Endzeit im Format HH:MM |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `status` | string | Antwortstatus |
| `data` | object | Aktualisierte Zeitplandaten |
| ↳ `id` | number | Zeitplan-ID |
| ↳ `ownerId` | number | Benutzer-ID des Eigentümers |
| ↳ `name` | string | Zeitplanname |
| ↳ `timeZone` | string | Zeitzone \(z. B. America/New_York\) |
| ↳ `isDefault` | boolean | Ob dies der Standardzeitplan ist |
| ↳ `availability` | array | Verfügbarkeitsfenster |
| ↳ `days` | array | Wochentage \(Montag, Dienstag usw.\) |
| ↳ `startTime` | string | Startzeit im Format HH:MM |
| ↳ `endTime` | string | Endzeit im Format HH:MM |
| ↳ `overrides` | array | Datumsspezifische Verfügbarkeitsüberschreibungen |
| ↳ `date` | string | Datum im Format JJJJ-MM-TT |
| ↳ `startTime` | string | Startzeit im Format HH:MM |
| ↳ `endTime` | string | Endzeit im Format HH:MM |
### `calcom_delete_schedule`
Einen Zeitplan aus Cal.com löschen
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `scheduleId` | string | Ja | ID des zu löschenden Zeitplans |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `status` | string | Antwortstatus \(Erfolg oder Fehler\) |
### `calcom_get_default_schedule`
Den Standard-Verfügbarkeitszeitplan aus Cal.com abrufen
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `status` | string | Antwortstatus |
| `data` | object | Standard-Zeitplandaten |
| ↳ `id` | number | Zeitplan-ID |
| ↳ `ownerId` | number | Benutzer-ID des Eigentümers |
| ↳ `name` | string | Zeitplanname |
| ↳ `timeZone` | string | Zeitzone \(z. B. America/New_York\) |
| ↳ `isDefault` | boolean | Ob dies der Standardzeitplan ist |
| ↳ `availability` | array | Verfügbarkeitsfenster |
| ↳ `days` | array | Wochentage \(Montag, Dienstag usw.\) |
| ↳ `startTime` | string | Startzeit im Format HH:MM |
| ↳ `endTime` | string | Endzeit im Format HH:MM |
| ↳ `overrides` | array | Datumsspezifische Verfügbarkeitsüberschreibungen |
| ↳ `date` | string | Datum im Format JJJJ-MM-TT |
| ↳ `startTime` | string | Startzeit im Format HH:MM |
| ↳ `endTime` | string | Endzeit im Format HH:MM |
### `calcom_get_slots`
Verfügbare Buchungsslots für einen Cal.com-Ereignistyp innerhalb eines Zeitraums abrufen
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `start` | string | Ja | Beginn des Zeitraums im UTC ISO 8601-Format \(z. B. 2024-01-15T00:00:00Z\) |
| `end` | string | Ja | Ende des Zeitraums im UTC ISO 8601-Format \(z. B. 2024-01-22T00:00:00Z\) |
| `eventTypeId` | number | Nein | Ereignistyp-ID für direkte Suche |
| `eventTypeSlug` | string | Nein | Ereignistyp-Slug \(erfordert, dass der Benutzername gesetzt ist\) |
| `username` | string | Nein | Benutzername für persönliche Ereignistypen \(erforderlich bei Verwendung von eventTypeSlug\) |
| `timeZone` | string | Nein | Zeitzone für zurückgegebene Slots \(Standard ist UTC\) |
| `duration` | number | Nein | Slot-Länge in Minuten |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `status` | string | Antwortstatus |
| `data` | json | Verfügbare Zeitslots gruppiert nach Datum \(Schlüssel im Format JJJJ-MM-TT\). Jedes Datum ist einem Array von Slot-Objekten mit Startzeit, optionaler Endzeit und Informationen zu Sitzplatz-Events zugeordnet. |

View File

@@ -165,3 +165,8 @@ Ein geplantes Ereignis stornieren
| Parameter | Typ | Beschreibung | | Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- | | --------- | ---- | ----------- |
| `resource` | object | Stornierungsdetails | | `resource` | object | Stornierungsdetails |
## Hinweise
- Kategorie: `tools`
- Typ: `calendly`

View File

@@ -1,54 +0,0 @@
---
title: Circleback
description: KI-gestützte Meeting-Notizen und Aufgaben
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="circleback"
color="linear-gradient(180deg, #E0F7FA 0%, #FFFFFF 100%)"
/>
{/* MANUAL-CONTENT-START:intro */}
[Circleback](https://circleback.ai/) ist eine KI-gestützte Plattform, die Meeting-Notizen, Aufgaben, Transkripte und Aufzeichnungen für Ihr Team automatisiert. Wenn ein Meeting abgeschlossen ist, verarbeitet Circleback die Konversation und liefert detaillierte Notizen und Aufgaben sowie ein Transkript und eine Aufzeichnung (sofern verfügbar). Dies hilft Teams dabei, Erkenntnisse effizient zu erfassen, Aufgaben zu verteilen und sicherzustellen, dass nichts übersehen wird alles nahtlos in Ihre Workflows integriert.
Mit der Sim Circleback-Integration können Sie:
- **Detaillierte Meeting-Notizen und Aufgaben erhalten**: Sammeln Sie automatisch gut formatierte Meeting-Zusammenfassungen und verfolgen Sie umsetzbare Aufgaben, die während Ihrer Anrufe besprochen wurden.
- **Auf vollständige Meeting-Aufzeichnungen und Transkripte zugreifen**: Erhalten Sie die vollständige Konversation und die zugehörige Aufzeichnung, um wichtige Momente einfach zu überprüfen oder mit Kollegen zu teilen.
- **Teilnehmerinformationen und Meeting-Kontext erfassen**: Teilnehmerlisten, Meeting-Metadaten und Tags helfen dabei, Ihre Daten organisiert und umsetzbar zu halten.
- **Erkenntnisse direkt in Ihre Workflows liefern**: Lösen Sie Automatisierungen aus oder senden Sie Circleback-Daten an andere Systeme, sobald ein Meeting beendet ist, mithilfe der leistungsstarken Webhook-Trigger von Sim.
**So funktioniert es in Sim:**
Circleback verwendet Webhook-Trigger: Sobald ein Meeting verarbeitet wurde, werden die Daten automatisch an Ihren Agenten oder Ihre Automatisierung übertragen. Sie können weitere Automatisierungen basierend auf folgenden Ereignissen erstellen:
- Meeting abgeschlossen (alle verarbeiteten Daten verfügbar)
- Neue Notizen (Notizen sind verfügbar, noch bevor das Meeting vollständig verarbeitet ist)
- Raw-Webhook-Integration für erweiterte Anwendungsfälle
**Die folgenden Informationen sind in der Circleback-Meeting-Webhook-Payload verfügbar:**
| Feld | Typ | Beschreibung |
|----------------|---------|----------------------------------------------------|
| `id` | number | Circleback Meeting-ID |
| `name` | string | Meeting-Titel |
| `url` | string | Virtueller Meeting-Link (Zoom, Meet, Teams usw.) |
| `createdAt` | string | Zeitstempel der Meeting-Erstellung |
| `duration` | number | Dauer in Sekunden |
| `recordingUrl` | string | Aufzeichnungs-URL (24 Stunden gültig) |
| `tags` | json | Array von Tags |
| `icalUid` | string | Kalender-Event-ID |
| `attendees` | json | Array von Teilnehmer-Objekten |
| `notes` | string | Meeting-Notizen in Markdown |
| `actionItems` | json | Array von Aufgaben |
| `transcript` | json | Array von Transkript-Segmenten |
| `insights` | json | Vom Nutzer erstellte Insights |
| `meeting` | json | Vollständige Meeting-Daten |
Egal, ob Sie sofortige Zusammenfassungen verteilen, Aufgaben protokollieren oder benutzerdefinierte Workflows erstellen möchten, die durch neue Meeting-Daten ausgelöst werden Circleback und Sim machen es nahtlos, alles rund um Ihre Meetings automatisch zu verwalten.
{/* MANUAL-CONTENT-END */}
## Nutzungsanleitung
Erhalten Sie Meeting-Notizen, Aufgaben, Transkripte und Aufzeichnungen, wenn Meetings verarbeitet werden. Circleback nutzt Webhooks, um Daten an Ihre Workflows zu übermitteln.

View File

@@ -53,3 +53,13 @@ Populate Clay with data from a JSON file. Enables direct communication and notif
| `authToken` | string | Nein | Optionaler Auth-Token für die Clay-Webhook-Authentifizierung \(die meisten Webhooks benötigen dies nicht\) | | `authToken` | string | Nein | Optionaler Auth-Token für die Clay-Webhook-Authentifizierung \(die meisten Webhooks benötigen dies nicht\) |
#### Output #### Output
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `data` | json | Antwortdaten vom Clay-Webhook |
| `metadata` | object | Webhook-Antwort-Metadaten |
## Notes
- Category: `tools`
- Type: `clay`

View File

@@ -1,437 +0,0 @@
---
title: Clerk
description: Verwalten Sie Benutzer, Organisationen und Sitzungen in Clerk
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="clerk"
color="#131316"
/>
{/* MANUAL-CONTENT-START:intro */}
[Clerk](https://clerk.com/) ist eine umfassende Identitätsinfrastruktur-Plattform, die Ihnen hilft, Benutzer, Authentifizierung und Sitzungen für Ihre Anwendungen zu verwalten.
In Sim ermöglicht die Clerk-Integration Ihren Agenten, die Benutzer- und Sitzungsverwaltung durch einfach zu bedienende API-basierte Tools zu automatisieren. Agenten können sicher Benutzer auflisten, Benutzerprofile aktualisieren, Organisationen verwalten, Sitzungen überwachen und den Zugriff direkt in Ihrem Workflow widerrufen.
Mit Clerk können Sie:
- **Benutzer authentifizieren und Sitzungen verwalten**: Steuern Sie nahtlos Anmeldung, Registrierung und den Sitzungslebenszyklus für Ihre Benutzer.
- **Benutzer auflisten und aktualisieren**: Rufen Sie automatisch Benutzerlisten ab, aktualisieren Sie Benutzerattribute oder zeigen Sie Profildetails als Teil Ihrer Agentenaufgaben an.
- **Organisationen und Mitgliedschaften verwalten**: Fügen Sie Organisationen hinzu oder aktualisieren Sie diese und verwalten Sie Benutzermitgliedschaften übersichtlich.
- **Sitzungen überwachen und widerrufen**: Sehen Sie aktive oder vergangene Benutzersitzungen ein und widerrufen Sie bei Bedarf sofort den Zugriff aus Sicherheitsgründen.
Die Integration ermöglicht eine Echtzeit- und nachvollziehbare Verwaltung Ihrer Benutzerbasis alles innerhalb von Sim. Verbundene Agenten können das Onboarding automatisieren, Richtlinien durchsetzen, Verzeichnisse aktuell halten und auf Authentifizierungsereignisse oder organisatorische Änderungen reagieren, sodass Sie sichere und flexible Prozesse mit Clerk als Ihrer Identitäts-Engine betreiben können.
{/* MANUAL-CONTENT-END */}
## Nutzungsanweisungen
Integrieren Sie Clerk-Authentifizierung und Benutzerverwaltung in Ihren Workflow. Erstellen, aktualisieren, löschen und listen Sie Benutzer auf. Verwalten Sie Organisationen und deren Mitgliedschaften. Überwachen und steuern Sie Benutzersitzungen.
## Tools
### `clerk_list_users`
Listen Sie alle Benutzer in Ihrer Clerk-Anwendung mit optionaler Filterung und Paginierung auf
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `secretKey` | string | Ja | Der Clerk Secret Key für die API-Authentifizierung |
| `limit` | number | Nein | Anzahl der Ergebnisse pro Seite \(z. B. 10, 50, 100; Bereich: 1-500, Standard: 10\) |
| `offset` | number | Nein | Anzahl der zu überspringenden Ergebnisse für die Paginierung \(z. B. 0, 10, 20\) |
| `orderBy` | string | Nein | Sortierfeld mit optionalem +/- Präfix für die Richtung \(Standard: -created_at\) |
| `emailAddress` | string | Nein | Filtern nach E-Mail-Adresse \(z. B. user@example.com oder user1@example.com,user2@example.com\) |
| `phoneNumber` | string | Nein | Filtern nach Telefonnummer \(durch Komma getrennt für mehrere\) |
| `externalId` | string | Nein | Filtern nach externer ID \(durch Komma getrennt für mehrere\) |
| `username` | string | Nein | Filtern nach Benutzername \(durch Komma getrennt für mehrere\) |
| `userId` | string | Nein | Filtern nach Benutzer-ID \(z. B. user_2NNEqL2nrIRdJ194ndJqAHwEfxC oder durch Komma getrennt für mehrere\) |
| `query` | string | Nein | Suchanfrage zur Übereinstimmung über E-Mail, Telefon, Benutzername und Namen \(z. B. john oder john@example.com\) |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `users` | array | Array von Clerk-Benutzerobjekten |
| ↳ `id` | string | Benutzer-ID |
| ↳ `username` | string | Benutzername |
| ↳ `firstName` | string | Vorname |
| ↳ `lastName` | string | Nachname |
| ↳ `imageUrl` | string | Profilbild-URL |
| ↳ `hasImage` | boolean | Ob Benutzer ein Profilbild hat |
| ↳ `primaryEmailAddressId` | string | Primäre E-Mail-Adressen-ID |
| ↳ `primaryPhoneNumberId` | string | Primäre Telefonnummer-ID |
| ↳ `emailAddresses` | array | E-Mail-Adressen des Benutzers |
| ↳ `id` | string | E-Mail-Adressen-ID |
| ↳ `emailAddress` | string | E-Mail-Adresse |
| ↳ `phoneNumbers` | array | Telefonnummern des Benutzers |
| ↳ `id` | string | Telefonnummer-ID |
| ↳ `phoneNumber` | string | Telefonnummer |
| ↳ `externalId` | string | Externe System-ID |
| ↳ `passwordEnabled` | boolean | Ob Passwort aktiviert ist |
| ↳ `twoFactorEnabled` | boolean | Ob 2FA aktiviert ist |
| ↳ `banned` | boolean | Ob Benutzer gesperrt ist |
| ↳ `locked` | boolean | Ob Benutzer blockiert ist |
| ↳ `lastSignInAt` | number | Zeitstempel der letzten Anmeldung |
| ↳ `lastActiveAt` | number | Zeitstempel der letzten Aktivität |
| ↳ `createdAt` | number | Erstellungszeitstempel |
| ↳ `updatedAt` | number | Zeitstempel der letzten Aktualisierung |
| ↳ `publicMetadata` | json | Öffentliche Metadaten |
| `totalCount` | number | Gesamtanzahl der Benutzer, die der Abfrage entsprechen |
| `success` | boolean | Erfolgsstatus der Operation |
### `clerk_get_user`
Einen einzelnen Benutzer anhand seiner ID von Clerk abrufen
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `secretKey` | string | Ja | Der Clerk Secret Key für die API-Authentifizierung |
| `userId` | string | Ja | Die ID des abzurufenden Benutzers \(z. B. user_2NNEqL2nrIRdJ194ndJqAHwEfxC\) |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `id` | string | Benutzer-ID |
| `username` | string | Benutzername |
| `firstName` | string | Vorname |
| `lastName` | string | Nachname |
| `imageUrl` | string | Profilbild-URL |
| `hasImage` | boolean | Ob der Benutzer ein Profilbild hat |
| `primaryEmailAddressId` | string | Primäre E-Mail-Adressen-ID |
| `primaryPhoneNumberId` | string | Primäre Telefonnummer-ID |
| `primaryWeb3WalletId` | string | Primäre Web3-Wallet-ID |
| `emailAddresses` | array | E-Mail-Adressen des Benutzers |
| ↳ `id` | string | E-Mail-Adressen-ID |
| ↳ `emailAddress` | string | E-Mail-Adresse |
| ↳ `verified` | boolean | Ob die E-Mail verifiziert ist |
| `phoneNumbers` | array | Telefonnummern des Benutzers |
| ↳ `id` | string | Telefonnummer-ID |
| ↳ `phoneNumber` | string | Telefonnummer |
| ↳ `verified` | boolean | Ob die Telefonnummer verifiziert ist |
| `externalId` | string | Externe System-ID |
| `passwordEnabled` | boolean | Ob das Passwort aktiviert ist |
| `twoFactorEnabled` | boolean | Ob 2FA aktiviert ist |
| `totpEnabled` | boolean | Ob TOTP aktiviert ist |
| `backupCodeEnabled` | boolean | Ob Backup-Codes aktiviert sind |
| `banned` | boolean | Ob der Benutzer gesperrt ist |
| `locked` | boolean | Ob der Benutzer blockiert ist |
| `deleteSelfEnabled` | boolean | Ob der Benutzer sich selbst löschen kann |
| `createOrganizationEnabled` | boolean | Ob der Benutzer Organisationen erstellen kann |
| `lastSignInAt` | number | Zeitstempel der letzten Anmeldung |
| `lastActiveAt` | number | Zeitstempel der letzten Aktivität |
| `createdAt` | number | Erstellungszeitstempel |
| `updatedAt` | number | Zeitstempel der letzten Aktualisierung |
| `publicMetadata` | json | Öffentliche Metadaten \(vom Frontend lesbar\) |
| `privateMetadata` | json | Private Metadaten \(nur Backend\) |
| `unsafeMetadata` | json | Unsichere Metadaten \(vom Frontend änderbar\) |
| `success` | boolean | Erfolgsstatus der Operation |
### `clerk_create_user`
Erstellen Sie einen neuen Benutzer in Ihrer Clerk-Anwendung
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `secretKey` | string | Ja | Der Clerk Secret Key für die API-Authentifizierung |
| `emailAddress` | string | Nein | E-Mail-Adressen für den Benutzer \(durch Komma getrennt für mehrere\) |
| `phoneNumber` | string | Nein | Telefonnummern für den Benutzer \(durch Komma getrennt für mehrere\) |
| `username` | string | Nein | Benutzername für den Benutzer \(muss eindeutig sein\) |
| `password` | string | Nein | Passwort für den Benutzer \(mindestens 8 Zeichen\) |
| `firstName` | string | Nein | Vorname des Benutzers |
| `lastName` | string | Nein | Nachname des Benutzers |
| `externalId` | string | Nein | Externe System-ID \(muss eindeutig sein\) |
| `publicMetadata` | json | Nein | Öffentliche Metadaten \(JSON-Objekt, lesbar vom Frontend\) |
| `privateMetadata` | json | Nein | Private Metadaten \(JSON-Objekt, nur Backend\) |
| `unsafeMetadata` | json | Nein | Unsichere Metadaten \(JSON-Objekt, änderbar vom Frontend\) |
| `skipPasswordChecks` | boolean | Nein | Passwortvalidierungsprüfungen überspringen |
| `skipPasswordRequirement` | boolean | Nein | Passwort optional machen |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `id` | string | Erstellte Benutzer-ID |
| `username` | string | Benutzername |
| `firstName` | string | Vorname |
| `lastName` | string | Nachname |
| `imageUrl` | string | Profilbild-URL |
| `primaryEmailAddressId` | string | Primäre E-Mail-Adressen-ID |
| `primaryPhoneNumberId` | string | Primäre Telefonnummer-ID |
| `emailAddresses` | array | E-Mail-Adressen des Benutzers |
| ↳ `id` | string | E-Mail-Adressen-ID |
| ↳ `emailAddress` | string | E-Mail-Adresse |
| ↳ `verified` | boolean | Ob die E-Mail verifiziert ist |
| `phoneNumbers` | array | Telefonnummern des Benutzers |
| ↳ `id` | string | Telefonnummer-ID |
| ↳ `phoneNumber` | string | Telefonnummer |
| ↳ `verified` | boolean | Ob die Telefonnummer verifiziert ist |
| `externalId` | string | Externe System-ID |
| `createdAt` | number | Erstellungszeitstempel |
| `updatedAt` | number | Letzter Aktualisierungszeitstempel |
| `publicMetadata` | json | Öffentliche Metadaten |
| `success` | boolean | Erfolgsstatus der Operation |
### `clerk_update_user`
Aktualisieren Sie einen bestehenden Benutzer in Ihrer Clerk-Anwendung
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `secretKey` | string | Ja | Der Clerk Secret Key für die API-Authentifizierung |
| `userId` | string | Ja | Die ID des zu aktualisierenden Benutzers \(z. B. user_2NNEqL2nrIRdJ194ndJqAHwEfxC\) |
| `firstName` | string | Nein | Vorname des Benutzers |
| `lastName` | string | Nein | Nachname des Benutzers |
| `username` | string | Nein | Benutzername \(muss eindeutig sein\) |
| `password` | string | Nein | Neues Passwort \(mindestens 8 Zeichen\) |
| `externalId` | string | Nein | Externe System-ID |
| `primaryEmailAddressId` | string | Nein | ID der verifizierten E-Mail, die als primär festgelegt werden soll |
| `primaryPhoneNumberId` | string | Nein | ID der verifizierten Telefonnummer, die als primär festgelegt werden soll |
| `publicMetadata` | json | Nein | Öffentliche Metadaten \(JSON-Objekt\) |
| `privateMetadata` | json | Nein | Private Metadaten \(JSON-Objekt\) |
| `unsafeMetadata` | json | Nein | Unsichere Metadaten \(JSON-Objekt\) |
| `skipPasswordChecks` | boolean | Nein | Passwortvalidierungsprüfungen überspringen |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `id` | string | Aktualisierte Benutzer-ID |
| `username` | string | Benutzername |
| `firstName` | string | Vorname |
| `lastName` | string | Nachname |
| `imageUrl` | string | Profilbild-URL |
| `primaryEmailAddressId` | string | Primäre E-Mail-Adressen-ID |
| `primaryPhoneNumberId` | string | Primäre Telefonnummer-ID |
| `emailAddresses` | array | E-Mail-Adressen des Benutzers |
| ↳ `id` | string | E-Mail-Adressen-ID |
| ↳ `emailAddress` | string | E-Mail-Adresse |
| ↳ `verified` | boolean | Ob die E-Mail verifiziert ist |
| `phoneNumbers` | array | Telefonnummern des Benutzers |
| ↳ `id` | string | Telefonnummer-ID |
| ↳ `phoneNumber` | string | Telefonnummer |
| ↳ `verified` | boolean | Ob die Telefonnummer verifiziert ist |
| `externalId` | string | Externe System-ID |
| `banned` | boolean | Ob der Benutzer gesperrt ist |
| `locked` | boolean | Ob der Benutzer blockiert ist |
| `createdAt` | number | Erstellungszeitstempel |
| `updatedAt` | number | Zeitstempel der letzten Aktualisierung |
| `publicMetadata` | json | Öffentliche Metadaten |
| `success` | boolean | Erfolgsstatus der Operation |
### `clerk_delete_user`
Einen Benutzer aus Ihrer Clerk-Anwendung löschen
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `secretKey` | string | Ja | Der Clerk Secret Key für die API-Authentifizierung |
| `userId` | string | Ja | Die ID des zu löschenden Benutzers (z. B. user_2NNEqL2nrIRdJ194ndJqAHwEfxC) |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `id` | string | ID des gelöschten Benutzers |
| `object` | string | Objekttyp (user) |
| `deleted` | boolean | Ob der Benutzer gelöscht wurde |
| `success` | boolean | Erfolgsstatus der Operation |
### `clerk_list_organizations`
Alle Organisationen in Ihrer Clerk-Anwendung mit optionaler Filterung auflisten
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `secretKey` | string | Ja | Der Clerk Secret Key für die API-Authentifizierung |
| `limit` | number | Nein | Anzahl der Ergebnisse pro Seite (z. B. 10, 50, 100; Bereich: 1500, Standard: 10) |
| `offset` | number | Nein | Anzahl der zu überspringenden Ergebnisse für die Paginierung (z. B. 0, 10, 20) |
| `includeMembersCount` | boolean | Nein | Mitgliederanzahl für jede Organisation einbeziehen |
| `query` | string | Nein | Suche nach Organisations-ID, Name oder Slug (z. B. Acme Corp oder acme-corp) |
| `orderBy` | string | Nein | Sortierfeld (name, created_at, members_count) mit +/- Präfix |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `organizations` | array | Array von Clerk-Organisationsobjekten |
| ↳ `id` | string | Organisations-ID |
| ↳ `name` | string | Organisationsname |
| ↳ `slug` | string | Organisations-Slug |
| ↳ `imageUrl` | string | URL des Organisationsbilds |
| ↳ `hasImage` | boolean | Ob die Organisation ein Bild hat |
| ↳ `membersCount` | number | Anzahl der Mitglieder |
| ↳ `pendingInvitationsCount` | number | Anzahl ausstehender Einladungen |
| ↳ `maxAllowedMemberships` | number | Maximal zulässige Mitgliedschaften |
| ↳ `adminDeleteEnabled` | boolean | Ob Admin-Löschung aktiviert ist |
| ↳ `createdBy` | string | Benutzer-ID des Erstellers |
| ↳ `createdAt` | number | Erstellungszeitstempel |
| ↳ `updatedAt` | number | Zeitstempel der letzten Aktualisierung |
| ↳ `publicMetadata` | json | Öffentliche Metadaten |
| `totalCount` | number | Gesamtanzahl der Organisationen |
| `success` | boolean | Erfolgsstatus der Operation |
### `clerk_get_organization`
Abrufen einer einzelnen Organisation nach ID oder Slug von Clerk
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `secretKey` | string | Ja | Der Clerk Secret Key für die API-Authentifizierung |
| `organizationId` | string | Ja | Die ID oder der Slug der abzurufenden Organisation \(z. B. org_2NNEqL2nrIRdJ194ndJqAHwEfxC oder my-org-slug\) |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `id` | string | Organisations-ID |
| `name` | string | Organisationsname |
| `slug` | string | Organisations-Slug |
| `imageUrl` | string | URL des Organisationsbilds |
| `hasImage` | boolean | Ob die Organisation ein Bild hat |
| `membersCount` | number | Anzahl der Mitglieder |
| `pendingInvitationsCount` | number | Anzahl ausstehender Einladungen |
| `maxAllowedMemberships` | number | Maximal zulässige Mitgliedschaften |
| `adminDeleteEnabled` | boolean | Ob Admin-Löschung aktiviert ist |
| `createdBy` | string | Benutzer-ID des Erstellers |
| `createdAt` | number | Erstellungszeitstempel |
| `updatedAt` | number | Zeitstempel der letzten Aktualisierung |
| `publicMetadata` | json | Öffentliche Metadaten |
| `success` | boolean | Erfolgsstatus der Operation |
### `clerk_create_organization`
Erstellen Sie eine neue Organisation in Ihrer Clerk-Anwendung
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `secretKey` | string | Ja | Der Clerk Secret Key für die API-Authentifizierung |
| `name` | string | Ja | Name der Organisation |
| `createdBy` | string | Ja | Benutzer-ID des Erstellers, der Administrator wird \(z. B. user_2NNEqL2nrIRdJ194ndJqAHwEfxC\) |
| `slug` | string | Nein | Slug-Identifikator für die Organisation |
| `maxAllowedMemberships` | number | Nein | Maximale Mitgliederkapazität \(0 für unbegrenzt\) |
| `publicMetadata` | json | Nein | Öffentliche Metadaten \(JSON-Objekt\) |
| `privateMetadata` | json | Nein | Private Metadaten \(JSON-Objekt\) |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `id` | string | Erstellte Organisations-ID |
| `name` | string | Organisationsname |
| `slug` | string | Organisations-Slug |
| `imageUrl` | string | URL des Organisationsbildes |
| `hasImage` | boolean | Ob die Organisation ein Bild hat |
| `membersCount` | number | Anzahl der Mitglieder |
| `pendingInvitationsCount` | number | Anzahl ausstehender Einladungen |
| `maxAllowedMemberships` | number | Maximal zulässige Mitgliedschaften |
| `adminDeleteEnabled` | boolean | Ob das Löschen durch den Administrator aktiviert ist |
| `createdBy` | string | Benutzer-ID des Erstellers |
| `createdAt` | number | Erstellungszeitstempel |
| `updatedAt` | number | Zeitstempel der letzten Aktualisierung |
| `publicMetadata` | json | Öffentliche Metadaten |
| `success` | boolean | Erfolgsstatus der Operation |
### `clerk_list_sessions`
Sitzungen für einen Benutzer oder Client in Ihrer Clerk-Anwendung auflisten
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `secretKey` | string | Ja | Der Clerk Secret Key für die API-Authentifizierung |
| `userId` | string | Nein | Benutzer-ID, für die Sitzungen aufgelistet werden sollen \(z. B. user_2NNEqL2nrIRdJ194ndJqAHwEfxC; erforderlich, wenn clientId nicht angegeben ist\) |
| `clientId` | string | Nein | Client-ID, für die Sitzungen aufgelistet werden sollen \(erforderlich, wenn userId nicht angegeben ist\) |
| `status` | string | Nein | Nach Sitzungsstatus filtern \(abandoned, active, ended, expired, pending, removed, replaced, revoked\) |
| `limit` | number | Nein | Anzahl der Ergebnisse pro Seite \(z. B. 10, 50, 100; Bereich: 1-500, Standard: 10\) |
| `offset` | number | Nein | Anzahl der zu überspringenden Ergebnisse für die Paginierung \(z. B. 0, 10, 20\) |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `sessions` | array | Array von Clerk-Sitzungsobjekten |
| ↳ `id` | string | Sitzungs-ID |
| ↳ `userId` | string | Benutzer-ID |
| ↳ `clientId` | string | Client-ID |
| ↳ `status` | string | Sitzungsstatus |
| ↳ `lastActiveAt` | number | Zeitstempel der letzten Aktivität |
| ↳ `lastActiveOrganizationId` | string | ID der zuletzt aktiven Organisation |
| ↳ `expireAt` | number | Ablaufzeitstempel |
| ↳ `abandonAt` | number | Abbruchzeitstempel |
| ↳ `createdAt` | number | Erstellungszeitstempel |
| ↳ `updatedAt` | number | Zeitstempel der letzten Aktualisierung |
| `totalCount` | number | Gesamtanzahl der Sitzungen |
| `success` | boolean | Erfolgsstatus der Operation |
### `clerk_get_session`
Eine einzelne Sitzung anhand der ID von Clerk abrufen
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `secretKey` | string | Ja | Der geheime Clerk-Schlüssel für die API-Authentifizierung |
| `sessionId` | string | Ja | Die ID der abzurufenden Sitzung \(z. B. sess_2NNEqL2nrIRdJ194ndJqAHwEfxC\) |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `id` | string | Sitzungs-ID |
| `userId` | string | Benutzer-ID |
| `clientId` | string | Client-ID |
| `status` | string | Sitzungsstatus |
| `lastActiveAt` | number | Zeitstempel der letzten Aktivität |
| `lastActiveOrganizationId` | string | ID der zuletzt aktiven Organisation |
| `expireAt` | number | Ablaufzeitstempel |
| `abandonAt` | number | Zeitstempel für Abbruch |
| `createdAt` | number | Erstellungszeitstempel |
| `updatedAt` | number | Zeitstempel der letzten Aktualisierung |
| `success` | boolean | Erfolgsstatus der Operation |
### `clerk_revoke_session`
Eine Sitzung widerrufen, um sie sofort ungültig zu machen
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `secretKey` | string | Ja | Der geheime Clerk-Schlüssel für die API-Authentifizierung |
| `sessionId` | string | Ja | Die ID der zu widerrufenden Sitzung \(z. B. sess_2NNEqL2nrIRdJ194ndJqAHwEfxC\) |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `id` | string | Sitzungs-ID |
| `userId` | string | Benutzer-ID |
| `clientId` | string | Client-ID |
| `status` | string | Sitzungsstatus \(sollte widerrufen sein\) |
| `lastActiveAt` | number | Zeitstempel der letzten Aktivität |
| `lastActiveOrganizationId` | string | ID der zuletzt aktiven Organisation |
| `expireAt` | number | Ablaufzeitstempel |
| `abandonAt` | number | Zeitstempel für Abbruch |
| `createdAt` | number | Erstellungszeitstempel |
| `updatedAt` | number | Zeitstempel der letzten Aktualisierung |
| `success` | boolean | Erfolgsstatus der Operation |

View File

@@ -354,512 +354,3 @@ Listet alle Confluence-Spaces auf, auf die der Benutzer zugreifen kann.
- Kategorie: `tools` - Kategorie: `tools`
- Typ: `confluence` - Typ: `confluence`
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `domain` | string | Ja | Ihre Confluence-Domain (z. B. ihrfirma.atlassian.net) |
| `blogPostId` | string | Ja | Die ID des abzurufenden Blogbeitrags |
| `bodyFormat` | string | Nein | Format für den Blogbeitrag-Body: storage, atlas_doc_format oder view |
| `cloudId` | string | Nein | Confluence Cloud-ID für die Instanz. Wenn nicht angegeben, wird sie über die Domain abgerufen. |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `ts` | string | ISO 8601-Zeitstempel der Operation |
| `id` | string | Blogbeitrag-ID |
| `title` | string | Blogbeitrag-Titel |
| `status` | string | Blogbeitrag-Status |
| `spaceId` | string | Space-ID |
| `authorId` | string | Konto-ID des Autors |
| `createdAt` | string | Erstellungszeitstempel |
| `version` | object | Versionsinformationen |
| ↳ `number` | number | Versionsnummer |
| ↳ `message` | string | Versionsnachricht |
| ↳ `minorEdit` | boolean | Ob dies eine geringfügige Bearbeitung ist |
| ↳ `authorId` | string | Konto-ID des Versionsautors |
| ↳ `createdAt` | string | ISO 8601-Zeitstempel der Versionserstellung |
| `body` | object | Blogbeitrag-Body-Inhalt im angeforderten Format |
| ↳ `storage` | object | Body im Speicherformat (Confluence-Markup) |
| ↳ `value` | string | Der Inhaltswert im angegebenen Format |
| ↳ `representation` | string | Inhaltsdarstellungstyp |
| ↳ `view` | object | Body im Ansichtsformat (gerendertes HTML) |
| ↳ `value` | string | Der Inhaltswert im angegebenen Format |
| ↳ `representation` | string | Inhaltsdarstellungstyp |
| ↳ `atlas_doc_format` | object | Body im Atlassian Document Format (ADF) |
| ↳ `value` | string | Der Inhaltswert im angegebenen Format |
| ↳ `representation` | string | Inhaltsdarstellungstyp |
| `webUrl` | string | URL zum Anzeigen des Blogbeitrags |
### `confluence_create_blogpost`
Erstellen Sie einen neuen Blogbeitrag in einem Confluence-Space.
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `domain` | string | Ja | Ihre Confluence-Domain (z.B. ihrfirma.atlassian.net) |
| `spaceId` | string | Ja | Die ID des Spaces, in dem der Blogbeitrag erstellt werden soll |
| `title` | string | Ja | Titel des Blogbeitrags |
| `content` | string | Ja | Blogbeitrag-Inhalt im Confluence-Speicherformat (HTML) |
| `status` | string | Nein | Blogbeitrag-Status: current (Standard) oder draft |
| `cloudId` | string | Nein | Confluence Cloud-ID für die Instanz. Wenn nicht angegeben, wird sie über die Domain abgerufen. |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `ts` | string | ISO 8601-Zeitstempel der Operation |
| `id` | string | Erstellte Blogbeitrag-ID |
| `title` | string | Blogbeitrag-Titel |
| `status` | string | Blogbeitrag-Status |
| `spaceId` | string | Space-ID |
| `authorId` | string | Konto-ID des Autors |
| `body` | object | Blogbeitrag-Inhalt |
| ↳ `storage` | object | Inhalt im Speicherformat (Confluence-Markup) |
| ↳ `value` | string | Der Inhaltswert im angegebenen Format |
| ↳ `representation` | string | Inhaltsdarstellungstyp |
| ↳ `view` | object | Inhalt im Anzeigeformat (gerendertes HTML) |
| ↳ `value` | string | Der Inhaltswert im angegebenen Format |
| ↳ `representation` | string | Inhaltsdarstellungstyp |
| ↳ `atlas_doc_format` | object | Inhalt im Atlassian Document Format (ADF) |
| ↳ `value` | string | Der Inhaltswert im angegebenen Format |
| ↳ `representation` | string | Inhaltsdarstellungstyp |
| `version` | object | Versionsinformationen des Blogbeitrags |
| ↳ `number` | number | Versionsnummer |
| ↳ `message` | string | Versionsnachricht |
| ↳ `minorEdit` | boolean | Ob dies eine geringfügige Bearbeitung ist |
| ↳ `authorId` | string | Konto-ID des Versionsautors |
| ↳ `createdAt` | string | ISO 8601-Zeitstempel der Versionserstellung |
| `webUrl` | string | URL zum Anzeigen des Blogbeitrags |
### `confluence_list_blogposts_in_space`
Listet alle Blogbeiträge innerhalb eines bestimmten Confluence-Spaces auf.
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `domain` | string | Ja | Ihre Confluence-Domain (z.B. ihrfirma.atlassian.net) |
| `spaceId` | string | Ja | Die ID des Confluence-Spaces, aus dem Blogbeiträge aufgelistet werden sollen |
| `limit` | number | Nein | Maximale Anzahl der zurückzugebenden Blogbeiträge (Standard: 25, max: 250) |
| `status` | string | Nein | Filtern nach Status: current, archived, trashed oder draft |
| `bodyFormat` | string | Nein | Format für den Blogbeitrags-Body: storage, atlas_doc_format oder view |
| `cursor` | string | Nein | Paginierungs-Cursor aus vorheriger Antwort |
| `cloudId` | string | Nein | Confluence Cloud-ID für die Instanz. Wenn nicht angegeben, wird sie über die Domain abgerufen. |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `ts` | string | ISO 8601-Zeitstempel der Operation |
| `blogPosts` | array | Array von Blogbeiträgen im Space |
| ↳ `id` | string | Blogbeitrags-ID |
| ↳ `title` | string | Blogbeitragstitel |
| ↳ `status` | string | Blogbeitragsstatus |
| ↳ `spaceId` | string | Space-ID |
| ↳ `authorId` | string | Konto-ID des Autors |
| ↳ `createdAt` | string | Erstellungszeitstempel |
| ↳ `version` | object | Versionsinformationen |
| ↳ `number` | number | Versionsnummer |
| ↳ `message` | string | Versionsnachricht |
| ↳ `minorEdit` | boolean | Ob dies eine geringfügige Bearbeitung ist |
| ↳ `authorId` | string | Konto-ID des Versionsautors |
| ↳ `createdAt` | string | ISO 8601-Zeitstempel der Versionserstellung |
| ↳ `body` | object | Blogbeitrags-Body-Inhalt |
| ↳ `storage` | object | Body im Speicherformat (Confluence-Markup) |
| ↳ `value` | string | Der Inhaltswert im angegebenen Format |
| ↳ `representation` | string | Inhaltsdarstellungstyp |
| ↳ `view` | object | Body im Ansichtsformat (gerendertes HTML) |
| ↳ `value` | string | Der Inhaltswert im angegebenen Format |
| ↳ `representation` | string | Inhaltsdarstellungstyp |
| ↳ `atlas_doc_format` | object | Body im Atlassian Document Format (ADF) |
| ↳ `value` | string | Der Inhaltswert im angegebenen Format |
| ↳ `representation` | string | Inhaltsdarstellungstyp |
| ↳ `webUrl` | string | URL zum Anzeigen des Blogbeitrags |
| `nextCursor` | string | Cursor zum Abrufen der nächsten Ergebnisseite |
### `confluence_create_comment`
Fügen Sie einen Kommentar zu einer Confluence-Seite hinzu.
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `domain` | string | Ja | Ihre Confluence-Domain (z.B. ihrfirma.atlassian.net) |
| `pageId` | string | Ja | Confluence-Seiten-ID, zu der ein Kommentar hinzugefügt werden soll |
| `comment` | string | Ja | Kommentartext im Confluence-Speicherformat |
| `cloudId` | string | Nein | Confluence Cloud-ID für die Instanz. Wenn nicht angegeben, wird sie über die Domain abgerufen. |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `ts` | string | Zeitstempel der Erstellung |
| `commentId` | string | Erstellte Kommentar-ID |
| `pageId` | string | Seiten-ID |
### `confluence_list_comments`
Listen Sie alle Kommentare auf einer Confluence-Seite auf.
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `domain` | string | Ja | Ihre Confluence-Domain (z.B. ihrfirma.atlassian.net) |
| `pageId` | string | Ja | Confluence-Seiten-ID, von der Kommentare aufgelistet werden sollen |
| `limit` | number | Nein | Maximale Anzahl der zurückzugebenden Kommentare (Standard: 25) |
| `bodyFormat` | string | Nein | Format für den Kommentartext: storage, atlas_doc_format, view oder export_view (Standard: storage) |
| `cursor` | string | Nein | Paginierungs-Cursor aus vorheriger Antwort |
| `cloudId` | string | Nein | Confluence Cloud-ID für die Instanz. Wenn nicht angegeben, wird sie über die Domain abgerufen. |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `ts` | string | ISO 8601-Zeitstempel der Operation |
| `comments` | array | Array von Confluence-Kommentaren |
| ↳ `id` | string | Eindeutige Kommentar-ID |
| ↳ `status` | string | Kommentarstatus (z. B. current) |
| ↳ `title` | string | Kommentartitel |
| ↳ `pageId` | string | ID der Seite, zu der der Kommentar gehört |
| ↳ `blogPostId` | string | ID des Blogposts, zu dem der Kommentar gehört |
| ↳ `parentCommentId` | string | ID des übergeordneten Kommentars |
| ↳ `body` | object | Kommentarinhalt |
| ↳ `value` | string | Kommentarinhalt |
| ↳ `representation` | string | Format der Inhaltsdarstellung (z. B. storage, view) |
| ↳ `createdAt` | string | ISO 8601-Zeitstempel der Kommentarerstellung |
| ↳ `authorId` | string | Account-ID des Kommentarautors |
| ↳ `version` | object | Versionsinformationen des Kommentars |
| ↳ `number` | number | Versionsnummer |
| ↳ `message` | string | Versionsnachricht |
| ↳ `minorEdit` | boolean | Ob es sich um eine geringfügige Bearbeitung handelt |
| ↳ `authorId` | string | Account-ID des Versionsautors |
| ↳ `createdAt` | string | ISO 8601-Zeitstempel der Versionserstellung |
| `nextCursor` | string | Cursor zum Abrufen der nächsten Ergebnisseite |
### `confluence_update_comment`
Aktualisiert einen vorhandenen Kommentar auf einer Confluence-Seite.
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `domain` | string | Ja | Ihre Confluence-Domain (z.B. ihrfirma.atlassian.net) |
| `commentId` | string | Ja | Confluence-Kommentar-ID zum Aktualisieren |
| `comment` | string | Ja | Aktualisierter Kommentartext im Confluence-Speicherformat |
| `cloudId` | string | Nein | Confluence Cloud-ID für die Instanz. Wenn nicht angegeben, wird sie über die Domain abgerufen. |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `ts` | string | Zeitstempel der Aktualisierung |
| `commentId` | string | Aktualisierte Kommentar-ID |
| `updated` | boolean | Aktualisierungsstatus |
### `confluence_delete_comment`
Löscht einen Kommentar von einer Confluence-Seite.
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `domain` | string | Ja | Ihre Confluence-Domain (z.B. ihrfirma.atlassian.net) |
| `commentId` | string | Ja | Confluence-Kommentar-ID zum Löschen |
| `cloudId` | string | Nein | Confluence Cloud-ID für die Instanz. Wenn nicht angegeben, wird sie über die Domain abgerufen. |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `ts` | string | Zeitstempel der Löschung |
| `commentId` | string | Gelöschte Kommentar-ID |
| `deleted` | boolean | Löschstatus |
### `confluence_upload_attachment`
Laden Sie eine Datei als Anhang zu einer Confluence-Seite hoch.
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `domain` | string | Ja | Ihre Confluence-Domain (z. B. ihrfirma.atlassian.net) |
| `pageId` | string | Ja | Confluence-Seiten-ID, an die die Datei angehängt werden soll |
| `file` | file | Ja | Die als Anhang hochzuladende Datei |
| `fileName` | string | Nein | Optionaler benutzerdefinierter Dateiname für den Anhang |
| `comment` | string | Nein | Optionaler Kommentar zum Anhang |
| `cloudId` | string | Nein | Confluence Cloud-ID für die Instanz. Wenn nicht angegeben, wird sie über die Domain abgerufen. |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `ts` | string | Zeitstempel des Uploads |
| `attachmentId` | string | Hochgeladene Anhangs-ID |
| `title` | string | Dateiname des Anhangs |
| `fileSize` | number | Dateigröße in Bytes |
| `mediaType` | string | MIME-Typ des Anhangs |
| `downloadUrl` | string | Download-URL für den Anhang |
| `pageId` | string | Seiten-ID, zu der der Anhang hinzugefügt wurde |
### `confluence_list_attachments`
Listen Sie alle Anhänge auf einer Confluence-Seite auf.
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `domain` | string | Ja | Ihre Confluence-Domain (z. B. ihrfirma.atlassian.net) |
| `pageId` | string | Ja | Confluence-Seiten-ID, von der Anhänge aufgelistet werden sollen |
| `limit` | number | Nein | Maximale Anzahl der zurückzugebenden Anhänge (Standard: 50, max: 250) |
| `cursor` | string | Nein | Paginierungs-Cursor aus vorheriger Antwort |
| `cloudId` | string | Nein | Confluence Cloud-ID für die Instanz. Wenn nicht angegeben, wird sie über die Domain abgerufen. |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `ts` | string | ISO 8601-Zeitstempel der Operation |
| `attachments` | array | Array von Confluence-Anhängen |
| ↳ `id` | string | Eindeutige Anhangs-ID (mit Präfix "att") |
| ↳ `title` | string | Dateiname des Anhangs |
| ↳ `status` | string | Anhangsstatus (z. B. current, archived, trashed) |
| ↳ `mediaType` | string | MIME-Typ des Anhangs |
| ↳ `fileSize` | number | Dateigröße in Bytes |
| ↳ `downloadUrl` | string | URL zum Herunterladen des Anhangs |
| ↳ `webuiUrl` | string | URL zum Anzeigen des Anhangs in der Confluence-Oberfläche |
| ↳ `pageId` | string | ID der Seite, zu der der Anhang gehört |
| ↳ `blogPostId` | string | ID des Blogbeitrags, zu dem der Anhang gehört |
| ↳ `comment` | string | Kommentar/Beschreibung des Anhangs |
| ↳ `version` | object | Versionsinformationen des Anhangs |
| ↳ `number` | number | Versionsnummer |
| ↳ `message` | string | Versionsnachricht |
| ↳ `minorEdit` | boolean | Ob es sich um eine geringfügige Bearbeitung handelt |
| ↳ `authorId` | string | Konto-ID des Versionsautors |
| ↳ `createdAt` | string | ISO 8601-Zeitstempel der Versionserstellung |
| `nextCursor` | string | Cursor zum Abrufen der nächsten Ergebnisseite |
### `confluence_delete_attachment`
Löschen eines Anhangs von einer Confluence-Seite (verschiebt ihn in den Papierkorb).
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `domain` | string | Ja | Ihre Confluence-Domain (z.B. ihrfirma.atlassian.net) |
| `attachmentId` | string | Ja | Confluence-Anhangs-ID zum Löschen |
| `cloudId` | string | Nein | Confluence Cloud-ID für die Instanz. Wenn nicht angegeben, wird sie über die Domain abgerufen. |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `ts` | string | Zeitstempel der Löschung |
| `attachmentId` | string | Gelöschte Anhangs-ID |
| `deleted` | boolean | Löschstatus |
### `confluence_list_labels`
Alle Labels auf einer Confluence-Seite auflisten.
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `domain` | string | Ja | Ihre Confluence-Domain (z.B. ihrfirma.atlassian.net) |
| `pageId` | string | Ja | Confluence-Seiten-ID, von der Labels aufgelistet werden sollen |
| `limit` | number | Nein | Maximale Anzahl der zurückzugebenden Labels (Standard: 25, max: 250) |
| `cursor` | string | Nein | Paginierungs-Cursor aus vorheriger Antwort |
| `cloudId` | string | Nein | Confluence Cloud-ID für die Instanz. Wenn nicht angegeben, wird sie über die Domain abgerufen. |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `ts` | string | Zeitstempel des Abrufs |
| `labels` | array | Array von Labels auf der Seite |
| ↳ `id` | string | Eindeutige Label-ID |
| ↳ `name` | string | Label-Name |
| ↳ `prefix` | string | Label-Präfix/Typ (z.B. global, my, team) |
| `nextCursor` | string | Cursor zum Abrufen der nächsten Ergebnisseite |
### `confluence_add_label`
Fügen Sie einer Confluence-Seite ein Label zur Organisation und Kategorisierung hinzu.
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `domain` | string | Ja | Ihre Confluence-Domain (z.B. ihrfirma.atlassian.net) |
| `pageId` | string | Ja | Confluence-Seiten-ID, zu der das Label hinzugefügt werden soll |
| `labelName` | string | Ja | Name des hinzuzufügenden Labels |
| `prefix` | string | Nein | Label-Präfix: global (Standard), my, team oder system |
| `cloudId` | string | Nein | Confluence Cloud-ID für die Instanz. Wenn nicht angegeben, wird sie über die Domain abgerufen. |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `ts` | string | ISO 8601-Zeitstempel der Operation |
| `pageId` | string | Seiten-ID, zu der das Label hinzugefügt wurde |
| `labelName` | string | Name des hinzugefügten Labels |
| `labelId` | string | ID des hinzugefügten Labels |
### `confluence_delete_label`
Entfernen Sie ein Label von einer Confluence-Seite.
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `domain` | string | Ja | Ihre Confluence-Domain (z.B. ihrfirma.atlassian.net) |
| `pageId` | string | Ja | Confluence-Seiten-ID, von der das Label entfernt werden soll |
| `labelName` | string | Ja | Name des zu entfernenden Labels |
| `cloudId` | string | Nein | Confluence Cloud-ID für die Instanz. Wenn nicht angegeben, wird sie über die Domain abgerufen. |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `ts` | string | ISO 8601-Zeitstempel der Operation |
| `pageId` | string | Seiten-ID, von der das Label entfernt wurde |
| `labelName` | string | Name des entfernten Labels |
| `deleted` | boolean | Löschstatus |
### `confluence_get_pages_by_label`
Ruft alle Seiten ab, auf die ein bestimmtes Label angewendet wurde.
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `domain` | string | Ja | Ihre Confluence-Domain (z. B. ihrfirma.atlassian.net) |
| `labelId` | string | Ja | Die ID des Labels, für das Seiten abgerufen werden sollen |
| `limit` | number | Nein | Maximale Anzahl der zurückzugebenden Seiten (Standard: 50, max: 250) |
| `cursor` | string | Nein | Paginierungs-Cursor aus vorheriger Antwort |
| `cloudId` | string | Nein | Confluence Cloud-ID für die Instanz. Wenn nicht angegeben, wird sie über die Domain abgerufen. |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `ts` | string | ISO 8601-Zeitstempel der Operation |
| `labelId` | string | ID des Labels |
| `pages` | array | Array von Seiten mit diesem Label |
| ↳ `id` | string | Eindeutige Seitenkennung |
| ↳ `title` | string | Seitentitel |
| ↳ `status` | string | Seitenstatus (z. B. current, archived, trashed, draft) |
| ↳ `spaceId` | string | ID des Spaces, der die Seite enthält |
| ↳ `parentId` | string | ID der übergeordneten Seite (null bei oberster Ebene) |
| ↳ `authorId` | string | Account-ID des Seitenautors |
| ↳ `createdAt` | string | ISO 8601-Zeitstempel der Seitenerstellung |
| ↳ `version` | object | Seitenversionsinformationen |
| ↳ `number` | number | Versionsnummer |
| ↳ `message` | string | Versionsnachricht |
| ↳ `minorEdit` | boolean | Ob dies eine geringfügige Bearbeitung ist |
| ↳ `authorId` | string | Account-ID des Versionsautors |
| ↳ `createdAt` | string | ISO 8601-Zeitstempel der Versionserstellung |
| `nextCursor` | string | Cursor zum Abrufen der nächsten Ergebnisseite |
### `confluence_list_space_labels`
Alle Labels auflisten, die mit einem Confluence-Space verknüpft sind.
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `domain` | string | Ja | Ihre Confluence-Domain (z.B. ihrfirma.atlassian.net) |
| `spaceId` | string | Ja | Die ID des Confluence-Space, von dem Labels aufgelistet werden sollen |
| `limit` | number | Nein | Maximale Anzahl der zurückzugebenden Labels (Standard: 25, max: 250) |
| `cursor` | string | Nein | Paginierungs-Cursor aus vorheriger Antwort |
| `cloudId` | string | Nein | Confluence Cloud-ID für die Instanz. Wenn nicht angegeben, wird sie über die Domain abgerufen. |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `ts` | string | ISO 8601-Zeitstempel der Operation |
| `spaceId` | string | ID des Space |
| `labels` | array | Array von Labels im Space |
| ↳ `id` | string | Eindeutige Label-ID |
| ↳ `name` | string | Label-Name |
| ↳ `prefix` | string | Label-Präfix/Typ (z.B. global, my, team) |
| `nextCursor` | string | Cursor zum Abrufen der nächsten Ergebnisseite |
### `confluence_get_space`
Details zu einem bestimmten Confluence-Space abrufen.
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `domain` | string | Ja | Ihre Confluence-Domain (z.B. ihrfirma.atlassian.net) |
| `spaceId` | string | Ja | Confluence-Space-ID zum Abrufen |
| `cloudId` | string | Nein | Confluence Cloud-ID für die Instanz. Wenn nicht angegeben, wird sie über die Domain abgerufen. |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `ts` | string | ISO 8601-Zeitstempel der Operation |
| `spaceId` | string | Space-ID |
| `name` | string | Space-Name |
| `key` | string | Space-Schlüssel |
| `type` | string | Space-Typ (global, personal) |
| `status` | string | Space-Status (current, archived) |
| `url` | string | URL zum Anzeigen des Space in Confluence |
| `authorId` | string | Account-ID des Space-Erstellers |
| `createdAt` | string | ISO 8601-Zeitstempel der Space-Erstellung |
| `homepageId` | string | ID der Space-Startseite |
| `description` | object | Space-Beschreibungsinhalt |
| ↳ `value` | string | Beschreibungstext |
| ↳ `representation` | string | Format der Inhaltsdarstellung (z. B. plain, view, storage) |
### `confluence_list_spaces`
Listet alle Confluence-Spaces auf, auf die der Benutzer zugreifen kann.
#### Eingabe
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `domain` | string | Ja | Ihre Confluence-Domain (z. B. ihrfirma.atlassian.net) |
| `limit` | number | Nein | Maximale Anzahl der zurückzugebenden Spaces (Standard: 25, max: 250) |
| `cursor` | string | Nein | Paginierungs-Cursor aus vorheriger Antwort |
| `cloudId` | string | Nein | Confluence Cloud-ID für die Instanz. Wenn nicht angegeben, wird sie über die Domain abgerufen. |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `ts` | string | ISO-8601-Zeitstempel der Operation |
| `spaces` | array | Array von Confluence-Spaces |
| ↳ `id` | string | Eindeutige Space-ID |
| ↳ `key` | string | Space-Schlüssel \(Kurzbezeichner für URLs\) |
| ↳ `name` | string | Space-Name |
| ↳ `type` | string | Space-Typ \(z. B. global, personal\) |
| ↳ `status` | string | Space-Status \(z. B. current, archived\) |
| ↳ `authorId` | string | Account-ID des Space-Erstellers |
| ↳ `createdAt` | string | ISO-8601-Zeitstempel der Space-Erstellung |
| ↳ `homepageId` | string | ID der Space-Startseite |
| ↳ `description` | object | Space-Beschreibung |
| ↳ `value` | string | Beschreibungstext |
| ↳ `representation` | string | Format der Inhaltsdarstellung \(z. B. plain, view, storage\) |
| `nextCursor` | string | Cursor zum Abrufen der nächsten Ergebnisseite |

View File

@@ -168,3 +168,15 @@ Löscht einen Cloud-Agenten dauerhaft. Diese Aktion kann nicht rückgängig gema
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `apiKey` | string | Ja | Cursor API-Schlüssel | | `apiKey` | string | Ja | Cursor API-Schlüssel |
| `agentId` | string | Ja | Eindeutige Kennung für den Cloud-Agenten \(z.B. bc_abc123\) | | `agentId` | string | Ja | Eindeutige Kennung für den Cloud-Agenten \(z.B. bc_abc123\) |
#### Ausgabe
| Parameter | Typ | Beschreibung |
| --------- | ---- | ----------- |
| `content` | string | Erfolgsmeldung |
| `metadata` | object | Ergebnis-Metadaten |
## Hinweise
- Kategorie: `tools`
- Typ: `cursor`

Some files were not shown because too many files have changed in this diff Show More