Skip to main content

add-block

Create or update a Sim integration block with correct subBlocks, conditions, dependsOn, modes, canonicalParamId usage, outputs, and tool wiring. Use when working on `apps/sim/blocks/blocks/{service}.ts` or aligning a block with its tools.

الانتقال إلى التثبيت

معلومات المصدر

المستودع
simstudioai/sim
آخر نشاط في المصدر
١١ سبتمبر ٢٠٢٦ في ١٩:٥٤
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٢٩٬٦٤٨
التفرعات
٣٬٨٣٤

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

مستكشف الملفات
2 ملفات

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
add-block
description
Create or update a Sim integration block with correct subBlocks, conditions, dependsOn, modes, canonicalParamId usage, outputs, and tool wiring. Use when working on `apps/sim/blocks/blocks/{service}.ts` or aligning a block with its tools.
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 ## No guessed tool outputs Block outputs mirror tool outputs. When a tool's response schema is neither documented nor live-verified, don't infer field names or JSON shapes — ask the user for sample responses or test credentials, limit the block to operations whose outputs are documented, or leave the uncertain outputs out and say exactly what remains unknown. ## Block Configuration Structure ```typescript import { {ServiceName}Icon } from '@/components/icons' import type { BlockConfig } from '@/blocks/types' import { AuthMode, IntegrationType } from '@/blocks/types' import { getScopesForService } from '@/lib/oauth/utils' 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/integrations/{service}', category: 'tools', // 'tools' | 'blocks' | 'triggers' integrationType: IntegrationType.X, // Primary category (see IntegrationType enum) tags: ['oauth', 'api'], // Cross-cutting tags (see IntegrationTag type) bgColor: '#HEXCOLOR', // Brand color icon: {ServiceName}Icon, // Auth mode authMode: AuthMode.OAuth, // or AuthMode.ApiKey // Card summary sentences — see "Canvas Sentences" below canvasPresentation: { defaultTitle: '{Default Operation}', sentences: { byOperation: { /* one per operation dropdown option id */ } }, }, 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 service key requiredScopes: getScopesForService('{service}'), // Import from @/lib/oauth/utils placeholder: 'Select account', required: true, } ``` **Scopes:** Always use `getScopesForService(serviceId)` from `@/lib/oauth/utils` for `requiredScopes`. Never hardcode scope arrays — the single source of truth is `OAUTH_PROVIDERS` in `lib/oauth/oauth.ts`. **Scope descriptions:** When adding a new OAuth provider, also add human-readable descriptions for all scopes in `SCOPE_DESCRIPTIONS` within `lib/oauth/utils.ts`. **Service accounts (shared, app-level credentials):** A plain `oauth-input` already lets users *select* an existing service account — those credentials fold into the picker automatically (a Google service account created for any Google service appears in every Google block's picker). You only set `credentialKind` when you want to change the *connect* action: ```typescript { id: 'credential', title: 'Account', type: 'oauth-input', serviceId: '{service}', requiredScopes: getScopesForService('{service}'), credentialKind: 'any', // omit | 'service-account' | 'any' } ``` - **omit (default):** lists OAuth accounts + any existing service accounts; the only connect action is "Connect account" (OAuth). Use this for the common "let users pick a service account someone set up elsewhere, but don't offer inline setup" case — no config needed. - **`'service-account'`:** service-account credentials *only*, plus an inline setup action that opens the provider's connect modal. Use when a block accepts *only* an app credential. - **`'any'`:** merged picker — OAuth accounts *and* service accounts in one grouped dropdown, with a connect action for each. Use when a block supports both (e.g. Slack: a personal account *or* a custom bot). Optional companions: `credentialLabels` (override the picker's section/connect-row copy) and `allowServiceAccounts: true` (trigger-mode only — list service accounts, which triggers otherwise exclude; set only when the trigger's polling path can resolve a service-account token). The connect modal, provider families (Google JSON key, Atlassian token, token-paste, client-credential, Slack bot), and the preview gate are all resolved from `serviceAccountProviderId` — you don't wire them per block. ### OAuth deployment availability (required for integration blocks) A visible tools-category block with OAuth is deployment-gated. Its `oauth-input.serviceId` is projected into `packages/deployment-config/src/integrations.json`, then resolved through `resolveOAuthClientCapabilityId()` in `packages/deployment-config/src/env-capabilities.ts`. When adding or changing an OAuth integration block: 1. Keep exactly one distinct OAuth `serviceId` across the block's `oauth-input` subBlocks. 2. Confirm that service ID resolves to an entry in `OAUTH_CLIENT_CAPABILITIES`. Google and Microsoft service IDs intentionally share their provider-level capability; do not add duplicate entries for those aliases. 3. For a new capability, add its required client fields to `OAUTH_CLIENT_CAPABILITIES` and ensure every referenced field exists in the env schema in `apps/sim/lib/core/config/env.ts`. Then add the matching `text` or `secret` input modes to `OAUTH_CLIENT_SETUP_FIELDS` in `packages/sim-setup/src/capability-config.ts`. The CLI catalog is exhaustively typed and checked against the runtime field list; do not infer secrecy from the field name. 4. If the canonical OAuth service declares `serviceAccountProviderId`, run `bun run deployment-config:generate`; this regenerates the provider-ID facts in `packages/deployment-config/src/service-account-providers.generated.ts`. Never hand-edit that generated map. Add `deploymentRequirement` policy in `packages/deployment-config/src/service-account-metadata.ts` only when the service-account path is preview-gated or depends on the OAuth client fields; otherwise omit it. Missing capability metadata is a runtime configuration error, not a reason to make the integration silently available. ### Selectors (with dynamic options) ```typescript // Channel selector (Slack, Discord, etc.) { id: 'channel', title: 'Channel', type: 'channel-selector', selectorKey: '{service}.channels', serviceId: '{service}', placeholder: 'Select channel', dependsOn: ['credential'], } // Project selector (Jira, etc.) { id: 'project', title: 'Project', type: 'project-selector', selectorKey: '{service}.projects', serviceId: '{service}', dependsOn: ['credential'], } // File selector (Google Drive, etc.) { id: 'file', title: 'File', type: 'file-selector', selectorKey: '{service}.files', serviceId: '{service}', mimeType: 'application/pdf', dependsOn: ['credential'], } // User selector { id: 'user', title: 'User', type: 'user-selector', selectorKey: '{service}.users', 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' }, }, ``` **Keep the pair to one logical thing.** Basic is the file upload, advanced is *only* a reference to a file from a previous block. Gmail attachments are the reference implementation (`apps/sim/blocks/blocks/gmail.ts` — `attachmentFiles` / `attachments`). Do not overload the advanced side with alternate identifiers (a remote URL, a provider asset ID, a path). A subblock whose meaning changes based on what the string looks like is impossible to reason about, forces the params function to sniff the value, and makes the field's type meaningless. Give each alternative its own subblock outside the pair: ```typescript // ✓ Good — the pair is "a file"; other sources are their own fields { id: 'mediaFile', type: 'file-upload', canonicalParamId: 'media', mode: 'basic' }, { id: 'mediaFileRef', type: 'short-input', canonicalParamId: 'media', mode: 'advanced' }, { id: 'mediaId', type: 'short-input', mode: 'advanced' }, // separate concept { id: 'mediaLink', type: 'short-input', mode: 'advanced' }, // separate concept // ✗ Bad — one field meaning three things, resolved by guessing { id: 'mediaRef', type: 'short-input', canonicalParamId: 'media', mode: 'advanced', placeholder: 'File reference, media ID, or public URL' }, ``` When several fields are mutually exclusive alternatives, mark them all `required: false` and enforce "exactly one" at execution — a conditionally-required canonical pair rejects the workflow before the other paths ever get a chance to supply the value. **Constraints (block-wide):** - `canonicalParamId` must not equal any subblock `id` in the block. - One canonical id links exactly one basic/advanced pair for one logical parameter. Groups are keyed by canonical id across every subblock and hold one `basicId`, so two operations that each need a pair need two canonical ids. - All members of a group share the same `required` status. ### Normalizing File Input in tools.config Put the normalization in `tools.config.params`, never in `tools.config.tool` — `tool` runs at serialization, before variable resolution, so a `<block.output>` file reference is not yet a value there. ```typescript import { normalizeFileInput } from '@/blocks/utils' tools: { access: ['service_upload'], config: { tool: (params) => `service_${params.operation}`, params: (params) => { // Read the CANONICAL id, not the subblock ids const { file: fileParam, ...rest } = params const file = normalizeFileInput(fileParam, { single: true }) return { ...rest, ...(file ? { file } : {}), } }, }, } ``` **Where the value actually lives at runtime.** The subblock `id` is where the UI *stores* the value, but it is not what the params function receives. `extractBlockParams` (`apps/sim/serializer/index.ts`) collapses each canonical group at serialization time: ```typescript const sourceIds = [group.basicId, ...group.advancedIds].filter(Boolean) sourceIds.forEach((id) => delete params[id]) // subblock ids are deleted if (chosen !== undefined) params[group.canonicalId] = chosen ``` So by the time `tools.config.params(inputs)` runs (`executor/handlers/generic/generic-handler.ts`),
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub